Upload upstream chromium 69.0.3497
[platform/framework/web/chromium-efl.git] / device / serial / serial_io_handler_win.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 "device/serial/serial_io_handler_win.h"
6
7 #define INITGUID
8 #include <devpkey.h>
9 #include <setupapi.h>
10 #include <windows.h>
11
12 #include "base/bind.h"
13 #include "base/macros.h"
14 #include "base/message_loop/message_loop_current.h"
15 #include "base/scoped_observer.h"
16 #include "base/sequence_checker.h"
17 #include "device/base/device_info_query_win.h"
18 #include "device/base/device_monitor_win.h"
19 #include "third_party/re2/src/re2/re2.h"
20
21 namespace device {
22
23 namespace {
24
25 int BitrateToSpeedConstant(int bitrate) {
26 #define BITRATE_TO_SPEED_CASE(x) \
27   case x:                        \
28     return CBR_##x;
29   switch (bitrate) {
30     BITRATE_TO_SPEED_CASE(110);
31     BITRATE_TO_SPEED_CASE(300);
32     BITRATE_TO_SPEED_CASE(600);
33     BITRATE_TO_SPEED_CASE(1200);
34     BITRATE_TO_SPEED_CASE(2400);
35     BITRATE_TO_SPEED_CASE(4800);
36     BITRATE_TO_SPEED_CASE(9600);
37     BITRATE_TO_SPEED_CASE(14400);
38     BITRATE_TO_SPEED_CASE(19200);
39     BITRATE_TO_SPEED_CASE(38400);
40     BITRATE_TO_SPEED_CASE(57600);
41     BITRATE_TO_SPEED_CASE(115200);
42     BITRATE_TO_SPEED_CASE(128000);
43     BITRATE_TO_SPEED_CASE(256000);
44     default:
45       // If the bitrate doesn't match that of one of the standard
46       // index constants, it may be provided as-is to the DCB
47       // structure, according to MSDN.
48       return bitrate;
49   }
50 #undef BITRATE_TO_SPEED_CASE
51 }
52
53 int DataBitsEnumToConstant(mojom::SerialDataBits data_bits) {
54   switch (data_bits) {
55     case mojom::SerialDataBits::SEVEN:
56       return 7;
57     case mojom::SerialDataBits::EIGHT:
58     default:
59       return 8;
60   }
61 }
62
63 int ParityBitEnumToConstant(mojom::SerialParityBit parity_bit) {
64   switch (parity_bit) {
65     case mojom::SerialParityBit::EVEN:
66       return EVENPARITY;
67     case mojom::SerialParityBit::ODD:
68       return ODDPARITY;
69     case mojom::SerialParityBit::NO_PARITY:
70     default:
71       return NOPARITY;
72   }
73 }
74
75 int StopBitsEnumToConstant(mojom::SerialStopBits stop_bits) {
76   switch (stop_bits) {
77     case mojom::SerialStopBits::TWO:
78       return TWOSTOPBITS;
79     case mojom::SerialStopBits::ONE:
80     default:
81       return ONESTOPBIT;
82   }
83 }
84
85 int SpeedConstantToBitrate(int speed) {
86 #define SPEED_TO_BITRATE_CASE(x) \
87   case CBR_##x:                  \
88     return x;
89   switch (speed) {
90     SPEED_TO_BITRATE_CASE(110);
91     SPEED_TO_BITRATE_CASE(300);
92     SPEED_TO_BITRATE_CASE(600);
93     SPEED_TO_BITRATE_CASE(1200);
94     SPEED_TO_BITRATE_CASE(2400);
95     SPEED_TO_BITRATE_CASE(4800);
96     SPEED_TO_BITRATE_CASE(9600);
97     SPEED_TO_BITRATE_CASE(14400);
98     SPEED_TO_BITRATE_CASE(19200);
99     SPEED_TO_BITRATE_CASE(38400);
100     SPEED_TO_BITRATE_CASE(57600);
101     SPEED_TO_BITRATE_CASE(115200);
102     SPEED_TO_BITRATE_CASE(128000);
103     SPEED_TO_BITRATE_CASE(256000);
104     default:
105       // If it's not one of the standard index constants,
106       // it should be an integral baud rate, according to
107       // MSDN.
108       return speed;
109   }
110 #undef SPEED_TO_BITRATE_CASE
111 }
112
113 mojom::SerialDataBits DataBitsConstantToEnum(int data_bits) {
114   switch (data_bits) {
115     case 7:
116       return mojom::SerialDataBits::SEVEN;
117     case 8:
118     default:
119       return mojom::SerialDataBits::EIGHT;
120   }
121 }
122
123 mojom::SerialParityBit ParityBitConstantToEnum(int parity_bit) {
124   switch (parity_bit) {
125     case EVENPARITY:
126       return mojom::SerialParityBit::EVEN;
127     case ODDPARITY:
128       return mojom::SerialParityBit::ODD;
129     case NOPARITY:
130     default:
131       return mojom::SerialParityBit::NO_PARITY;
132   }
133 }
134
135 mojom::SerialStopBits StopBitsConstantToEnum(int stop_bits) {
136   switch (stop_bits) {
137     case TWOSTOPBITS:
138       return mojom::SerialStopBits::TWO;
139     case ONESTOPBIT:
140     default:
141       return mojom::SerialStopBits::ONE;
142   }
143 }
144
145 // Searches for the COM port in the device's friendly name, assigns its value to
146 // com_port, and returns whether the operation was successful.
147 bool GetCOMPort(const std::string friendly_name, std::string* com_port) {
148   return RE2::PartialMatch(friendly_name, ".* \\((COM[0-9]+)\\)", com_port);
149 }
150
151 }  // namespace
152
153 // static
154 scoped_refptr<SerialIoHandler> SerialIoHandler::Create(
155     scoped_refptr<base::SingleThreadTaskRunner> ui_thread_task_runner) {
156   return new SerialIoHandlerWin(ui_thread_task_runner);
157 }
158
159 class SerialIoHandlerWin::UiThreadHelper final
160     : public DeviceMonitorWin::Observer {
161  public:
162   UiThreadHelper(
163       base::WeakPtr<SerialIoHandlerWin> io_handler,
164       scoped_refptr<base::SingleThreadTaskRunner> io_thread_task_runner)
165       : device_observer_(this),
166         io_handler_(io_handler),
167         io_thread_task_runner_(io_thread_task_runner) {}
168
169   ~UiThreadHelper() { DCHECK(thread_checker_.CalledOnValidThread()); }
170
171   static void Start(UiThreadHelper* self) {
172     self->thread_checker_.DetachFromThread();
173     DeviceMonitorWin* device_monitor = DeviceMonitorWin::GetForAllInterfaces();
174     if (device_monitor)
175       self->device_observer_.Add(device_monitor);
176   }
177
178  private:
179   // DeviceMonitorWin::Observer
180   void OnDeviceRemoved(const GUID& class_guid,
181                        const std::string& device_path) override {
182     DCHECK(thread_checker_.CalledOnValidThread());
183     io_thread_task_runner_->PostTask(
184         FROM_HERE, base::Bind(&SerialIoHandlerWin::OnDeviceRemoved, io_handler_,
185                               device_path));
186   }
187
188   base::ThreadChecker thread_checker_;
189   ScopedObserver<DeviceMonitorWin, DeviceMonitorWin::Observer> device_observer_;
190
191   // This weak pointer is only valid when checked on this task runner.
192   base::WeakPtr<SerialIoHandlerWin> io_handler_;
193   scoped_refptr<base::SingleThreadTaskRunner> io_thread_task_runner_;
194
195   DISALLOW_COPY_AND_ASSIGN(UiThreadHelper);
196 };
197
198 void SerialIoHandlerWin::OnDeviceRemoved(const std::string& device_path) {
199   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
200
201   DeviceInfoQueryWin device_info_query;
202   if (!device_info_query.device_info_list_valid()) {
203     DVPLOG(1) << "Failed to create a device information set";
204     return;
205   }
206
207   // This will add the device so we can query driver info.
208   if (!device_info_query.AddDevice(device_path)) {
209     DVPLOG(1) << "Failed to get device interface data for " << device_path;
210     return;
211   }
212
213   if (!device_info_query.GetDeviceInfo()) {
214     DVPLOG(1) << "Failed to get device info for " << device_path;
215     return;
216   }
217
218   std::string friendly_name;
219   if (!device_info_query.GetDeviceStringProperty(DEVPKEY_Device_FriendlyName,
220                                                  &friendly_name)) {
221     DVPLOG(1) << "Failed to get device service property";
222     return;
223   }
224
225   std::string com_port;
226   if (!GetCOMPort(friendly_name, &com_port)) {
227     DVPLOG(1) << "Failed to get port name from \"" << friendly_name << "\".";
228     return;
229   }
230
231   if (port() == com_port)
232     CancelRead(mojom::SerialReceiveError::DEVICE_LOST);
233 }
234
235 bool SerialIoHandlerWin::PostOpen() {
236   DCHECK(!comm_context_);
237   DCHECK(!read_context_);
238   DCHECK(!write_context_);
239
240   base::MessageLoopCurrentForIO::Get()->RegisterIOHandler(
241       file().GetPlatformFile(), this);
242
243   comm_context_.reset(new base::MessagePumpForIO::IOContext());
244   read_context_.reset(new base::MessagePumpForIO::IOContext());
245   write_context_.reset(new base::MessagePumpForIO::IOContext());
246
247   scoped_refptr<base::SingleThreadTaskRunner> io_thread_task_runner =
248       base::ThreadTaskRunnerHandle::Get();
249   helper_ =
250       new UiThreadHelper(weak_factory_.GetWeakPtr(), io_thread_task_runner);
251   ui_thread_task_runner()->PostTask(
252       FROM_HERE, base::Bind(&UiThreadHelper::Start, helper_));
253
254   // A ReadIntervalTimeout of MAXDWORD will cause async reads to complete
255   // immediately with any data that's available, even if there is none.
256   // This is OK because we never issue a read request until WaitCommEvent
257   // signals that data is available.
258   COMMTIMEOUTS timeouts = {0};
259   timeouts.ReadIntervalTimeout = MAXDWORD;
260   if (!::SetCommTimeouts(file().GetPlatformFile(), &timeouts)) {
261     VPLOG(1) << "Failed to set serial timeouts";
262     return false;
263   }
264
265   return true;
266 }
267
268 void SerialIoHandlerWin::ReadImpl() {
269   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
270   DCHECK(pending_read_buffer());
271   DCHECK(file().IsValid());
272
273   if (!SetCommMask(file().GetPlatformFile(), EV_RXCHAR)) {
274     VPLOG(1) << "Failed to set serial event flags";
275   }
276
277   event_mask_ = 0;
278   BOOL ok = ::WaitCommEvent(
279       file().GetPlatformFile(), &event_mask_, &comm_context_->overlapped);
280   if (!ok && GetLastError() != ERROR_IO_PENDING) {
281     VPLOG(1) << "Failed to receive serial event";
282     QueueReadCompleted(0, mojom::SerialReceiveError::SYSTEM_ERROR);
283   }
284   is_comm_pending_ = true;
285 }
286
287 void SerialIoHandlerWin::WriteImpl() {
288   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
289   DCHECK(pending_write_buffer());
290   DCHECK(file().IsValid());
291
292   BOOL ok = ::WriteFile(file().GetPlatformFile(),
293                         pending_write_buffer(),
294                         pending_write_buffer_len(),
295                         NULL,
296                         &write_context_->overlapped);
297   if (!ok && GetLastError() != ERROR_IO_PENDING) {
298     VPLOG(1) << "Write failed";
299     QueueWriteCompleted(0, mojom::SerialSendError::SYSTEM_ERROR);
300   }
301 }
302
303 void SerialIoHandlerWin::CancelReadImpl() {
304   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
305   DCHECK(file().IsValid());
306   ::CancelIo(file().GetPlatformFile());
307 }
308
309 void SerialIoHandlerWin::CancelWriteImpl() {
310   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
311   DCHECK(file().IsValid());
312   ::CancelIo(file().GetPlatformFile());
313 }
314
315 bool SerialIoHandlerWin::ConfigurePortImpl() {
316   DCB config = {0};
317   config.DCBlength = sizeof(config);
318   if (!GetCommState(file().GetPlatformFile(), &config)) {
319     VPLOG(1) << "Failed to get serial port info";
320     return false;
321   }
322
323   // Set up some sane default options that are not configurable.
324   config.fBinary = TRUE;
325   config.fParity = TRUE;
326   config.fAbortOnError = TRUE;
327   config.fOutxDsrFlow = FALSE;
328   config.fDtrControl = DTR_CONTROL_ENABLE;
329   config.fDsrSensitivity = FALSE;
330   config.fOutX = FALSE;
331   config.fInX = FALSE;
332
333   DCHECK(options().bitrate);
334   config.BaudRate = BitrateToSpeedConstant(options().bitrate);
335
336   DCHECK(options().data_bits != mojom::SerialDataBits::NONE);
337   config.ByteSize = DataBitsEnumToConstant(options().data_bits);
338
339   DCHECK(options().parity_bit != mojom::SerialParityBit::NONE);
340   config.Parity = ParityBitEnumToConstant(options().parity_bit);
341
342   DCHECK(options().stop_bits != mojom::SerialStopBits::NONE);
343   config.StopBits = StopBitsEnumToConstant(options().stop_bits);
344
345   DCHECK(options().has_cts_flow_control);
346   if (options().cts_flow_control) {
347     config.fOutxCtsFlow = TRUE;
348     config.fRtsControl = RTS_CONTROL_HANDSHAKE;
349   } else {
350     config.fOutxCtsFlow = FALSE;
351     config.fRtsControl = RTS_CONTROL_ENABLE;
352   }
353
354   if (!SetCommState(file().GetPlatformFile(), &config)) {
355     VPLOG(1) << "Failed to set serial port info";
356     return false;
357   }
358   return true;
359 }
360
361 SerialIoHandlerWin::SerialIoHandlerWin(
362     scoped_refptr<base::SingleThreadTaskRunner> ui_thread_task_runner)
363     : SerialIoHandler(ui_thread_task_runner),
364       event_mask_(0),
365       is_comm_pending_(false),
366       helper_(nullptr),
367       weak_factory_(this) {}
368
369 SerialIoHandlerWin::~SerialIoHandlerWin() {
370   ui_thread_task_runner()->DeleteSoon(FROM_HERE, helper_);
371 }
372
373 void SerialIoHandlerWin::OnIOCompleted(
374     base::MessagePumpForIO::IOContext* context,
375     DWORD bytes_transferred,
376     DWORD error) {
377   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
378   if (context == comm_context_.get()) {
379     DWORD errors;
380     COMSTAT status;
381     if (!ClearCommError(file().GetPlatformFile(), &errors, &status) ||
382         errors != 0) {
383       if (errors & CE_BREAK) {
384         ReadCompleted(0, mojom::SerialReceiveError::BREAK);
385       } else if (errors & CE_FRAME) {
386         ReadCompleted(0, mojom::SerialReceiveError::FRAME_ERROR);
387       } else if (errors & CE_OVERRUN) {
388         ReadCompleted(0, mojom::SerialReceiveError::OVERRUN);
389       } else if (errors & CE_RXOVER) {
390         ReadCompleted(0, mojom::SerialReceiveError::BUFFER_OVERFLOW);
391       } else if (errors & CE_RXPARITY) {
392         ReadCompleted(0, mojom::SerialReceiveError::PARITY_ERROR);
393       } else {
394         ReadCompleted(0, mojom::SerialReceiveError::SYSTEM_ERROR);
395       }
396       return;
397     }
398
399     if (read_canceled()) {
400       ReadCompleted(bytes_transferred, read_cancel_reason());
401     } else if (error != ERROR_SUCCESS && error != ERROR_OPERATION_ABORTED) {
402       ReadCompleted(0, mojom::SerialReceiveError::SYSTEM_ERROR);
403     } else if (pending_read_buffer()) {
404       BOOL ok = ::ReadFile(file().GetPlatformFile(),
405                            pending_read_buffer(),
406                            pending_read_buffer_len(),
407                            NULL,
408                            &read_context_->overlapped);
409       if (!ok && GetLastError() != ERROR_IO_PENDING) {
410         VPLOG(1) << "Read failed";
411         ReadCompleted(0, mojom::SerialReceiveError::SYSTEM_ERROR);
412       }
413     }
414   } else if (context == read_context_.get()) {
415     if (read_canceled()) {
416       ReadCompleted(bytes_transferred, read_cancel_reason());
417     } else if (error != ERROR_SUCCESS && error != ERROR_OPERATION_ABORTED) {
418       ReadCompleted(0, mojom::SerialReceiveError::SYSTEM_ERROR);
419     } else {
420       ReadCompleted(bytes_transferred,
421                     error == ERROR_SUCCESS
422                         ? mojom::SerialReceiveError::NONE
423                         : mojom::SerialReceiveError::SYSTEM_ERROR);
424     }
425   } else if (context == write_context_.get()) {
426     DCHECK(pending_write_buffer());
427     if (write_canceled()) {
428       WriteCompleted(0, write_cancel_reason());
429     } else if (error != ERROR_SUCCESS && error != ERROR_OPERATION_ABORTED) {
430       WriteCompleted(0, mojom::SerialSendError::SYSTEM_ERROR);
431       if (error == ERROR_GEN_FAILURE && IsReadPending()) {
432         // For devices using drivers such as FTDI, CP2xxx, when device is
433         // disconnected, the context is comm_context_ and the error is
434         // ERROR_OPERATION_ABORTED.
435         // However, for devices using CDC-ACM driver, when device is
436         // disconnected, the context is write_context_ and the error is
437         // ERROR_GEN_FAILURE. In this situation, in addition to a write error
438         // signal, also need to generate a read error signal
439         // mojom::SerialOnReceiveError which will notify the app about the
440         // disconnection.
441         CancelRead(mojom::SerialReceiveError::SYSTEM_ERROR);
442       }
443     } else {
444       WriteCompleted(bytes_transferred,
445                      error == ERROR_SUCCESS
446                          ? mojom::SerialSendError::NONE
447                          : mojom::SerialSendError::SYSTEM_ERROR);
448     }
449   } else {
450     NOTREACHED() << "Invalid IOContext";
451   }
452 }
453
454 bool SerialIoHandlerWin::Flush() const {
455   if (!PurgeComm(file().GetPlatformFile(), PURGE_RXCLEAR | PURGE_TXCLEAR)) {
456     VPLOG(1) << "Failed to flush serial port";
457     return false;
458   }
459   return true;
460 }
461
462 mojom::SerialDeviceControlSignalsPtr SerialIoHandlerWin::GetControlSignals()
463     const {
464   DWORD status;
465   if (!GetCommModemStatus(file().GetPlatformFile(), &status)) {
466     VPLOG(1) << "Failed to get port control signals";
467     return mojom::SerialDeviceControlSignalsPtr();
468   }
469
470   auto signals = mojom::SerialDeviceControlSignals::New();
471   signals->dcd = (status & MS_RLSD_ON) != 0;
472   signals->cts = (status & MS_CTS_ON) != 0;
473   signals->dsr = (status & MS_DSR_ON) != 0;
474   signals->ri = (status & MS_RING_ON) != 0;
475   return signals;
476 }
477
478 bool SerialIoHandlerWin::SetControlSignals(
479     const mojom::SerialHostControlSignals& signals) {
480   if (signals.has_dtr) {
481     if (!EscapeCommFunction(file().GetPlatformFile(),
482                             signals.dtr ? SETDTR : CLRDTR)) {
483       VPLOG(1) << "Failed to configure DTR signal";
484       return false;
485     }
486   }
487   if (signals.has_rts) {
488     if (!EscapeCommFunction(file().GetPlatformFile(),
489                             signals.rts ? SETRTS : CLRRTS)) {
490       VPLOG(1) << "Failed to configure RTS signal";
491       return false;
492     }
493   }
494   return true;
495 }
496
497 mojom::SerialConnectionInfoPtr SerialIoHandlerWin::GetPortInfo() const {
498   DCB config = {0};
499   config.DCBlength = sizeof(config);
500   if (!GetCommState(file().GetPlatformFile(), &config)) {
501     VPLOG(1) << "Failed to get serial port info";
502     return mojom::SerialConnectionInfoPtr();
503   }
504   auto info = mojom::SerialConnectionInfo::New();
505   info->bitrate = SpeedConstantToBitrate(config.BaudRate);
506   info->data_bits = DataBitsConstantToEnum(config.ByteSize);
507   info->parity_bit = ParityBitConstantToEnum(config.Parity);
508   info->stop_bits = StopBitsConstantToEnum(config.StopBits);
509   info->cts_flow_control = config.fOutxCtsFlow != 0;
510   return info;
511 }
512
513 bool SerialIoHandlerWin::SetBreak() {
514   if (!SetCommBreak(file().GetPlatformFile())) {
515     VPLOG(1) << "Failed to set break";
516     return false;
517   }
518   return true;
519 }
520
521 bool SerialIoHandlerWin::ClearBreak() {
522   if (!ClearCommBreak(file().GetPlatformFile())) {
523     VPLOG(1) << "Failed to clear break";
524     return false;
525   }
526   return true;
527 }
528
529 std::string SerialIoHandler::MaybeFixUpPortName(const std::string& port_name) {
530   // For COM numbers less than 9, CreateFile is called with a string such as
531   // "COM1". For numbers greater than 9, a prefix of "\\\\.\\" must be added.
532   if (port_name.length() > std::string("COM9").length())
533     return std::string("\\\\.\\").append(port_name);
534
535   return port_name;
536 }
537
538 }  // namespace device