cec: clean up lib/platform
[deb_libcec.git] / src / lib / platform / windows / os-socket.h
1 #pragma once
2 /*
3 * This file is part of the libCEC(R) library.
4 *
5 * libCEC(R) is Copyright (C) 2011-2012 Pulse-Eight Limited. All rights reserved.
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
37 #pragma comment(lib, "Ws2_32.lib")
38 #include <ws2spi.h>
39 #include <ws2ipdef.h>
40 #include <ws2tcpip.h>
41
42 #define SHUT_RDWR SD_BOTH
43
44 #ifndef ETIMEDOUT
45 #define ETIMEDOUT 138
46 #endif
47
48 namespace PLATFORM
49 {
50 #ifndef MSG_WAITALL
51 #define MSG_WAITALL 0x8
52 #endif
53
54 inline int GetSocketError(void)
55 {
56 int error = WSAGetLastError();
57 switch(error)
58 {
59 case WSAEINPROGRESS: return EINPROGRESS;
60 case WSAECONNRESET : return ECONNRESET;
61 case WSAETIMEDOUT : return ETIMEDOUT;
62 case WSAEWOULDBLOCK: return EAGAIN;
63 default : return error;
64 }
65 }
66
67 // Serial port
68 //@{
69 inline void SerialSocketClose(serial_socket_t socket)
70 {
71 if (socket != INVALID_HANDLE_VALUE)
72 CloseHandle(socket);
73 }
74
75 inline ssize_t SerialSocketWrite(serial_socket_t socket, int *iError, void* data, size_t len)
76 {
77 DWORD iBytesWritten(0);
78 if (socket != INVALID_HANDLE_VALUE)
79 {
80 if (!WriteFile(socket, data, len, &iBytesWritten, NULL))
81 {
82 *iError = GetLastError();
83 return -1;
84 }
85 return (ssize_t)iBytesWritten;
86 }
87
88 return -1;
89 }
90
91 inline ssize_t SerialSocketRead(serial_socket_t socket, int *iError, void* data, size_t len, uint64_t iTimeoutMs /*= 0*/)
92 {
93 DWORD iBytesRead(0);
94 if (socket != INVALID_HANDLE_VALUE)
95 {
96 if(!ReadFile(socket, data, len, &iBytesRead, NULL) != 0)
97 {
98 *iError = GetLastError();
99 return -1;
100 }
101 return (ssize_t)iBytesRead;
102 }
103 return -1;
104 }
105 //@}
106
107 // TCP
108 //@{
109 inline void TcpSocketSetBlocking(tcp_socket_t socket, bool bSetTo)
110 {
111 u_long iSetTo = bSetTo ? 0 : 1;
112 ioctlsocket(socket, FIONBIO, &iSetTo);
113 }
114
115 inline void TcpSocketClose(tcp_socket_t socket)
116 {
117 closesocket(socket);
118 }
119
120 inline void TcpSocketShutdown(tcp_socket_t socket)
121 {
122 if (socket != INVALID_SOCKET &&
123 socket != SOCKET_ERROR)
124 shutdown(socket, SHUT_RDWR);
125 }
126
127 inline ssize_t TcpSocketWrite(tcp_socket_t socket, int *iError, void* data, size_t len)
128 {
129 if (socket == INVALID_SOCKET ||
130 socket == SOCKET_ERROR)
131 {
132 *iError = EINVAL;
133 return -1;
134 }
135
136 ssize_t iReturn = send(socket, (char*)data, len, 0);
137 if (iReturn < (ssize_t)len)
138 *iError = errno;
139 return iReturn;
140 }
141
142 inline ssize_t TcpSocketRead(tcp_socket_t socket, int *iError, void* data, size_t len, uint64_t iTimeoutMs /*= 0*/)
143 {
144 int64_t iNow(0), iTarget(0);
145 ssize_t iBytesRead(0);
146 *iError = 0;
147
148 if (socket == INVALID_SOCKET ||
149 socket == SOCKET_ERROR)
150 {
151 *iError = EINVAL;
152 return -1;
153 }
154
155 if (iTimeoutMs > 0)
156 {
157 iNow = GetTimeMs();
158 iTarget = iNow + (int64_t) iTimeoutMs;
159 }
160
161 fd_set fd_read;
162 struct timeval tv;
163 while (iBytesRead >= 0 && iBytesRead < (ssize_t)len && (iTimeoutMs == 0 || iTarget > iNow))
164 {
165 if (iTimeoutMs > 0)
166 {
167 tv.tv_sec = (long)(iTimeoutMs / 1000);
168 tv.tv_usec = 1000 * (long)(iTimeoutMs % 1000);
169
170 FD_ZERO(&fd_read);
171 FD_SET(socket, &fd_read);
172
173 if (select(socket + 1, &fd_read, NULL, NULL, &tv) == 0)
174 return ETIMEDOUT;
175 TcpSocketSetBlocking(socket, false);
176 }
177
178 ssize_t iReadResult = (iTimeoutMs > 0) ?
179 recv(socket, (char*)data + iBytesRead, len - iBytesRead, MSG_WAITALL) :
180 recv(socket, (char*)data, len, MSG_WAITALL);
181 *iError = GetSocketError();
182 if (iReadResult < 0)
183 {
184 if (errno == EAGAIN && iTimeoutMs > 0)
185 continue;
186 *iError = errno;
187 return -1;
188 }
189 else if (iReadResult == 0 || (iReadResult != (ssize_t)len && iTimeoutMs == 0))
190 {
191 *iError = ECONNRESET;
192 return -1;
193 }
194
195 iBytesRead += iReadResult;
196
197 if (iTimeoutMs > 0)
198 {
199 TcpSocketSetBlocking(socket, true);
200 iNow = GetTimeMs();
201 }
202 }
203 return 0;
204 }
205
206 inline bool TcpResolveAddress(const char *strHost, uint16_t iPort, int *iError, struct addrinfo **info)
207 {
208 struct addrinfo hints;
209 char service[33];
210 memset(&hints, 0, sizeof(hints));
211 hints.ai_family = AF_UNSPEC;
212 hints.ai_socktype = SOCK_STREAM;
213 hints.ai_protocol = IPPROTO_TCP;
214 sprintf(service, "%d", iPort);
215
216 *iError = getaddrinfo(strHost, service, &hints, info);
217 return !(*iError);
218 }
219
220 inline int TcpGetSocketError(tcp_socket_t socket)
221 {
222 int iReturn(0);
223 socklen_t optLen = sizeof(tcp_socket_t);
224 getsockopt(socket, SOL_SOCKET, SO_ERROR, (char *)&iReturn, &optLen);
225 return iReturn;
226 }
227
228 inline bool TcpSetNoDelay(tcp_socket_t socket)
229 {
230 int iSetTo(1);
231 setsockopt(socket, IPPROTO_TCP, TCP_NODELAY, (const char*)&iSetTo, sizeof(iSetTo));
232 return true;
233 }
234
235 inline bool TcpConnectSocket(tcp_socket_t socket, struct addrinfo* addr, int *iError, uint64_t iTimeout = 0)
236 {
237 TcpSocketSetBlocking(socket, false);
238
239 *iError = 0;
240 int iConnectResult = connect(socket, addr->ai_addr, addr->ai_addrlen);
241 if (iConnectResult == -1)
242 {
243 if (GetSocketError() == EINPROGRESS ||
244 GetSocketError() == EAGAIN)
245 {
246 fd_set fd_write, fd_except;
247 struct timeval tv;
248 tv.tv_sec = (long)(iTimeout / 1000);
249 tv.tv_usec = 1000 * (long)(iTimeout % 1000);
250
251 FD_ZERO(&fd_write);
252 FD_ZERO(&fd_except);
253 FD_SET(socket, &fd_write);
254 FD_SET(socket, &fd_except);
255
256 int iPollResult = select(sizeof(socket)*8, NULL, &fd_write, &fd_except, &tv);
257 if (iPollResult == 0)
258 *iError = ETIMEDOUT;
259 else if (iPollResult == -1)
260 *iError = GetSocketError();
261 else
262 {
263 socklen_t errlen = sizeof(int);
264 getsockopt(socket, SOL_SOCKET, SO_ERROR, (char *)iError, &errlen);
265 }
266 }
267 else
268 {
269 *iError = errno;
270 }
271 }
272
273 TcpSocketSetBlocking(socket, true);
274
275 return *iError == 0;
276 }
277 }