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