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