1 #include "stdafx.h"
2 #include "RFXComSerial.h"
3 #include "../main/Logger.h"
4 #include "../main/Helper.h"
5 #include "../main/mainworker.h"
6 #include "../main/localtime_r.h"
7 #include "../main/SQLHelper.h"
8 #include "../main/WebServer.h"
9 #include "../webserver/cWebem.h"
10 #include <json/json.h>
11 
12 #include <string>
13 #include <algorithm>
14 #include <iostream>
15 #include <boost/bind.hpp>
16 
17 #include <ctime>
18 
19 #ifndef WIN32
20 #include <sys/stat.h>
21 #include <unistd.h>
22 #include <sys/types.h>
23 #include <pwd.h>
24 #endif
25 
26 #define RETRY_DELAY 30
27 
28 #define RFX_WRITE_DELAY 300
29 
30 extern std::string szStartupFolder;
31 
32 #define round(a) ( int ) ( a + .5 )
33 
34 const unsigned char PKT_STX = 0x55;
35 const unsigned char PKT_ETX = 0x04;
36 const unsigned char PKT_DLE = 0x05;
37 
38 #define PKT_writeblock 256
39 #define PKT_readblock 4
40 #define PKT_eraseblock 2048
41 #define PKT_maxpacket 261
42 #define PKT_bytesperaddr 2
43 
44 #define PKT_pmrangelow	0x001800
45 #define PKT_pmrangehigh	0x00A7FF
46 
47 #define PKT_pmrangelow868	0x001000
48 #define PKT_pmrangehigh868 0x0147FF
49 
50 #define PKT_userresetvector 0x100
51 #define PKT_bootdelay 0x102
52 
53 #define COMMAND_WRITEPM 2
54 #define COMMAND_ERASEPM 3
55 
56 const unsigned char PKT_STARTBOOT[5] = { 0x01, 0x01, 0x00, 0x00, 0xFF };
57 const unsigned char PKT_RESET[2] = { 0x00, 0x00 };
58 const unsigned char PKT_VERSION[2] = { 0x00, 0x02 };
59 const unsigned char PKT_VERIFY_OK[5] = { 0x08, 0x01, 0x00, 0x00, 0x00 };
60 
61 //
62 //Class RFXComSerial
63 //
RFXComSerial(const int ID,const std::string & devname,unsigned int baud_rate,const _eRFXAsyncType AsyncType)64 RFXComSerial::RFXComSerial(const int ID, const std::string& devname, unsigned int baud_rate, const _eRFXAsyncType AsyncType) :
65 	m_szSerialPort(devname)
66 {
67 	m_HwdID = ID;
68 	m_iBaudRate = baud_rate;
69 
70 	m_AsyncType = AsyncType;
71 
72 	m_bReceiverStarted = false;
73 	m_bInBootloaderMode = false;
74 	m_bStartFirmwareUpload = false;
75 	m_FirmwareUploadPercentage = 0;
76 	m_bHaveRX = false;
77 	m_rx_tot_bytes = 0;
78 	m_retrycntr = RETRY_DELAY;
79 
80 	m_serial.setPort(m_szSerialPort);
81 	m_serial.setBaudrate(m_iBaudRate);
82 	m_serial.setBytesize(serial::eightbits);
83 	m_serial.setParity(serial::parity_none);
84 	m_serial.setStopbits(serial::stopbits_one);
85 	m_serial.setFlowcontrol(serial::flowcontrol_none);
86 
87 	serial::Timeout stimeout = serial::Timeout::simpleTimeout(200);
88 	m_serial.setTimeout(stimeout);
89 }
90 
~RFXComSerial()91 RFXComSerial::~RFXComSerial()
92 {
93 
94 }
95 
StartHardware()96 bool RFXComSerial::StartHardware()
97 {
98 	RequestStart();
99 
100 	//return OpenSerialDevice();
101 	//somehow retry does not seem to work?!
102 	m_bReceiverStarted = false;
103 
104 	m_retrycntr = RETRY_DELAY; //will force reconnect first thing
105 
106 	//Start worker thread
107 	m_thread = std::make_shared<std::thread>(&RFXComSerial::Do_Work, this);
108 	SetThreadNameInt(m_thread->native_handle());
109 
110 	return (m_thread != nullptr);
111 
112 }
113 
StopHardware()114 bool RFXComSerial::StopHardware()
115 {
116 	if (m_thread)
117 	{
118 		RequestStop();
119 		m_thread->join();
120 		m_thread.reset();
121 	}
122 	m_bIsStarted = false;
123 	return true;
124 }
125 
Do_Work()126 void RFXComSerial::Do_Work()
127 {
128 	int sec_counter = 0;
129 
130 	Log(LOG_STATUS, "Worker started...");
131 
132 	while (IsStopRequested(1000) == false)
133 	{
134 		sec_counter++;
135 
136 		if (sec_counter % 12 == 0) {
137 			m_LastHeartbeat = mytime(NULL);
138 		}
139 
140 		if (m_bStartFirmwareUpload)
141 		{
142 			m_bStartFirmwareUpload = false;
143 			terminate();
144 			try {
145 				sleep_seconds(1);
146 				UpgradeFirmware();
147 			}
148 			catch (...)
149 			{
150 			}
151 		}
152 
153 		if (!isOpen())
154 		{
155 			if (m_retrycntr == 0)
156 			{
157 				Log(LOG_STATUS, "retrying in %d seconds...", RETRY_DELAY);
158 			}
159 			m_retrycntr++;
160 			if (m_retrycntr >= RETRY_DELAY)
161 			{
162 				m_retrycntr = 0;
163 				OpenSerialDevice();
164 			}
165 		}
166 	}
167 	terminate(); //Close serial port (if open)
168 
169 	Log(LOG_STATUS, "Worker stopped...");
170 }
171 
172 
OpenSerialDevice(const bool bIsFirmwareUpgrade)173 bool RFXComSerial::OpenSerialDevice(const bool bIsFirmwareUpgrade)
174 {
175 	//Try to open the Serial Port
176 	try
177 	{
178 		open(m_szSerialPort, m_iBaudRate);
179 		Log(LOG_STATUS, "Using serial port: %s", m_szSerialPort.c_str());
180 	}
181 	catch (boost::exception & e)
182 	{
183 		Log(LOG_ERROR, "Error opening serial port!");
184 #ifdef _DEBUG
185 		Log(LOG_ERROR, "-----------------\n%s\n----------------", boost::diagnostic_information(e).c_str());
186 #else
187 		(void)e;
188 #endif
189 		return false;
190 	}
191 	catch (...)
192 	{
193 		Log(LOG_ERROR, "Error opening serial port!!!");
194 		return false;
195 	}
196 	m_bIsStarted = true;
197 	m_rxbufferpos = 0;
198 	setReadCallback(boost::bind(&RFXComSerial::readCallback, this, _1, _2));
199 	if (!bIsFirmwareUpgrade)
200 		sOnConnected(this);
201 	return true;
202 }
203 
UploadFirmware(const std::string & szFilename)204 bool RFXComSerial::UploadFirmware(const std::string &szFilename)
205 {
206 	m_szFirmwareFile = szFilename;
207 	m_FirmwareUploadPercentage = 0;
208 	m_bStartFirmwareUpload = true;
209 	try {
210 		clearReadCallback();
211 	}
212 	catch (...)
213 	{
214 		//Don't throw from a Stop command
215 	}
216 	return true;
217 }
218 
UpgradeFirmware()219 bool RFXComSerial::UpgradeFirmware()
220 {
221 	int AddressLow = PKT_pmrangelow;
222 	int AddressHigh = PKT_pmrangehigh;
223 	if (HwdType == HTYPE_RFXtrx868)
224 	{
225 		AddressLow = PKT_pmrangelow868;
226 		AddressHigh = PKT_pmrangehigh868;
227 	}
228 	m_FirmwareUploadPercentage = 0;
229 	m_bStartFirmwareUpload = false;
230 	std::map<unsigned long, std::string> firmwareBuffer;
231 	int icntr = 0;
232 	if (!Read_Firmware_File(m_szFirmwareFile.c_str(), firmwareBuffer))
233 	{
234 		m_FirmwareUploadPercentage = -1;
235 		goto exitfirmwareupload;
236 	}
237 
238 	try
239 	{
240 		m_serial.open();
241 	}
242 	catch (...)
243 	{
244 		m_szUploadMessage = "Error opening serial port!!!";
245 		Log(LOG_ERROR, m_szUploadMessage);
246 		m_FirmwareUploadPercentage = -1;
247 		goto exitfirmwareupload;
248 	}
249 	//Start bootloader mode
250 	m_szUploadMessage = "Start bootloader process...";
251 	Log(LOG_STATUS, m_szUploadMessage);
252 	Write_TX_PKT(PKT_STARTBOOT, sizeof(PKT_STARTBOOT), 1);
253 	Write_TX_PKT(PKT_STARTBOOT, sizeof(PKT_STARTBOOT), 1);
254 	m_szUploadMessage = "Get bootloader version...";
255 	Log(LOG_STATUS, m_szUploadMessage);
256 	//read bootloader version
257 	if (!Write_TX_PKT(PKT_VERSION, sizeof(PKT_VERSION)))
258 	{
259 		m_szUploadMessage = "Error getting bootloader version!!!";
260 		Log(LOG_ERROR, m_szUploadMessage);
261 		m_FirmwareUploadPercentage = -1;
262 		goto exitfirmwareupload;
263 	}
264 	Log(LOG_STATUS, "bootloader version v%d.%d", m_rx_input_buffer[3], m_rx_input_buffer[2]);
265 
266 	if (!EraseMemory(AddressLow, AddressHigh))
267 	{
268 		m_FirmwareUploadPercentage = -1;
269 		goto exitfirmwareupload;
270 	}
271 
272 #ifndef WIN32
273 	try
274 	{
275 		m_serial.flush();
276 	}
277 	catch (...)
278 	{
279 		m_szUploadMessage = "Bootloader, unable to flush serial device!!!";
280 		Log(LOG_ERROR, m_szUploadMessage);
281 		m_FirmwareUploadPercentage = -1;
282 		goto exitfirmwareupload;
283 	}
284 #endif
285 	try
286 	{
287 		m_serial.close();
288 	}
289 	catch (...)
290 	{
291 	}
292 	sleep_seconds(1);
293 
294 	try
295 	{
296 		m_serial.open();
297 	}
298 	catch (...)
299 	{
300 		m_szUploadMessage = "Error opening serial port!!!";
301 		Log(LOG_ERROR, m_szUploadMessage);
302 		m_FirmwareUploadPercentage = -1;
303 		goto exitfirmwareupload;
304 	}
305 
306 	m_szUploadMessage = "Start bootloader version...";
307 	Log(LOG_STATUS, m_szUploadMessage);
308 	Write_TX_PKT(PKT_STARTBOOT, sizeof(PKT_STARTBOOT), 1);
309 	Write_TX_PKT(PKT_STARTBOOT, sizeof(PKT_STARTBOOT), 1);
310 
311 	m_szUploadMessage = "Bootloader, Start programming...";
312 	Log(LOG_STATUS, m_szUploadMessage);
313 	for (auto itt : firmwareBuffer)
314 	{
315 		icntr++;
316 		if (icntr % 5 == 0)
317 		{
318 			m_LastHeartbeat = mytime(NULL);
319 		}
320 		unsigned long Address = itt.first;
321 		m_FirmwareUploadPercentage = (100.0f / float(firmwareBuffer.size()))*icntr;
322 		if (m_FirmwareUploadPercentage > 100)
323 			m_FirmwareUploadPercentage = 100;
324 
325 		//if ((Address >= AddressLow) && (Address <= AddressHigh))
326 		{
327 			std::stringstream saddress;
328 			saddress << "Programming Address: 0x" << std::hex << std::uppercase << std::setw(4) << std::setfill('0') << Address;
329 
330 			std::stringstream spercentage;
331 			spercentage.precision(2);
332 			spercentage << std::setprecision(2) << std::fixed << m_FirmwareUploadPercentage;
333 			m_szUploadMessage = saddress.str() + ", " + spercentage.str() + " %";
334 			_log.Log(LOG_STATUS, m_szUploadMessage);
335 
336 			unsigned char bcmd[PKT_writeblock + 10];
337 			bcmd[0] = COMMAND_WRITEPM;
338 			bcmd[1] = 1;
339 			bcmd[2] = Address & 0xFF;
340 			bcmd[3] = (Address & 0xFF00) >> 8;
341 			bcmd[4] = (unsigned char)((Address & 0xFF0000) >> 16);
342 			memcpy(bcmd + 5, itt.second.c_str(), itt.second.size());
343 			bool ret = Write_TX_PKT(bcmd, 5 + itt.second.size(), 20);
344 			if (!ret)
345 			{
346 				m_szUploadMessage = "Bootloader, unable to program firmware memory, please try again!!!";
347 				Log(LOG_ERROR, m_szUploadMessage);
348 				m_FirmwareUploadPercentage = -1;
349 				goto exitfirmwareupload;
350 			}
351 		}
352 	}
353 	firmwareBuffer.clear();
354 #ifndef WIN32
355 	try
356 	{
357 		m_serial.flush();
358 	}
359 	catch (...)
360 	{
361 		m_szUploadMessage = "Bootloader, unable to flush serial device!!!";
362 		Log(LOG_ERROR, m_szUploadMessage);
363 		m_FirmwareUploadPercentage = -1;
364 		goto exitfirmwareupload;
365 	}
366 #endif
367 	//Verify
368 	m_szUploadMessage = "Start bootloader verify...";
369 	Log(LOG_STATUS, m_szUploadMessage);
370 	if (!Write_TX_PKT(PKT_VERIFY_OK, sizeof(PKT_VERIFY_OK)))
371 	{
372 		m_szUploadMessage = "Bootloader,  program firmware memory not succeeded, please try again!!!";
373 		Log(LOG_ERROR, m_szUploadMessage);
374 		m_FirmwareUploadPercentage = -1;
375 		goto exitfirmwareupload;
376 	}
377 	if (m_rx_input_buffer[0] != PKT_VERIFY_OK[0])
378 	{
379 		m_FirmwareUploadPercentage = -1;
380 		m_szUploadMessage = "Bootloader,  program firmware memory not succeeded, please try again!!!";
381 		Log(LOG_ERROR, m_szUploadMessage);
382 	}
383 	else
384 	{
385 		m_szUploadMessage = "Bootloader, Programming completed successfully...";
386 		Log(LOG_STATUS, m_szUploadMessage);
387 	}
388 exitfirmwareupload:
389 	m_szUploadMessage = "bootloader reset...";
390 	Log(LOG_STATUS, m_szUploadMessage);
391 	Write_TX_PKT(PKT_RESET, sizeof(PKT_RESET), 1);
392 #ifndef WIN32
393 	try
394 	{
395 		m_serial.flush();
396 	}
397 	catch (...)
398 	{
399 		m_szUploadMessage = "Bootloader, unable to flush serial device!!!";
400 		Log(LOG_ERROR, m_szUploadMessage);
401 		m_FirmwareUploadPercentage = -1;
402 		goto exitfirmwareupload;
403 	}
404 #endif
405 	sleep_seconds(1);
406 	try
407 	{
408 		if (m_serial.isOpen())
409 		{
410 			m_serial.close();
411 		}
412 	}
413 	catch (...)
414 	{
415 	}
416 
417 	m_rxbufferpos = 0;
418 	m_bInBootloaderMode = false;
419 	m_FirmwareUploadPercentage = 100;
420 	RequestStart();
421 	OpenSerialDevice();
422 	return true;
423 }
424 
425 //returns -1 when failed
GetUploadPercentage()426 float RFXComSerial::GetUploadPercentage()
427 {
428 	return m_FirmwareUploadPercentage;
429 }
430 
GetUploadMessage()431 std::string RFXComSerial::GetUploadMessage()
432 {
433 	return m_szUploadMessage;
434 }
435 
Read_Firmware_File(const char * szFilename,std::map<unsigned long,std::string> & fileBuffer)436 bool RFXComSerial::Read_Firmware_File(const char *szFilename, std::map<unsigned long, std::string>& fileBuffer)
437 {
438 #ifndef WIN32
439 	struct stat info;
440 	if (stat(szFilename, &info) == 0)
441 	{
442 		struct passwd *pw = getpwuid(info.st_uid);
443 		int ret = chown(szFilename, pw->pw_uid, pw->pw_gid);
444 		if (ret != 0)
445 		{
446 			m_szUploadMessage = "Error setting firmware ownership (chown returned an error!)";
447 			Log(LOG_ERROR, m_szUploadMessage);
448 			return false;
449 		}
450 	}
451 #endif
452 
453 	std::ifstream infile;
454 	std::string sLine;
455 	infile.open(szFilename);
456 	if (!infile.is_open())
457 	{
458 		m_szUploadMessage = "bootloader, unable to open file: " + std::string(szFilename);
459 		Log(LOG_ERROR, m_szUploadMessage);
460 		return false;
461 	}
462 
463 	m_szUploadMessage = "start reading Firmware...";
464 	Log(LOG_STATUS, m_szUploadMessage);
465 
466 	unsigned char rawLineBuf[PKT_writeblock];
467 	int raw_length = 0;
468 	unsigned long dest_address = 0;
469 	int line = 0;
470 	int addrh = 0;
471 
472 	fileBuffer.clear();
473 	std::string dstring = "";
474 	bool bHaveEOF = false;
475 
476 	while (!infile.eof())
477 	{
478 		getline(infile, sLine);
479 		if (sLine.empty())
480 			continue;
481 		//Every line should start with ':'
482 		if (sLine[0] != ':')
483 		{
484 			infile.close();
485 			m_szUploadMessage = "bootloader, firmware does not start with ':'";
486 			Log(LOG_ERROR, m_szUploadMessage);
487 			return false;
488 		}
489 		sLine = sLine.substr(1);
490 		if (sLine.size() > 1)
491 		{
492 			if (sLine[sLine.size() - 1] == '\n')
493 			{
494 				sLine = sLine.substr(0, sLine.size() - 1);
495 			}
496 			if (sLine[sLine.size() - 1] == '\r')
497 			{
498 				sLine = sLine.substr(0, sLine.size() - 1);
499 			}
500 		}
501 		if (sLine.size() % 2 != 0)
502 		{
503 			infile.close();
504 			m_szUploadMessage = "bootloader, firmware line not equals 2 digests";
505 			Log(LOG_ERROR, m_szUploadMessage);
506 			return false;
507 		}
508 		raw_length = 0;
509 		unsigned char chksum = 0;
510 		while (!sLine.empty())
511 		{
512 			std::string szHex = sLine.substr(0, 2); sLine = sLine.substr(2);
513 			std::stringstream sstr;
514 			int iByte = 0;
515 			sstr << std::hex << szHex;
516 			sstr >> iByte;
517 			rawLineBuf[raw_length++] = (unsigned char)iByte;
518 			if (!sLine.empty())
519 				chksum += iByte;
520 			if (raw_length > sizeof(rawLineBuf) - 1)
521 			{
522 				infile.close();
523 				m_szUploadMessage = "bootloader, incorrect length";
524 				Log(LOG_ERROR, m_szUploadMessage);
525 				return false;
526 			}
527 
528 		}
529 		//
530 		chksum = ~chksum + 1;
531 		if ((chksum != rawLineBuf[raw_length - 1]) || (raw_length < 4))
532 		{
533 			infile.close();
534 			m_szUploadMessage = "bootloader, checksum mismatch!";
535 			Log(LOG_ERROR, m_szUploadMessage);
536 			return false;
537 		}
538 		int byte_count = rawLineBuf[0];
539 		int faddress = (rawLineBuf[1] << 8) | rawLineBuf[2];
540 		int rtype = rawLineBuf[3];
541 
542 		switch (rtype)
543 		{
544 		case 0:
545 			//Data record
546 			dstring += std::string((const char*)&rawLineBuf + 4, (const char*)rawLineBuf + 4 + byte_count);
547 			if (dstring.size() == PKT_writeblock)
548 			{
549 				dest_address = (((((addrh << 16) | (faddress + byte_count)) - PKT_writeblock)) / PKT_bytesperaddr);
550 				fileBuffer[dest_address] = dstring;
551 				dstring.clear();
552 			}
553 			break;
554 		case 1:
555 			//EOF Record
556 			bHaveEOF = dstring.empty();
557 			if (!bHaveEOF)
558 			{
559 				m_szUploadMessage = "Bootloader invalid size!";
560 				Log(LOG_ERROR, m_szUploadMessage);
561 			}
562 			break;
563 		case 2:
564 			//Extended Segment Address Record
565 			m_szUploadMessage = "Bootloader type 2 not supported!";
566 			Log(LOG_ERROR, m_szUploadMessage);
567 			infile.close();
568 			return false;
569 		case 3:
570 			//Start Segment Address Record
571 			m_szUploadMessage = "Bootloader type 3 not supported!";
572 			Log(LOG_ERROR, m_szUploadMessage);
573 			infile.close();
574 			return false;
575 		case 4:
576 			//Extended Linear Address Record
577 			if (raw_length < 7)
578 			{
579 				m_szUploadMessage = "Invalid line length!!";
580 				Log(LOG_ERROR, m_szUploadMessage);
581 				infile.close();
582 				return false;
583 			}
584 			addrh = (rawLineBuf[4] << 8) | rawLineBuf[5];
585 			break;
586 		case 5:
587 			//Start Linear Address Record
588 			m_szUploadMessage = "Bootloader type 5 not supported!";
589 			Log(LOG_ERROR, m_szUploadMessage);
590 			infile.close();
591 			return false;
592 		}
593 		line++;
594 	}
595 	infile.close();
596 	if (!bHaveEOF)
597 	{
598 		m_szUploadMessage = "No end-of-line found!!";
599 		Log(LOG_ERROR, m_szUploadMessage);
600 		return false;
601 	}
602 	m_szUploadMessage = "Firmware read correctly...";
603 	Log(LOG_STATUS, m_szUploadMessage);
604 	return true;
605 }
606 
EraseMemory(const int StartAddress,const int StopAddress)607 bool RFXComSerial::EraseMemory(const int StartAddress, const int StopAddress)
608 {
609 	m_szUploadMessage = "Erasing memory....";
610 	Log(LOG_STATUS, m_szUploadMessage);
611 	int BootAddr = StartAddress;
612 
613 	while (BootAddr < StopAddress)
614 	{
615 		int nBlocks = ((StopAddress - StartAddress + 1) * PKT_bytesperaddr) / PKT_eraseblock;
616 		nBlocks = (StopAddress - StartAddress + 1) / (PKT_eraseblock / PKT_bytesperaddr);
617 		if (nBlocks > 255)
618 			nBlocks = 255;
619 
620 		unsigned char bcmd[5];
621 		bcmd[0] = COMMAND_ERASEPM;
622 		bcmd[1] = nBlocks;
623 		bcmd[2] = BootAddr & 0xFF;
624 		bcmd[3] = (BootAddr & 0xFF00) >> 8;
625 		bcmd[4] = (BootAddr & 0xFF0000) >> 16;
626 
627 		if (!Write_TX_PKT(bcmd, sizeof(bcmd), 5))
628 		{
629 			m_szUploadMessage = "Error erasing memory!";
630 			Log(LOG_ERROR, m_szUploadMessage);
631 			return false;
632 		}
633 		BootAddr += (PKT_eraseblock * nBlocks);
634 	}
635 	m_szUploadMessage = "Erasing memory completed....";
636 	Log(LOG_STATUS, m_szUploadMessage);
637 	return true;
638 }
639 
Write_TX_PKT(const unsigned char * pdata,size_t length,int max_retry)640 bool RFXComSerial::Write_TX_PKT(const unsigned char *pdata, size_t length, int max_retry)
641 {
642 	if (!m_serial.isOpen())
643 		return false;
644 
645 	unsigned char output_buffer[512];
646 	int tot_bytes = 0;
647 
648 	output_buffer[tot_bytes++] = PKT_STX;
649 	output_buffer[tot_bytes++] = PKT_STX;
650 
651 	// Generate the checksum
652 	unsigned char chksum = 0;
653 	for (size_t ii = 0; ii < length; ii++)
654 	{
655 		unsigned char dbyte = pdata[ii];
656 		chksum += dbyte;
657 
658 		//if control character, stuff DLE
659 		if (dbyte == PKT_STX || dbyte == PKT_ETX || dbyte == PKT_DLE)
660 		{
661 			output_buffer[tot_bytes++] = PKT_DLE;
662 		}
663 		output_buffer[tot_bytes++] = dbyte;
664 	}
665 	chksum = ~chksum + 1;
666 	if (chksum == PKT_STX || chksum == PKT_ETX || chksum == PKT_DLE)
667 	{
668 		output_buffer[tot_bytes++] = PKT_DLE;
669 	}
670 	output_buffer[tot_bytes++] = chksum;
671 	output_buffer[tot_bytes++] = PKT_ETX;
672 
673 	m_bInBootloaderMode = true;
674 	/*
675 	if (!WaitForResponse)
676 	{
677 		write((const char*)&output_buffer, tot_bytes);
678 		sleep_milliseconds(500);
679 		return true;
680 	}
681 	*/
682 	while (max_retry > 0)
683 	{
684 		try
685 		{
686 			size_t twrite = m_serial.write((const uint8_t *)&output_buffer, tot_bytes);
687 			sleep_milliseconds(RFX_WRITE_DELAY);
688 			if (twrite == tot_bytes)
689 			{
690 				int rcount = 0;
691 				while (rcount < 2)
692 				{
693 					if (Read_TX_PKT())
694 						return true;
695 					sleep_milliseconds(500);
696 					rcount++;
697 				}
698 			}
699 			max_retry--;
700 		}
701 		catch (...)
702 		{
703 			return false;
704 		}
705 	}
706 	return m_bHaveRX;
707 }
708 
709 //Reads data from serial port between STX/ETX
Read_TX_PKT()710 bool RFXComSerial::Read_TX_PKT()
711 {
712 	uint8_t sbuffer[512];
713 	size_t buffer_offset = 0;
714 	bool bSTXFound1 = false;
715 	bool bSTXFound2 = false;
716 	bool bETXFound = false;
717 	bool bHadDLE = false;
718 	unsigned char chksum = 0;
719 	m_rx_tot_bytes = 0;
720 	while (m_rx_tot_bytes < sizeof(m_rx_input_buffer))
721 	{
722 		size_t tot_read = m_serial.read((uint8_t*)&sbuffer, sizeof(sbuffer));
723 		if (tot_read <= 0)
724 			return false;
725 		int ii = 0;
726 		while (tot_read > 0)
727 		{
728 			uint8_t tByte = sbuffer[ii++];
729 			if (!bSTXFound1)
730 			{
731 				if (tByte != PKT_STX)
732 					return false;
733 				bSTXFound1 = true;
734 			}
735 			else if (!bSTXFound2)
736 			{
737 				if (tByte != PKT_STX)
738 					return false;
739 				bSTXFound2 = true;
740 				chksum = 0;
741 				bHadDLE = false;
742 				m_rx_tot_bytes = 0;
743 			}
744 			else
745 			{
746 				//Read data until ETX is found
747 				if ((tByte == PKT_ETX) && (!bHadDLE))
748 				{
749 					chksum = ~chksum + 1; //test checksum
750 					if (chksum != 0)
751 					{
752 						Log(LOG_ERROR, "bootloader, received response with invalid checksum!");
753 						return false;
754 					}
755 					m_bHaveRX = true;
756 					return true;
757 				}
758 				else if (tByte == PKT_DLE)
759 				{
760 					bHadDLE = true;
761 				}
762 				else
763 				{
764 					bHadDLE = false;
765 					chksum += tByte;
766 					m_rx_input_buffer[m_rx_tot_bytes++] = tByte;
767 				}
768 			}
769 			tot_read--;
770 		}
771 	}
772 	return ((buffer_offset > 0) && (bETXFound));
773 }
774 
Handle_RX_PKT(const unsigned char * pdata,size_t length)775 bool RFXComSerial::Handle_RX_PKT(const unsigned char *pdata, size_t length)
776 {
777 	if (length < 2)
778 		return false;
779 	if ((pdata[0] != PKT_STX) || (pdata[1] != PKT_STX))
780 		return false;
781 
782 	unsigned char chksum = 0;
783 	m_rx_tot_bytes = 0;
784 	size_t ii = 1;
785 	//	std::string szRespone = "Received: ";
786 	//	int jj;
787 	while ((ii < length) && (m_rx_tot_bytes < sizeof(m_rx_input_buffer) - 1))
788 	{
789 		unsigned char dbyte = pdata[ii];
790 		switch (dbyte)
791 		{
792 		case PKT_STX:
793 			m_rx_tot_bytes = 0;
794 			chksum = 0;
795 			break;
796 		case PKT_ETX:
797 			chksum = ~chksum + 1; //test checksum
798 			if (chksum != 0)
799 			{
800 				Log(LOG_ERROR, "bootloader, received response with invalid checksum!");
801 				return false;
802 			}
803 			//Message OK
804 			/*
805 			for (jj = 0; jj < m_rx_tot_bytes; jj++)
806 			{
807 				std::stringstream sstr;
808 				sstr << "0x" << std::hex << std::uppercase << std::setw(2) << std::setfill('0') << int(m_rx_input_buffer[jj]);
809 				if (jj != m_rx_tot_bytes - 1)
810 					sstr << ", ";
811 				szRespone+=sstr.str();
812 			}
813 			_log.Log(LOG_STATUS, "%s", szRespone.c_str());
814 			*/
815 			m_bHaveRX = true;
816 			return true;
817 			break;
818 		case PKT_DLE:
819 			dbyte = pdata[ii + 1];
820 			ii++;
821 			if (ii >= length)
822 				return false;
823 		default:
824 			chksum += dbyte;
825 			m_rx_input_buffer[m_rx_tot_bytes++] = dbyte;
826 			break;
827 		}
828 		ii++;
829 	}
830 	return false;
831 }
832 
readCallback(const char * data,size_t len)833 void RFXComSerial::readCallback(const char *data, size_t len)
834 {
835 	std::lock_guard<std::mutex> l(readQueueMutex);
836 	try
837 	{
838 		if (!m_bInBootloaderMode)
839 		{
840 			bool bRet = onInternalMessage((const unsigned char *)data, len);
841 			if (bRet == false)
842 			{
843 				//close serial connection, and restart
844 				terminate();
845 
846 			}
847 		}
848 	}
849 	catch (...)
850 	{
851 
852 	}
853 }
854 
WriteToHardware(const char * pdata,const unsigned char length)855 bool RFXComSerial::WriteToHardware(const char *pdata, const unsigned char length)
856 {
857 	if (!isOpen())
858 		return false;
859 	if (m_bInBootloaderMode)
860 		return false;
861 	write(pdata, length);
862 	return true;
863 }
864 
865 //Webserver helpers
866 namespace http {
867 	namespace server {
RFXComUpgradeFirmware(WebEmSession & session,const request & req,std::string & redirect_uri)868 		void CWebServer::RFXComUpgradeFirmware(WebEmSession & session, const request& req, std::string & redirect_uri)
869 		{
870 			redirect_uri = "/index.html";
871 			if (session.rights != 2)
872 			{
873 				session.reply_status = reply::forbidden;
874 				return; //Only admin user allowed
875 			}
876 
877 			std::string hardwareid = request::findValue(&req, "hardwareid");
878 			std::string firmwarefile = request::findValue(&req, "firmwarefile");
879 
880 			if (firmwarefile.empty())
881 			{
882 				return;
883 			}
884 
885 			CDomoticzHardwareBase *pHardware = NULL;
886 			if ((!hardwareid.empty()) && (hardwareid != "undefined"))
887 			{
888 				pHardware = m_mainworker.GetHardware(atoi(hardwareid.c_str()));
889 			}
890 			if (pHardware == NULL)
891 			{
892 				//Direct Entry, try to find the RFXCom hardware
893 				pHardware = m_mainworker.GetHardwareByType(HTYPE_RFXtrx433);
894 				if (pHardware == NULL)
895 				{
896 					pHardware = m_mainworker.GetHardwareByType(HTYPE_RFXtrx868);
897 					if (pHardware == NULL)
898 					{
899 						return;
900 					}
901 				}
902 			}
903 #ifdef WIN32
904 			std::string outputfile = szStartupFolder + "rfx_firmware.hex";
905 #else
906 			std::string outputfile = "/tmp/rfx_firmware.hex";
907 #endif
908 			std::ofstream outfile;
909 			outfile.open(outputfile.c_str(), std::ios::out | std::ios::binary | std::ios::trunc);
910 			if (!outfile.is_open())
911 				return;
912 			outfile << firmwarefile;
913 			outfile.flush();
914 			outfile.close();
915 
916 			if (
917 				(pHardware->HwdType == HTYPE_RFXtrx315) ||
918 				(pHardware->HwdType == HTYPE_RFXtrx433) ||
919 				(pHardware->HwdType == HTYPE_RFXtrx868)
920 				)
921 			{
922 				RFXComSerial *pRFXComSerial = reinterpret_cast<RFXComSerial *>(pHardware);
923 				pRFXComSerial->UploadFirmware(outputfile);
924 			}
925 		}
926 
SetRFXCOMMode(WebEmSession & session,const request & req,std::string & redirect_uri)927 		void CWebServer::SetRFXCOMMode(WebEmSession & session, const request& req, std::string & redirect_uri)
928 		{
929 			redirect_uri = "/index.html";
930 
931 			if (session.rights != 2)
932 			{
933 				session.reply_status = reply::forbidden;
934 				return; //Only admin user allowed
935 			}
936 
937 			std::string idx = request::findValue(&req, "idx");
938 			if (idx == "") {
939 				return;
940 			}
941 			std::vector<std::vector<std::string> > result;
942 
943 			result = m_sql.safe_query("SELECT Mode1, Mode2, Mode3, Mode4, Mode5, Mode6, [Type] FROM Hardware WHERE (ID='%q')", idx.c_str());
944 			if (result.empty())
945 				return;
946 
947 			unsigned char Mode1 = atoi(result[0][0].c_str());
948 			unsigned char Mode2 = atoi(result[0][1].c_str());
949 			unsigned char Mode3 = atoi(result[0][2].c_str());
950 			unsigned char Mode4 = atoi(result[0][3].c_str());
951 			unsigned char Mode5 = atoi(result[0][4].c_str());
952 			unsigned char Mode6 = atoi(result[0][5].c_str());
953 
954 			_eHardwareTypes HWType = (_eHardwareTypes)atoi(result[0][6].c_str());
955 
956 			tRBUF Response;
957 			Response.ICMND.freqsel = Mode1;
958 			Response.ICMND.xmitpwr = Mode2;
959 			Response.ICMND.msg3 = Mode3;
960 			Response.ICMND.msg4 = Mode4;
961 			Response.ICMND.msg5 = Mode5;
962 			Response.ICMND.msg6 = Mode6;
963 
964 			if (HWType != HTYPE_RFXtrx868)
965 			{
966 				Response.IRESPONSE.UNDECODEDenabled = (request::findValue(&req, "undecon") == "on") ? 1 : 0;
967 				Response.IRESPONSE.X10enabled = (request::findValue(&req, "X10") == "on") ? 1 : 0;
968 				Response.IRESPONSE.ARCenabled = (request::findValue(&req, "ARC") == "on") ? 1 : 0;
969 				Response.IRESPONSE.ACenabled = (request::findValue(&req, "AC") == "on") ? 1 : 0;
970 				Response.IRESPONSE.HEEUenabled = (request::findValue(&req, "HomeEasyEU") == "on") ? 1 : 0;
971 				Response.IRESPONSE.MEIANTECHenabled = (request::findValue(&req, "Meiantech") == "on") ? 1 : 0;
972 				Response.IRESPONSE.OREGONenabled = (request::findValue(&req, "OregonScientific") == "on") ? 1 : 0;
973 				Response.IRESPONSE.ATIenabled = (request::findValue(&req, "ATIremote") == "on") ? 1 : 0;
974 				Response.IRESPONSE.VISONICenabled = (request::findValue(&req, "Visonic") == "on") ? 1 : 0;
975 				Response.IRESPONSE.MERTIKenabled = (request::findValue(&req, "Mertik") == "on") ? 1 : 0;
976 				Response.IRESPONSE.LWRFenabled = (request::findValue(&req, "ADLightwaveRF") == "on") ? 1 : 0;
977 				Response.IRESPONSE.HIDEKIenabled = (request::findValue(&req, "HidekiUPM") == "on") ? 1 : 0;
978 				Response.IRESPONSE.LACROSSEenabled = (request::findValue(&req, "LaCrosse") == "on") ? 1 : 0;
979 				Response.IRESPONSE.LEGRANDenabled = (request::findValue(&req, "Legrand") == "on") ? 1 : 0;
980 				Response.IRESPONSE.MSG4Reserved5 = (request::findValue(&req, "ProGuard") == "on") ? 1 : 0;
981 				Response.IRESPONSE.BLINDST0enabled = (request::findValue(&req, "BlindT0") == "on") ? 1 : 0;
982 				Response.IRESPONSE.BLINDST1enabled = (request::findValue(&req, "BlindT1T2T3T4") == "on") ? 1 : 0;
983 				Response.IRESPONSE.AEenabled = (request::findValue(&req, "AEBlyss") == "on") ? 1 : 0;
984 				Response.IRESPONSE.RUBICSONenabled = (request::findValue(&req, "Rubicson") == "on") ? 1 : 0;
985 				Response.IRESPONSE.FINEOFFSETenabled = (request::findValue(&req, "FineOffsetViking") == "on") ? 1 : 0;
986 				Response.IRESPONSE.LIGHTING4enabled = (request::findValue(&req, "Lighting4") == "on") ? 1 : 0;
987 				Response.IRESPONSE.RSLenabled = (request::findValue(&req, "RSL") == "on") ? 1 : 0;
988 				Response.IRESPONSE.SXenabled = (request::findValue(&req, "ByronSX") == "on") ? 1 : 0;
989 				Response.IRESPONSE.IMAGINTRONIXenabled = (request::findValue(&req, "ImaginTronix") == "on") ? 1 : 0;
990 				Response.IRESPONSE.KEELOQenabled = (request::findValue(&req, "Keeloq") == "on") ? 1 : 0;
991 				Response.IRESPONSE.HCEnabled = (request::findValue(&req, "HC") == "on") ? 1 : 0;
992 
993 				CDomoticzHardwareBase *pHardware = m_mainworker.GetHardware(atoi(idx.c_str()));
994 				if (pHardware)
995 				{
996 					CRFXBase *pBase = reinterpret_cast<CRFXBase *>(pHardware);
997 					pBase->SetRFXCOMHardwaremodes(Response.ICMND.freqsel, Response.ICMND.xmitpwr, Response.ICMND.msg3, Response.ICMND.msg4, Response.ICMND.msg5, Response.ICMND.msg6);
998 
999 					if (pBase->m_Version.find("Pro XL") != std::string::npos)
1000 					{
1001 						std::string AsyncMode = request::findValue(&req, "combo_rfx_xl_async_type");
1002 						if (AsyncMode == "")
1003 							AsyncMode = "0";
1004 						result = m_sql.safe_query("UPDATE Hardware SET Extra='%q' WHERE (ID='%q')", AsyncMode.c_str(), idx.c_str());
1005 						pBase->SetAsyncType((CRFXBase::_eRFXAsyncType)atoi(AsyncMode.c_str()));
1006 					}
1007 				}
1008 			}
1009 			else
1010 			{
1011 				//For now disable setting the protocols on a 868Mhz device
1012 			}
1013 		}
1014 
Cmd_RFXComGetFirmwarePercentage(WebEmSession & session,const request & req,Json::Value & root)1015 		void CWebServer::Cmd_RFXComGetFirmwarePercentage(WebEmSession & session, const request& req, Json::Value &root)
1016 		{
1017 			root["status"] = "ERR";
1018 			root["title"] = "GetFirmwareUpgradePercentage";
1019 			std::string hardwareid = request::findValue(&req, "hardwareid");
1020 
1021 			CDomoticzHardwareBase *pHardware = NULL;
1022 			if ((!hardwareid.empty()) && (hardwareid != "undefined"))
1023 			{
1024 				pHardware = m_mainworker.GetHardware(atoi(hardwareid.c_str()));
1025 			}
1026 			if (pHardware == NULL)
1027 			{
1028 				//Direct Entry, try to find the RFXCom hardware
1029 				pHardware = m_mainworker.GetHardwareByType(HTYPE_RFXtrx433);
1030 				if (pHardware == NULL)
1031 				{
1032 					pHardware = m_mainworker.GetHardwareByType(HTYPE_RFXtrx868);
1033 				}
1034 			}
1035 			if (pHardware != NULL)
1036 			{
1037 				if (
1038 					(pHardware->HwdType == HTYPE_RFXtrx315) ||
1039 					(pHardware->HwdType == HTYPE_RFXtrx433) ||
1040 					(pHardware->HwdType == HTYPE_RFXtrx868)
1041 					)
1042 				{
1043 					RFXComSerial *pRFXComSerial = reinterpret_cast<RFXComSerial *>(pHardware);
1044 					root["status"] = "OK";
1045 					root["percentage"] = pRFXComSerial->GetUploadPercentage();
1046 					root["message"] = pRFXComSerial->GetUploadMessage();
1047 				}
1048 			}
1049 			else
1050 				root["message"] = "Hardware not found, or not enabled!";
1051 		}
1052 	}
1053 }
1054