cec: handle bus scan from LG TVs
[deb_libcec.git] / src / lib / platform / linux / serialport.cpp
1 /*
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
12 * (at your option) any later version.
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/
31 */
32
33 #include <stdio.h>
34 #include <fcntl.h>
35 #include "../serialport.h"
36 #include "../baudrate.h"
37 #include "../timeutils.h"
38
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
50 using namespace std;
51 using namespace CEC;
52
53 CSerialPort::CSerialPort()
54 {
55 m_fd = -1;
56 m_tostdout = false;
57 }
58
59 CSerialPort::~CSerialPort()
60 {
61 Close();
62 }
63
64 int8_t CSerialPort::Write(CCECAdapterMessage *data)
65 {
66 fd_set port;
67
68 CLockObject lock(&m_mutex);
69 if (m_fd == -1)
70 {
71 m_error = "port closed";
72 return -1;
73 }
74
75 int32_t byteswritten = 0;
76
77 while (byteswritten < (int32_t) data->size())
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 {
84 m_error = strerror(errno);
85 return -1;
86 }
87
88 returnv = write(m_fd, data->packet.data + byteswritten, data->size() - byteswritten);
89 if (returnv == -1)
90 {
91 m_error = strerror(errno);
92 return -1;
93 }
94 byteswritten += returnv;
95 }
96
97 //print what's written to stdout for debugging
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 }
106
107 return byteswritten;
108 }
109
110 int32_t CSerialPort::Read(uint8_t* data, uint32_t len, uint64_t iTimeoutMs /*= 0*/)
111 {
112 fd_set port;
113 struct timeval timeout, *tv;
114 int64_t now(0), target(0);
115 int32_t bytesread = 0;
116
117 CLockObject lock(&m_mutex);
118 if (m_fd == -1)
119 {
120 m_error = "port closed";
121 return -1;
122 }
123
124 if (iTimeoutMs > 0)
125 {
126 now = GetTimeMs();
127 target = now + (int64_t) iTimeoutMs;
128 }
129
130 while (bytesread < (int32_t) len && (iTimeoutMs == 0 || target > now))
131 {
132 if (iTimeoutMs == 0)
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);
145 int32_t returnv = select(m_fd + 1, &port, NULL, NULL, tv);
146
147 if (returnv == -1)
148 {
149 m_error = strerror(errno);
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 {
160 m_error = strerror(errno);
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
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 }
179
180 return bytesread;
181 }
182
183 //setting all this stuff up is a pain in the ass
184 bool CSerialPort::Open(string name, uint32_t baudrate, uint8_t databits /* = 8 */, uint8_t stopbits /* = 1 */, uint8_t parity /* = PAR_NONE */)
185 {
186 m_name = name;
187 m_error = strerror(errno);
188 CLockObject lock(&m_mutex);
189
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 {
212 m_error = strerror(errno);
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 {
264 m_error = strerror(errno);
265 return false;
266 }
267
268 //non-blocking port
269 fcntl(m_fd, F_SETFL, FNDELAY);
270
271 return true;
272 }
273
274 void CSerialPort::Close()
275 {
276 CLockObject lock(&m_mutex);
277 if (m_fd != -1)
278 {
279 close(m_fd);
280 m_fd = -1;
281 m_name = "";
282 m_error = "";
283 }
284 }
285
286 bool CSerialPort::SetBaudRate(uint32_t baudrate)
287 {
288 int rate = IntToBaudrate(baudrate);
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 {
300 m_error = strerror(errno);
301 return false;
302 }
303
304 if (cfsetispeed(&m_options, rate) != 0)
305 {
306 m_error = strerror(errno);
307 return false;
308 }
309
310 if (cfsetospeed(&m_options, rate) != 0)
311 {
312 m_error = strerror(errno);
313 return false;
314 }
315
316 return true;
317 }
318
319 bool CSerialPort::IsOpen()
320 {
321 CLockObject lock(&m_mutex);
322 return m_fd != -1;
323 }