[M73 Dev][Tizen] Fix linker errors
[platform/framework/web/chromium-efl.git] / services / device / serial / serial_io_handler_posix.cc
1 // Copyright 2014 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "services/device/serial/serial_io_handler_posix.h"
6
7 #include <sys/ioctl.h>
8 #include <termios.h>
9
10 #include <algorithm>
11 #include <utility>
12
13 #include "base/files/file_util.h"
14 #include "base/posix/eintr_wrapper.h"
15 #include "build/build_config.h"
16
17 #if defined(OS_LINUX)
18 #include <asm-generic/ioctls.h>
19 #include <linux/serial.h>
20
21 // The definition of struct termios2 is copied from asm-generic/termbits.h
22 // because including that header directly conflicts with termios.h.
23 extern "C" {
24 struct termios2 {
25   tcflag_t c_iflag;  // input mode flags
26   tcflag_t c_oflag;  // output mode flags
27   tcflag_t c_cflag;  // control mode flags
28   tcflag_t c_lflag;  // local mode flags
29   cc_t c_line;       // line discipline
30   cc_t c_cc[19];     // control characters
31   speed_t c_ispeed;  // input speed
32   speed_t c_ospeed;  // output speed
33 };
34 }
35
36 #endif  // defined(OS_LINUX)
37
38 #if defined(OS_MACOSX)
39 #include <IOKit/serial/ioss.h>
40 #endif
41
42 namespace {
43
44 // Convert an integral bit rate to a nominal one. Returns |true|
45 // if the conversion was successful and |false| otherwise.
46 bool BitrateToSpeedConstant(int bitrate, speed_t* speed) {
47 #define BITRATE_TO_SPEED_CASE(x) \
48   case x:                        \
49     *speed = B##x;               \
50     return true;
51   switch (bitrate) {
52     BITRATE_TO_SPEED_CASE(0)
53     BITRATE_TO_SPEED_CASE(50)
54     BITRATE_TO_SPEED_CASE(75)
55     BITRATE_TO_SPEED_CASE(110)
56     BITRATE_TO_SPEED_CASE(134)
57     BITRATE_TO_SPEED_CASE(150)
58     BITRATE_TO_SPEED_CASE(200)
59     BITRATE_TO_SPEED_CASE(300)
60     BITRATE_TO_SPEED_CASE(600)
61     BITRATE_TO_SPEED_CASE(1200)
62     BITRATE_TO_SPEED_CASE(1800)
63     BITRATE_TO_SPEED_CASE(2400)
64     BITRATE_TO_SPEED_CASE(4800)
65     BITRATE_TO_SPEED_CASE(9600)
66     BITRATE_TO_SPEED_CASE(19200)
67     BITRATE_TO_SPEED_CASE(38400)
68 #if !defined(OS_MACOSX)
69     BITRATE_TO_SPEED_CASE(57600)
70     BITRATE_TO_SPEED_CASE(115200)
71     BITRATE_TO_SPEED_CASE(230400)
72     BITRATE_TO_SPEED_CASE(460800)
73     BITRATE_TO_SPEED_CASE(576000)
74     BITRATE_TO_SPEED_CASE(921600)
75 #endif
76     default:
77       return false;
78   }
79 #undef BITRATE_TO_SPEED_CASE
80 }
81
82 #if !defined(OS_LINUX)
83 // Convert a known nominal speed into an integral bitrate. Returns |true|
84 // if the conversion was successful and |false| otherwise.
85 bool SpeedConstantToBitrate(speed_t speed, int* bitrate) {
86 #define SPEED_TO_BITRATE_CASE(x) \
87   case B##x:                     \
88     *bitrate = x;                \
89     return true;
90   switch (speed) {
91     SPEED_TO_BITRATE_CASE(0)
92     SPEED_TO_BITRATE_CASE(50)
93     SPEED_TO_BITRATE_CASE(75)
94     SPEED_TO_BITRATE_CASE(110)
95     SPEED_TO_BITRATE_CASE(134)
96     SPEED_TO_BITRATE_CASE(150)
97     SPEED_TO_BITRATE_CASE(200)
98     SPEED_TO_BITRATE_CASE(300)
99     SPEED_TO_BITRATE_CASE(600)
100     SPEED_TO_BITRATE_CASE(1200)
101     SPEED_TO_BITRATE_CASE(1800)
102     SPEED_TO_BITRATE_CASE(2400)
103     SPEED_TO_BITRATE_CASE(4800)
104     SPEED_TO_BITRATE_CASE(9600)
105     SPEED_TO_BITRATE_CASE(19200)
106     SPEED_TO_BITRATE_CASE(38400)
107     default:
108       return false;
109   }
110 #undef SPEED_TO_BITRATE_CASE
111 }
112 #endif
113
114 }  // namespace
115
116 namespace device {
117
118 // static
119 scoped_refptr<SerialIoHandler> SerialIoHandler::Create(
120     const base::FilePath& port,
121     scoped_refptr<base::SingleThreadTaskRunner> ui_thread_task_runner) {
122   return new SerialIoHandlerPosix(port, std::move(ui_thread_task_runner));
123 }
124
125 void SerialIoHandlerPosix::ReadImpl() {
126   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
127   DCHECK(pending_read_buffer());
128   DCHECK(file().IsValid());
129
130   // Try to read immediately. This is needed because on some platforms
131   // (e.g., OSX) there may not be a notification from the message loop
132   // when the fd is ready to read immediately after it is opened. There
133   // is no danger of blocking because the fd is opened with async flag.
134   AttemptRead(true);
135 }
136
137 void SerialIoHandlerPosix::WriteImpl() {
138   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
139   DCHECK(pending_write_buffer());
140   DCHECK(file().IsValid());
141
142   EnsureWatchingWrites();
143 }
144
145 void SerialIoHandlerPosix::CancelReadImpl() {
146   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
147   file_read_watcher_.reset();
148   QueueReadCompleted(0, read_cancel_reason());
149 }
150
151 void SerialIoHandlerPosix::CancelWriteImpl() {
152   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
153   file_write_watcher_.reset();
154   QueueWriteCompleted(0, write_cancel_reason());
155 }
156
157 bool SerialIoHandlerPosix::ConfigurePortImpl() {
158 #if defined(OS_LINUX)
159   struct termios2 config;
160   if (ioctl(file().GetPlatformFile(), TCGETS2, &config) < 0) {
161 #else
162   struct termios config;
163   if (tcgetattr(file().GetPlatformFile(), &config) != 0) {
164 #endif
165     VPLOG(1) << "Failed to get port configuration";
166     return false;
167   }
168
169   // Set flags for 'raw' operation
170   config.c_lflag &= ~(ICANON | ECHO | ECHOE | ECHONL | ISIG);
171   config.c_iflag &= ~(IGNBRK | BRKINT | ISTRIP | INLCR | IGNCR | ICRNL | IXON);
172   config.c_iflag |= PARMRK;
173   config.c_oflag &= ~OPOST;
174
175   // CLOCAL causes the system to disregard the DCD signal state.
176   // CREAD enables reading from the port.
177   config.c_cflag |= (CLOCAL | CREAD);
178
179   DCHECK(options().bitrate);
180   speed_t bitrate_opt = B0;
181 #if defined(OS_MACOSX)
182   bool need_iossiospeed = false;
183 #endif
184   if (BitrateToSpeedConstant(options().bitrate, &bitrate_opt)) {
185 #if defined(OS_LINUX)
186     config.c_cflag &= ~CBAUD;
187     config.c_cflag |= bitrate_opt;
188 #else
189     cfsetispeed(&config, bitrate_opt);
190     cfsetospeed(&config, bitrate_opt);
191 #endif
192   } else {
193     // Attempt to set a custom speed.
194 #if defined(OS_LINUX)
195     config.c_cflag &= ~CBAUD;
196     config.c_cflag |= CBAUDEX;
197     config.c_ispeed = config.c_ospeed = options().bitrate;
198 #elif defined(OS_MACOSX)
199     // cfsetispeed and cfsetospeed sometimes work for custom baud rates on OS
200     // X but the IOSSIOSPEED ioctl is more reliable but has to be done after
201     // the rest of the port parameters are set or else it will be overwritten.
202     need_iossiospeed = true;
203 #else
204     return false;
205 #endif
206   }
207
208   DCHECK(options().data_bits != mojom::SerialDataBits::NONE);
209   config.c_cflag &= ~CSIZE;
210   switch (options().data_bits) {
211     case mojom::SerialDataBits::SEVEN:
212       config.c_cflag |= CS7;
213       break;
214     case mojom::SerialDataBits::EIGHT:
215     default:
216       config.c_cflag |= CS8;
217       break;
218   }
219
220   DCHECK(options().parity_bit != mojom::SerialParityBit::NONE);
221   switch (options().parity_bit) {
222     case mojom::SerialParityBit::EVEN:
223       config.c_cflag |= PARENB;
224       config.c_cflag &= ~PARODD;
225       break;
226     case mojom::SerialParityBit::ODD:
227       config.c_cflag |= (PARODD | PARENB);
228       break;
229     case mojom::SerialParityBit::NO_PARITY:
230     default:
231       config.c_cflag &= ~(PARODD | PARENB);
232       break;
233   }
234
235   error_detect_state_ = ErrorDetectState::NO_ERROR;
236   num_chars_stashed_ = 0;
237
238   if (config.c_cflag & PARENB) {
239     config.c_iflag &= ~IGNPAR;
240     config.c_iflag |= INPCK;
241     parity_check_enabled_ = true;
242   } else {
243     config.c_iflag |= IGNPAR;
244     config.c_iflag &= ~INPCK;
245     parity_check_enabled_ = false;
246   }
247
248   DCHECK(options().stop_bits != mojom::SerialStopBits::NONE);
249   switch (options().stop_bits) {
250     case mojom::SerialStopBits::TWO:
251       config.c_cflag |= CSTOPB;
252       break;
253     case mojom::SerialStopBits::ONE:
254     default:
255       config.c_cflag &= ~CSTOPB;
256       break;
257   }
258
259   DCHECK(options().has_cts_flow_control);
260   if (options().cts_flow_control) {
261     config.c_cflag |= CRTSCTS;
262   } else {
263     config.c_cflag &= ~CRTSCTS;
264   }
265
266 #if defined(OS_LINUX)
267   if (ioctl(file().GetPlatformFile(), TCSETS2, &config) < 0) {
268 #else
269   if (tcsetattr(file().GetPlatformFile(), TCSANOW, &config) != 0) {
270 #endif
271     VPLOG(1) << "Failed to set port attributes";
272     return false;
273   }
274
275 #if defined(OS_MACOSX)
276   if (need_iossiospeed) {
277     speed_t bitrate = options().bitrate;
278     if (ioctl(file().GetPlatformFile(), IOSSIOSPEED, &bitrate) == -1) {
279       VPLOG(1) << "Failed to set custom baud rate";
280       return false;
281     }
282   }
283 #endif
284
285   return true;
286 }
287
288 bool SerialIoHandlerPosix::PostOpen() {
289 #if defined(OS_CHROMEOS)
290   // The Chrome OS permission broker does not open devices in async mode.
291   return base::SetNonBlocking(file().GetPlatformFile());
292 #else
293   return true;
294 #endif
295 }
296
297 SerialIoHandlerPosix::SerialIoHandlerPosix(
298     const base::FilePath& port,
299     scoped_refptr<base::SingleThreadTaskRunner> ui_thread_task_runner)
300     : SerialIoHandler(port, std::move(ui_thread_task_runner)) {}
301
302 SerialIoHandlerPosix::~SerialIoHandlerPosix() = default;
303
304 void SerialIoHandlerPosix::AttemptRead(bool within_read) {
305   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
306
307   if (pending_read_buffer()) {
308     int bytes_read =
309         HANDLE_EINTR(read(file().GetPlatformFile(), pending_read_buffer(),
310                           pending_read_buffer_len()));
311     if (bytes_read < 0) {
312       if (errno == EAGAIN) {
313         // The fd does not have data to read yet so continue waiting.
314         EnsureWatchingReads();
315       } else if (errno == ENXIO) {
316         RunReadCompleted(within_read, 0,
317                          mojom::SerialReceiveError::DEVICE_LOST);
318       } else {
319         RunReadCompleted(within_read, 0,
320                          mojom::SerialReceiveError::SYSTEM_ERROR);
321       }
322     } else if (bytes_read == 0) {
323       RunReadCompleted(within_read, 0, mojom::SerialReceiveError::DEVICE_LOST);
324     } else {
325       bool break_detected = false;
326       bool parity_error_detected = false;
327       int new_bytes_read =
328           CheckReceiveError(pending_read_buffer(), pending_read_buffer_len(),
329                             bytes_read, break_detected, parity_error_detected);
330
331       if (break_detected) {
332         RunReadCompleted(within_read, new_bytes_read,
333                          mojom::SerialReceiveError::BREAK);
334       } else if (parity_error_detected) {
335         RunReadCompleted(within_read, new_bytes_read,
336                          mojom::SerialReceiveError::PARITY_ERROR);
337       } else {
338         RunReadCompleted(within_read, new_bytes_read,
339                          mojom::SerialReceiveError::NONE);
340       }
341     }
342   } else {
343     // Stop watching the fd if we get notifications with no pending
344     // reads or writes to avoid starving the message loop.
345     file_read_watcher_.reset();
346   }
347 }
348
349 void SerialIoHandlerPosix::RunReadCompleted(bool within_read,
350                                             int bytes_read,
351                                             mojom::SerialReceiveError error) {
352   if (within_read) {
353     // Stop watching the fd to avoid more reads until the queued ReadCompleted()
354     // completes and releases the pending_read_buffer.
355     file_read_watcher_.reset();
356
357     QueueReadCompleted(bytes_read, error);
358   } else {
359     ReadCompleted(bytes_read, error);
360   }
361 }
362
363 void SerialIoHandlerPosix::OnFileCanWriteWithoutBlocking() {
364   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
365
366   if (pending_write_buffer()) {
367     int bytes_written =
368         HANDLE_EINTR(write(file().GetPlatformFile(), pending_write_buffer(),
369                            pending_write_buffer_len()));
370     if (bytes_written < 0) {
371       WriteCompleted(0, mojom::SerialSendError::SYSTEM_ERROR);
372     } else {
373       WriteCompleted(bytes_written, mojom::SerialSendError::NONE);
374     }
375   } else {
376     // Stop watching the fd if we get notifications with no pending
377     // writes to avoid starving the message loop.
378     file_write_watcher_.reset();
379   }
380 }
381
382 void SerialIoHandlerPosix::EnsureWatchingReads() {
383   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
384   DCHECK(file().IsValid());
385   if (!file_read_watcher_) {
386     file_read_watcher_ = base::FileDescriptorWatcher::WatchReadable(
387         file().GetPlatformFile(),
388         base::BindRepeating(&SerialIoHandlerPosix::AttemptRead,
389                             base::Unretained(this), false));
390   }
391 }
392
393 void SerialIoHandlerPosix::EnsureWatchingWrites() {
394   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
395   DCHECK(file().IsValid());
396   if (!file_write_watcher_) {
397     file_write_watcher_ = base::FileDescriptorWatcher::WatchWritable(
398         file().GetPlatformFile(),
399         base::BindRepeating(
400             &SerialIoHandlerPosix::OnFileCanWriteWithoutBlocking,
401             base::Unretained(this)));
402   }
403 }
404
405 bool SerialIoHandlerPosix::Flush() const {
406   if (tcflush(file().GetPlatformFile(), TCIOFLUSH) != 0) {
407     VPLOG(1) << "Failed to flush port";
408     return false;
409   }
410   return true;
411 }
412
413 mojom::SerialPortControlSignalsPtr SerialIoHandlerPosix::GetControlSignals()
414     const {
415   int status;
416   if (ioctl(file().GetPlatformFile(), TIOCMGET, &status) == -1) {
417     VPLOG(1) << "Failed to get port control signals";
418     return mojom::SerialPortControlSignalsPtr();
419   }
420
421   auto signals = mojom::SerialPortControlSignals::New();
422   signals->dcd = (status & TIOCM_CAR) != 0;
423   signals->cts = (status & TIOCM_CTS) != 0;
424   signals->dsr = (status & TIOCM_DSR) != 0;
425   signals->ri = (status & TIOCM_RI) != 0;
426   return signals;
427 }
428
429 bool SerialIoHandlerPosix::SetControlSignals(
430     const mojom::SerialHostControlSignals& signals) {
431   int status;
432
433   if (ioctl(file().GetPlatformFile(), TIOCMGET, &status) == -1) {
434     VPLOG(1) << "Failed to get port control signals";
435     return false;
436   }
437
438   if (signals.has_dtr) {
439     if (signals.dtr) {
440       status |= TIOCM_DTR;
441     } else {
442       status &= ~TIOCM_DTR;
443     }
444   }
445
446   if (signals.has_rts) {
447     if (signals.rts) {
448       status |= TIOCM_RTS;
449     } else {
450       status &= ~TIOCM_RTS;
451     }
452   }
453
454   if (ioctl(file().GetPlatformFile(), TIOCMSET, &status) != 0) {
455     VPLOG(1) << "Failed to set port control signals";
456     return false;
457   }
458   return true;
459 }
460
461 mojom::SerialConnectionInfoPtr SerialIoHandlerPosix::GetPortInfo() const {
462 #if defined(OS_LINUX)
463   struct termios2 config;
464   if (ioctl(file().GetPlatformFile(), TCGETS2, &config) < 0) {
465 #else
466   struct termios config;
467   if (tcgetattr(file().GetPlatformFile(), &config) == -1) {
468 #endif
469     VPLOG(1) << "Failed to get port info";
470     return mojom::SerialConnectionInfoPtr();
471   }
472
473   auto info = mojom::SerialConnectionInfo::New();
474 #if defined(OS_LINUX)
475   // Linux forces c_ospeed to contain the correct value, which is nice.
476   info->bitrate = config.c_ospeed;
477 #else
478   speed_t ispeed = cfgetispeed(&config);
479   speed_t ospeed = cfgetospeed(&config);
480   if (ispeed == ospeed) {
481     int bitrate = 0;
482     if (SpeedConstantToBitrate(ispeed, &bitrate)) {
483       info->bitrate = bitrate;
484     } else if (ispeed > 0) {
485       info->bitrate = static_cast<int>(ispeed);
486     }
487   }
488 #endif
489
490   if ((config.c_cflag & CSIZE) == CS7) {
491     info->data_bits = mojom::SerialDataBits::SEVEN;
492   } else if ((config.c_cflag & CSIZE) == CS8) {
493     info->data_bits = mojom::SerialDataBits::EIGHT;
494   } else {
495     info->data_bits = mojom::SerialDataBits::NONE;
496   }
497   if (config.c_cflag & PARENB) {
498     info->parity_bit = (config.c_cflag & PARODD) ? mojom::SerialParityBit::ODD
499                                                  : mojom::SerialParityBit::EVEN;
500   } else {
501     info->parity_bit = mojom::SerialParityBit::NO_PARITY;
502   }
503   info->stop_bits = (config.c_cflag & CSTOPB) ? mojom::SerialStopBits::TWO
504                                               : mojom::SerialStopBits::ONE;
505   info->cts_flow_control = (config.c_cflag & CRTSCTS) != 0;
506   return info;
507 }
508
509 bool SerialIoHandlerPosix::SetBreak() {
510   if (ioctl(file().GetPlatformFile(), TIOCSBRK, 0) != 0) {
511     VPLOG(1) << "Failed to set break";
512     return false;
513   }
514
515   return true;
516 }
517
518 bool SerialIoHandlerPosix::ClearBreak() {
519   if (ioctl(file().GetPlatformFile(), TIOCCBRK, 0) != 0) {
520     VPLOG(1) << "Failed to clear break";
521     return false;
522   }
523   return true;
524 }
525
526 // break sequence:
527 // '\377'       -->        ErrorDetectState::MARK_377_SEEN
528 // '\0'         -->          ErrorDetectState::MARK_0_SEEN
529 // '\0'         -->                         break detected
530 //
531 // parity error sequence:
532 // '\377'       -->        ErrorDetectState::MARK_377_SEEN
533 // '\0'         -->          ErrorDetectState::MARK_0_SEEN
534 // character with parity error  -->  parity error detected
535 //
536 // break/parity error sequences are removed from the byte stream
537 // '\377' '\377' sequence is replaced with '\377'
538 int SerialIoHandlerPosix::CheckReceiveError(char* buffer,
539                                             int buffer_len,
540                                             int bytes_read,
541                                             bool& break_detected,
542                                             bool& parity_error_detected) {
543   int new_bytes_read = num_chars_stashed_;
544   DCHECK_LE(new_bytes_read, 2);
545
546   for (int i = 0; i < bytes_read; ++i) {
547     char ch = buffer[i];
548     if (new_bytes_read == 0) {
549       chars_stashed_[0] = ch;
550     } else if (new_bytes_read == 1) {
551       chars_stashed_[1] = ch;
552     } else {
553       buffer[new_bytes_read - 2] = ch;
554     }
555     ++new_bytes_read;
556     switch (error_detect_state_) {
557       case ErrorDetectState::NO_ERROR:
558         if (ch == '\377') {
559           error_detect_state_ = ErrorDetectState::MARK_377_SEEN;
560         }
561         break;
562       case ErrorDetectState::MARK_377_SEEN:
563         DCHECK_GE(new_bytes_read, 2);
564         if (ch == '\0') {
565           error_detect_state_ = ErrorDetectState::MARK_0_SEEN;
566         } else {
567           if (ch == '\377') {
568             // receive two bytes '\377' '\377', since ISTRIP is not set and
569             // PARMRK is set, a valid byte '\377' is passed to the program as
570             // two bytes, '\377' '\377'. Replace these two bytes with one byte
571             // of '\377', and set error_detect_state_ back to
572             // ErrorDetectState::NO_ERROR.
573             --new_bytes_read;
574           }
575           error_detect_state_ = ErrorDetectState::NO_ERROR;
576         }
577         break;
578       case ErrorDetectState::MARK_0_SEEN:
579         DCHECK_GE(new_bytes_read, 3);
580         if (ch == '\0') {
581           break_detected = true;
582           new_bytes_read -= 3;
583           error_detect_state_ = ErrorDetectState::NO_ERROR;
584         } else {
585           if (parity_check_enabled_) {
586             parity_error_detected = true;
587             new_bytes_read -= 3;
588             error_detect_state_ = ErrorDetectState::NO_ERROR;
589           } else if (ch == '\377') {
590             error_detect_state_ = ErrorDetectState::MARK_377_SEEN;
591           } else {
592             error_detect_state_ = ErrorDetectState::NO_ERROR;
593           }
594         }
595         break;
596     }
597   }
598   // Now new_bytes_read bytes should be returned to the caller (including the
599   // previously stashed characters that were stored at chars_stashed_[]) and are
600   // now stored at: chars_stashed_[0], chars_stashed_[1], buffer[...].
601
602   // Stash up to 2 characters that are potentially part of a break/parity error
603   // sequence. The buffer may also not be large enough to store all the bytes.
604   // tmp[] stores the characters that need to be stashed for this read.
605   char tmp[2];
606   num_chars_stashed_ = 0;
607   if (error_detect_state_ == ErrorDetectState::MARK_0_SEEN ||
608       new_bytes_read - buffer_len == 2) {
609     // need to stash the last two characters
610     if (new_bytes_read == 2) {
611       memcpy(tmp, chars_stashed_, new_bytes_read);
612     } else {
613       if (new_bytes_read == 3) {
614         tmp[0] = chars_stashed_[1];
615       } else {
616         tmp[0] = buffer[new_bytes_read - 4];
617       }
618       tmp[1] = buffer[new_bytes_read - 3];
619     }
620     num_chars_stashed_ = 2;
621   } else if (error_detect_state_ == ErrorDetectState::MARK_377_SEEN ||
622              new_bytes_read - buffer_len == 1) {
623     // need to stash the last character
624     if (new_bytes_read <= 2) {
625       tmp[0] = chars_stashed_[new_bytes_read - 1];
626     } else {
627       tmp[0] = buffer[new_bytes_read - 3];
628     }
629     num_chars_stashed_ = 1;
630   }
631
632   new_bytes_read -= num_chars_stashed_;
633   if (new_bytes_read > 2) {
634     // right shift two bytes to store bytes from chars_stashed_[]
635     memmove(buffer + 2, buffer, new_bytes_read - 2);
636   }
637   memcpy(buffer, chars_stashed_, std::min(new_bytes_read, 2));
638   memcpy(chars_stashed_, tmp, num_chars_stashed_);
639   return new_bytes_read;
640 }
641
642 }  // namespace device