1 /*
2  * The contents of this file are subject to the Mozilla Public License
3  * Version 1.0 (the "License"); you may not use this file except in
4  * compliance with the License. You may obtain a copy of the License at
5  * http://www.mozilla.org/MPL/
6  *
7  * Software distributed under the License is distributed on an "AS IS"
8  * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the
9  * License for the specific language governing rights and limitations
10  * under the License.
11  *
12  * The Initial Developer of this code is David Baum.
13  * Portions created by David Baum are Copyright (C) 1998 David Baum.
14  * All Rights Reserved.
15  *
16  * Portions created by John Hansen are Copyright (C) 2005 John Hansen.
17  * All Rights Reserved.
18  *
19  */
20 
21 #include <cstring>
22 #include <cstdio>
23 #include <cstdlib>
24 
25 #include "RCX_PipeTransport.h"
26 #include "RCX_Link.h"
27 #include "RCX_SerialPipe.h"
28 
29 using std::printf;
30 
31 #ifdef DEBUG
32 #define DEBUG_TIMEOUT
33 #endif
34 
35 #define kMaxTxData	(2*RCX_Link::kMaxCmdLength + 6)
36 #define kMaxRxData	(kMaxTxData + 2*RCX_Link::kMaxReplyLength + 5)
37 
38 #define	kDefaultRetryCount 4
39 
40 static UByte cmSync[] = { 1, 0xff };
41 static UByte rcxSync[] = { 3, 0x55, 0xff, 0x00 };
42 static UByte rcxNo55Sync[] = { 2, 0xff, 0x00 };
43 static UByte spyboticsSync[] = {1, 0x98 };
44 
45 static int FindSync(const UByte *data, int length, const UByte *sync, const UByte cmd);
46 static UByte ComputeChecksum(UByte dataSum, RCX_TargetType targetType);
47 
48 // receive states
49 enum
50 {
51 	kReplyState = 0,	// state when rx buffer used for replies
52 	kIdleState,
53 	kSync1State,
54 	kSync2State,
55 	kDataState
56 };
57 
58 
RCX_PipeTransport(RCX_Pipe * pipe)59 RCX_PipeTransport::RCX_PipeTransport(RCX_Pipe *pipe) : fPipe(pipe)
60 {
61 	fTxData = new UByte[kMaxTxData];
62 	fRxData = new UByte[kMaxRxData];
63 	fVerbose = false;
64 	fTxLastCommand = 0;
65 	fFastMode = false;
66         fOmitHeader = false;
67 }
68 
69 
~RCX_PipeTransport()70 RCX_PipeTransport::~RCX_PipeTransport()
71 {
72 	Close();
73 	delete [] fTxData;
74 	delete [] fRxData;
75 	delete fPipe;
76 }
77 
78 
Open(RCX_TargetType target,const char * deviceName,ULong options)79 RCX_Result RCX_PipeTransport::Open(RCX_TargetType target, const char *deviceName, ULong options)
80 {
81 	RCX_Result err;
82 
83 	fTarget = target;
84 	fComplementData = true; // default
85 
86 	int mode;
87 	switch(fTarget)
88 	{
89 		case kRCX_CMTarget:
90 			mode = RCX_Pipe::kCyberMasterMode;
91 			fSync = cmSync;
92 			break;
93 		case kRCX_SpyboticsTarget:
94 			mode = RCX_Pipe::kSpyboticsMode;
95 			fSync = spyboticsSync;
96 			fComplementData = false;
97 			break;
98 		default:
99 			mode = RCX_Pipe::kNormalIrMode;
100 			fSync = (fPipe->GetCapabilities() & RCX_Pipe::kAbsorb55Flag) ? rcxNo55Sync : rcxSync;
101 			break;
102 	}
103 
104 	err = fPipe->Open(deviceName, mode);
105 	if (RCX_ERROR(err)) return err;
106 
107 	fVerbose = (options & RCX_Link::kVerboseMode);
108 
109 	fRxTimeout = (options & RCX_Link::kRxTimeoutMask);
110 	if (fRxTimeout==0) fRxTimeout = kMaxTimeout;
111 
112 	fDynamicTimeout = false;
113 
114 	fRxState = kReplyState;
115 	return kRCX_OK;
116 }
117 
118 
Close()119 void RCX_PipeTransport::Close()
120 {
121 	fPipe->Close();
122 }
123 
124 
FastModeSupported() const125 bool RCX_PipeTransport::FastModeSupported() const
126 {
127 	return (fPipe->GetCapabilities() & RCX_Pipe::kFastIrMode);
128 }
129 
130 
SetFastMode(bool fast)131 void RCX_PipeTransport::SetFastMode(bool fast)
132 {
133 	if (fast == fFastMode) return;
134 
135 	if (fast)
136 	{
137 		fComplementData = false;
138 		fPipe->SetMode(RCX_Pipe::kFastIrMode);
139 	}
140 	else
141 	{
142 		fComplementData = (fTarget != kRCX_SpyboticsTarget);
143 		fPipe->SetMode(RCX_Pipe::kNormalIrMode);
144 	}
145 
146 	fFastMode = fast;
147 }
148 
149 
150 
Send(const UByte * txData,int txLength,UByte * rxData,int rxExpected,int rxMax,bool retry,int timeout)151 RCX_Result RCX_PipeTransport::Send(const UByte *txData, int txLength, UByte *rxData, int rxExpected, int rxMax, bool retry, int timeout)
152 {
153 	RCX_Result result;
154 
155 	// format the command
156 	BuildTxData(txData, txLength, retry);
157 
158 	// try sending
159 	int tries = retry ? kDefaultRetryCount : 1;
160 	int originalTimeout = fRxTimeout;
161 
162 	for(int i=0; i<tries; i++)
163 	{
164 		SendFromTxBuffer(fFastMode ? 100 : 0);
165 
166 		// if no reply is expected, we can just return now (no retries, no errors, etc)
167 		if (!rxExpected) return kRCX_OK;
168 
169                 int tmpTimeout;
170                 if (timeout > 0)
171                   tmpTimeout = timeout;
172                 else
173                   tmpTimeout = fRxTimeout;
174 
175 
176 		int replyOffset;
177 		result = ReceiveReply(rxExpected, tmpTimeout, replyOffset);
178 		if (fDynamicTimeout) AdjustTimeout(result, i);
179 
180 		if (!RCX_ERROR(result))
181 		{
182 			if (rxData)
183 			{
184 				int length = result+1;
185 				if (length > rxMax) length = rxMax;
186 				CopyReply(rxData, replyOffset, length);
187 			}
188 
189 			return result;
190 		}
191 
192 		// only the second kRCX_IREchoError is catastrophic
193 		// this is somewhat of a hack - I really should keep track
194 		// of the echo, but for now this makes sure that a serial
195 		// level failure on a single packet doesn't kill the entire
196 		// send
197 		if (result == kRCX_IREchoError && i > 0) break;
198                 if (fVerbose)
199                 {
200                         printf("Retrying...\n");
201                 }
202 	}
203 
204 	if (retry)
205 	{
206 		// retries exceeded, restore original timeout and lose the sync
207 		if (fDynamicTimeout)
208 			fRxTimeout = originalTimeout;
209 		fSynced = false;
210 	}
211 
212 	return result;
213 }
214 
215 
BuildTxData(const UByte * data,int length,bool duplicateReduction)216 void RCX_PipeTransport::BuildTxData(const UByte *data, int length, bool duplicateReduction)
217 {
218 	int i;
219 	UByte dataSum = 0;
220 	UByte byte;
221 	UByte *ptr = fTxData;
222 
223 	if (fTarget == kRCX_CMTarget)
224 	{
225 		// CM sync pattern
226 		*ptr++ = 0xfe;
227 		*ptr++ = 0x00;
228 		*ptr++ = 0x00;
229 		*ptr++ = 0xff;
230 	}
231 	else if (fTarget == kRCX_SpyboticsTarget)
232 	{
233 		*ptr++ = 0x98;
234 	}
235 	else if (fFastMode)
236 	{
237 		*ptr++ = 0xff;
238 	}
239 	else
240 	{
241                 if (!fOmitHeader)
242                 {
243                         // RCX sync pattern
244                         *ptr++ = 0x55;
245                         *ptr++ = 0xff;
246                         *ptr++ = 0x00;
247                 }
248 	}
249 
250 	// interleaved data & inverse data
251 	for(i=0; i<length; i++)
252 	{
253 		byte = *data++;
254 
255 		if (i==0)
256 		{
257 			if (duplicateReduction && byte==fTxLastCommand)
258 				byte ^= 8;
259 			fTxLastCommand = byte;
260 		}
261 
262 
263 		*ptr++ = byte;
264 		if (fComplementData)
265 			*ptr++ = (UByte)~byte;
266 		dataSum += byte;
267 	}
268 
269 	UByte checksum = ComputeChecksum(dataSum, fTarget);
270 
271 	// checksum
272 	*ptr++ = checksum;
273 	if (fComplementData)
274 		*ptr++ = (UByte)~checksum;
275 
276 	fTxLength = ptr - fTxData;
277 }
278 
279 
SendFromTxBuffer(int delay)280 void RCX_PipeTransport::SendFromTxBuffer(int delay)
281 {
282 	// drain serial rx buffer
283         fPipe->FlushRead(delay);
284 
285 	// send command
286 	fPipe->Write(fTxData, fTxLength);
287 	if (fVerbose)
288 	{
289 		printf("Tx: ");
290 		DumpData(fTxData, fTxLength);
291 	}
292 }
293 
294 
295 
ReceiveReply(int rxExpected,int timeout,int & replyOffset)296 RCX_Result RCX_PipeTransport::ReceiveReply(int rxExpected, int timeout, int &replyOffset)
297 {
298 	int receiveLen = ExpectedReceiveLen(rxExpected);
299 	if (!((fTarget == kRCX_SpyboticsTarget) || fPipe->IsUSB()))
300 	{
301 		receiveLen += fTxLength; // serial tower echoes the sent bytes
302 	}
303 
304 	// get the reply
305 	fRxState = kReplyState;
306 	fRxLength = 0;
307 
308 	int length = 0;
309         bool bFirstRead = true;
310 	while(fRxLength < kMaxRxData)
311 	{
312 		if (bFirstRead)
313 		{
314 			bFirstRead = false;
315 			if (fVerbose) printf("reading %ld bytes, timeout = %ld\n", receiveLen, timeout);
316 			int bytesRead = fPipe->Read(fRxData+fRxLength, receiveLen, timeout);
317 			if (bytesRead == 0) break;
318 			fRxLength += bytesRead;
319 		}
320 		else
321 		{
322 			if (fVerbose) printf("reading 1 byte, timeout = %ld\n", timeout);
323 			if (fPipe->Read(fRxData+fRxLength, 1, timeout) != 1) break;
324 			fRxLength++;
325 
326 		}
327 /*
328 		if (fPipe->Read(fRxData+fRxLength, 1, timeout) != 1) break;
329 		fRxLength++;
330 
331 		if (fRxLength < receiveLen) continue;
332 */
333 		// check for replies
334 		length = FindReply(rxExpected, replyOffset);
335 		if (length == rxExpected) break;
336 	}
337 
338 	if (fVerbose)
339 	{
340 		printf("Rx: ");
341 		DumpData(fRxData, fRxLength);
342 	}
343 
344 	if (fRxLength == 0 &&
345 		(fPipe->GetCapabilities() & RCX_Pipe::kTxEchoFlag) &&
346 		(fTarget!=kRCX_CMTarget))
347 		return  kRCX_IREchoError;
348 
349 	if (length == 0)
350 		return kRCX_ReplyError;
351 
352 	return length - 1;
353 }
354 
355 
356 /* this function is now obsolete
357 
358 int RCX_PipeTransport::ValidateRxData()
359 {
360 	// must be an even number of bytes
361 	if (fRxLength & 1) return 0;
362 
363 	// validate complements
364 	int count = fRxLength / 2;
365 	for(int i=0; i<count; ++i)
366 	{
367 		UByte d = fRxData[2*i];
368 
369 		if (d + fRxData[2*i+1] != 0xff) return 0;
370 
371 		fRxData[i] = d;
372 	}
373 
374 	// compute checksum
375 	--count;
376 	UByte checksum = 0;
377 	for(int i=0; i<count; ++i)
378 		checksum += fRxData[i];
379 
380 	if (checksum != fRxData[count]) return 0;
381 
382 	return count;
383 }
384 */
385 
386 
FindReply(const int rxExpected,int & offset)387 int RCX_PipeTransport::FindReply(const int rxExpected, int &offset)
388 {
389 	int length;
390 
391 	offset = 0;
392 	while(1)
393 	{
394 		int start = FindSync(fRxData + offset, fRxLength - offset, fSync, fTxLastCommand);
395 
396 		if (start == 0) return 0;
397 
398 		offset += start;
399 
400 		length = VerifyReply(rxExpected, fRxData + offset, fRxLength - offset, fTxLastCommand);
401 
402 		if (length > 0) return length;
403 	}
404 }
405 
406 
CopyReply(UByte * dst,int offset,RCX_Result length)407 void RCX_PipeTransport::CopyReply(UByte *dst, int offset, RCX_Result length)
408 {
409 	const UByte *src = fRxData + offset;
410 
411 	while(length>0)
412 	{
413 		*dst++ = *src++;
414 		if (fComplementData) src++;
415 		length--;
416 	}
417 }
418 
419 
AdjustTimeout(RCX_Result result,int attempt)420 void RCX_PipeTransport::AdjustTimeout(RCX_Result result, int attempt)
421 {
422 	int newTimeout = fRxTimeout;
423 
424 	if (!RCX_ERROR(result) && attempt==0)
425 	{
426 		// worked on first try, lets see if we can go faster next time
427 		newTimeout = fRxTimeout - (fRxTimeout / 10);
428 		if (newTimeout < kMinTimeout)
429 			newTimeout = kMinTimeout;
430 	}
431 	else if (RCX_ERROR(result) && attempt > 0)
432 	{
433 		// failed on try other than first - slow down
434 		newTimeout *= 2;
435 		if (newTimeout > kMaxTimeout) newTimeout = kMaxTimeout;
436 	}
437 
438 	if (newTimeout != fRxTimeout)
439 	{
440 		fRxTimeout = newTimeout;
441 		#ifdef DEBUG_TIMEOUT
442 			printf("Timeout = %d\n", fRxTimeout);
443 		#endif
444 	}
445 }
446 
447 
Receive(UByte * data,int maxLength,bool)448 RCX_Result RCX_PipeTransport::Receive(UByte *data, int maxLength, bool /* echo */)
449 {
450 	if (fRxState == kReplyState) fRxState = kIdleState;
451 
452 	while(true)
453 	{
454 		UByte b;
455 
456 		if (fPipe->Read(&b, 1, 100) != 1) return 0;
457 
458 //		if (echo) fPipe->Write(&b, 1);
459 		ProcessRxByte(b);
460 
461 		if (fRxState == kDataState && fRxLength>3 && (fRxLength & 1)==0) {
462 			// compute checksum
463 			int count = fRxLength/2 - 1;
464 			UByte checksum = 0;
465 
466 			for(int i=0; i<count; ++i)
467 			{
468 				checksum += fRxData[i*2];
469 			}
470 
471 			if (fRxData[count*2] != checksum) continue;
472 
473 			if (count > maxLength) count = maxLength;
474 			for(int i=0; i<count; ++i) {
475 				data[i] = fRxData[i*2];
476 			}
477 
478 			fRxState = kIdleState;
479 			return count;
480 		}
481 	}
482 }
483 
484 
485 
ProcessRxByte(UByte b)486 void RCX_PipeTransport::ProcessRxByte(UByte b)
487 {
488 	int newState = fRxState;
489 	bool reset = false;
490 
491 	if (fVerbose) {
492 		DumpData(&b, 1);
493 	}
494 
495 	switch(fRxState)
496 	{
497 		case kIdleState:
498 			if (b == 0x55)
499 				newState = kSync1State;
500 			break;
501 		case kSync1State:
502 			if (b == 0xff)
503 				newState = kSync2State;
504 			else
505 				reset = true;
506 			break;
507 		case kSync2State:
508 			if (b == 0x00)
509 			{
510 				fRxLength = 0;
511 				newState = kDataState;
512 			}
513 			else
514 				reset = true;
515 			break;
516 		case kDataState:
517 			// verify that odd bytes are complements of previous bytes
518 			if ((fRxLength & 1) && (b + fRxData[fRxLength-1] != 0xff))
519 			{
520 				// complement failed
521 				reset = true;
522 			}
523 			else if (fRxLength < kMaxRxData)
524 			{
525 				fRxData[fRxLength++] = b;
526 			}
527 			else
528 			{
529 				reset = true;
530 			}
531 			break;
532 	}
533 
534 	if (reset) {
535 		newState = kIdleState;
536 
537 		switch(b) {
538 			case 0x55:
539 				newState = kSync1State;
540 				break;
541 			case 0xff:
542 				if (fRxState==kDataState && fRxLength > 0 && fRxData[fRxLength-1]==0x55) {
543 					newState = kSync2State;
544 				}
545 				break;
546 		}
547 
548 	}
549 
550 	fRxState = newState;
551 }
552 
553 
ExpectedReceiveLen(const int rxExpected)554 int RCX_PipeTransport::ExpectedReceiveLen(const int rxExpected)
555 {
556         int result = rxExpected;
557         if (fComplementData)
558             result *= 2;
559         if ((fFastMode && ((fTxLastCommand & 0xf7) == 0xa5)) || fComplementData)
560             result += 2;
561         else
562             result += 1;
563         // allow at least one byte for the header
564         result += 3; // 3 byte header
565         return result;
566 }
567 
VerifyReply(const int rxExpected,const UByte * data,int length,UByte cmd)568 int RCX_PipeTransport::VerifyReply(const int rxExpected, const UByte *data, int length, UByte cmd)
569 {
570 	UByte dataSum = data[0];
571 	const UByte *ptr = data;
572 	const UByte *match = nil;
573 	int width = (fComplementData ? 2 : 1);
574 	const UByte *end = data + length + 1 - width;
575 
576 	// this is a hack to work around the problem that the reply for
577 	// opcode a5 has a complemented command byte even in fast mode
578 	bool complementCmd;
579 	if (fFastMode && ((cmd & 0xf7) == 0xa5))
580 		complementCmd = true;
581 	else
582 		complementCmd = fComplementData;
583 
584 	// always need a cmd and a checksum
585 	if (length < ((complementCmd ? 2 : 1) + width)) return 0;
586 
587 	// check the cmd
588 	if ((*ptr & 0xf7) != (~cmd & 0xf7)) return 0;
589         ptr++;
590 
591 	if (complementCmd)
592 	{
593 		if ((*ptr & 0xf7) != (cmd & 0xf7)) return 0;
594                 ptr++;
595 	}
596 
597 	while(ptr < end)
598 	{
599 		if (fComplementData && ((ptr[0] & 0xf7) != (~ptr[1] & 0xf7))) break;
600 
601 		if (ptr[0] == ComputeChecksum(dataSum, fTarget)) match = ptr;
602 
603 		dataSum += ptr[0];
604 		ptr += width;
605 	}
606 
607         // certain spybot responses have screwed-up checksums when
608         // communicating via USB tower
609         if (!match && (fTarget == kRCX_SpyboticsTarget) && fPipe->IsUSB())
610         {
611               if (length == rxExpected+1)
612                 match = end - 1;
613         }
614 	if (!match) return 0;
615 
616 	return ((match - data) / width);
617 }
618 
619 
ComputeChecksum(UByte dataSum,RCX_TargetType targetType)620 UByte ComputeChecksum(UByte dataSum, RCX_TargetType targetType)
621 {
622 	if (targetType == kRCX_SpyboticsTarget)
623 	{
624 		// preamble + dataSum + checksum = 0
625 		return -0x98 - dataSum;
626 	}
627 	else
628 	{
629 		// checksum = dataSum
630 		return dataSum;
631 	}
632 }
633 
FindSync(const UByte * data,int length,const UByte * sync,const UByte cmd)634 int FindSync(const UByte *data, int length, const UByte *sync, const UByte cmd)
635 {
636 	int syncLen = *sync++;
637         while (syncLen > 0)
638         {
639                 const UByte *end = data + length - syncLen + 1;
640                 const UByte *ptr;
641 
642                 for(ptr=data; ptr<end; ptr++)
643                 {
644                         int i;
645                         for(i=0; i<syncLen; i++)
646                         {
647                                 if (ptr[i] != sync[i]) break;
648                         }
649                         // check the next byte to see if it matches the command.
650                         // if it doesn't then we haven't really found the sync
651                         if ((i==syncLen) && ((ptr[syncLen] & 0xf7) == (~cmd & 0xf7)))
652                         {
653                           return ptr-data+syncLen;
654                         }
655                 }
656                 sync++;
657                 syncLen--;
658         }
659 
660 	return 0;
661 }
662