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.
7 #include "device/serial/serial_io_handler_win.h"
13 int BitrateToSpeedConstant(int bitrate
) {
14 #define BITRATE_TO_SPEED_CASE(x) \
18 BITRATE_TO_SPEED_CASE(110);
19 BITRATE_TO_SPEED_CASE(300);
20 BITRATE_TO_SPEED_CASE(600);
21 BITRATE_TO_SPEED_CASE(1200);
22 BITRATE_TO_SPEED_CASE(2400);
23 BITRATE_TO_SPEED_CASE(4800);
24 BITRATE_TO_SPEED_CASE(9600);
25 BITRATE_TO_SPEED_CASE(14400);
26 BITRATE_TO_SPEED_CASE(19200);
27 BITRATE_TO_SPEED_CASE(38400);
28 BITRATE_TO_SPEED_CASE(57600);
29 BITRATE_TO_SPEED_CASE(115200);
30 BITRATE_TO_SPEED_CASE(128000);
31 BITRATE_TO_SPEED_CASE(256000);
33 // If the bitrate doesn't match that of one of the standard
34 // index constants, it may be provided as-is to the DCB
35 // structure, according to MSDN.
38 #undef BITRATE_TO_SPEED_CASE
41 int DataBitsEnumToConstant(serial::DataBits data_bits
) {
43 case serial::DATA_BITS_SEVEN
:
45 case serial::DATA_BITS_EIGHT
:
51 int ParityBitEnumToConstant(serial::ParityBit parity_bit
) {
53 case serial::PARITY_BIT_EVEN
:
55 case serial::PARITY_BIT_ODD
:
57 case serial::PARITY_BIT_NO
:
63 int StopBitsEnumToConstant(serial::StopBits stop_bits
) {
65 case serial::STOP_BITS_TWO
:
67 case serial::STOP_BITS_ONE
:
73 int SpeedConstantToBitrate(int speed
) {
74 #define SPEED_TO_BITRATE_CASE(x) \
78 SPEED_TO_BITRATE_CASE(110);
79 SPEED_TO_BITRATE_CASE(300);
80 SPEED_TO_BITRATE_CASE(600);
81 SPEED_TO_BITRATE_CASE(1200);
82 SPEED_TO_BITRATE_CASE(2400);
83 SPEED_TO_BITRATE_CASE(4800);
84 SPEED_TO_BITRATE_CASE(9600);
85 SPEED_TO_BITRATE_CASE(14400);
86 SPEED_TO_BITRATE_CASE(19200);
87 SPEED_TO_BITRATE_CASE(38400);
88 SPEED_TO_BITRATE_CASE(57600);
89 SPEED_TO_BITRATE_CASE(115200);
90 SPEED_TO_BITRATE_CASE(128000);
91 SPEED_TO_BITRATE_CASE(256000);
93 // If it's not one of the standard index constants,
94 // it should be an integral baud rate, according to
98 #undef SPEED_TO_BITRATE_CASE
101 serial::DataBits
DataBitsConstantToEnum(int data_bits
) {
104 return serial::DATA_BITS_SEVEN
;
107 return serial::DATA_BITS_EIGHT
;
111 serial::ParityBit
ParityBitConstantToEnum(int parity_bit
) {
112 switch (parity_bit
) {
114 return serial::PARITY_BIT_EVEN
;
116 return serial::PARITY_BIT_ODD
;
119 return serial::PARITY_BIT_NO
;
123 serial::StopBits
StopBitsConstantToEnum(int stop_bits
) {
126 return serial::STOP_BITS_TWO
;
129 return serial::STOP_BITS_ONE
;
136 scoped_refptr
<SerialIoHandler
> SerialIoHandler::Create(
137 scoped_refptr
<base::MessageLoopProxy
> file_thread_message_loop
,
138 scoped_refptr
<base::MessageLoopProxy
> ui_thread_message_loop
) {
139 return new SerialIoHandlerWin(file_thread_message_loop
,
140 ui_thread_message_loop
);
143 bool SerialIoHandlerWin::PostOpen() {
144 DCHECK(!comm_context_
);
145 DCHECK(!read_context_
);
146 DCHECK(!write_context_
);
148 base::MessageLoopForIO::current()->RegisterIOHandler(file().GetPlatformFile(),
151 comm_context_
.reset(new base::MessageLoopForIO::IOContext());
152 comm_context_
->handler
= this;
153 memset(&comm_context_
->overlapped
, 0, sizeof(comm_context_
->overlapped
));
155 read_context_
.reset(new base::MessageLoopForIO::IOContext());
156 read_context_
->handler
= this;
157 memset(&read_context_
->overlapped
, 0, sizeof(read_context_
->overlapped
));
159 write_context_
.reset(new base::MessageLoopForIO::IOContext());
160 write_context_
->handler
= this;
161 memset(&write_context_
->overlapped
, 0, sizeof(write_context_
->overlapped
));
163 // A ReadIntervalTimeout of MAXDWORD will cause async reads to complete
164 // immediately with any data that's available, even if there is none.
165 // This is OK because we never issue a read request until WaitCommEvent
166 // signals that data is available.
167 COMMTIMEOUTS timeouts
= {0};
168 timeouts
.ReadIntervalTimeout
= MAXDWORD
;
169 if (!::SetCommTimeouts(file().GetPlatformFile(), &timeouts
)) {
170 VPLOG(1) << "Failed to set serial timeouts";
177 void SerialIoHandlerWin::ReadImpl() {
178 DCHECK(CalledOnValidThread());
179 DCHECK(pending_read_buffer());
180 DCHECK(file().IsValid());
184 if (!ClearCommError(file().GetPlatformFile(), &errors
, &status
) ||
186 QueueReadCompleted(0, serial::RECEIVE_ERROR_SYSTEM_ERROR
);
190 if (!SetCommMask(file().GetPlatformFile(), EV_RXCHAR
)) {
191 VPLOG(1) << "Failed to set serial event flags";
195 BOOL ok
= ::WaitCommEvent(
196 file().GetPlatformFile(), &event_mask_
, &comm_context_
->overlapped
);
197 if (!ok
&& GetLastError() != ERROR_IO_PENDING
) {
198 VPLOG(1) << "Failed to receive serial event";
199 QueueReadCompleted(0, serial::RECEIVE_ERROR_SYSTEM_ERROR
);
201 is_comm_pending_
= true;
204 void SerialIoHandlerWin::WriteImpl() {
205 DCHECK(CalledOnValidThread());
206 DCHECK(pending_write_buffer());
207 DCHECK(file().IsValid());
209 BOOL ok
= ::WriteFile(file().GetPlatformFile(),
210 pending_write_buffer(),
211 pending_write_buffer_len(),
213 &write_context_
->overlapped
);
214 if (!ok
&& GetLastError() != ERROR_IO_PENDING
) {
215 QueueWriteCompleted(0, serial::SEND_ERROR_SYSTEM_ERROR
);
219 void SerialIoHandlerWin::CancelReadImpl() {
220 DCHECK(CalledOnValidThread());
221 DCHECK(file().IsValid());
222 ::CancelIo(file().GetPlatformFile());
225 void SerialIoHandlerWin::CancelWriteImpl() {
226 DCHECK(CalledOnValidThread());
227 DCHECK(file().IsValid());
228 ::CancelIo(file().GetPlatformFile());
231 bool SerialIoHandlerWin::ConfigurePortImpl() {
233 config
.DCBlength
= sizeof(config
);
234 if (!GetCommState(file().GetPlatformFile(), &config
)) {
235 VPLOG(1) << "Failed to get serial port info";
239 // Set up some sane default options that are not configurable.
240 config
.fBinary
= TRUE
;
241 config
.fParity
= FALSE
;
242 config
.fAbortOnError
= TRUE
;
243 config
.fOutxDsrFlow
= FALSE
;
244 config
.fDtrControl
= DTR_CONTROL_ENABLE
;
245 config
.fDsrSensitivity
= FALSE
;
246 config
.fOutX
= FALSE
;
249 DCHECK(options().bitrate
);
250 config
.BaudRate
= BitrateToSpeedConstant(options().bitrate
);
252 DCHECK(options().data_bits
!= serial::DATA_BITS_NONE
);
253 config
.ByteSize
= DataBitsEnumToConstant(options().data_bits
);
255 DCHECK(options().parity_bit
!= serial::PARITY_BIT_NONE
);
256 config
.Parity
= ParityBitEnumToConstant(options().parity_bit
);
258 DCHECK(options().stop_bits
!= serial::STOP_BITS_NONE
);
259 config
.StopBits
= StopBitsEnumToConstant(options().stop_bits
);
261 DCHECK(options().has_cts_flow_control
);
262 if (options().cts_flow_control
) {
263 config
.fOutxCtsFlow
= TRUE
;
264 config
.fRtsControl
= RTS_CONTROL_HANDSHAKE
;
266 config
.fOutxCtsFlow
= FALSE
;
267 config
.fRtsControl
= RTS_CONTROL_ENABLE
;
270 if (!SetCommState(file().GetPlatformFile(), &config
)) {
271 VPLOG(1) << "Failed to set serial port info";
277 SerialIoHandlerWin::SerialIoHandlerWin(
278 scoped_refptr
<base::MessageLoopProxy
> file_thread_message_loop
,
279 scoped_refptr
<base::MessageLoopProxy
> ui_thread_message_loop
)
280 : SerialIoHandler(file_thread_message_loop
, ui_thread_message_loop
),
282 is_comm_pending_(false) {
285 SerialIoHandlerWin::~SerialIoHandlerWin() {
288 void SerialIoHandlerWin::OnIOCompleted(
289 base::MessageLoopForIO::IOContext
* context
,
290 DWORD bytes_transferred
,
292 DCHECK(CalledOnValidThread());
293 if (context
== comm_context_
) {
294 if (read_canceled()) {
295 ReadCompleted(bytes_transferred
, read_cancel_reason());
296 } else if (error
!= ERROR_SUCCESS
&& error
!= ERROR_OPERATION_ABORTED
) {
297 ReadCompleted(0, serial::RECEIVE_ERROR_SYSTEM_ERROR
);
298 } else if (pending_read_buffer()) {
299 BOOL ok
= ::ReadFile(file().GetPlatformFile(),
300 pending_read_buffer(),
301 pending_read_buffer_len(),
303 &read_context_
->overlapped
);
304 if (!ok
&& GetLastError() != ERROR_IO_PENDING
) {
305 ReadCompleted(0, serial::RECEIVE_ERROR_SYSTEM_ERROR
);
308 } else if (context
== read_context_
) {
309 if (read_canceled()) {
310 ReadCompleted(bytes_transferred
, read_cancel_reason());
311 } else if (error
!= ERROR_SUCCESS
&& error
!= ERROR_OPERATION_ABORTED
) {
312 ReadCompleted(0, serial::RECEIVE_ERROR_SYSTEM_ERROR
);
314 ReadCompleted(bytes_transferred
,
315 error
== ERROR_SUCCESS
316 ? serial::RECEIVE_ERROR_NONE
317 : serial::RECEIVE_ERROR_SYSTEM_ERROR
);
319 } else if (context
== write_context_
) {
320 DCHECK(pending_write_buffer());
321 if (write_canceled()) {
322 WriteCompleted(0, write_cancel_reason());
323 } else if (error
!= ERROR_SUCCESS
&& error
!= ERROR_OPERATION_ABORTED
) {
324 WriteCompleted(0, serial::SEND_ERROR_SYSTEM_ERROR
);
326 WriteCompleted(bytes_transferred
,
327 error
== ERROR_SUCCESS
? serial::SEND_ERROR_NONE
328 : serial::SEND_ERROR_SYSTEM_ERROR
);
331 NOTREACHED() << "Invalid IOContext";
335 bool SerialIoHandlerWin::Flush() const {
336 if (!PurgeComm(file().GetPlatformFile(), PURGE_RXCLEAR
| PURGE_TXCLEAR
)) {
337 VPLOG(1) << "Failed to flush serial port";
343 serial::DeviceControlSignalsPtr
SerialIoHandlerWin::GetControlSignals() const {
345 if (!GetCommModemStatus(file().GetPlatformFile(), &status
)) {
346 VPLOG(1) << "Failed to get port control signals";
347 return serial::DeviceControlSignalsPtr();
350 serial::DeviceControlSignalsPtr
signals(serial::DeviceControlSignals::New());
351 signals
->dcd
= (status
& MS_RLSD_ON
) != 0;
352 signals
->cts
= (status
& MS_CTS_ON
) != 0;
353 signals
->dsr
= (status
& MS_DSR_ON
) != 0;
354 signals
->ri
= (status
& MS_RING_ON
) != 0;
355 return signals
.Pass();
358 bool SerialIoHandlerWin::SetControlSignals(
359 const serial::HostControlSignals
& signals
) {
360 if (signals
.has_dtr
) {
361 if (!EscapeCommFunction(file().GetPlatformFile(),
362 signals
.dtr
? SETDTR
: CLRDTR
)) {
363 VPLOG(1) << "Failed to configure DTR signal";
367 if (signals
.has_rts
) {
368 if (!EscapeCommFunction(file().GetPlatformFile(),
369 signals
.rts
? SETRTS
: CLRRTS
)) {
370 VPLOG(1) << "Failed to configure RTS signal";
377 serial::ConnectionInfoPtr
SerialIoHandlerWin::GetPortInfo() const {
379 config
.DCBlength
= sizeof(config
);
380 if (!GetCommState(file().GetPlatformFile(), &config
)) {
381 VPLOG(1) << "Failed to get serial port info";
382 return serial::ConnectionInfoPtr();
384 serial::ConnectionInfoPtr
info(serial::ConnectionInfo::New());
385 info
->bitrate
= SpeedConstantToBitrate(config
.BaudRate
);
386 info
->data_bits
= DataBitsConstantToEnum(config
.ByteSize
);
387 info
->parity_bit
= ParityBitConstantToEnum(config
.Parity
);
388 info
->stop_bits
= StopBitsConstantToEnum(config
.StopBits
);
389 info
->cts_flow_control
= config
.fOutxCtsFlow
!= 0;
393 std::string
SerialIoHandler::MaybeFixUpPortName(const std::string
& port_name
) {
394 // For COM numbers less than 9, CreateFile is called with a string such as
395 // "COM1". For numbers greater than 9, a prefix of "\\\\.\\" must be added.
396 if (port_name
.length() > std::string("COM9").length())
397 return std::string("\\\\.\\").append(port_name
);
402 } // namespace device