cec: fix segfault on exit
[deb_libcec.git] / src / lib / platform / linux / serialport.cpp
CommitLineData
abbca718
LOK
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
abbca718 19#include <stdio.h>
abbca718 20#include <fcntl.h>
abbca718
LOK
21#include "../serialport.h"
22#include "../baudrate.h"
b9187cc6 23#include "../timeutils.h"
abbca718
LOK
24
25using namespace std;
b9187cc6 26using namespace CEC;
abbca718
LOK
27
28CSerialPort::CSerialPort()
29{
b9187cc6 30 m_fd = -1;
abbca718
LOK
31}
32
33CSerialPort::~CSerialPort()
34{
35 Close();
36}
37
38int CSerialPort::Write(uint8_t* data, int len)
39{
40 fd_set port;
41
42 if (m_fd == -1)
43 {
44 m_error = "port closed";
45 return -1;
46 }
47
48 int byteswritten = 0;
49
50 while (byteswritten < len)
51 {
52 FD_ZERO(&port);
53 FD_SET(m_fd, &port);
54 int returnv = select(m_fd + 1, NULL, &port, NULL, NULL);
55 if (returnv == -1)
56 {
b9187cc6 57 m_error = strerror(errno);
abbca718
LOK
58 return -1;
59 }
60
61 returnv = write(m_fd, data + byteswritten, len - byteswritten);
62 if (returnv == -1)
63 {
b9187cc6 64 m_error = strerror(errno);
abbca718
LOK
65 return -1;
66 }
67 byteswritten += returnv;
68 }
69
70 //print what's written to stdout for debugging
71// if (m_tostdout)
72// {
73// printf("%s write:", m_name.c_str());
74// for (int i = 0; i < byteswritten; i++)
75// printf(" %02x", (unsigned int)data[i]);
76
77// printf("\n");
78// }
79
80 return byteswritten;
81}
82
83int CSerialPort::Read(uint8_t* data, int len, int iTimeoutMs /*= -1*/)
84{
85 fd_set port;
86 struct timeval timeout, *tv;
87 int64_t now, target;
88 int bytesread = 0;
89
90 if (m_fd == -1)
91 {
92 m_error = "port closed";
93 return -1;
94 }
95
96 if (iTimeoutMs >= 0)
97 {
98 now = GetTimeMs();
99 target = now + (int64_t) iTimeoutMs;
100 }
101
102 while (bytesread < len && (iTimeoutMs < 0 || target > now))
103 {
104 if (iTimeoutMs < 0)
105 {
106 tv = NULL;
107 }
108 else
109 {
110 timeout.tv_sec = ((long int)target - (long int)now) / (long int)1000.;
111 timeout.tv_usec = ((long int)target - (long int)now) % (long int)1000.;
112 tv = &timeout;
113 }
114
115 FD_ZERO(&port);
116 FD_SET(m_fd, &port);
117 int returnv = select(m_fd + 1, &port, NULL, NULL, tv);
118
119 if (returnv == -1)
120 {
b9187cc6 121 m_error = strerror(errno);
abbca718
LOK
122 return -1;
123 }
124 else if (returnv == 0)
125 {
126 break; //nothing to read
127 }
128
129 returnv = read(m_fd, data + bytesread, len - bytesread);
130 if (returnv == -1)
131 {
b9187cc6 132 m_error = strerror(errno);
abbca718
LOK
133 return -1;
134 }
135
136 bytesread += returnv;
137
138 if (iTimeoutMs > 0)
139 now = GetTimeMs();
140 }
141
142 //print what's read to stdout for debugging
143// if (m_tostdout && bytesread > 0)
144// {
145// printf("%s read:", m_name.c_str());
146// for (int i = 0; i < bytesread; i++)
147// printf(" %02x", (unsigned int)data[i]);
148//
149// printf("\n");
150// }
151
152 return bytesread;
153}
154
155//setting all this stuff up is a pain in the ass
156bool CSerialPort::Open(string name, int baudrate, int databits/* = 8*/, int stopbits/* = 1*/, int parity/* = PAR_NONE*/)
157{
158 m_name = name;
b9187cc6 159 m_error = strerror(errno);
abbca718
LOK
160
161 if (databits < 5 || databits > 8)
162 {
163 m_error = "Databits has to be between 5 and 8";
164 return false;
165 }
166
167 if (stopbits != 1 && stopbits != 2)
168 {
169 m_error = "Stopbits has to be 1 or 2";
170 return false;
171 }
172
173 if (parity != PAR_NONE && parity != PAR_EVEN && parity != PAR_ODD)
174 {
175 m_error = "Parity has to be none, even or odd";
176 return false;
177 }
178
179 m_fd = open(name.c_str(), O_RDWR | O_NOCTTY | O_NDELAY);
180
181 if (m_fd == -1)
182 {
b9187cc6 183 m_error = strerror(errno);
abbca718
LOK
184 return false;
185 }
186
187 fcntl(m_fd, F_SETFL, 0);
188
189 if (!SetBaudRate(baudrate))
190 {
191 return false;
192 }
193
194 m_options.c_cflag |= (CLOCAL | CREAD);
195 m_options.c_cflag &= ~HUPCL;
196
197 m_options.c_cflag &= ~CSIZE;
198 if (databits == 5) m_options.c_cflag |= CS5;
199 if (databits == 6) m_options.c_cflag |= CS6;
200 if (databits == 7) m_options.c_cflag |= CS7;
201 if (databits == 8) m_options.c_cflag |= CS8;
202
203 m_options.c_cflag &= ~PARENB;
204 if (parity == PAR_EVEN || parity == PAR_ODD)
205 m_options.c_cflag |= PARENB;
206 if (parity == PAR_ODD)
207 m_options.c_cflag |= PARODD;
208
209#ifdef CRTSCTS
210 m_options.c_cflag &= ~CRTSCTS;
211#elif defined(CNEW_RTSCTS)
212 m_options.c_cflag &= ~CNEW_RTSCTS;
213#endif
214
215 if (stopbits == 1) m_options.c_cflag &= ~CSTOPB;
216 else m_options.c_cflag |= CSTOPB;
217
218 //I guessed a little here
219 m_options.c_lflag &= ~(ICANON | ECHO | ECHOE | ISIG | XCASE | ECHOK | ECHONL | ECHOCTL | ECHOPRT | ECHOKE | TOSTOP);
220
221 if (parity == PAR_NONE)
222 {
223 m_options.c_iflag &= ~INPCK;
224 }
225 else
226 {
227 m_options.c_iflag |= INPCK | ISTRIP;
228 }
229
230 m_options.c_iflag &= ~(IXON | IXOFF | IXANY | BRKINT | INLCR | IGNCR | ICRNL | IUCLC | IMAXBEL);
231 m_options.c_oflag &= ~(OPOST | ONLCR | OCRNL);
232
233 if (tcsetattr(m_fd, TCSANOW, &m_options) != 0)
234 {
b9187cc6 235 m_error = strerror(errno);
abbca718
LOK
236 return false;
237 }
238
239 //non-blocking port
240 fcntl(m_fd, F_SETFL, FNDELAY);
241
242 return true;
243}
244
245void CSerialPort::Close()
246{
247 if (m_fd != -1)
248 {
249 close(m_fd);
250 m_fd = -1;
251 m_name = "";
252 m_error = "";
253 }
254}
255
256bool CSerialPort::SetBaudRate(int baudrate)
257{
b9187cc6 258 int rate = IntToBaudrate(baudrate);
abbca718
LOK
259 if (rate == -1)
260 {
261 char buff[255];
262 sprintf(buff, "%i is not a valid baudrate", baudrate);
263 m_error = buff;
264 return false;
265 }
266
267 //get the current port attributes
268 if (tcgetattr(m_fd, &m_options) != 0)
269 {
b9187cc6 270 m_error = strerror(errno);
abbca718
LOK
271 return false;
272 }
273
274 if (cfsetispeed(&m_options, rate) != 0)
275 {
b9187cc6 276 m_error = strerror(errno);
abbca718
LOK
277 return false;
278 }
279
280 if (cfsetospeed(&m_options, rate) != 0)
281 {
b9187cc6 282 m_error = strerror(errno);
abbca718
LOK
283 return false;
284 }
285
286 return true;
287}
b9187cc6
LOK
288
289bool CSerialPort::IsOpen() const
290{
291 return m_fd != -1;
292}