1 /*
2  * UsbCdcIoChannel.cpp
3  *
4  * IOChannel via CDC (VCOM) over USB communication.
5  *
6  * Copyright (C) 2007 - 2011 Texas Instruments Incorporated - http://www.ti.com/
7  *
8  *
9  *  Redistribution and use in source and binary forms, with or without
10  *  modification, are permitted provided that the following conditions
11  *  are met:
12  *
13  *    Redistributions of source code must retain the above copyright
14  *    notice, this list of conditions and the following disclaimer.
15  *
16  *    Redistributions in binary form must reproduce the above copyright
17  *    notice, this list of conditions and the following disclaimer in the
18  *    documentation and/or other materials provided with the
19  *    distribution.
20  *
21  *    Neither the name of Texas Instruments Incorporated nor the names of
22  *    its contributors may be used to endorse or promote products derived
23  *    from this software without specific prior written permission.
24  *
25  *  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
26  *  "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
27  *  LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
28  *  A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
29  *  OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
30  *  SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
31  *  LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
32  *  DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
33  *  THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
34  *  (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
35  *  OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
36  */
37 
38 #include <pch.h>
39 
40 #include "UsbCdcIoChannel.h"
41 #include "logging/Logging.h"
42 
43 #include <boost/asio/read.hpp>
44 #include <boost/asio/write.hpp>
45 #include <boost/asio/io_service.hpp>
46 
47 #if defined(_WIN32) || defined(_WIN64)
48 
49 extern "C" {
50 	#include <setupapi.h>
51 	#include <dbt.h>
52 }
53 
54 #elif defined(__APPLE__)
55 
56 #include <CoreFoundation/CoreFoundation.h>
57 #include <IOKit/IOKitLib.h>
58 #include <IOKit/IOCFPlugIn.h>
59 #include <IOKit/usb/IOUSBLib.h>
60 #include <IOKit/IOMessage.h>
61 #include <mach/mach_port.h>
62 #include <IOKit/serial/IOSerialKeys.h>
63 #include <sys/stat.h>
64 #define MAXPATHLEN 128
65 #define MAXNAMELEN 64
66 
67 #else
68 
69 	#include <unistd.h>
70 	#include <boost/filesystem.hpp>
71 
72 	using namespace boost::filesystem;
73 
74 #endif
75 
76 using namespace TI::DLL430;
77 using namespace std;
78 using namespace std::placeholders;
79 using namespace boost::asio;
80 
81 #define  XOFF		0x13
82 #define  XON		0x11
83 #define  XMASK		0x10
84 #define  XMASK_OFF	0x03
85 #define  XMASK_ON	0x01
86 #define  XMASK_MASK	0x00
87 
88 
UsbCdcIoChannel(const PortInfo & portInfo)89 UsbCdcIoChannel::UsbCdcIoChannel(const PortInfo& portInfo)
90  : UsbIoChannel(portInfo)
91  , inputBuffer(260)
92  , ioService(0)
93  , port(0)
94  , comState(ComStateRcv)
95  , bytesReceived(0)
96  , timerEvent(false)
97  , readEvent(false)
98  , cancelled(false)
99 {
100 	retrieveStatus();
101 }
102 
~UsbCdcIoChannel()103 UsbCdcIoChannel::~UsbCdcIoChannel()
104 {
105 	this->cleanup();
106 }
107 
createCdcPortList(const uint16_t vendorId,const uint16_t productId,PortMap & portList)108 void UsbCdcIoChannel::createCdcPortList(const uint16_t vendorId, const uint16_t productId, PortMap& portList)
109 {
110 #if defined(_WIN32) || defined(_WIN64)
111 	stringstream cdcIdStream;
112 	cdcIdStream << hex << setfill('0') << "USB\\VID_" << setw(4) << vendorId << "&PID_" << setw(4) << productId;
113 
114 	const int BUFFER_SIZE = 128;
115 
116 	HDEVINFO hDevInfo = ::SetupDiGetClassDevs(nullptr, "USB", 0, DIGCF_PRESENT | DIGCF_ALLCLASSES );
117 	SP_DEVINFO_DATA devInfoData;
118 	devInfoData.cbSize = sizeof(SP_DEVINFO_DATA);
119 
120 	for (int i = 0; ::SetupDiEnumDeviceInfo(hDevInfo, i, &devInfoData); ++i )
121 	{
122 		char deviceId[BUFFER_SIZE] = {0};
123 		BOOL result = ::SetupDiGetDeviceInstanceId(hDevInfo, &devInfoData, deviceId, BUFFER_SIZE, nullptr);
124 
125 		//not TI and/or not CDC
126 		if (result && string(deviceId).find(cdcIdStream.str()) != string::npos)
127 		{
128 			DWORD propertyType = 0;
129 			BYTE property[BUFFER_SIZE] = {0};
130 
131 			::SetupDiGetDeviceRegistryProperty(hDevInfo, &devInfoData, SPDRP_FRIENDLYNAME, &propertyType, property, BUFFER_SIZE, nullptr);
132 
133 			stringstream sstr;
134 			for (int k = 0; k < BUFFER_SIZE && property[k] != 0; ++k)
135 			{
136 				sstr << property[k];
137 			}
138 
139 			const size_t idBegin = sstr.str().find_last_of('(') + 1;
140 			const size_t idEnd = sstr.str().find_last_of(')');
141 			assert(idEnd > idBegin);
142 
143 			const string name = sstr.str().substr(idBegin, idEnd - idBegin);
144 
145 			if ((name[0] && (sstr.str().compare(0, 19, "MSP Debug Interface") == 0 ))|| (name[0] && (sstr.str().compare(0, 19, "MSP-FET430UIF - CDC") == 0 )))
146 			{
147 				PortInfo portInfo(name, string("\\\\.\\")+name, PortInfo::CDC, retrieveSerialFromId(deviceId));
148 				if (name[0] && (sstr.str().compare(0, 19, "MSP Debug Interface") == 0 ))
149 				{
150 					portInfo.useFlowControl = false;
151 					portInfo.useCrc = false;
152 				}
153 				else if (name[0] && (sstr.str().compare(0, 19, "MSP-FET430UIF - CDC") == 0 ))
154 				{
155 					portInfo.useFlowControl = true;
156 					portInfo.useCrc = true;
157 				}
158 
159 				//if (open)
160 				{
161 					portInfo.status = UsbCdcIoChannel(portInfo).getStatus();
162 				}
163 				portList[portInfo.name] = portInfo;
164 			}
165 		}
166 	}
167 	::SetupDiDestroyDeviceInfoList(hDevInfo);//free resources
168 
169 #elif defined(__APPLE__)
170 	CFMutableDictionaryRef matchingDict;
171 	kern_return_t kernResult;
172 	io_iterator_t iterator;
173 
174 	matchingDict = IOServiceMatching(kIOSerialBSDServiceValue);
175 	CFDictionarySetValue(matchingDict, CFSTR(kIOSerialBSDTypeKey), CFSTR(kIOSerialBSDAllTypes));
176 	kernResult = IOServiceGetMatchingServices(kIOMasterPortDefault, matchingDict, &iterator);
177 
178 	if (kernResult != KERN_SUCCESS) {
179 		return;
180 	}
181 
182 	for (;;)
183 	{
184 		io_service_t device = IOIteratorNext(iterator);
185 		if (device == 0)
186 		{
187 			if (!IOIteratorIsValid(iterator))
188 			{
189 				/*
190 				 * Apple documentation advises resetting the iterator if
191 				 * it should become invalid during iteration.
192 				 */
193 				IOIteratorReset(iterator);
194 				device = IOIteratorNext(iterator);
195 				if (device == 0)
196 				{
197 					break;
198 				}
199 			}
200 			else
201 			{
202 				break;
203 			}
204 		}
205 
206 		CFTypeRef bsdPathAsCFString = nullptr;
207 		CFTypeRef vendorIdAsCFNumber = nullptr;
208 		CFTypeRef productIdAsCFNumber = nullptr;
209 		CFTypeRef ttyDeviceAsCFString = nullptr;
210 		CFTypeRef interfaceNumberAsCFNumber = nullptr;
211 
212 		char ttyDevice[MAXNAMELEN];
213 		SInt32 interfaceNumber;
214 		char path[MAXPATHLEN];
215 		SInt32 vID = 0;
216 		SInt32 pID = 0;
217 
218 		// Get the name of the modem's callout device
219 		bsdPathAsCFString = IORegistryEntryCreateCFProperty(device, CFSTR(kIOCalloutDeviceKey),
220 		                                                    kCFAllocatorDefault, 0);
221 
222 		ttyDeviceAsCFString = IORegistryEntryCreateCFProperty(device, CFSTR(kIOTTYDeviceKey),
223 		                                                      kCFAllocatorDefault, 0);
224 
225 		io_name_t name;
226 		IORegistryEntryGetName(device, name);
227 
228 		// wander up the hierarchy until we find the level that can give us the
229 		// vendor/product IDs and the product name, if available
230 		io_registry_entry_t parent;
231 		kern_return_t kernResult = IORegistryEntryGetParentEntry(device, kIOServicePlane, &parent);
232 		IOObjectRelease(device);
233 
234         while ( kernResult == KERN_SUCCESS && ( !vendorIdAsCFNumber || !productIdAsCFNumber || !interfaceNumberAsCFNumber) )
235 		{
236 			if (!vendorIdAsCFNumber)
237 			{
238 				vendorIdAsCFNumber = IORegistryEntrySearchCFProperty(parent,
239 				                                                     kIOServicePlane,
240 				                                                     CFSTR(kUSBVendorID),
241 				                                                     kCFAllocatorDefault, 0);
242 			}
243 
244 			if (!productIdAsCFNumber)
245 			{
246 				productIdAsCFNumber = IORegistryEntrySearchCFProperty(parent,
247 				                                                      kIOServicePlane,
248 				                                                      CFSTR(kUSBProductID),
249 				                                                      kCFAllocatorDefault, 0);
250 			}
251 
252 			if (!interfaceNumberAsCFNumber)
253 			{
254 				interfaceNumberAsCFNumber = IORegistryEntrySearchCFProperty(parent,
255 				                                                            kIOServicePlane,
256 				                                                            CFSTR(kUSBInterfaceNumber),
257 				                                                            kCFAllocatorDefault, 0);
258 			}
259 
260 			io_registry_entry_t oldparent = parent;
261 			kernResult = IORegistryEntryGetParentEntry(parent, kIOServicePlane, &parent);
262 			IOObjectRelease(oldparent);
263 		}
264 
265 		if (interfaceNumberAsCFNumber)
266 		{
267 			CFNumberGetValue((CFNumberRef)interfaceNumberAsCFNumber, kCFNumberSInt32Type, &interfaceNumber);
268 			CFRelease(interfaceNumberAsCFNumber);
269 			if (interfaceNumber != 1)
270 			{
271 				continue;
272 			}
273 		}
274 
275 		if (ttyDeviceAsCFString)
276 		{
277 			CFStringGetCString((CFStringRef)ttyDeviceAsCFString, ttyDevice, PATH_MAX, kCFStringEncodingUTF8);
278 			CFRelease(ttyDeviceAsCFString);
279 		}
280 
281 		if (bsdPathAsCFString)
282 		{
283 			CFStringGetCString((CFStringRef)bsdPathAsCFString, path, PATH_MAX, kCFStringEncodingUTF8);
284 			CFRelease(bsdPathAsCFString);
285 		}
286 
287 		if (vendorIdAsCFNumber)
288 		{
289 			CFNumberGetValue((CFNumberRef)vendorIdAsCFNumber, kCFNumberSInt32Type, &vID);
290 			CFRelease(vendorIdAsCFNumber);
291 		}
292 
293 		if (productIdAsCFNumber)
294 		{
295 			CFNumberGetValue((CFNumberRef)productIdAsCFNumber, kCFNumberSInt32Type, &pID);
296 			CFRelease(productIdAsCFNumber);
297 		}
298 
299 		if ((vID == vendorId) && (pID == productId))
300 		{
301 			PortInfo portInfo(ttyDevice, path, PortInfo::CDC);
302 			if (productId == 0x0010)
303 			{
304 				portInfo.useFlowControl = true;
305 				portInfo.useCrc = true;
306 			}
307 			portInfo.status = UsbCdcIoChannel(portInfo).getStatus();
308 			portList[portInfo.name] = portInfo;
309 		}
310 	}
311 #else
312 	stringstream cdcIdStream;
313 
314 #ifdef __FreeBSD__
315 
316 	path p( "/dev" );
317 	if( exists(p) && is_directory(p) ) {
318 
319 		cdcIdStream << hex << setfill('0') << "mspfet" << setw(4) << productId;
320 
321 		const directory_iterator end;
322 		for( directory_iterator it(p); it != end; ++it ) {
323 
324 			string dir = it->path().string();
325 			if( dir.find( cdcIdStream.str() ) != string::npos ) {
326 	      			{
327 
328 #else
329 	cdcIdStream << hex << setfill('0') << "usb:v" << setw(4) << vendorId << "p" << setw(4) << productId;
330 
331 	path p("/sys/class/tty/");
332 	if (exists(p) && is_directory(p))
333 	{
334 		const directory_iterator end;
335 		for (directory_iterator it(p); it != end; ++it)
336 		{
337 			string dir = it->path().string();
338 			if (dir.find("ttyACM") != string::npos)
339 			{
340 				string modalias;
341 				int interfaceNumber = -1;
342 
343 				std::ifstream modAliasStream((it->path()/"device/modalias").string().c_str());
344 				modAliasStream >> modalias;
345 
346 				std::ifstream ifNumStream((it->path()/"device/bInterfaceNumber").string().c_str());
347 				ifNumStream >> interfaceNumber;
348 				if (modalias.find(cdcIdStream.str()) == 0 && interfaceNumber == 0)
349 				{
350 #endif
351 					const string filename = it->path().filename().string();
352 					const string portPath = string("/dev/") + filename;
353 
354 					PortInfo portInfo(filename, portPath, PortInfo::CDC);
355 
356 					if (productId == 0x0010)
357 					{
358 						portInfo.useFlowControl = true;
359 						portInfo.useCrc = true;
360 					}
361 
362 					//if (open)
363 					{
364 						portInfo.status = UsbCdcIoChannel(portInfo).getStatus();
365 					}
366 					portList[portInfo.name] = portInfo;
367 				}
368 			}
369 		}
370 	}
371 #endif
372 }
373 
374 
375 void UsbCdcIoChannel::enumeratePorts (PortMap& portList, bool open)
376 {
377 	createCdcPortList(0x2047, 0x0013, portList); //eZ-FET
378 	createCdcPortList(0x2047, 0x0014, portList); //MSP-FET
379 	createCdcPortList(0x2047, 0x0010, portList); //UIF
380 }
381 
382 std::string UsbCdcIoChannel::retrieveSerialFromId(const std::string& id)
383 {
384 #if defined(_WIN32) || defined(_WIN64)
385 	const size_t idBegin = id.find_last_of('\\') + 1;
386 	return id.substr(idBegin, 16);
387 #else
388 	const size_t begin = id.find_last_of('_') + 1;
389 	const size_t end = id.find_last_of('-');
390 	return id.substr( begin, end - begin );
391 #endif
392 }
393 
394 bool UsbCdcIoChannel::openPort()
395 {
396 	ioService = new boost::asio::io_service;
397 	port = new boost::asio::serial_port(*ioService);
398 	timer = new boost::asio::deadline_timer(*ioService);
399 
400 	boost::system::error_code ec = port->open(portInfo.path, ec);
401 	if (ec != boost::system::error_condition(boost::system::errc::success))
402 	{
403 		int retry = 5;
404 		while ((ec != boost::system::error_condition(boost::system::errc::success))
405 			&& (--retry ))
406 		{
407 			std::this_thread::sleep_for(std::chrono::milliseconds(5));
408 			ec = port->open(portInfo.path, ec);
409 		}
410 
411 		if (ec == boost::system::error_condition(boost::system::errc::permission_denied))
412 		{
413 			portInfo.status = PortInfo::inUseByAnotherInstance;
414 		}
415 		if (ec != boost::system::error_condition(boost::system::errc::success))
416 		{
417 			close();
418 			return false;
419 		}
420 	}
421 	return true;
422 }
423 
424 void UsbCdcIoChannel::retrieveStatus()
425 {
426 	portInfo.status = PortInfo::freeForUse;
427 
428 	if (!isOpen())
429 	{
430 		openPort();
431 		//Seeing issues on some platforms (eg. Ubuntu) when port is immediately closed again
432 		std::this_thread::sleep_for(std::chrono::milliseconds(100));
433 		close();
434 	}
435 }
436 
437 
438 bool UsbCdcIoChannel::open()
439 {
440 	if (!isOpen() && !openPort())
441 	{
442 		return false;
443 	}
444 
445 	portInfo.status = PortInfo::freeForUse;
446 
447 	try
448 	{
449 		const int baudrate = 460800;
450 
451 #if defined(__APPLE__)
452 		// Vanilla boost does not provide method to set non-standard baudrates,
453 		// so we have to set it low-level
454 		if (ioctl (port->native_handle(), _IOW('T', 2, speed_t), &baudrate, 1) < 0)
455 		{
456 			return false;
457 		}
458 #else
459 		port->set_option( serial_port::baud_rate( baudrate ) );
460 #endif
461 		port->set_option( serial_port::flow_control( serial_port::flow_control::none ) );
462 		port->set_option( serial_port::parity( serial_port::parity::none ) );
463 		port->set_option( serial_port::stop_bits( serial_port::stop_bits::one ) );
464 		port->set_option( serial_port::character_size(8) );
465 	}
466 	catch (const boost::system::system_error&)
467 	{
468 		return false;
469 	}
470 
471 	return true;
472 }
473 
474 void UsbCdcIoChannel::cleanup()
475 {
476 	if (isOpen())
477 	{
478 		boost::system::error_code ec = port->close(ec);
479 	}
480 	delete timer;
481 	timer = 0;
482 	delete port;
483 	port = 0;
484 	delete ioService;
485 	ioService = 0;
486 }
487 
488 bool UsbCdcIoChannel::close()
489 {
490 	cleanup();
491 	return true;
492 }
493 
494 
495 bool UsbCdcIoChannel::isOpen() const
496 {
497 	return port && port->is_open();
498 }
499 
500 
501 void UsbCdcIoChannel::cancel()
502 {
503 	cancelled = true;
504 
505 	boost::system::error_code ec;
506 	if (timer && timer->expires_from_now(boost::posix_time::milliseconds(0), ec) > 0)
507 	{
508 		timer->async_wait(std::bind(&UsbCdcIoChannel::onTimer, this, std::placeholders::_1));
509 	}
510 }
511 
512 
513 void UsbCdcIoChannel::setTimer(uint32_t duration)
514 {
515 	timerEvent = false;
516 
517 	if (timer)
518 	{
519 		boost::system::error_code ec;
520 		timer->expires_from_now(boost::posix_time::milliseconds(duration), ec);
521 		timer->async_wait(bind(&UsbCdcIoChannel::onTimer, this, std::placeholders::_1));
522 	}
523 }
524 
525 
526 void UsbCdcIoChannel::startRead(size_t offset, size_t numBytes)
527 {
528 	bytesReceived = 0;
529 	readEvent = false;
530 	async_read(*port, buffer(&inputBuffer[offset], numBytes), bind(&UsbCdcIoChannel::onRead, this, std::placeholders::_1, std::placeholders::_2));
531 }
532 
533 
534 void UsbCdcIoChannel::onTimer(const boost::system::error_code& ec)
535 {
536 	timerEvent = (ec != error::operation_aborted);
537 }
538 
539 
540 void UsbCdcIoChannel::onRead(const boost::system::error_code& ec, size_t numBytes)
541 {
542 	readEvent = (ec != error::operation_aborted);
543 	bytesReceived = numBytes;
544 }
545 
546 
547 size_t UsbCdcIoChannel::read(HalResponse& resp)
548 {
549 	if (!isOpen())
550 		return 0;
551 
552 	size_t actSize = 0;
553 	size_t expSize = 1;
554 
555 	setTimer(1000);
556 	startRead(0, expSize);
557 
558 	boost::system::error_code ec;
559 
560 	while (ioService->run_one(ec))
561 	{
562 		if (readEvent)
563 		{
564 			if (bytesReceived > 0)
565 			{
566 				if (actSize == 0)
567 					expSize = inputBuffer[0] + ( (inputBuffer[0] & 0x01) ? 3 : 4);
568 
569 				actSize += bytesReceived;
570 
571 				if (actSize == expSize)
572 				{
573 					timer->cancel(ec);
574 					break;
575 				}
576 			}
577 
578 			startRead(actSize, expSize - actSize);
579 		}
580 
581 		else if (timerEvent)
582 		{
583 			if (wasUnplugged() || cancelled)
584 			{
585 				cancelled = false;
586 				port->cancel(ec);
587 				break;
588 			}
589 
590 			setTimer(1000);
591 		}
592 
593 		if (ioService->stopped())
594 		{
595 			ioService->reset();
596 		}
597 	}
598 
599 	//Let cancelled tasks finish
600 	ioService->run(ec);
601 	ioService->reset();
602 
603 
604 	if (actSize == expSize)
605 	{
606 		processMessage(actSize, resp);
607 		return actSize;
608 	}
609 	return 0;
610 }
611 
612 
613 bool UsbCdcIoChannel::wasUnplugged()
614 {
615 #if defined(_WIN32) || defined(_WIN64)
616 	boost::system::error_code ec = serial_port(*ioService).open(portInfo.path, ec);
617 	if (ec == boost::system::error_condition(boost::system::errc::no_such_file_or_directory))
618 	{
619 		comState = ComStateDisconnect;
620 	}
621 #else
622 	/*
623 	 * Workaround for El Capitan with UIF: the above branch triggers sending of SET_LINE_CODING
624 	 * Do not trigger SET_LINE_CODING here
625 	 */
626 
627 	/* Also workaround for Ubuntu 16 with UIF: the above branch triggers sending of SetControlLineState, which we don't want here */
628 
629 	struct stat dev;
630 	if (stat(portInfo.path.c_str(), &dev) != 0)
631 	{
632 		comState = ComStateDisconnect;
633 	}
634 #endif
635 	return comState == ComStateDisconnect;
636 }
637 
638 
639 void UsbCdcIoChannel::processMessage(size_t msgSize, HalResponse& resp)
640 {
641 #ifdef DB_PRINT
642 	Logging::DefaultLogger().PrintReceiveBuffer(&inputBuffer[0], static_cast<long>(msgSize));
643 #endif // DB_PRINT
644 
645 	if (portInfo.useCrc)
646 	{
647 		const uint16_t expCrc = createCrc(&inputBuffer[0]);
648 		const uint16_t actCrc = (inputBuffer[msgSize-1] << 8) | inputBuffer[msgSize-2];
649 		if (actCrc != expCrc)
650 		{
651 			resp.setError(HalResponse::Error_CRC);
652 		}
653 	}
654 	resp.setType(inputBuffer[1]);
655 	resp.setId(inputBuffer[2] & 0x7f); //Don't mask async bit (0x40)
656 	resp.setIsComplete(inputBuffer[2]);
657 
658 	if (msgSize >= 2)
659 	{
660 		resp.append(&inputBuffer[1], inputBuffer[0]);
661 	}
662 }
663 
664 
665 enum ComState UsbCdcIoChannel::poll()
666 {
667 	return comState;
668 }
669 
670 
671 size_t UsbCdcIoChannel::write(const uint8_t* payload, size_t len)
672 {
673 	if (!isOpen())
674 		return 0;
675 
676 	const size_t ret_len = len;
677 
678 	uint8_t report[256] = {0};
679 
680 	if (payload)
681 		memcpy(report, payload, len);
682 
683 	// test for fill byte
684 	if (!(report[0] & 0x01))
685 		report[len++] = 0x00;
686 
687 	if (portInfo.useCrc)
688 	{
689 		//create crc and append it to data
690 		uint16_t crc = createCrc(report);
691 
692 		report[len++] = crc & 0x00ff;
693 		report[len++] = (crc & 0xff00) >> 8;
694 	}
695 
696 	size_t n_write = 0;
697 	uint8_t send_buf[512];
698 
699 	if (portInfo.useFlowControl)
700 	{
701 		//mask XOFF, XON and MASK in data stream
702 		size_t j = 0;
703 
704 		for (size_t i = 0; i < len; i++)
705 		{
706 			const uint8_t ch = report[i];
707 			switch (ch)
708 			{
709 			case XOFF:
710 			case XON:
711 			case XMASK:
712 				send_buf[j] = XMASK;
713 				j++;
714 				send_buf[j] = ch & 0x3;
715 				break;
716 			default:
717 				send_buf[j] = ch;
718 			}
719 			j++;
720 		}
721 		n_write = j;
722 	}
723 	else
724 	{
725 		n_write = len;
726 		memcpy(send_buf, report, n_write);
727 	}
728 
729 #ifdef DB_PRINT
730 	Logging::DefaultLogger().PrintSendBuffer(send_buf, static_cast<long>(n_write));
731 #endif // DB_PRINT
732 
733 	boost::system::error_code ec;
734 	const size_t nWritten = boost::asio::write(*port, buffer(send_buf, n_write), transfer_all(), ec);
735 
736 	if (nWritten != n_write)
737 	{
738 		return 0;
739 	}
740 
741 	return ret_len;
742 }
743 
744 const char* UsbCdcIoChannel::getName() const
745 {
746 	return portInfo.name.c_str();
747 }
748 
749 string UsbCdcIoChannel::getSerial() const
750 {
751 	return portInfo.serial;
752 }
753 
754 PortInfo::Status UsbCdcIoChannel::getStatus() const
755 {
756  	return portInfo.status;
757 }
758