1 /*
2  * This file is part of the libCEC(R) library.
3  *
4  * libCEC(R) is Copyright (C) 2011-2015 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., 51 Franklin Street, Fifth Floor, Boston, MA
22  * 02110-1301  USA
23  *
24  *
25  * Alternatively, you can license this library under a commercial license,
26  * please contact Pulse-Eight Licensing for more information.
27  *
28  * For more information contact:
29  * Pulse-Eight Licensing       <license@pulse-eight.com>
30  *     http://www.pulse-eight.com/
31  *     http://www.pulse-eight.net/
32  */
33 
34 #include "env.h"
35 #include <stdio.h>
36 #include <fcntl.h>
37 #include "../sockets/serialport.h"
38 #include "../util/baudrate.h"
39 #include "p8-platform/posix/os-socket.h"
40 
41 #if defined(__APPLE__) || defined(__FreeBSD__) || defined(__DragonFly__)
42 #ifndef XCASE
43 #define XCASE	0
44 #endif
45 #ifndef OLCUC
46 #define OLCUC	0
47 #endif
48 #ifndef IUCLC
49 #define IUCLC	0
50 #endif
51 #else
52 #ifdef HAVE_SYS_FILE_HEADER
53 #include <sys/file.h>
54 #endif
55 #endif
56 
57 /** XCASE was deprecated on removed from posix */
58 #ifndef XCASE
59 #define XCASE  0
60 #endif
61 
62 using namespace P8PLATFORM;
63 
RemoveLock(int sock)64 inline bool RemoveLock(int sock)
65 {
66   #if HAVE_FLOCK
67   return flock(sock, LOCK_UN) == 0;
68   #else
69   (void)strDeviceName; // silence unused warning
70   return true;
71   #endif
72 }
73 
Close(void)74 void CSerialSocket::Close(void)
75 {
76   if (IsOpen())
77   {
78     RemoveLock(m_socket);
79     SocketClose(m_socket);
80   }
81 }
82 
Shutdown(void)83 void CSerialSocket::Shutdown(void)
84 {
85   if (IsOpen())
86   {
87     RemoveLock(m_socket);
88     SocketClose(m_socket);
89   }
90 }
91 
Write(void * data,size_t len)92 ssize_t CSerialSocket::Write(void* data, size_t len)
93 {
94   return IsOpen() ? SocketWrite(m_socket, &m_iError, data, len) : -1;
95 }
96 
Read(void * data,size_t len,uint64_t iTimeoutMs)97 ssize_t CSerialSocket::Read(void* data, size_t len, uint64_t iTimeoutMs /* = 0 */)
98 {
99   return IsOpen() ? SocketRead(m_socket, &m_iError, data, len, iTimeoutMs) : -1;
100 }
101 
102 //setting all this stuff up is a pain in the ass
Open(uint64_t iTimeoutMs)103 bool CSerialSocket::Open(uint64_t iTimeoutMs /* = 0 */)
104 {
105   iTimeoutMs = 0; if (!iTimeoutMs){} // silence unused warning
106   if (IsOpen())
107   {
108     m_iError = EINVAL;
109     return false;
110   }
111 
112   if (m_iDatabits != SERIAL_DATA_BITS_FIVE && m_iDatabits != SERIAL_DATA_BITS_SIX &&
113       m_iDatabits != SERIAL_DATA_BITS_SEVEN && m_iDatabits != SERIAL_DATA_BITS_EIGHT)
114   {
115     m_strError = "Databits has to be between 5 and 8";
116     m_iError = EINVAL;
117     return false;
118   }
119 
120   if (m_iStopbits != SERIAL_STOP_BITS_ONE && m_iStopbits != SERIAL_STOP_BITS_TWO)
121   {
122     m_strError = "Stopbits has to be 1 or 2";
123     m_iError = EINVAL;
124     return false;
125   }
126 
127   if (m_iParity != SERIAL_PARITY_NONE && m_iParity != SERIAL_PARITY_EVEN && m_iParity != SERIAL_PARITY_ODD)
128   {
129     m_strError = "Parity has to be none, even or odd";
130     m_iError = EINVAL;
131     return false;
132   }
133 
134   m_socket = open(m_strName.c_str(), O_RDWR | O_NOCTTY | O_NDELAY | O_CLOEXEC);
135 
136   if (m_socket == INVALID_SERIAL_SOCKET_VALUE)
137   {
138     m_strError = strerror(errno);
139     return false;
140   }
141 
142 #if HAVE_FLOCK
143   if (flock(m_socket, LOCK_EX | LOCK_NB) != 0)
144   {
145     m_strError = "Couldn't lock the serial port";
146     m_iError = EBUSY;
147     SocketClose(m_socket);
148     return false;
149   }
150 #endif
151 
152   SocketSetBlocking(m_socket, false);
153 
154   if (!SetBaudRate(m_iBaudrate))
155     return false;
156 
157   m_options.c_cflag |= (CLOCAL | CREAD);
158   m_options.c_cflag &= ~HUPCL;
159 
160   m_options.c_cflag &= ~CSIZE;
161   if (m_iDatabits == SERIAL_DATA_BITS_FIVE)  m_options.c_cflag |= CS5;
162   if (m_iDatabits == SERIAL_DATA_BITS_SIX)   m_options.c_cflag |= CS6;
163   if (m_iDatabits == SERIAL_DATA_BITS_SEVEN) m_options.c_cflag |= CS7;
164   if (m_iDatabits == SERIAL_DATA_BITS_EIGHT) m_options.c_cflag |= CS8;
165 
166   m_options.c_cflag &= ~PARENB;
167   if (m_iParity == SERIAL_PARITY_EVEN || m_iParity == SERIAL_PARITY_ODD)
168     m_options.c_cflag |= PARENB;
169   if (m_iParity == SERIAL_PARITY_ODD)
170     m_options.c_cflag |= PARODD;
171 
172 #ifdef CRTSCTS
173   m_options.c_cflag &= ~CRTSCTS;
174 #elif defined(CNEW_RTSCTS)
175   m_options.c_cflag &= ~CNEW_RTSCTS;
176 #endif
177 
178   if (m_iStopbits == SERIAL_STOP_BITS_ONE) m_options.c_cflag &= ~CSTOPB;
179   else m_options.c_cflag |= CSTOPB;
180 
181   //I guessed a little here
182   m_options.c_lflag &= ~(ICANON | ECHO | ECHOE | ISIG | XCASE | ECHOK | ECHONL | ECHOCTL | ECHOPRT | ECHOKE | TOSTOP);
183 
184   if (m_iParity == SERIAL_PARITY_NONE)
185     m_options.c_iflag &= ~INPCK;
186   else
187     m_options.c_iflag |= INPCK | ISTRIP;
188 
189   m_options.c_iflag &= ~(IXON | IXOFF | IXANY | BRKINT | INLCR | IGNCR | ICRNL | IUCLC | IMAXBEL);
190   m_options.c_oflag &= ~(OPOST | ONLCR | OCRNL);
191 
192   if (tcsetattr(m_socket, TCSANOW, &m_options) != 0)
193   {
194     m_strError = strerror(errno);
195     RemoveLock(m_socket);
196     SocketClose(m_socket);
197     return false;
198   }
199 
200   SocketSetBlocking(m_socket, true);
201   m_bIsOpen = true;
202 
203   return true;
204 }
205 
SetBaudRate(uint32_t baudrate)206 bool CSerialSocket::SetBaudRate(uint32_t baudrate)
207 {
208   int rate = IntToBaudrate(baudrate);
209   if (rate == -1)
210   {
211     char buff[255];
212     sprintf(buff, "%i is not a valid baudrate", baudrate);
213     m_strError = buff;
214     return false;
215   }
216 
217   //get the current port attributes
218   if (tcgetattr(m_socket, &m_options) != 0)
219   {
220     m_strError = strerror(errno);
221     return false;
222   }
223 
224   if (cfsetispeed(&m_options, rate) != 0)
225   {
226     m_strError = strerror(errno);
227     return false;
228   }
229 
230   if (cfsetospeed(&m_options, rate) != 0)
231   {
232     m_strError = strerror(errno);
233     return false;
234   }
235 
236   return true;
237 }
238