Merge branch 'master' into release
[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 24
6df2c52f 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
abbca718 36using namespace std;
b9187cc6 37using namespace CEC;
abbca718
LOK
38
39CSerialPort::CSerialPort()
40{
b9187cc6 41 m_fd = -1;
abbca718
LOK
42}
43
44CSerialPort::~CSerialPort()
45{
46 Close();
47}
48
9dee1670 49int8_t CSerialPort::Write(const cec_adapter_message &data)
abbca718
LOK
50{
51 fd_set port;
b1f50952
LOK
52
53 CLockObject lock(&m_mutex);
abbca718
LOK
54 if (m_fd == -1)
55 {
56 m_error = "port closed";
57 return -1;
58 }
59
7eb13cca 60 int32_t byteswritten = 0;
abbca718 61
a6b6469c 62 while (byteswritten < (int32_t) data.size())
abbca718
LOK
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 {
b9187cc6 69 m_error = strerror(errno);
abbca718
LOK
70 return -1;
71 }
72
a6b6469c 73 returnv = write(m_fd, data.packet.data + byteswritten, data.size() - byteswritten);
abbca718
LOK
74 if (returnv == -1)
75 {
b9187cc6 76 m_error = strerror(errno);
abbca718
LOK
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++)
9dee1670 87// printf(" %02x", (unsigned int)data[i]);
25701fa6 88//
abbca718
LOK
89// printf("\n");
90// }
91
92 return byteswritten;
93}
94
7eb13cca 95int32_t CSerialPort::Read(uint8_t* data, uint32_t len, uint64_t iTimeoutMs /*= 0*/)
abbca718
LOK
96{
97 fd_set port;
98 struct timeval timeout, *tv;
7eb13cca
LOK
99 int64_t now(0), target(0);
100 int32_t bytesread = 0;
abbca718 101
b1f50952 102 CLockObject lock(&m_mutex);
abbca718
LOK
103 if (m_fd == -1)
104 {
105 m_error = "port closed";
106 return -1;
107 }
108
7eb13cca 109 if (iTimeoutMs > 0)
abbca718
LOK
110 {
111 now = GetTimeMs();
112 target = now + (int64_t) iTimeoutMs;
113 }
114
7eb13cca 115 while (bytesread < (int32_t) len && (iTimeoutMs == 0 || target > now))
abbca718 116 {
7eb13cca 117 if (iTimeoutMs == 0)
abbca718
LOK
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);
7eb13cca 130 int32_t returnv = select(m_fd + 1, &port, NULL, NULL, tv);
abbca718
LOK
131
132 if (returnv == -1)
133 {
b9187cc6 134 m_error = strerror(errno);
abbca718
LOK
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 {
b9187cc6 145 m_error = strerror(errno);
abbca718
LOK
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
7eb13cca 169bool CSerialPort::Open(string name, uint32_t baudrate, uint8_t databits /* = 8 */, uint8_t stopbits /* = 1 */, uint8_t parity /* = PAR_NONE */)
abbca718
LOK
170{
171 m_name = name;
b9187cc6 172 m_error = strerror(errno);
b1f50952
LOK
173 CLockObject lock(&m_mutex);
174
abbca718
LOK
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 {
b9187cc6 197 m_error = strerror(errno);
abbca718
LOK
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 {
b9187cc6 249 m_error = strerror(errno);
abbca718
LOK
250 return false;
251 }
252
253 //non-blocking port
254 fcntl(m_fd, F_SETFL, FNDELAY);
255
256 return true;
257}
258
259void CSerialPort::Close()
260{
b1f50952 261 CLockObject lock(&m_mutex);
abbca718
LOK
262 if (m_fd != -1)
263 {
264 close(m_fd);
265 m_fd = -1;
266 m_name = "";
267 m_error = "";
268 }
269}
270
7eb13cca 271bool CSerialPort::SetBaudRate(uint32_t baudrate)
abbca718 272{
b9187cc6 273 int rate = IntToBaudrate(baudrate);
abbca718
LOK
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 {
b9187cc6 285 m_error = strerror(errno);
abbca718
LOK
286 return false;
287 }
288
289 if (cfsetispeed(&m_options, rate) != 0)
290 {
b9187cc6 291 m_error = strerror(errno);
abbca718
LOK
292 return false;
293 }
294
295 if (cfsetospeed(&m_options, rate) != 0)
296 {
b9187cc6 297 m_error = strerror(errno);
abbca718
LOK
298 return false;
299 }
300
301 return true;
302}
b9187cc6 303
b1f50952 304bool CSerialPort::IsOpen()
b9187cc6 305{
b1f50952 306 CLockObject lock(&m_mutex);
b9187cc6
LOK
307 return m_fd != -1;
308}