1// Copyright 2012 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 "chrome/browser/extensions/api/serial/serial_connection.h"
6
7#include <windows.h>
8
9#include <string>
10
11namespace extensions {
12
13namespace {
14
15int BitrateToSpeedConstant(int bitrate) {
16#define BITRATE_TO_SPEED_CASE(x) case x: return CBR_ ## x;
17  switch (bitrate) {
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);
32    default:
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.
36      return bitrate;
37  }
38#undef BITRATE_TO_SPEED_CASE
39}
40
41int DataBitsEnumToConstant(api::serial::DataBits data_bits) {
42  switch (data_bits) {
43    case api::serial::DATA_BITS_SEVEN:
44      return 7;
45    case api::serial::DATA_BITS_EIGHT:
46    default:
47      return 8;
48  }
49}
50
51int ParityBitEnumToConstant(api::serial::ParityBit parity_bit) {
52  switch (parity_bit) {
53    case api::serial::PARITY_BIT_EVEN:
54      return EVENPARITY;
55    case api::serial::PARITY_BIT_ODD:
56      return SPACEPARITY;
57    case api::serial::PARITY_BIT_NO:
58    default:
59      return NOPARITY;
60  }
61}
62
63int StopBitsEnumToConstant(api::serial::StopBits stop_bits) {
64  switch (stop_bits) {
65    case api::serial::STOP_BITS_TWO:
66      return TWOSTOPBITS;
67    case api::serial::STOP_BITS_ONE:
68    default:
69      return ONESTOPBIT;
70  }
71}
72
73int SpeedConstantToBitrate(int speed) {
74#define SPEED_TO_BITRATE_CASE(x) case CBR_ ## x: return x;
75  switch (speed) {
76    SPEED_TO_BITRATE_CASE(110);
77    SPEED_TO_BITRATE_CASE(300);
78    SPEED_TO_BITRATE_CASE(600);
79    SPEED_TO_BITRATE_CASE(1200);
80    SPEED_TO_BITRATE_CASE(2400);
81    SPEED_TO_BITRATE_CASE(4800);
82    SPEED_TO_BITRATE_CASE(9600);
83    SPEED_TO_BITRATE_CASE(14400);
84    SPEED_TO_BITRATE_CASE(19200);
85    SPEED_TO_BITRATE_CASE(38400);
86    SPEED_TO_BITRATE_CASE(57600);
87    SPEED_TO_BITRATE_CASE(115200);
88    SPEED_TO_BITRATE_CASE(128000);
89    SPEED_TO_BITRATE_CASE(256000);
90    default:
91      // If it's not one of the standard index constants,
92      // it should be an integral baud rate, according to
93      // MSDN.
94      return speed;
95  }
96#undef SPEED_TO_BITRATE_CASE
97}
98
99api::serial::DataBits DataBitsConstantToEnum(int data_bits) {
100  switch (data_bits) {
101    case 7:
102      return api::serial::DATA_BITS_SEVEN;
103    case 8:
104    default:
105      return api::serial::DATA_BITS_EIGHT;
106  }
107}
108
109api::serial::ParityBit ParityBitConstantToEnum(int parity_bit) {
110  switch (parity_bit) {
111    case EVENPARITY:
112      return api::serial::PARITY_BIT_EVEN;
113    case ODDPARITY:
114      return api::serial::PARITY_BIT_ODD;
115    case NOPARITY:
116    default:
117      return api::serial::PARITY_BIT_NO;
118  }
119}
120
121api::serial::StopBits StopBitsConstantToEnum(int stop_bits) {
122  switch (stop_bits) {
123    case TWOSTOPBITS:
124      return api::serial::STOP_BITS_TWO;
125    case ONESTOPBIT:
126    default:
127      return api::serial::STOP_BITS_ONE;
128  }
129}
130
131}  // namespace
132
133bool SerialConnection::ConfigurePort(
134    const api::serial::ConnectionOptions& options) {
135  DCB config = { 0 };
136  config.DCBlength = sizeof(config);
137  if (!GetCommState(file_.GetPlatformFile(), &config)) {
138    return false;
139  }
140  if (options.bitrate.get())
141    config.BaudRate = BitrateToSpeedConstant(*options.bitrate);
142  if (options.data_bits != api::serial::DATA_BITS_NONE)
143    config.ByteSize = DataBitsEnumToConstant(options.data_bits);
144  if (options.parity_bit != api::serial::PARITY_BIT_NONE)
145    config.Parity = ParityBitEnumToConstant(options.parity_bit);
146  if (options.stop_bits != api::serial::STOP_BITS_NONE)
147    config.StopBits = StopBitsEnumToConstant(options.stop_bits);
148  if (options.cts_flow_control.get()) {
149    if (*options.cts_flow_control) {
150      config.fOutxCtsFlow = TRUE;
151      config.fRtsControl = RTS_CONTROL_HANDSHAKE;
152    } else {
153      config.fOutxCtsFlow = FALSE;
154      config.fRtsControl = RTS_CONTROL_ENABLE;
155    }
156  }
157  return SetCommState(file_.GetPlatformFile(), &config) != 0;
158}
159
160bool SerialConnection::PostOpen() {
161  // A ReadIntervalTimeout of MAXDWORD will cause async reads to complete
162  // immediately with any data that's available, even if there is none.
163  // This is OK because we never issue a read request until WaitCommEvent
164  // signals that data is available.
165  COMMTIMEOUTS timeouts = { 0 };
166  timeouts.ReadIntervalTimeout = MAXDWORD;
167  if (!::SetCommTimeouts(file_.GetPlatformFile(), &timeouts)) {
168    return false;
169  }
170
171  DCB config = { 0 };
172  config.DCBlength = sizeof(config);
173  if (!GetCommState(file_.GetPlatformFile(), &config)) {
174    return false;
175  }
176  // Setup some sane default state.
177  config.fBinary = TRUE;
178  config.fParity = FALSE;
179  config.fAbortOnError = TRUE;
180  config.fOutxCtsFlow = FALSE;
181  config.fOutxDsrFlow = FALSE;
182  config.fRtsControl = RTS_CONTROL_ENABLE;
183  config.fDtrControl = DTR_CONTROL_ENABLE;
184  config.fDsrSensitivity = FALSE;
185  config.fOutX = FALSE;
186  config.fInX = FALSE;
187  return SetCommState(file_.GetPlatformFile(), &config) != 0;
188}
189
190bool SerialConnection::Flush() const {
191  return PurgeComm(file_.GetPlatformFile(), PURGE_RXCLEAR | PURGE_TXCLEAR) != 0;
192}
193
194bool SerialConnection::GetControlSignals(
195    api::serial::DeviceControlSignals* signals) const {
196  DWORD status;
197  if (!GetCommModemStatus(file_.GetPlatformFile(), &status)) {
198    return false;
199  }
200  signals->dcd = (status & MS_RLSD_ON) != 0;
201  signals->cts = (status & MS_CTS_ON) != 0;
202  signals->dsr = (status & MS_DSR_ON) != 0;
203  signals->ri = (status & MS_RING_ON) != 0;
204  return true;
205}
206
207bool SerialConnection::SetControlSignals(
208    const api::serial::HostControlSignals& signals) {
209  if (signals.dtr.get()) {
210    if (!EscapeCommFunction(file_.GetPlatformFile(),
211                            *signals.dtr ? SETDTR : CLRDTR)) {
212      return false;
213    }
214  }
215  if (signals.rts.get()) {
216    if (!EscapeCommFunction(file_.GetPlatformFile(),
217                            *signals.rts ? SETRTS : CLRRTS)) {
218      return false;
219    }
220  }
221  return true;
222}
223
224bool SerialConnection::GetPortInfo(api::serial::ConnectionInfo* info) const {
225  DCB config = { 0 };
226  config.DCBlength = sizeof(config);
227  if (!GetCommState(file_.GetPlatformFile(), &config)) {
228    return false;
229  }
230  info->bitrate.reset(new int(SpeedConstantToBitrate(config.BaudRate)));
231  info->data_bits = DataBitsConstantToEnum(config.ByteSize);
232  info->parity_bit = ParityBitConstantToEnum(config.Parity);
233  info->stop_bits = StopBitsConstantToEnum(config.StopBits);
234  info->cts_flow_control.reset(new bool(config.fOutxCtsFlow != 0));
235  return true;
236}
237
238std::string SerialConnection::MaybeFixUpPortName(
239    const std::string &port_name) {
240  // For COM numbers less than 9, CreateFile is called with a string such as
241  // "COM1". For numbers greater than 9, a prefix of "\\\\.\\" must be added.
242  if (port_name.length() > std::string("COM9").length())
243    return std::string("\\\\.\\").append(port_name);
244
245  return port_name;
246}
247
248}  // namespace extensions
249