win32: implemented timeouts in serial socket reads
[deb_libcec.git] / src / lib / platform / windows / os-socket.h
CommitLineData
24048d57
LOK
1#pragma once
2/*
3 * This file is part of the libCEC(R) library.
4 *
b492c10e 5 * libCEC(R) is Copyright (C) 2011-2012 Pulse-Eight Limited. All rights reserved.
24048d57
LOK
6 * libCEC(R) is an original work, containing original code.
7 *
8 * libCEC(R) is a trademark of Pulse-Eight Limited.
9 *
10 * This program is dual-licensed; you can redistribute it and/or modify
11 * it under the terms of the GNU General Public License as published by
12 * the Free Software Foundation; either version 2 of the License, or
13 * (at your option) any later version.
14 *
15 * This program is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 * GNU General Public License for more details.
19 *
20 * You should have received a copy of the GNU General Public License
21 * along with this program; if not, write to the Free Software
22 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, 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 "../os.h"
35#include "../util/timeutils.h"
36
1d9111bd
LOK
37#include <ws2spi.h>
38#include <ws2ipdef.h>
39#include <ws2tcpip.h>
40
41#define SHUT_RDWR SD_BOTH
42
43#ifndef ETIMEDOUT
44#define ETIMEDOUT 138
45#endif
46
24048d57
LOK
47namespace PLATFORM
48{
1d9111bd
LOK
49 #ifndef MSG_WAITALL
50 #define MSG_WAITALL 0x8
51 #endif
52
53 inline int GetSocketError(void)
54 {
55 int error = WSAGetLastError();
56 switch(error)
57 {
58 case WSAEINPROGRESS: return EINPROGRESS;
59 case WSAECONNRESET : return ECONNRESET;
60 case WSAETIMEDOUT : return ETIMEDOUT;
61 case WSAEWOULDBLOCK: return EAGAIN;
62 default : return error;
63 }
64 }
65
99666519
LOK
66 // Serial port
67 //@{
68 inline void SerialSocketClose(serial_socket_t socket)
24048d57 69 {
99666519
LOK
70 if (socket != INVALID_HANDLE_VALUE)
71 CloseHandle(socket);
24048d57
LOK
72 }
73
99666519 74 inline ssize_t SerialSocketWrite(serial_socket_t socket, int *iError, void* data, size_t len)
24048d57 75 {
642daae3
LOK
76 if (len != (DWORD)len)
77 {
78 *iError = EINVAL;
79 return -1;
80 }
81
99666519
LOK
82 DWORD iBytesWritten(0);
83 if (socket != INVALID_HANDLE_VALUE)
84 {
642daae3 85 if (!WriteFile(socket, data, (DWORD)len, &iBytesWritten, NULL))
99666519
LOK
86 {
87 *iError = GetLastError();
88 return -1;
89 }
90 return (ssize_t)iBytesWritten;
91 }
92
93 return -1;
24048d57
LOK
94 }
95
99666519 96 inline ssize_t SerialSocketRead(serial_socket_t socket, int *iError, void* data, size_t len, uint64_t iTimeoutMs /*= 0*/)
24048d57 97 {
5347b94b
LOK
98 *iError = 0;
99
642daae3
LOK
100 if (len != (DWORD)len)
101 {
102 *iError = EINVAL;
103 return -1;
104 }
105
99666519
LOK
106 DWORD iBytesRead(0);
107 if (socket != INVALID_HANDLE_VALUE)
1d9111bd 108 {
642daae3 109 if(!ReadFile(socket, data, (DWORD)len, &iBytesRead, NULL) != 0)
99666519
LOK
110 {
111 *iError = GetLastError();
112 return -1;
113 }
114 return (ssize_t)iBytesRead;
1d9111bd 115 }
99666519 116 return -1;
24048d57 117 }
99666519 118 //@}
24048d57 119
99666519
LOK
120 // TCP
121 //@{
122 inline void TcpSocketSetBlocking(tcp_socket_t socket, bool bSetTo)
24048d57 123 {
99666519
LOK
124 u_long iSetTo = bSetTo ? 0 : 1;
125 ioctlsocket(socket, FIONBIO, &iSetTo);
126 }
1d9111bd 127
99666519
LOK
128 inline void TcpSocketClose(tcp_socket_t socket)
129 {
130 closesocket(socket);
131 }
1d9111bd 132
99666519
LOK
133 inline void TcpSocketShutdown(tcp_socket_t socket)
134 {
135 if (socket != INVALID_SOCKET &&
136 socket != SOCKET_ERROR)
137 shutdown(socket, SHUT_RDWR);
138 }
139
140 inline ssize_t TcpSocketWrite(tcp_socket_t socket, int *iError, void* data, size_t len)
141 {
142 if (socket == INVALID_SOCKET ||
642daae3
LOK
143 socket == SOCKET_ERROR ||
144 len != (int)len)
1d9111bd 145 {
99666519
LOK
146 *iError = EINVAL;
147 return -1;
1d9111bd 148 }
99666519 149
642daae3 150 ssize_t iReturn = send(socket, (char*)data, (int)len, 0);
99666519 151 if (iReturn < (ssize_t)len)
3459c4f9 152 *iError = GetSocketError();
99666519 153 return iReturn;
1d9111bd
LOK
154 }
155
99666519 156 inline ssize_t TcpSocketRead(tcp_socket_t socket, int *iError, void* data, size_t len, uint64_t iTimeoutMs /*= 0*/)
1d9111bd 157 {
99666519
LOK
158 int64_t iNow(0), iTarget(0);
159 ssize_t iBytesRead(0);
160 *iError = 0;
1d9111bd 161
99666519 162 if (socket == INVALID_SOCKET ||
642daae3
LOK
163 socket == SOCKET_ERROR ||
164 len != (int)len)
99666519
LOK
165 {
166 *iError = EINVAL;
167 return -1;
168 }
1d9111bd 169
99666519
LOK
170 if (iTimeoutMs > 0)
171 {
172 iNow = GetTimeMs();
173 iTarget = iNow + (int64_t) iTimeoutMs;
174 }
1d9111bd 175
1d9111bd
LOK
176 fd_set fd_read;
177 struct timeval tv;
99666519
LOK
178 while (iBytesRead >= 0 && iBytesRead < (ssize_t)len && (iTimeoutMs == 0 || iTarget > iNow))
179 {
180 if (iTimeoutMs > 0)
181 {
182 tv.tv_sec = (long)(iTimeoutMs / 1000);
183 tv.tv_usec = 1000 * (long)(iTimeoutMs % 1000);
1d9111bd 184
99666519
LOK
185 FD_ZERO(&fd_read);
186 FD_SET(socket, &fd_read);
1d9111bd 187
642daae3 188 if (select((int)socket + 1, &fd_read, NULL, NULL, &tv) == 0)
3459c4f9
LOK
189 {
190 *iError = ETIMEDOUT;
99666519 191 return ETIMEDOUT;
3459c4f9 192 }
99666519
LOK
193 TcpSocketSetBlocking(socket, false);
194 }
71194cf4 195
99666519 196 ssize_t iReadResult = (iTimeoutMs > 0) ?
3459c4f9 197 recv(socket, (char*)data + iBytesRead, (int)(len - iBytesRead), 0) :
642daae3 198 recv(socket, (char*)data, (int)len, MSG_WAITALL);
99666519 199 *iError = GetSocketError();
3459c4f9
LOK
200
201 if (iTimeoutMs > 0)
202 {
203 TcpSocketSetBlocking(socket, true);
204 iNow = GetTimeMs();
205 }
206
99666519
LOK
207 if (iReadResult < 0)
208 {
3459c4f9 209 if (*iError == EAGAIN && iTimeoutMs > 0)
99666519 210 continue;
99666519
LOK
211 return -1;
212 }
213 else if (iReadResult == 0 || (iReadResult != (ssize_t)len && iTimeoutMs == 0))
214 {
215 *iError = ECONNRESET;
216 return -1;
217 }
1d9111bd 218
99666519 219 iBytesRead += iReadResult;
99666519 220 }
3459c4f9
LOK
221
222 if (iBytesRead < (ssize_t)len && *iError == 0)
223 *iError = ETIMEDOUT;
224
225 return iBytesRead;
99666519 226 }
1d9111bd 227
99666519
LOK
228 inline bool TcpResolveAddress(const char *strHost, uint16_t iPort, int *iError, struct addrinfo **info)
229 {
230 struct addrinfo hints;
231 char service[33];
232 memset(&hints, 0, sizeof(hints));
233 hints.ai_family = AF_UNSPEC;
234 hints.ai_socktype = SOCK_STREAM;
235 hints.ai_protocol = IPPROTO_TCP;
236 sprintf(service, "%d", iPort);
1d9111bd 237
99666519
LOK
238 *iError = getaddrinfo(strHost, service, &hints, info);
239 return !(*iError);
240 }
1d9111bd 241
99666519
LOK
242 inline int TcpGetSocketError(tcp_socket_t socket)
243 {
244 int iReturn(0);
245 socklen_t optLen = sizeof(tcp_socket_t);
246 getsockopt(socket, SOL_SOCKET, SO_ERROR, (char *)&iReturn, &optLen);
247 return iReturn;
248 }
1d9111bd 249
99666519
LOK
250 inline bool TcpSetNoDelay(tcp_socket_t socket)
251 {
252 int iSetTo(1);
253 setsockopt(socket, IPPROTO_TCP, TCP_NODELAY, (const char*)&iSetTo, sizeof(iSetTo));
254 return true;
255 }
256
257 inline bool TcpConnectSocket(tcp_socket_t socket, struct addrinfo* addr, int *iError, uint64_t iTimeout = 0)
258 {
259 TcpSocketSetBlocking(socket, false);
260
261 *iError = 0;
642daae3 262 int iConnectResult = connect(socket, addr->ai_addr, (int)addr->ai_addrlen);
99666519
LOK
263 if (iConnectResult == -1)
264 {
265 if (GetSocketError() == EINPROGRESS ||
266 GetSocketError() == EAGAIN)
1d9111bd 267 {
99666519
LOK
268 fd_set fd_write, fd_except;
269 struct timeval tv;
270 tv.tv_sec = (long)(iTimeout / 1000);
271 tv.tv_usec = 1000 * (long)(iTimeout % 1000);
1d9111bd 272
99666519
LOK
273 FD_ZERO(&fd_write);
274 FD_ZERO(&fd_except);
275 FD_SET(socket, &fd_write);
276 FD_SET(socket, &fd_except);
277
278 int iPollResult = select(sizeof(socket)*8, NULL, &fd_write, &fd_except, &tv);
279 if (iPollResult == 0)
280 *iError = ETIMEDOUT;
281 else if (iPollResult == -1)
282 *iError = GetSocketError();
283 else
284 {
285 socklen_t errlen = sizeof(int);
286 getsockopt(socket, SOL_SOCKET, SO_ERROR, (char *)iError, &errlen);
287 }
288 }
289 else
290 {
3459c4f9 291 *iError = GetSocketError();
99666519 292 }
1d9111bd 293 }
71194cf4 294
99666519
LOK
295 TcpSocketSetBlocking(socket, true);
296
297 return *iError == 0;
24048d57
LOK
298 }
299}