cec: handle bus scan from LG TVs
[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;
855a3a98 56 m_tostdout = false;
abbca718
LOK
57}
58
59CSerialPort::~CSerialPort()
60{
61 Close();
62}
63
28352a04 64int8_t CSerialPort::Write(CCECAdapterMessage *data)
abbca718
LOK
65{
66 fd_set port;
b1f50952
LOK
67
68 CLockObject lock(&m_mutex);
abbca718
LOK
69 if (m_fd == -1)
70 {
71 m_error = "port closed";
72 return -1;
73 }
74
7eb13cca 75 int32_t byteswritten = 0;
abbca718 76
5a1e87c8 77 while (byteswritten < (int32_t) data->size())
abbca718
LOK
78 {
79 FD_ZERO(&port);
80 FD_SET(m_fd, &port);
81 int returnv = select(m_fd + 1, NULL, &port, NULL, NULL);
82 if (returnv == -1)
83 {
b9187cc6 84 m_error = strerror(errno);
abbca718
LOK
85 return -1;
86 }
87
5a1e87c8 88 returnv = write(m_fd, data->packet.data + byteswritten, data->size() - byteswritten);
abbca718
LOK
89 if (returnv == -1)
90 {
b9187cc6 91 m_error = strerror(errno);
abbca718
LOK
92 return -1;
93 }
94 byteswritten += returnv;
95 }
96
97 //print what's written to stdout for debugging
855a3a98
LOK
98 if (m_tostdout)
99 {
100 printf("%s write:", m_name.c_str());
101 for (int i = 0; i < byteswritten; i++)
102 printf(" %02x", data->at(i));
103
104 printf("\n");
105 }
abbca718
LOK
106
107 return byteswritten;
108}
109
7eb13cca 110int32_t CSerialPort::Read(uint8_t* data, uint32_t len, uint64_t iTimeoutMs /*= 0*/)
abbca718
LOK
111{
112 fd_set port;
113 struct timeval timeout, *tv;
7eb13cca
LOK
114 int64_t now(0), target(0);
115 int32_t bytesread = 0;
abbca718 116
b1f50952 117 CLockObject lock(&m_mutex);
abbca718
LOK
118 if (m_fd == -1)
119 {
120 m_error = "port closed";
121 return -1;
122 }
123
7eb13cca 124 if (iTimeoutMs > 0)
abbca718
LOK
125 {
126 now = GetTimeMs();
127 target = now + (int64_t) iTimeoutMs;
128 }
129
7eb13cca 130 while (bytesread < (int32_t) len && (iTimeoutMs == 0 || target > now))
abbca718 131 {
7eb13cca 132 if (iTimeoutMs == 0)
abbca718
LOK
133 {
134 tv = NULL;
135 }
136 else
137 {
138 timeout.tv_sec = ((long int)target - (long int)now) / (long int)1000.;
139 timeout.tv_usec = ((long int)target - (long int)now) % (long int)1000.;
140 tv = &timeout;
141 }
142
143 FD_ZERO(&port);
144 FD_SET(m_fd, &port);
7eb13cca 145 int32_t returnv = select(m_fd + 1, &port, NULL, NULL, tv);
abbca718
LOK
146
147 if (returnv == -1)
148 {
b9187cc6 149 m_error = strerror(errno);
abbca718
LOK
150 return -1;
151 }
152 else if (returnv == 0)
153 {
154 break; //nothing to read
155 }
156
157 returnv = read(m_fd, data + bytesread, len - bytesread);
158 if (returnv == -1)
159 {
b9187cc6 160 m_error = strerror(errno);
abbca718
LOK
161 return -1;
162 }
163
164 bytesread += returnv;
165
166 if (iTimeoutMs > 0)
167 now = GetTimeMs();
168 }
169
170 //print what's read to stdout for debugging
855a3a98
LOK
171 if (m_tostdout && bytesread > 0)
172 {
173 printf("%s read:", m_name.c_str());
174 for (int i = 0; i < bytesread; i++)
175 printf(" %02x", data[i]);
176
177 printf("\n");
178 }
abbca718
LOK
179
180 return bytesread;
181}
182
183//setting all this stuff up is a pain in the ass
7eb13cca 184bool CSerialPort::Open(string name, uint32_t baudrate, uint8_t databits /* = 8 */, uint8_t stopbits /* = 1 */, uint8_t parity /* = PAR_NONE */)
abbca718
LOK
185{
186 m_name = name;
b9187cc6 187 m_error = strerror(errno);
b1f50952
LOK
188 CLockObject lock(&m_mutex);
189
abbca718
LOK
190 if (databits < 5 || databits > 8)
191 {
192 m_error = "Databits has to be between 5 and 8";
193 return false;
194 }
195
196 if (stopbits != 1 && stopbits != 2)
197 {
198 m_error = "Stopbits has to be 1 or 2";
199 return false;
200 }
201
202 if (parity != PAR_NONE && parity != PAR_EVEN && parity != PAR_ODD)
203 {
204 m_error = "Parity has to be none, even or odd";
205 return false;
206 }
207
208 m_fd = open(name.c_str(), O_RDWR | O_NOCTTY | O_NDELAY);
209
210 if (m_fd == -1)
211 {
b9187cc6 212 m_error = strerror(errno);
abbca718
LOK
213 return false;
214 }
215
216 fcntl(m_fd, F_SETFL, 0);
217
218 if (!SetBaudRate(baudrate))
219 {
220 return false;
221 }
222
223 m_options.c_cflag |= (CLOCAL | CREAD);
224 m_options.c_cflag &= ~HUPCL;
225
226 m_options.c_cflag &= ~CSIZE;
227 if (databits == 5) m_options.c_cflag |= CS5;
228 if (databits == 6) m_options.c_cflag |= CS6;
229 if (databits == 7) m_options.c_cflag |= CS7;
230 if (databits == 8) m_options.c_cflag |= CS8;
231
232 m_options.c_cflag &= ~PARENB;
233 if (parity == PAR_EVEN || parity == PAR_ODD)
234 m_options.c_cflag |= PARENB;
235 if (parity == PAR_ODD)
236 m_options.c_cflag |= PARODD;
237
238#ifdef CRTSCTS
239 m_options.c_cflag &= ~CRTSCTS;
240#elif defined(CNEW_RTSCTS)
241 m_options.c_cflag &= ~CNEW_RTSCTS;
242#endif
243
244 if (stopbits == 1) m_options.c_cflag &= ~CSTOPB;
245 else m_options.c_cflag |= CSTOPB;
246
247 //I guessed a little here
248 m_options.c_lflag &= ~(ICANON | ECHO | ECHOE | ISIG | XCASE | ECHOK | ECHONL | ECHOCTL | ECHOPRT | ECHOKE | TOSTOP);
249
250 if (parity == PAR_NONE)
251 {
252 m_options.c_iflag &= ~INPCK;
253 }
254 else
255 {
256 m_options.c_iflag |= INPCK | ISTRIP;
257 }
258
259 m_options.c_iflag &= ~(IXON | IXOFF | IXANY | BRKINT | INLCR | IGNCR | ICRNL | IUCLC | IMAXBEL);
260 m_options.c_oflag &= ~(OPOST | ONLCR | OCRNL);
261
262 if (tcsetattr(m_fd, TCSANOW, &m_options) != 0)
263 {
b9187cc6 264 m_error = strerror(errno);
abbca718
LOK
265 return false;
266 }
267
268 //non-blocking port
269 fcntl(m_fd, F_SETFL, FNDELAY);
270
271 return true;
272}
273
274void CSerialPort::Close()
275{
b1f50952 276 CLockObject lock(&m_mutex);
abbca718
LOK
277 if (m_fd != -1)
278 {
279 close(m_fd);
280 m_fd = -1;
281 m_name = "";
282 m_error = "";
283 }
284}
285
7eb13cca 286bool CSerialPort::SetBaudRate(uint32_t baudrate)
abbca718 287{
b9187cc6 288 int rate = IntToBaudrate(baudrate);
abbca718
LOK
289 if (rate == -1)
290 {
291 char buff[255];
292 sprintf(buff, "%i is not a valid baudrate", baudrate);
293 m_error = buff;
294 return false;
295 }
296
297 //get the current port attributes
298 if (tcgetattr(m_fd, &m_options) != 0)
299 {
b9187cc6 300 m_error = strerror(errno);
abbca718
LOK
301 return false;
302 }
303
304 if (cfsetispeed(&m_options, rate) != 0)
305 {
b9187cc6 306 m_error = strerror(errno);
abbca718
LOK
307 return false;
308 }
309
310 if (cfsetospeed(&m_options, rate) != 0)
311 {
b9187cc6 312 m_error = strerror(errno);
abbca718
LOK
313 return false;
314 }
315
316 return true;
317}
b9187cc6 318
b1f50952 319bool CSerialPort::IsOpen()
b9187cc6 320{
b1f50952 321 CLockObject lock(&m_mutex);
b9187cc6
LOK
322 return m_fd != -1;
323}