Merge branch 'master' into release
[deb_libcec.git] / src / lib / platform / linux / serialport.cpp
1 /*
2 * boblight
3 * Copyright (C) Bob 2009
4 *
5 * boblight is free software: you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License as published by the
7 * Free Software Foundation, either version 3 of the License, or
8 * (at your option) any later version.
9 *
10 * boblight is distributed in the hope that it will be useful, but
11 * WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
13 * See the GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License along
16 * with this program. If not, see <http://www.gnu.org/licenses/>.
17 */
18
19 #include <stdio.h>
20 #include <fcntl.h>
21 #include "../serialport.h"
22 #include "../baudrate.h"
23 #include "../timeutils.h"
24
25 #if defined(__APPLE__)
26 #ifndef XCASE
27 #define XCASE 0
28 #endif
29 #ifndef OLCUC
30 #define OLCUC 0
31 #endif
32 #ifndef IUCLC
33 #define IUCLC 0
34 #endif
35 #endif
36 using namespace std;
37 using namespace CEC;
38
39 CSerialPort::CSerialPort()
40 {
41 m_fd = -1;
42 }
43
44 CSerialPort::~CSerialPort()
45 {
46 Close();
47 }
48
49 int8_t CSerialPort::Write(const cec_adapter_message &data)
50 {
51 fd_set port;
52
53 CLockObject lock(&m_mutex);
54 if (m_fd == -1)
55 {
56 m_error = "port closed";
57 return -1;
58 }
59
60 int32_t byteswritten = 0;
61
62 while (byteswritten < (int32_t) data.size())
63 {
64 FD_ZERO(&port);
65 FD_SET(m_fd, &port);
66 int returnv = select(m_fd + 1, NULL, &port, NULL, NULL);
67 if (returnv == -1)
68 {
69 m_error = strerror(errno);
70 return -1;
71 }
72
73 returnv = write(m_fd, data.packet.data + byteswritten, data.size() - byteswritten);
74 if (returnv == -1)
75 {
76 m_error = strerror(errno);
77 return -1;
78 }
79 byteswritten += returnv;
80 }
81
82 //print what's written to stdout for debugging
83 // if (m_tostdout)
84 // {
85 // printf("%s write:", m_name.c_str());
86 // for (int i = 0; i < byteswritten; i++)
87 // printf(" %02x", (unsigned int)data[i]);
88 //
89 // printf("\n");
90 // }
91
92 return byteswritten;
93 }
94
95 int32_t CSerialPort::Read(uint8_t* data, uint32_t len, uint64_t iTimeoutMs /*= 0*/)
96 {
97 fd_set port;
98 struct timeval timeout, *tv;
99 int64_t now(0), target(0);
100 int32_t bytesread = 0;
101
102 CLockObject lock(&m_mutex);
103 if (m_fd == -1)
104 {
105 m_error = "port closed";
106 return -1;
107 }
108
109 if (iTimeoutMs > 0)
110 {
111 now = GetTimeMs();
112 target = now + (int64_t) iTimeoutMs;
113 }
114
115 while (bytesread < (int32_t) len && (iTimeoutMs == 0 || target > now))
116 {
117 if (iTimeoutMs == 0)
118 {
119 tv = NULL;
120 }
121 else
122 {
123 timeout.tv_sec = ((long int)target - (long int)now) / (long int)1000.;
124 timeout.tv_usec = ((long int)target - (long int)now) % (long int)1000.;
125 tv = &timeout;
126 }
127
128 FD_ZERO(&port);
129 FD_SET(m_fd, &port);
130 int32_t returnv = select(m_fd + 1, &port, NULL, NULL, tv);
131
132 if (returnv == -1)
133 {
134 m_error = strerror(errno);
135 return -1;
136 }
137 else if (returnv == 0)
138 {
139 break; //nothing to read
140 }
141
142 returnv = read(m_fd, data + bytesread, len - bytesread);
143 if (returnv == -1)
144 {
145 m_error = strerror(errno);
146 return -1;
147 }
148
149 bytesread += returnv;
150
151 if (iTimeoutMs > 0)
152 now = GetTimeMs();
153 }
154
155 //print what's read to stdout for debugging
156 // if (m_tostdout && bytesread > 0)
157 // {
158 // printf("%s read:", m_name.c_str());
159 // for (int i = 0; i < bytesread; i++)
160 // printf(" %02x", (unsigned int)data[i]);
161 //
162 // printf("\n");
163 // }
164
165 return bytesread;
166 }
167
168 //setting all this stuff up is a pain in the ass
169 bool CSerialPort::Open(string name, uint32_t baudrate, uint8_t databits /* = 8 */, uint8_t stopbits /* = 1 */, uint8_t parity /* = PAR_NONE */)
170 {
171 m_name = name;
172 m_error = strerror(errno);
173 CLockObject lock(&m_mutex);
174
175 if (databits < 5 || databits > 8)
176 {
177 m_error = "Databits has to be between 5 and 8";
178 return false;
179 }
180
181 if (stopbits != 1 && stopbits != 2)
182 {
183 m_error = "Stopbits has to be 1 or 2";
184 return false;
185 }
186
187 if (parity != PAR_NONE && parity != PAR_EVEN && parity != PAR_ODD)
188 {
189 m_error = "Parity has to be none, even or odd";
190 return false;
191 }
192
193 m_fd = open(name.c_str(), O_RDWR | O_NOCTTY | O_NDELAY);
194
195 if (m_fd == -1)
196 {
197 m_error = strerror(errno);
198 return false;
199 }
200
201 fcntl(m_fd, F_SETFL, 0);
202
203 if (!SetBaudRate(baudrate))
204 {
205 return false;
206 }
207
208 m_options.c_cflag |= (CLOCAL | CREAD);
209 m_options.c_cflag &= ~HUPCL;
210
211 m_options.c_cflag &= ~CSIZE;
212 if (databits == 5) m_options.c_cflag |= CS5;
213 if (databits == 6) m_options.c_cflag |= CS6;
214 if (databits == 7) m_options.c_cflag |= CS7;
215 if (databits == 8) m_options.c_cflag |= CS8;
216
217 m_options.c_cflag &= ~PARENB;
218 if (parity == PAR_EVEN || parity == PAR_ODD)
219 m_options.c_cflag |= PARENB;
220 if (parity == PAR_ODD)
221 m_options.c_cflag |= PARODD;
222
223 #ifdef CRTSCTS
224 m_options.c_cflag &= ~CRTSCTS;
225 #elif defined(CNEW_RTSCTS)
226 m_options.c_cflag &= ~CNEW_RTSCTS;
227 #endif
228
229 if (stopbits == 1) m_options.c_cflag &= ~CSTOPB;
230 else m_options.c_cflag |= CSTOPB;
231
232 //I guessed a little here
233 m_options.c_lflag &= ~(ICANON | ECHO | ECHOE | ISIG | XCASE | ECHOK | ECHONL | ECHOCTL | ECHOPRT | ECHOKE | TOSTOP);
234
235 if (parity == PAR_NONE)
236 {
237 m_options.c_iflag &= ~INPCK;
238 }
239 else
240 {
241 m_options.c_iflag |= INPCK | ISTRIP;
242 }
243
244 m_options.c_iflag &= ~(IXON | IXOFF | IXANY | BRKINT | INLCR | IGNCR | ICRNL | IUCLC | IMAXBEL);
245 m_options.c_oflag &= ~(OPOST | ONLCR | OCRNL);
246
247 if (tcsetattr(m_fd, TCSANOW, &m_options) != 0)
248 {
249 m_error = strerror(errno);
250 return false;
251 }
252
253 //non-blocking port
254 fcntl(m_fd, F_SETFL, FNDELAY);
255
256 return true;
257 }
258
259 void CSerialPort::Close()
260 {
261 CLockObject lock(&m_mutex);
262 if (m_fd != -1)
263 {
264 close(m_fd);
265 m_fd = -1;
266 m_name = "";
267 m_error = "";
268 }
269 }
270
271 bool CSerialPort::SetBaudRate(uint32_t baudrate)
272 {
273 int rate = IntToBaudrate(baudrate);
274 if (rate == -1)
275 {
276 char buff[255];
277 sprintf(buff, "%i is not a valid baudrate", baudrate);
278 m_error = buff;
279 return false;
280 }
281
282 //get the current port attributes
283 if (tcgetattr(m_fd, &m_options) != 0)
284 {
285 m_error = strerror(errno);
286 return false;
287 }
288
289 if (cfsetispeed(&m_options, rate) != 0)
290 {
291 m_error = strerror(errno);
292 return false;
293 }
294
295 if (cfsetospeed(&m_options, rate) != 0)
296 {
297 m_error = strerror(errno);
298 return false;
299 }
300
301 return true;
302 }
303
304 bool CSerialPort::IsOpen()
305 {
306 CLockObject lock(&m_mutex);
307 return m_fd != -1;
308 }