AJA NTV2 SDK  17.5.0.1242
NTV2 SDK 17.5.0.1242
ntv2mcsfile.cpp
Go to the documentation of this file.
1 /* SPDX-License-Identifier: MIT */
7 #include "ntv2mcsfile.h"
8 #include "ntv2bitfile.h"
9 #include "ajabase/system/debug.h"
10 #include <iostream>
11 #include <sys/stat.h>
12 #include <assert.h>
13 #include <algorithm>
14 #if defined (AJALinux) || defined (AJAMac)
15  #include <arpa/inet.h>
16  #include "string.h"
17 #endif
18 #include "ntv2utils.h"
19 #include <string.h>
20 #include <fcntl.h>
21 #if !defined (AJAMac) && !defined (AJALinux) && !defined(AJABareMetal)
22  #include <io.h>
23 #endif
24 #include <time.h>
25 using namespace std;
26 
27 
28 
30 {
31  Close(); // Reset everything
32 }
33 
35 {
36  Close();
37 }
38 
39 
41 {
42  if (mMCSFileStream.is_open())
43  mMCSFileStream.close();
44  mFileSize = 0;
45  mFileLines.clear();
46  mBaseELARLocation = mCurrentLocation = mFileLines.end();
47  mCommentString = mMCSInfoString = mCurrentRecord = mBitfileDate = mBitfileTime = mBitfileDesignName = mBitfilePartName = "";
48 }
49 
50 
51 bool CNTV2MCSfile::isReady (void) const
52 {
53  return true;
54 // if (mFileLines.size() > 0)
55 // return true;
56 // else
57 // return false;
58 }
59 
60 void CNTV2MCSfile::SetLastError (const string & inStr, const bool inAppend)
61 {
63  if (inAppend)
64  {
65  if (!mLastError.empty())
66  mLastError += "\n";
67  mLastError += inStr;
68  }
69  else
70  mLastError = inStr;
71 }
72 
73 bool CNTV2MCSfile::Open (const string & inMCSFileName)
74 {
75  Close();
76  struct stat fsinfo;
77  ::stat(inMCSFileName.c_str(), &fsinfo);
78  mFileSize = uint32_t(fsinfo.st_size);
79 
80  struct tm * fileTimeInfo = localtime(&fsinfo.st_ctime);
81 
82  time_t rawGenerationTime;
83  time(&rawGenerationTime);
84  struct tm * generationTimeInfo = localtime(&rawGenerationTime);
85  ostringstream comment;
86  comment << "Generation Time: " << asctime(generationTimeInfo) << " Original MCS Time: " << asctime(fileTimeInfo) << endl;
87  mCommentString = comment.str();
88 
89  mMCSFileStream.open(inMCSFileName.c_str(), std::ios::in);
90  if (mMCSFileStream.fail())
91  return false;
92 
93  GetFileByteStream();
94  GetMCSInfo();
95 
96  if (mMCSFileStream.is_open())
97  mMCSFileStream.close();
98  return true;
99 
100 } // Open
101 
102 
103 bool CNTV2MCSfile::GetMCSHeaderInfo (const string & inMCSFileName)
104 {
105  Close();
106  mMCSFileStream.open(inMCSFileName.c_str(), std::ios::in);
107  if (mMCSFileStream.fail())
108  return false;
109 
110  GetFileByteStream(50);
111  GetMCSInfo();
112  return true;
113 }
114 
115 
116 void CNTV2MCSfile::GetMCSInfo()
117 {
118  uint16_t mainPartitionAddress = 0x0000, mainPartitionOffset = 0x0000;
119  UByteSequence mainBitfilePartition;
120  GetPartition(mainBitfilePartition, mainPartitionAddress, mainPartitionOffset, false);
121  if (mainBitfilePartition.size() > 0)
122  {
123  CNTV2Bitfile bitfileInfo;
124  bitfileInfo.ParseHeaderFromBuffer(&mainBitfilePartition[0], mainBitfilePartition.size());
125  mBitfileDate = bitfileInfo.GetDate();
126  mBitfileDesignName = bitfileInfo.GetDesignName();
127  mBitfilePartName = bitfileInfo.GetPartName();
128  mBitfileTime = bitfileInfo.GetTime();
129  }
130 
131  mMCSInfoString = mFileLines[0];
132 }
133 
134 
136 {
137  const size_t pkgNumPos(mMCSInfoString.find("PACKAGE_NUMBER"));
138  const size_t datePos(mMCSInfoString.find("DATE"));
139  if (pkgNumPos == string::npos || datePos == string::npos)
140  return "";
141  if (datePos <= pkgNumPos)
142  return "";
143  return mMCSInfoString.substr(pkgNumPos, datePos - pkgNumPos - 1);
144 }
145 
147 {
148  const size_t datePos(mMCSInfoString.find("DATE"));
149  if (datePos == string::npos)
150  return "";
151  return mMCSInfoString.substr(datePos + 5, mMCSInfoString.npos - datePos + 5);
152 }
153 
154 
155 bool CNTV2MCSfile::InsertBitFile (const string & inBitFileName, const string & inMCSFileName, const string & inUserMessage)
156 {
157  CNTV2Bitfile bitfile;
158  CNTV2MCSfile mcsFile;
159  ostringstream oss;
160  char iRecord[100];
161  uint64_t recordSize = 0;
162  UWord baseAddress = 0x0000;
163  UWord ExtendedBaseAddress = 0x0000;
164  UByte checksum = 0;
165  UByte recordType = 0x00;
166 
167  if (!Open(inMCSFileName))
168  {
169  oss << "CNTV2MCSfile::InsertBitFile: mcsFile '" << inMCSFileName << "' not found";
170  SetLastError(oss.str());
171  return false;
172  }
173 
174  //Read in the bitfile
175  if (!bitfile.Open(inBitFileName))
176  {
177  oss << "CNTV2MCSfile::InsertBitFile: Bitfile '" << inBitFileName << "' not found";
178  SetLastError(oss.str());
179  return false;
180  }
181 
182  const size_t bitfileLength(bitfile.GetFileStreamLength());
183  NTV2Buffer bitfileBuffer(bitfileLength + 512);
184  if (!bitfileBuffer)
185  {
186  oss << "CNTV2MCSfile::InsertBitFile: Unable to allocate " << DEC(bitfileLength+512) << "-byte bitfile buffer";
187  SetLastError(oss.str());
188  return false;
189  }
190 
191  bitfileBuffer.Fill(0xFFFFFF);
192  const size_t readBytes(bitfile.GetFileByteStream(bitfileBuffer));
193  const string designName(bitfile.GetDesignName());
194  if (readBytes != bitfileLength)
195  {
196  oss << "CNTV2MCSfile::InsertBitFile: Invalid bitfile length, read " << readBytes << " bytes, expected " << bitfileLength;
197  SetLastError(oss.str());
198  return false;
199  }
200 
201  // First, write out the bitfile, then add the date and comment, and then the mcs file...
202  size_t bitfileBufferNdx(0);
203  uint64_t bytesLeftToWrite (bitfileLength);
204  while (bytesLeftToWrite)
205  {
206  recordSize = bytesLeftToWrite > 16 ? 16 : bytesLeftToWrite;
207  int i = 0;
208  int index = 0;
209  checksum = 0;
210 
211  if (baseAddress == 0x0000)
212  {
213  //Insert ELAR
214  string ELARString(":02000004000000");
215  ::sprintf(&ELARString[9], "%04X", ExtendedBaseAddress);
216  for (i = 1; i < 13; i++)
217  checksum += UByte(ELARString[i]) - 0x30;
218  checksum = (~checksum) + 1;
219  ::sprintf(&ELARString[13], "%02X", checksum);
220  IRecordOutput(ELARString.c_str());
221  ExtendedBaseAddress++;
222  checksum = 0;
223  }
224 
225  iRecord[0] = ':';
226 
227  ::sprintf(&iRecord[1], "%02X", UByte(recordSize));
228  checksum += UByte(recordSize);
229 
230  UWord addr = baseAddress;
231  UByte aa = ((addr >> 8) & 0xff);
232  ::sprintf(&iRecord[3], "%02X", aa);
233  checksum += aa;
234 
235  aa = ((addr)& 0xff);
236  ::sprintf(&iRecord[5], "%02X", aa);
237  checksum += aa;
238 
239  ::sprintf(&iRecord[7], "%02X", recordType);
240 
241  index = 9;
242 
243  while (i < int(recordSize))
244  {
245  unsigned char dd = bitfileBuffer.U8(int(bitfileBufferNdx++));
246  ::sprintf(&iRecord[index], "%02X", dd);
247  checksum += dd;
248  i++;
249  index += 2;
250  bytesLeftToWrite--;
251  }
252 
253  baseAddress += 0x0010;
254  checksum = (~checksum) + 1;
255  ::sprintf(&iRecord[index], "%02X", checksum);
256 
257  IRecordOutput(iRecord);
258  } // while bytesLeftToWrite > 0
259 
260  //Insert the date at the 3rd to last partition in Bank 2
261  if (!inUserMessage.empty())
262  mCommentString.append(inUserMessage);
263  uint32_t commentSize = static_cast<uint32_t>(mCommentString.length());
264  bytesLeftToWrite = commentSize;
265  //32M - 3*256*1024
266  ExtendedBaseAddress = 0x01f4;
267  baseAddress = 0x0000;
268  uint32_t commentIndex = 0;
269  while (bytesLeftToWrite > 0)
270  {
271  recordSize = bytesLeftToWrite > 16 ? 16 : bytesLeftToWrite;
272  int i = 0;
273  int index = 0;
274  checksum = 0;
275 
276  if (ExtendedBaseAddress == 0x01f4)
277  {
278  //Insert ELAR
279  string ELARString(":02000004000000");
280  ::sprintf(&ELARString[9], "%04X", ExtendedBaseAddress);
281  for (i = 1; i < 13; i++)
282  checksum += ELARString[i] - 0x30;
283  checksum = (~checksum) + 1;
284  ::sprintf(&ELARString[13], "%02X", checksum);
285  IRecordOutput(ELARString.c_str());
286  ExtendedBaseAddress++;
287  checksum = 0;
288  }
289 
290  iRecord[0] = ':';
291 
292  ::sprintf(&iRecord[1], "%02X", UByte(recordSize));
293  checksum += UByte(recordSize);
294 
295  UWord addr = baseAddress;
296  UByte aa = ((addr >> 8) & 0xff);
297  ::sprintf(&iRecord[3], "%02X", aa);
298  checksum += aa;
299 
300  aa = ((addr)& 0xff);
301  ::sprintf(&iRecord[5], "%02X", aa);
302  checksum += aa;
303 
304  ::sprintf(&iRecord[7], "%02X", recordType);
305 
306  index = 9;
307  while (i < int(recordSize))
308  {
309  unsigned char dd = mCommentString.at(commentIndex);
310  ::sprintf(&iRecord[index], "%02X", dd);
311  checksum += dd;
312  i++;
313  index += 2;
314  bytesLeftToWrite--;
315  commentIndex++;
316  }
317 
318  baseAddress += 0x0010;
319  checksum = (~checksum) + 1;
320  ::sprintf(&iRecord[index], "%02X", checksum);
321  IRecordOutput(iRecord);
322  }
323 
324  //Finished with bitfile now just read a line and output a line from the mcs file
325  //32M offset is assumed to be the start of SOC stuff
326  if (!FindExtendedLinearAddressRecord(0x0200))
327  {
328  SetLastError("FindExtendedLinearAddressRecord failed");
329  return false;
330  }
331  mCurrentLocation = mBaseELARLocation;
332  while (mCurrentLocation != mFileLines.end())
333  {
334  IRecordOutput(mCurrentLocation->c_str());
335  mCurrentLocation++;
336  }
337  return true;
338 }
339 
340 
341 void CNTV2MCSfile::IRecordOutput (const char *pIRecord)
342 {
343  //_setmode(_fileno(stdout), _O_U8TEXT);
344  ::printf("%s\n", pIRecord);
345 }
346 
347 
348 // Get maximum of fileLength bytes worth of configuration stream data in buffer.
349 // Return value:
350 // number of bytes of data copied into buffer. This can be <= bufferLength
351 // zero means configuration stream has finished.
352 // Return value < 0 indicates error
353 uint32_t CNTV2MCSfile::GetFileByteStream (uint32_t numberOfLines)
354 {
355  const uint32_t maxNumLines = 2000000;
356  string line;
357 
358  if (!mMCSFileStream.is_open())
359  return 0;
360 
361  mMCSFileStream.seekg(0, std::ios::beg);
362  if (numberOfLines == 0)
363  {
364  mFileLines.resize(maxNumLines);
365  numberOfLines = maxNumLines;
366  }
367  else
368  mFileLines.resize(numberOfLines+1);
369 
370  uint32_t index = 0;
371  mMCSFileStream.sync_with_stdio(false);
372  while (getline(mMCSFileStream, line) && index < numberOfLines)
373  {
374  mFileLines[index] = line;
375  index++;
376  }
377  if (numberOfLines < maxNumLines)
378  mFileLines[index] = ":00000001FF";
379  return mFileSize;
380 
381 } // GetFileByteStream
382 
383 
384 bool CNTV2MCSfile::FindExtendedLinearAddressRecord (uint16_t address /*= 0x0000*/)
385 {
386  string ELARString(":02000004000000");
387  ::sprintf(&ELARString[9], "%04X", address);
388  uint8_t checksum = 0;
389  for (int i = 1; i < 13; i++)
390  checksum += ELARString[i] - 0x30;
391  checksum = (~checksum) + 1;
392  ::sprintf(&ELARString[13], "%02X", checksum);
393 
394  // Do a search for a match, don't search on the checksum
395  string needle(ELARString, 0, 13);
396  NTV2StringListIter it = mFileLines.begin();
397  mBaseELARLocation = mFileLines.end();
398  while (it != mFileLines.end())
399  {
400  string hay(*it, 0, 13);
401  if (needle == hay)
402  {
403  mBaseELARLocation = it;
404  break;
405  }
406  ++it;
407  }
408  return mBaseELARLocation != mFileLines.end();
409 }
410 
411 
413 {
414  IntelRecordInfo currentRecordInfo;
415  bool status = ParseCurrentRecord(currentRecordInfo);
416  if (status)
417  {
418  recordInfo.byteCount = currentRecordInfo.byteCount;
419  recordInfo.recordType = currentRecordInfo.recordType;
420  recordInfo.linearAddress = currentRecordInfo.linearAddress;
421  memcpy(recordInfo.dataBuffer, currentRecordInfo.dataBuffer, 16);
422  recordInfo.checkSum = currentRecordInfo.checkSum;
423  }
424  return status;
425 }
426 
427 
428 bool CNTV2MCSfile::ParseCurrentRecord (IntelRecordInfo & recordInfo)
429 {
430  if (mCurrentLocation->size() == 0)
431  {
432  recordInfo.recordType = IRT_UNKNOWN;
433  return false;
434  }
435 
436  if (!mCurrentLocation[0].compare(":"))
437  {
438  recordInfo.recordType = IRT_UNKNOWN;
439  return false;
440  }
441 
442  uint32_t rType = 0;
443  uint16_t byteCount16 = 0;
444  ::sscanf(mCurrentLocation[0].c_str(), ":%02hX%04hX%02X", &byteCount16, &recordInfo.linearAddress, &rType);
445  recordInfo.byteCount = uint8_t(byteCount16);
446  recordInfo.segmentAddress = 0; //Fix this for the correct base address
447  switch (rType)
448  {
449  default: recordInfo.recordType = IRT_UNKNOWN; break;
450  case 0x00: recordInfo.recordType = IRT_DR; break;
451  case 0x01: recordInfo.recordType = IRT_EOFR; break;
452  case 0x02: recordInfo.recordType = IRT_ESAR; break;
453 
454  case 0x04: recordInfo.recordType = IRT_ELAR;
455  ::sscanf(mCurrentLocation[0].c_str(), ":%02hX%04hX%02X%04hX", &byteCount16, &recordInfo.linearAddress, &rType, &recordInfo.linearAddress);
456  recordInfo.byteCount = uint8_t(byteCount16);
457  break;
458  }
459  return true;
460 }
461 
462 
463 uint32_t CNTV2MCSfile::GetPartition (UByteSequence & partitionBuffer, uint16_t baseELARaddress, uint16_t & partitionOffset, bool nextPartition)
464 {
465  if (!isReady())
466  return 0;
467  IntelRecordInfo recordInfo;
468 
469  if (!nextPartition)
470  {
471  //32M offset is assumed to be the start of SOC stuff
472  if (!FindExtendedLinearAddressRecord(baseELARaddress))
473  return 0;
474  mCurrentLocation = mBaseELARLocation;
475  }
476  else
477  {
478  ParseCurrentRecord(recordInfo);
479  baseELARaddress = recordInfo.linearAddress;
480  mBaseELARLocation = mCurrentLocation;
481  }
482 
483  uint16_t lastELARAddress = baseELARaddress;
484  mCurrentLocation++;
485  ParseCurrentRecord(recordInfo);
486  if (recordInfo.recordType == IRT_DR)
487  partitionOffset = recordInfo.linearAddress;
488  while (recordInfo.recordType == IRT_DR)
489  {
490  string temp(mCurrentLocation[0].c_str());
491  for (int i(0); i < recordInfo.byteCount*2; i+=2)
492  {
493  uint32_t c = 0;
494  ::sscanf(&mCurrentLocation[0].c_str()[9 + i], "%02X", &c);
495  partitionBuffer.push_back(uint8_t(c & 0x000000FF));
496  }
497  mCurrentLocation++;
498  ParseCurrentRecord(recordInfo);
499  if (recordInfo.recordType == IRT_DR)
500  continue;
501  //We need to check if this is another ELAR with the same partition
502  if (recordInfo.recordType == IRT_ELAR)
503  {
504  //if this is part of last packet
505  lastELARAddress++;
506  if (recordInfo.linearAddress == lastELARAddress)
507  {
508  mCurrentLocation++;
509  ParseCurrentRecord(recordInfo);
510  continue;
511  }
512  return uint32_t(partitionBuffer.size()); //We are at the next partition
513  }
514  return uint32_t(partitionBuffer.size());
515  }
516  return uint32_t(partitionBuffer.size());
517 }
IntelRecordInfo::dataBuffer
uint8_t * dataBuffer[16]
Definition: ntv2mcsfile.h:43
CNTV2MCSfile::CNTV2MCSfile
CNTV2MCSfile()
My constructor.
Definition: ntv2mcsfile.cpp:29
IRT_UNKNOWN
@ IRT_UNKNOWN
Definition: ntv2mcsfile.h:22
IntelRecordInfo::segmentAddress
uint32_t segmentAddress
Definition: ntv2mcsfile.h:41
CNTV2MCSfile::GetFileByteStream
virtual uint32_t GetFileByteStream(uint32_t numberOfLines=0)
Definition: ntv2mcsfile.cpp:353
NTV2Buffer
A generic user-space buffer object that has an address and a length. Used most often to share an arbi...
Definition: ntv2publicinterface.h:6022
CNTV2MCSfile::Open
virtual bool Open(const std::string &inMCSFilePath)
Opens the bitfile at the given path, then parses its header.
Definition: ntv2mcsfile.cpp:73
IntelRecordInfo::checkSum
uint8_t checkSum
Definition: ntv2mcsfile.h:44
CNTV2Bitfile
Instances of me can parse a bitfile.
Definition: ntv2bitfile.h:86
IRT_ESAR
@ IRT_ESAR
Definition: ntv2mcsfile.h:25
CNTV2MCSfile::GetPartition
virtual uint32_t GetPartition(UByteSequence &patitionBuffer, uint16_t baseELARaddress, uint16_t &partitionOffset, bool nextPartition=false)
Definition: ntv2mcsfile.cpp:463
UByteSequence
std::vector< uint8_t > UByteSequence
An ordered sequence of UByte (uint8_t) values.
Definition: ntv2publicinterface.h:38
CNTV2MCSfile::FindExtendedLinearAddressRecord
virtual bool FindExtendedLinearAddressRecord(uint16_t address=0x0000)
Definition: ntv2mcsfile.cpp:384
NTV2StringListIter
NTV2StringList::iterator NTV2StringListIter
Definition: ntv2utils.h:1144
CNTV2Bitfile::GetFileStreamLength
virtual size_t GetFileStreamLength(void) const
Definition: ntv2bitfile.h:215
CNTV2MCSfile::GetMCSHeaderInfo
virtual bool GetMCSHeaderInfo(const std::string &inMCSFileName)
Definition: ntv2mcsfile.cpp:103
CNTV2MCSfile::IRecordOutput
virtual void IRecordOutput(const char *pIRecord)
Definition: ntv2mcsfile.cpp:341
CNTV2MCSfile::InsertBitFile
virtual bool InsertBitFile(const std::string &inBitFileName, const std::string &inMCSFileName, const std::string &inUserMessage)
Definition: ntv2mcsfile.cpp:155
CNTV2Bitfile::GetTime
virtual const std::string & GetTime(void) const
Definition: ntv2bitfile.h:134
IntelRecordInfo::byteCount
uint8_t byteCount
Definition: ntv2mcsfile.h:39
ntv2mcsfile.h
Declares the CNTV2MCSfile class.
IntelRecordInfo::linearAddress
uint16_t linearAddress
Definition: ntv2mcsfile.h:40
CNTV2Bitfile::Open
virtual bool Open(const std::string &inBitfilePath)
Opens the bitfile at the given path, then parses its header.
Definition: ntv2bitfile.cpp:322
CNTV2Bitfile::GetFileByteStream
virtual size_t GetFileByteStream(NTV2Buffer &outBuffer)
Retrieves the file bitstream.
Definition: ntv2bitfile.cpp:421
IRT_DR
@ IRT_DR
Definition: ntv2mcsfile.h:23
CNTV2MCSfile::GetMCSPackageVersionString
virtual std::string GetMCSPackageVersionString(void) const
Definition: ntv2mcsfile.cpp:135
CNTV2MCSfile
Instances of me can parse an MCS file.
Definition: ntv2mcsfile.h:51
CNTV2MCSfile::GetMCSPackageDateString
virtual std::string GetMCSPackageDateString(void) const
Definition: ntv2mcsfile.cpp:146
CNTV2MCSfile::GetCurrentParsedRecord
virtual bool GetCurrentParsedRecord(IntelRecordInfo &recordInfo)
Definition: ntv2mcsfile.cpp:412
AJA_DebugUnit_Firmware
@ AJA_DebugUnit_Firmware
Definition: debugshare.h:104
CNTV2Bitfile::GetDesignName
virtual std::string GetDesignName(void) const
Definition: ntv2bitfile.h:139
UWord
uint16_t UWord
Definition: ajatypes.h:253
ntv2utils.h
Declares numerous NTV2 utility functions.
CNTV2MCSfile::SetLastError
virtual void SetLastError(const std::string &inStr, const bool inAppend=false)
Definition: ntv2mcsfile.cpp:60
IntelRecordInfo::recordType
IntelRecordType recordType
Definition: ntv2mcsfile.h:42
NTV2Buffer::U8
uint8_t U8(const int inIndex) const
Definition: ntv2publicinterface.h:6536
ntv2bitfile.h
Declares the CNTV2Bitfile class.
DEC
#define DEC(__x__)
Definition: ntv2publicinterface.h:5606
UByte
uint8_t UByte
Definition: ajatypes.h:250
IRT_ELAR
@ IRT_ELAR
Definition: ntv2mcsfile.h:26
CNTV2Bitfile::GetDate
virtual const std::string & GetDate(void) const
Definition: ntv2bitfile.h:129
std
Definition: json.hpp:5362
AJA_sERROR
#define AJA_sERROR(_index_, _expr_)
Definition: debug.h:176
CNTV2MCSfile::isReady
virtual bool isReady(void) const
Definition: ntv2mcsfile.cpp:51
IRT_EOFR
@ IRT_EOFR
Definition: ntv2mcsfile.h:24
CNTV2MCSfile::~CNTV2MCSfile
virtual ~CNTV2MCSfile()
My destructor.
Definition: ntv2mcsfile.cpp:34
NTV2Buffer::Fill
bool Fill(const T &inValue)
Fills me with the given scalar value.
Definition: ntv2publicinterface.h:6248
CNTV2Bitfile::ParseHeaderFromBuffer
virtual std::string ParseHeaderFromBuffer(const uint8_t *inBitfileBuffer, const size_t inBufferSize)
Parse a bitfile header that's stored in a buffer.
Definition: ntv2bitfile.cpp:361
IntelRecordInfo
Definition: ntv2mcsfile.h:30
CNTV2Bitfile::GetPartName
virtual const std::string & GetPartName(void) const
Definition: ntv2bitfile.h:144
debug.h
Declares the AJADebug class.
CNTV2MCSfile::Close
virtual void Close(void)
Closes mcs file (if open).
Definition: ntv2mcsfile.cpp:40