David 'Digit' Turner | d4d688e | 2011-04-26 18:09:17 +0200 | [diff] [blame] | 1 | /* |
| 2 | * Copyright (C) 2011 The Android Open Source Project |
| 3 | * |
| 4 | * Licensed under the Apache License, Version 2.0 (the "License"); |
| 5 | * you may not use this file except in compliance with the License. |
| 6 | * You may obtain a copy of the License at |
| 7 | * |
| 8 | * http://www.apache.org/licenses/LICENSE-2.0 |
| 9 | * |
| 10 | * Unless required by applicable law or agreed to in writing, software |
| 11 | * distributed under the License is distributed on an "AS IS" BASIS, |
| 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| 13 | * See the License for the specific language governing permissions and |
| 14 | * limitations under the License. |
| 15 | */ |
| 16 | |
| 17 | /* This file implements the 'tcp:' goldfish pipe type which allows |
| 18 | * guest clients to directly connect to a TCP port through /dev/qemu_pipe. |
| 19 | */ |
| 20 | |
David 'Digit' Turner | cc330d4 | 2013-12-14 23:26:42 +0100 | [diff] [blame] | 21 | #include "android/sockets.h" |
David 'Digit' Turner | d4d688e | 2011-04-26 18:09:17 +0200 | [diff] [blame] | 22 | #include "android/utils/assert.h" |
| 23 | #include "android/utils/panic.h" |
| 24 | #include "android/utils/system.h" |
| 25 | #include "android/async-utils.h" |
David 'Digit' Turner | cb88e79 | 2011-08-26 01:35:14 +0200 | [diff] [blame] | 26 | #include "android/opengles.h" |
David 'Digit' Turner | d4d688e | 2011-04-26 18:09:17 +0200 | [diff] [blame] | 27 | #include "android/looper.h" |
David 'Digit' Turner | f5bc01c | 2013-12-17 10:33:07 +0100 | [diff] [blame] | 28 | #include "hw/android/goldfish/pipe.h" |
David 'Digit' Turner | d4d688e | 2011-04-26 18:09:17 +0200 | [diff] [blame] | 29 | |
| 30 | /* Implement the OpenGL fast-pipe */ |
| 31 | |
| 32 | /* Set to 1 or 2 for debug traces */ |
Jesse Hall | 6b32ee5 | 2011-12-16 10:39:05 -0800 | [diff] [blame] | 33 | // #define DEBUG 1 |
David 'Digit' Turner | d4d688e | 2011-04-26 18:09:17 +0200 | [diff] [blame] | 34 | |
| 35 | #if DEBUG >= 1 |
| 36 | # define D(...) printf(__VA_ARGS__), printf("\n") |
| 37 | #else |
| 38 | # define D(...) ((void)0) |
| 39 | #endif |
| 40 | |
| 41 | #if DEBUG >= 2 |
| 42 | # define DD(...) printf(__VA_ARGS__), printf("\n") |
| 43 | # define DDASSERT(cond) _ANDROID_ASSERT(cond, "Assertion failure: ", #cond) |
| 44 | # define DDASSERT_INT_OP(cond,val,op) _ANDROID_ASSERT_INT_OP(cond,val,op) |
| 45 | #else |
| 46 | # define DD(...) ((void)0) |
| 47 | # define DDASSERT(cond) ((void)0) |
| 48 | # define DDASSERT_INT_OP(cond,val,op) ((void)0) |
| 49 | #endif |
| 50 | |
| 51 | #define DDASSERT_INT_LT(cond,val) DDASSERT_INT_OP(cond,val,<) |
| 52 | #define DDASSERT_INT_LTE(cond,val) DDASSERT_INT_OP(cond,val,<=) |
| 53 | #define DDASSERT_INT_GT(cond,val) DDASSERT_INT_OP(cond,val,>) |
| 54 | #define DDASSERT_INT_GTE(cond,val) DDASSERT_INT_OP(cond,val,>=) |
| 55 | #define DDASSERT_INT_EQ(cond,val) DDASSERT_INT_OP(cond,val,==) |
| 56 | #define DDASSERT_INT_NEQ(cond,val) DDASSERT_INT_OP(cond,val,!=) |
| 57 | |
| 58 | enum { |
| 59 | STATE_INIT, |
| 60 | STATE_CONNECTING, |
| 61 | STATE_CONNECTED, |
| 62 | STATE_CLOSING_GUEST, |
| 63 | STATE_CLOSING_SOCKET |
| 64 | }; |
| 65 | |
| 66 | typedef struct { |
| 67 | void* hwpipe; |
| 68 | int state; |
| 69 | int wakeWanted; |
| 70 | LoopIo io[1]; |
| 71 | AsyncConnector connector[1]; |
David 'Digit' Turner | d4d688e | 2011-04-26 18:09:17 +0200 | [diff] [blame] | 72 | } NetPipe; |
| 73 | |
| 74 | static void |
| 75 | netPipe_free( NetPipe* pipe ) |
| 76 | { |
| 77 | int fd; |
| 78 | |
| 79 | /* Close the socket */ |
| 80 | fd = pipe->io->fd; |
| 81 | loopIo_done(pipe->io); |
| 82 | socket_close(fd); |
| 83 | |
| 84 | /* Release the pipe object */ |
| 85 | AFREE(pipe); |
| 86 | } |
| 87 | |
| 88 | |
| 89 | static void |
| 90 | netPipe_resetState( NetPipe* pipe ) |
| 91 | { |
| 92 | if ((pipe->wakeWanted & PIPE_WAKE_WRITE) != 0) { |
| 93 | loopIo_wantWrite(pipe->io); |
| 94 | } else { |
| 95 | loopIo_dontWantWrite(pipe->io); |
| 96 | } |
| 97 | |
| 98 | if (pipe->state == STATE_CONNECTED && (pipe->wakeWanted & PIPE_WAKE_READ) != 0) { |
| 99 | loopIo_wantRead(pipe->io); |
| 100 | } else { |
| 101 | loopIo_dontWantRead(pipe->io); |
| 102 | } |
| 103 | } |
| 104 | |
| 105 | |
| 106 | /* This function is only called when the socket is disconnected. |
| 107 | * See netPipe_closeFromGuest() for the case when the guest requires |
| 108 | * the disconnection. */ |
| 109 | static void |
| 110 | netPipe_closeFromSocket( void* opaque ) |
| 111 | { |
| 112 | NetPipe* pipe = opaque; |
| 113 | |
| 114 | D("%s", __FUNCTION__); |
| 115 | |
| 116 | /* If the guest already ordered the pipe to be closed, delete immediately */ |
| 117 | if (pipe->state == STATE_CLOSING_GUEST) { |
| 118 | netPipe_free(pipe); |
| 119 | return; |
| 120 | } |
| 121 | |
| 122 | /* Force the closure of the QEMUD channel - if a guest is blocked |
| 123 | * waiting for a wake signal, it will receive an error. */ |
| 124 | if (pipe->hwpipe != NULL) { |
| 125 | goldfish_pipe_close(pipe->hwpipe); |
| 126 | pipe->hwpipe = NULL; |
| 127 | } |
| 128 | |
| 129 | pipe->state = STATE_CLOSING_SOCKET; |
| 130 | netPipe_resetState(pipe); |
| 131 | } |
| 132 | |
| 133 | |
| 134 | /* This is the function that gets called each time there is an asynchronous |
| 135 | * event on the network pipe. |
| 136 | */ |
| 137 | static void |
| 138 | netPipe_io_func( void* opaque, int fd, unsigned events ) |
| 139 | { |
| 140 | NetPipe* pipe = opaque; |
| 141 | int wakeFlags = 0; |
| 142 | |
| 143 | /* Run the connector if we are in the CONNECTING state */ |
| 144 | /* TODO: Add some sort of time-out, to deal with the case */ |
| 145 | /* when the server is wedged. */ |
| 146 | if (pipe->state == STATE_CONNECTING) { |
| 147 | AsyncStatus status = asyncConnector_run(pipe->connector); |
| 148 | if (status == ASYNC_NEED_MORE) { |
| 149 | return; |
| 150 | } |
| 151 | else if (status == ASYNC_ERROR) { |
| 152 | /* Could not connect, tell our client by closing the channel. */ |
| 153 | |
| 154 | netPipe_closeFromSocket(pipe); |
| 155 | return; |
| 156 | } |
| 157 | pipe->state = STATE_CONNECTED; |
| 158 | netPipe_resetState(pipe); |
| 159 | return; |
| 160 | } |
| 161 | |
| 162 | /* Otherwise, accept incoming data */ |
| 163 | if ((events & LOOP_IO_READ) != 0) { |
| 164 | if ((pipe->wakeWanted & PIPE_WAKE_READ) != 0) { |
| 165 | wakeFlags |= PIPE_WAKE_READ; |
| 166 | } |
| 167 | } |
| 168 | |
| 169 | if ((events & LOOP_IO_WRITE) != 0) { |
| 170 | if ((pipe->wakeWanted & PIPE_WAKE_WRITE) != 0) { |
| 171 | wakeFlags |= PIPE_WAKE_WRITE; |
| 172 | } |
| 173 | } |
| 174 | |
| 175 | /* Send wake signal to the guest if needed */ |
| 176 | if (wakeFlags != 0) { |
| 177 | goldfish_pipe_wake(pipe->hwpipe, wakeFlags); |
| 178 | pipe->wakeWanted &= ~wakeFlags; |
| 179 | } |
| 180 | |
| 181 | /* Reset state */ |
| 182 | netPipe_resetState(pipe); |
| 183 | } |
| 184 | |
| 185 | |
| 186 | void* |
| 187 | netPipe_initFromAddress( void* hwpipe, const SockAddress* address, Looper* looper ) |
| 188 | { |
| 189 | NetPipe* pipe; |
| 190 | |
| 191 | ANEW0(pipe); |
| 192 | |
| 193 | pipe->hwpipe = hwpipe; |
| 194 | pipe->state = STATE_INIT; |
| 195 | |
| 196 | { |
| 197 | AsyncStatus status; |
| 198 | |
| 199 | int fd = socket_create( sock_address_get_family(address), SOCKET_STREAM ); |
| 200 | if (fd < 0) { |
| 201 | D("%s: Could create socket from address family!", __FUNCTION__); |
| 202 | netPipe_free(pipe); |
| 203 | return NULL; |
| 204 | } |
| 205 | |
| 206 | loopIo_init(pipe->io, looper, fd, netPipe_io_func, pipe); |
Jiang, Yunhong | 33f5c65 | 2012-04-29 03:46:19 +0800 | [diff] [blame] | 207 | status = asyncConnector_init(pipe->connector, address, pipe->io); |
David 'Digit' Turner | d4d688e | 2011-04-26 18:09:17 +0200 | [diff] [blame] | 208 | pipe->state = STATE_CONNECTING; |
| 209 | |
David 'Digit' Turner | d4d688e | 2011-04-26 18:09:17 +0200 | [diff] [blame] | 210 | if (status == ASYNC_ERROR) { |
| 211 | D("%s: Could not connect to socket: %s", |
| 212 | __FUNCTION__, errno_str); |
| 213 | netPipe_free(pipe); |
| 214 | return NULL; |
| 215 | } |
| 216 | if (status == ASYNC_COMPLETE) { |
| 217 | pipe->state = STATE_CONNECTED; |
| 218 | netPipe_resetState(pipe); |
| 219 | } |
| 220 | } |
| 221 | |
| 222 | return pipe; |
| 223 | } |
| 224 | |
| 225 | |
| 226 | /* Called when the guest wants to close the channel. This is different |
| 227 | * from netPipe_closeFromSocket() which is called when the socket is |
| 228 | * disconnected. */ |
| 229 | static void |
| 230 | netPipe_closeFromGuest( void* opaque ) |
| 231 | { |
| 232 | NetPipe* pipe = opaque; |
| 233 | netPipe_free(pipe); |
| 234 | } |
| 235 | |
Jiang, Yunhong | 33f5c65 | 2012-04-29 03:46:19 +0800 | [diff] [blame] | 236 | static int netPipeReadySend(NetPipe *pipe) |
| 237 | { |
| 238 | if (pipe->state == STATE_CONNECTED) |
| 239 | return 0; |
| 240 | else if (pipe->state == STATE_CONNECTING) |
| 241 | return PIPE_ERROR_AGAIN; |
| 242 | else if (pipe->hwpipe == NULL) |
| 243 | return PIPE_ERROR_INVAL; |
| 244 | else |
| 245 | return PIPE_ERROR_IO; |
| 246 | } |
David 'Digit' Turner | d4d688e | 2011-04-26 18:09:17 +0200 | [diff] [blame] | 247 | |
| 248 | static int |
| 249 | netPipe_sendBuffers( void* opaque, const GoldfishPipeBuffer* buffers, int numBuffers ) |
| 250 | { |
| 251 | NetPipe* pipe = opaque; |
| 252 | int count = 0; |
| 253 | int ret = 0; |
| 254 | int buffStart = 0; |
| 255 | const GoldfishPipeBuffer* buff = buffers; |
| 256 | const GoldfishPipeBuffer* buffEnd = buff + numBuffers; |
| 257 | |
Jiang, Yunhong | 33f5c65 | 2012-04-29 03:46:19 +0800 | [diff] [blame] | 258 | ret = netPipeReadySend(pipe); |
| 259 | if (ret != 0) |
| 260 | return ret; |
| 261 | |
David 'Digit' Turner | d4d688e | 2011-04-26 18:09:17 +0200 | [diff] [blame] | 262 | for (; buff < buffEnd; buff++) |
| 263 | count += buff->size; |
| 264 | |
| 265 | buff = buffers; |
| 266 | while (count > 0) { |
| 267 | int avail = buff->size - buffStart; |
David 'Digit' Turner | 82e6277 | 2011-06-21 10:01:01 +0200 | [diff] [blame] | 268 | int len = socket_send(pipe->io->fd, buff->data + buffStart, avail); |
David 'Digit' Turner | d4d688e | 2011-04-26 18:09:17 +0200 | [diff] [blame] | 269 | |
| 270 | /* the write succeeded */ |
| 271 | if (len > 0) { |
| 272 | buffStart += len; |
| 273 | if (buffStart >= buff->size) { |
| 274 | buff++; |
| 275 | buffStart = 0; |
| 276 | } |
| 277 | count -= len; |
| 278 | ret += len; |
| 279 | continue; |
| 280 | } |
| 281 | |
| 282 | /* we reached the end of stream? */ |
| 283 | if (len == 0) { |
| 284 | if (ret == 0) |
| 285 | ret = PIPE_ERROR_IO; |
| 286 | break; |
| 287 | } |
| 288 | |
David 'Digit' Turner | d4d688e | 2011-04-26 18:09:17 +0200 | [diff] [blame] | 289 | /* if we already wrote some stuff, simply return */ |
| 290 | if (ret > 0) { |
| 291 | break; |
| 292 | } |
| 293 | |
| 294 | /* need to return an appropriate error code */ |
| 295 | if (errno == EAGAIN || errno == EWOULDBLOCK) { |
| 296 | ret = PIPE_ERROR_AGAIN; |
| 297 | } else { |
| 298 | ret = PIPE_ERROR_IO; |
| 299 | } |
| 300 | break; |
| 301 | } |
| 302 | |
| 303 | return ret; |
| 304 | } |
| 305 | |
| 306 | static int |
| 307 | netPipe_recvBuffers( void* opaque, GoldfishPipeBuffer* buffers, int numBuffers ) |
| 308 | { |
| 309 | NetPipe* pipe = opaque; |
| 310 | int count = 0; |
| 311 | int ret = 0; |
| 312 | int buffStart = 0; |
| 313 | GoldfishPipeBuffer* buff = buffers; |
| 314 | GoldfishPipeBuffer* buffEnd = buff + numBuffers; |
| 315 | |
| 316 | for (; buff < buffEnd; buff++) |
| 317 | count += buff->size; |
| 318 | |
| 319 | buff = buffers; |
| 320 | while (count > 0) { |
| 321 | int avail = buff->size - buffStart; |
David 'Digit' Turner | 82e6277 | 2011-06-21 10:01:01 +0200 | [diff] [blame] | 322 | int len = socket_recv(pipe->io->fd, buff->data + buffStart, avail); |
David 'Digit' Turner | d4d688e | 2011-04-26 18:09:17 +0200 | [diff] [blame] | 323 | |
| 324 | /* the read succeeded */ |
| 325 | if (len > 0) { |
| 326 | buffStart += len; |
| 327 | if (buffStart >= buff->size) { |
| 328 | buff++; |
| 329 | buffStart = 0; |
| 330 | } |
| 331 | count -= len; |
| 332 | ret += len; |
| 333 | continue; |
| 334 | } |
| 335 | |
| 336 | /* we reached the end of stream? */ |
| 337 | if (len == 0) { |
| 338 | if (ret == 0) |
| 339 | ret = PIPE_ERROR_IO; |
| 340 | break; |
| 341 | } |
| 342 | |
David 'Digit' Turner | d4d688e | 2011-04-26 18:09:17 +0200 | [diff] [blame] | 343 | /* if we already read some stuff, simply return */ |
| 344 | if (ret > 0) { |
| 345 | break; |
| 346 | } |
| 347 | |
| 348 | /* need to return an appropriate error code */ |
| 349 | if (errno == EAGAIN || errno == EWOULDBLOCK) { |
| 350 | ret = PIPE_ERROR_AGAIN; |
| 351 | } else { |
| 352 | ret = PIPE_ERROR_IO; |
| 353 | } |
| 354 | break; |
| 355 | } |
| 356 | return ret; |
| 357 | } |
| 358 | |
| 359 | static unsigned |
| 360 | netPipe_poll( void* opaque ) |
| 361 | { |
| 362 | NetPipe* pipe = opaque; |
| 363 | unsigned mask = loopIo_poll(pipe->io); |
| 364 | unsigned ret = 0; |
| 365 | |
| 366 | if (mask & LOOP_IO_READ) |
Vladimir Chtchetkine | fd16505 | 2011-08-25 08:06:26 -0700 | [diff] [blame] | 367 | ret |= PIPE_POLL_IN; |
David 'Digit' Turner | d4d688e | 2011-04-26 18:09:17 +0200 | [diff] [blame] | 368 | if (mask & LOOP_IO_WRITE) |
Vladimir Chtchetkine | fd16505 | 2011-08-25 08:06:26 -0700 | [diff] [blame] | 369 | ret |= PIPE_POLL_OUT; |
David 'Digit' Turner | d4d688e | 2011-04-26 18:09:17 +0200 | [diff] [blame] | 370 | |
| 371 | return ret; |
| 372 | } |
| 373 | |
| 374 | static void |
| 375 | netPipe_wakeOn( void* opaque, int flags ) |
| 376 | { |
| 377 | NetPipe* pipe = opaque; |
| 378 | |
| 379 | DD("%s: flags=%d", __FUNCTION__, flags); |
| 380 | |
| 381 | pipe->wakeWanted |= flags; |
| 382 | netPipe_resetState(pipe); |
| 383 | } |
| 384 | |
| 385 | |
| 386 | void* |
| 387 | netPipe_initTcp( void* hwpipe, void* _looper, const char* args ) |
| 388 | { |
| 389 | /* Build SockAddress from arguments. Acceptable formats are: |
David 'Digit' Turner | d4d688e | 2011-04-26 18:09:17 +0200 | [diff] [blame] | 390 | * <port> |
David 'Digit' Turner | d4d688e | 2011-04-26 18:09:17 +0200 | [diff] [blame] | 391 | */ |
| 392 | SockAddress address; |
David 'Digit' Turner | afe299d | 2011-07-07 04:46:27 +0200 | [diff] [blame] | 393 | uint16_t port; |
David 'Digit' Turner | d4d688e | 2011-04-26 18:09:17 +0200 | [diff] [blame] | 394 | void* ret; |
| 395 | |
| 396 | if (args == NULL) { |
| 397 | D("%s: Missing address!", __FUNCTION__); |
| 398 | return NULL; |
| 399 | } |
David 'Digit' Turner | afe299d | 2011-07-07 04:46:27 +0200 | [diff] [blame] | 400 | D("%s: Port is '%s'", __FUNCTION__, args); |
David 'Digit' Turner | d4d688e | 2011-04-26 18:09:17 +0200 | [diff] [blame] | 401 | |
| 402 | /* Now, look at the port number */ |
| 403 | { |
| 404 | char* end; |
| 405 | long val = strtol(args, &end, 10); |
| 406 | if (end == NULL || *end != '\0' || val <= 0 || val > 65535) { |
| 407 | D("%s: Invalid port number: '%s'", __FUNCTION__, args); |
| 408 | } |
David 'Digit' Turner | afe299d | 2011-07-07 04:46:27 +0200 | [diff] [blame] | 409 | port = (uint16_t)val; |
David 'Digit' Turner | d4d688e | 2011-04-26 18:09:17 +0200 | [diff] [blame] | 410 | } |
David 'Digit' Turner | afe299d | 2011-07-07 04:46:27 +0200 | [diff] [blame] | 411 | sock_address_init_inet(&address, SOCK_ADDRESS_INET_LOOPBACK, port); |
David 'Digit' Turner | d4d688e | 2011-04-26 18:09:17 +0200 | [diff] [blame] | 412 | |
| 413 | ret = netPipe_initFromAddress(hwpipe, &address, _looper); |
| 414 | |
| 415 | sock_address_done(&address); |
| 416 | return ret; |
| 417 | } |
| 418 | |
David 'Digit' Turner | 82e6277 | 2011-06-21 10:01:01 +0200 | [diff] [blame] | 419 | #ifndef _WIN32 |
David 'Digit' Turner | d4d688e | 2011-04-26 18:09:17 +0200 | [diff] [blame] | 420 | void* |
| 421 | netPipe_initUnix( void* hwpipe, void* _looper, const char* args ) |
| 422 | { |
| 423 | /* Build SockAddress from arguments. Acceptable formats are: |
| 424 | * |
| 425 | * <path> |
| 426 | */ |
| 427 | SockAddress address; |
| 428 | void* ret; |
| 429 | |
| 430 | if (args == NULL || args[0] == '\0') { |
| 431 | D("%s: Missing address!", __FUNCTION__); |
| 432 | return NULL; |
| 433 | } |
| 434 | D("%s: Address is '%s'", __FUNCTION__, args); |
| 435 | |
| 436 | sock_address_init_unix(&address, args); |
| 437 | |
| 438 | ret = netPipe_initFromAddress(hwpipe, &address, _looper); |
| 439 | |
| 440 | sock_address_done(&address); |
| 441 | return ret; |
| 442 | } |
David 'Digit' Turner | 82e6277 | 2011-06-21 10:01:01 +0200 | [diff] [blame] | 443 | #endif |
David 'Digit' Turner | d4d688e | 2011-04-26 18:09:17 +0200 | [diff] [blame] | 444 | |
| 445 | /********************************************************************** |
| 446 | ********************************************************************** |
| 447 | ***** |
| 448 | ***** N E T W O R K P I P E M E S S A G E S |
| 449 | ***** |
| 450 | *****/ |
| 451 | |
| 452 | static const GoldfishPipeFuncs netPipeTcp_funcs = { |
| 453 | netPipe_initTcp, |
| 454 | netPipe_closeFromGuest, |
| 455 | netPipe_sendBuffers, |
| 456 | netPipe_recvBuffers, |
| 457 | netPipe_poll, |
| 458 | netPipe_wakeOn, |
David 'Digit' Turner | 3e92c2d | 2011-10-11 03:02:41 +0200 | [diff] [blame] | 459 | NULL, /* we can't save these */ |
| 460 | NULL, /* we can't load these */ |
David 'Digit' Turner | d4d688e | 2011-04-26 18:09:17 +0200 | [diff] [blame] | 461 | }; |
| 462 | |
David 'Digit' Turner | 82e6277 | 2011-06-21 10:01:01 +0200 | [diff] [blame] | 463 | #ifndef _WIN32 |
David 'Digit' Turner | d4d688e | 2011-04-26 18:09:17 +0200 | [diff] [blame] | 464 | static const GoldfishPipeFuncs netPipeUnix_funcs = { |
| 465 | netPipe_initUnix, |
| 466 | netPipe_closeFromGuest, |
| 467 | netPipe_sendBuffers, |
| 468 | netPipe_recvBuffers, |
| 469 | netPipe_poll, |
| 470 | netPipe_wakeOn, |
David 'Digit' Turner | 3e92c2d | 2011-10-11 03:02:41 +0200 | [diff] [blame] | 471 | NULL, /* we can't save these */ |
| 472 | NULL, /* we can't load these */ |
David 'Digit' Turner | d4d688e | 2011-04-26 18:09:17 +0200 | [diff] [blame] | 473 | }; |
David 'Digit' Turner | 82e6277 | 2011-06-21 10:01:01 +0200 | [diff] [blame] | 474 | #endif |
David 'Digit' Turner | d4d688e | 2011-04-26 18:09:17 +0200 | [diff] [blame] | 475 | |
David 'Digit' Turner | cb88e79 | 2011-08-26 01:35:14 +0200 | [diff] [blame] | 476 | /* This is set to 1 in android_init_opengles() below, and tested |
| 477 | * by openglesPipe_init() to refuse a pipe connection if the function |
| 478 | * was never called. |
| 479 | */ |
| 480 | static int _opengles_init; |
David 'Digit' Turner | d4d688e | 2011-04-26 18:09:17 +0200 | [diff] [blame] | 481 | |
| 482 | static void* |
| 483 | openglesPipe_init( void* hwpipe, void* _looper, const char* args ) |
| 484 | { |
Guy Zadickario | e2790e0 | 2011-07-08 22:25:44 +0300 | [diff] [blame] | 485 | NetPipe *pipe; |
David 'Digit' Turner | d4d688e | 2011-04-26 18:09:17 +0200 | [diff] [blame] | 486 | |
David 'Digit' Turner | cb88e79 | 2011-08-26 01:35:14 +0200 | [diff] [blame] | 487 | if (!_opengles_init) { |
| 488 | /* This should never happen, unless there is a bug in the |
| 489 | * emulator's initialization, or the system image. */ |
| 490 | D("Trying to open the OpenGLES pipe without GPU emulation!"); |
| 491 | return NULL; |
| 492 | } |
| 493 | |
Jesse Hall | 055adab | 2012-07-11 16:48:28 -0700 | [diff] [blame] | 494 | char server_addr[PATH_MAX]; |
| 495 | android_gles_server_path(server_addr, sizeof(server_addr)); |
David Turner | 7b56a4a | 2011-09-12 18:21:58 +0200 | [diff] [blame] | 496 | #ifndef _WIN32 |
| 497 | if (android_gles_fast_pipes) { |
Jesse Hall | 055adab | 2012-07-11 16:48:28 -0700 | [diff] [blame] | 498 | pipe = (NetPipe *)netPipe_initUnix(hwpipe, _looper, server_addr); |
| 499 | D("Creating Unix OpenGLES pipe for GPU emulation: %s", server_addr); |
David Turner | 7b56a4a | 2011-09-12 18:21:58 +0200 | [diff] [blame] | 500 | } else { |
| 501 | #else /* _WIN32 */ |
| 502 | { |
| 503 | #endif |
| 504 | /* Connect through TCP as a fallback */ |
Jesse Hall | 055adab | 2012-07-11 16:48:28 -0700 | [diff] [blame] | 505 | pipe = (NetPipe *)netPipe_initTcp(hwpipe, _looper, server_addr); |
David Turner | 7b56a4a | 2011-09-12 18:21:58 +0200 | [diff] [blame] | 506 | D("Creating TCP OpenGLES pipe for GPU emulation!"); |
| 507 | } |
David 'Digit' Turner | 6a8b698 | 2011-08-26 15:19:25 +0200 | [diff] [blame] | 508 | if (pipe != NULL) { |
| 509 | // Disable TCP nagle algorithm to improve throughput of small packets |
| 510 | socket_set_nodelay(pipe->io->fd); |
David Turner | 6d02d6c | 2011-08-15 23:07:03 +0200 | [diff] [blame] | 511 | |
| 512 | // On Win32, adjust buffer sizes |
| 513 | #ifdef _WIN32 |
David 'Digit' Turner | 6a8b698 | 2011-08-26 15:19:25 +0200 | [diff] [blame] | 514 | { |
| 515 | int sndbuf = 128 * 1024; |
| 516 | int len = sizeof(sndbuf); |
| 517 | if (setsockopt(pipe->io->fd, SOL_SOCKET, SO_SNDBUF, |
| 518 | (char*)&sndbuf, len) == SOCKET_ERROR) { |
| 519 | D("Failed to set SO_SNDBUF to %d error=0x%x\n", |
| 520 | sndbuf, WSAGetLastError()); |
| 521 | } |
David Turner | 6d02d6c | 2011-08-15 23:07:03 +0200 | [diff] [blame] | 522 | } |
David Turner | 6d02d6c | 2011-08-15 23:07:03 +0200 | [diff] [blame] | 523 | #endif /* _WIN32 */ |
David 'Digit' Turner | 6a8b698 | 2011-08-26 15:19:25 +0200 | [diff] [blame] | 524 | } |
Guy Zadickario | e2790e0 | 2011-07-08 22:25:44 +0300 | [diff] [blame] | 525 | |
| 526 | return pipe; |
David 'Digit' Turner | d4d688e | 2011-04-26 18:09:17 +0200 | [diff] [blame] | 527 | } |
| 528 | |
| 529 | static const GoldfishPipeFuncs openglesPipe_funcs = { |
| 530 | openglesPipe_init, |
| 531 | netPipe_closeFromGuest, |
| 532 | netPipe_sendBuffers, |
| 533 | netPipe_recvBuffers, |
| 534 | netPipe_poll, |
| 535 | netPipe_wakeOn, |
David 'Digit' Turner | 3e92c2d | 2011-10-11 03:02:41 +0200 | [diff] [blame] | 536 | NULL, /* we can't save these */ |
| 537 | NULL, /* we can't load these */ |
David 'Digit' Turner | d4d688e | 2011-04-26 18:09:17 +0200 | [diff] [blame] | 538 | }; |
| 539 | |
David 'Digit' Turner | d4d688e | 2011-04-26 18:09:17 +0200 | [diff] [blame] | 540 | void |
| 541 | android_net_pipes_init(void) |
| 542 | { |
| 543 | Looper* looper = looper_newCore(); |
| 544 | |
| 545 | goldfish_pipe_add_type( "tcp", looper, &netPipeTcp_funcs ); |
David 'Digit' Turner | 82e6277 | 2011-06-21 10:01:01 +0200 | [diff] [blame] | 546 | #ifndef _WIN32 |
David 'Digit' Turner | d4d688e | 2011-04-26 18:09:17 +0200 | [diff] [blame] | 547 | goldfish_pipe_add_type( "unix", looper, &netPipeUnix_funcs ); |
David 'Digit' Turner | 82e6277 | 2011-06-21 10:01:01 +0200 | [diff] [blame] | 548 | #endif |
David 'Digit' Turner | d4d688e | 2011-04-26 18:09:17 +0200 | [diff] [blame] | 549 | goldfish_pipe_add_type( "opengles", looper, &openglesPipe_funcs ); |
| 550 | } |
David 'Digit' Turner | cb88e79 | 2011-08-26 01:35:14 +0200 | [diff] [blame] | 551 | |
| 552 | int |
| 553 | android_init_opengles_pipes(void) |
| 554 | { |
| 555 | /* TODO: Check that we can load and initialize the host emulation |
| 556 | * libraries, and return -1 in case of error. |
| 557 | */ |
| 558 | _opengles_init = 1; |
| 559 | return 0; |
| 560 | } |