• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 <sys/ioctl.h>
8 #include <termios.h>
9 
10 #if defined(OS_LINUX)
11 #include <linux/serial.h>
12 #endif
13 
14 namespace extensions {
15 
16 namespace {
17 
18 // Convert an integral bit rate to a nominal one. Returns |true|
19 // if the conversion was successful and |false| otherwise.
BitrateToSpeedConstant(int bitrate,speed_t * speed)20 bool BitrateToSpeedConstant(int bitrate, speed_t* speed) {
21 #define BITRATE_TO_SPEED_CASE(x) case x: *speed = B ## x; return true;
22   switch (bitrate) {
23     BITRATE_TO_SPEED_CASE(0)
24     BITRATE_TO_SPEED_CASE(50)
25     BITRATE_TO_SPEED_CASE(75)
26     BITRATE_TO_SPEED_CASE(110)
27     BITRATE_TO_SPEED_CASE(134)
28     BITRATE_TO_SPEED_CASE(150)
29     BITRATE_TO_SPEED_CASE(200)
30     BITRATE_TO_SPEED_CASE(300)
31     BITRATE_TO_SPEED_CASE(600)
32     BITRATE_TO_SPEED_CASE(1200)
33     BITRATE_TO_SPEED_CASE(1800)
34     BITRATE_TO_SPEED_CASE(2400)
35     BITRATE_TO_SPEED_CASE(4800)
36     BITRATE_TO_SPEED_CASE(9600)
37     BITRATE_TO_SPEED_CASE(19200)
38     BITRATE_TO_SPEED_CASE(38400)
39 #if defined(OS_POSIX) && !defined(OS_MACOSX)
40     BITRATE_TO_SPEED_CASE(57600)
41     BITRATE_TO_SPEED_CASE(115200)
42     BITRATE_TO_SPEED_CASE(230400)
43     BITRATE_TO_SPEED_CASE(460800)
44     BITRATE_TO_SPEED_CASE(576000)
45     BITRATE_TO_SPEED_CASE(921600)
46 #endif
47     default:
48       return false;
49   }
50 #undef BITRATE_TO_SPEED_CASE
51 }
52 
53 // Convert a known nominal speed into an integral bitrate. Returns |true|
54 // if the conversion was successful and |false| otherwise.
SpeedConstantToBitrate(speed_t speed,int * bitrate)55 bool SpeedConstantToBitrate(speed_t speed, int* bitrate) {
56 #define SPEED_TO_BITRATE_CASE(x) case B ## x: *bitrate = x; return true;
57   switch (speed) {
58     SPEED_TO_BITRATE_CASE(0)
59     SPEED_TO_BITRATE_CASE(50)
60     SPEED_TO_BITRATE_CASE(75)
61     SPEED_TO_BITRATE_CASE(110)
62     SPEED_TO_BITRATE_CASE(134)
63     SPEED_TO_BITRATE_CASE(150)
64     SPEED_TO_BITRATE_CASE(200)
65     SPEED_TO_BITRATE_CASE(300)
66     SPEED_TO_BITRATE_CASE(600)
67     SPEED_TO_BITRATE_CASE(1200)
68     SPEED_TO_BITRATE_CASE(1800)
69     SPEED_TO_BITRATE_CASE(2400)
70     SPEED_TO_BITRATE_CASE(4800)
71     SPEED_TO_BITRATE_CASE(9600)
72     SPEED_TO_BITRATE_CASE(19200)
73     SPEED_TO_BITRATE_CASE(38400)
74 #if defined(OS_POSIX) && !defined(OS_MACOSX)
75     SPEED_TO_BITRATE_CASE(57600)
76     SPEED_TO_BITRATE_CASE(115200)
77     SPEED_TO_BITRATE_CASE(230400)
78     SPEED_TO_BITRATE_CASE(460800)
79     SPEED_TO_BITRATE_CASE(576000)
80     SPEED_TO_BITRATE_CASE(921600)
81 #endif
82     default:
83       return false;
84   }
85 #undef SPEED_TO_BITRATE_CASE
86 }
87 
SetCustomBitrate(base::PlatformFile file,struct termios * config,int bitrate)88 bool SetCustomBitrate(base::PlatformFile file,
89                       struct termios* config,
90                       int bitrate) {
91 #if defined(OS_LINUX)
92   struct serial_struct serial;
93   if (ioctl(file, TIOCGSERIAL, &serial) < 0) {
94     return false;
95   }
96   serial.flags = (serial.flags & ~ASYNC_SPD_MASK) | ASYNC_SPD_CUST;
97   serial.custom_divisor = serial.baud_base / bitrate;
98   if (serial.custom_divisor < 1) {
99     serial.custom_divisor = 1;
100   }
101   cfsetispeed(config, B38400);
102   cfsetospeed(config, B38400);
103   return ioctl(file, TIOCSSERIAL, &serial) >= 0;
104 #elif defined(OS_MACOSX)
105   speed_t speed = static_cast<speed_t>(bitrate);
106   cfsetispeed(config, speed);
107   cfsetospeed(config, speed);
108   return true;
109 #else
110   return false;
111 #endif
112 }
113 
114 }  // namespace
115 
ConfigurePort(const api::serial::ConnectionOptions & options)116 bool SerialConnection::ConfigurePort(
117     const api::serial::ConnectionOptions& options) {
118   struct termios config;
119   tcgetattr(file_.GetPlatformFile(), &config);
120   if (options.bitrate.get()) {
121     if (*options.bitrate >= 0) {
122       speed_t bitrate_opt = B0;
123       if (BitrateToSpeedConstant(*options.bitrate, &bitrate_opt)) {
124         cfsetispeed(&config, bitrate_opt);
125         cfsetospeed(&config, bitrate_opt);
126       } else {
127         // Attempt to set a custom speed.
128         if (!SetCustomBitrate(file_.GetPlatformFile(), &config,
129                               *options.bitrate)) {
130           return false;
131         }
132       }
133     }
134   }
135   if (options.data_bits != api::serial::DATA_BITS_NONE) {
136     config.c_cflag &= ~CSIZE;
137     switch (options.data_bits) {
138       case api::serial::DATA_BITS_SEVEN:
139         config.c_cflag |= CS7;
140         break;
141       case api::serial::DATA_BITS_EIGHT:
142       default:
143         config.c_cflag |= CS8;
144         break;
145     }
146   }
147   if (options.parity_bit != api::serial::PARITY_BIT_NONE) {
148     switch (options.parity_bit) {
149       case api::serial::PARITY_BIT_EVEN:
150         config.c_cflag |= PARENB;
151         config.c_cflag &= ~PARODD;
152         break;
153       case api::serial::PARITY_BIT_ODD:
154         config.c_cflag |= (PARODD | PARENB);
155         break;
156       case api::serial::PARITY_BIT_NO:
157       default:
158         config.c_cflag &= ~(PARODD | PARENB);
159         break;
160     }
161   }
162   if (options.stop_bits != api::serial::STOP_BITS_NONE) {
163     switch (options.stop_bits) {
164       case api::serial::STOP_BITS_TWO:
165         config.c_cflag |= CSTOPB;
166         break;
167       case api::serial::STOP_BITS_ONE:
168       default:
169         config.c_cflag &= ~CSTOPB;
170         break;
171     }
172   }
173   if (options.cts_flow_control.get()) {
174     if (*options.cts_flow_control){
175       config.c_cflag |= CRTSCTS;
176     } else {
177       config.c_cflag &= ~CRTSCTS;
178     }
179   }
180   return tcsetattr(file_.GetPlatformFile(), TCSANOW, &config) == 0;
181 }
182 
PostOpen()183 bool SerialConnection::PostOpen() {
184   struct termios config;
185   tcgetattr(file_.GetPlatformFile(), &config);
186 
187   // Set flags for 'raw' operation
188   config.c_lflag &= ~(ICANON | ECHO | ECHOE | ECHONL | ISIG);
189   config.c_iflag &= ~(IGNBRK | BRKINT | PARMRK | ISTRIP | INLCR | IGNCR |
190                       ICRNL | IXON);
191   config.c_oflag &= ~OPOST;
192 
193   // CLOCAL causes the system to disregard the DCD signal state.
194   // CREAD enables reading from the port.
195   config.c_cflag |= (CLOCAL | CREAD);
196 
197   return tcsetattr(file_.GetPlatformFile(), TCSANOW, &config) == 0;
198 }
199 
Flush() const200 bool SerialConnection::Flush() const {
201   return tcflush(file_.GetPlatformFile(), TCIOFLUSH) == 0;
202 }
203 
GetControlSignals(api::serial::DeviceControlSignals * signals) const204 bool SerialConnection::GetControlSignals(
205     api::serial::DeviceControlSignals* signals) const {
206   int status;
207   if (ioctl(file_.GetPlatformFile(), TIOCMGET, &status) == -1) {
208     return false;
209   }
210 
211   signals->dcd = (status & TIOCM_CAR) != 0;
212   signals->cts = (status & TIOCM_CTS) != 0;
213   signals->dsr = (status & TIOCM_DSR) != 0;
214   signals->ri = (status & TIOCM_RI) != 0;
215   return true;
216 }
217 
SetControlSignals(const api::serial::HostControlSignals & signals)218 bool SerialConnection::SetControlSignals(
219     const api::serial::HostControlSignals& signals) {
220   int status;
221 
222   if (ioctl(file_.GetPlatformFile(), TIOCMGET, &status) == -1) {
223     return false;
224   }
225 
226   if (signals.dtr.get()) {
227     if (*signals.dtr) {
228       status |= TIOCM_DTR;
229     } else {
230       status &= ~TIOCM_DTR;
231     }
232   }
233 
234   if (signals.rts.get()) {
235     if (*signals.rts){
236       status |= TIOCM_RTS;
237     } else{
238       status &= ~TIOCM_RTS;
239     }
240   }
241 
242   return ioctl(file_.GetPlatformFile(), TIOCMSET, &status) == 0;
243 }
244 
GetPortInfo(api::serial::ConnectionInfo * info) const245 bool SerialConnection::GetPortInfo(api::serial::ConnectionInfo* info) const {
246   struct termios config;
247   if (tcgetattr(file_.GetPlatformFile(), &config) == -1) {
248     return false;
249   }
250   speed_t ispeed = cfgetispeed(&config);
251   speed_t ospeed = cfgetospeed(&config);
252   if (ispeed == ospeed) {
253     int bitrate = 0;
254     if (SpeedConstantToBitrate(ispeed, &bitrate)) {
255       info->bitrate.reset(new int(bitrate));
256     } else if (ispeed > 0) {
257       info->bitrate.reset(new int(static_cast<int>(ispeed)));
258     }
259   }
260   if ((config.c_cflag & CSIZE) == CS7) {
261     info->data_bits = api::serial::DATA_BITS_SEVEN;
262   } else if ((config.c_cflag & CSIZE) == CS8) {
263     info->data_bits = api::serial::DATA_BITS_EIGHT;
264   } else {
265     info->data_bits = api::serial::DATA_BITS_NONE;
266   }
267   if (config.c_cflag & PARENB) {
268     info->parity_bit = (config.c_cflag & PARODD) ? api::serial::PARITY_BIT_ODD
269                                                  : api::serial::PARITY_BIT_EVEN;
270   } else {
271     info->parity_bit = api::serial::PARITY_BIT_NO;
272   }
273   info->stop_bits = (config.c_cflag & CSTOPB) ? api::serial::STOP_BITS_TWO
274                                               : api::serial::STOP_BITS_ONE;
275   info->cts_flow_control.reset(new bool((config.c_cflag & CRTSCTS) != 0));
276   return true;
277 }
278 
MaybeFixUpPortName(const std::string & port_name)279 std::string SerialConnection::MaybeFixUpPortName(
280     const std::string &port_name) {
281   return port_name;
282 }
283 
284 }  // namespace extensions
285