Vladimir Chtchetkine | c8aa2c5 | 2012-04-05 16:22:55 -0700 | [diff] [blame] | 1 | /* |
| 2 | * Copyright (C) 2012 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 | /* |
| 18 | * Encapsulates exchange protocol between the emulator, and an Android device |
| 19 | * that is connected to the host via USB. The communication is established over |
| 20 | * a TCP port forwarding, enabled by ADB. |
| 21 | */ |
| 22 | |
Vladimir Chtchetkine | c8aa2c5 | 2012-04-05 16:22:55 -0700 | [diff] [blame] | 23 | #include "android/utils/debug.h" |
| 24 | #include "android/async-socket-connector.h" |
| 25 | #include "android/async-socket.h" |
| 26 | #include "android/sdk-controller-socket.h" |
| 27 | #include "utils/panic.h" |
David 'Digit' Turner | d413fa5 | 2013-12-14 23:35:20 +0100 | [diff] [blame^] | 28 | #include "android/iolooper.h" |
Vladimir Chtchetkine | c8aa2c5 | 2012-04-05 16:22:55 -0700 | [diff] [blame] | 29 | |
| 30 | #define E(...) derror(__VA_ARGS__) |
| 31 | #define W(...) dwarning(__VA_ARGS__) |
| 32 | #define D(...) VERBOSE_PRINT(sdkctlsocket,__VA_ARGS__) |
| 33 | #define D_ACTIVE VERBOSE_CHECK(sdkctlsocket) |
| 34 | |
Vladimir Chtchetkine | 7136b05 | 2012-04-10 13:39:24 -0700 | [diff] [blame] | 35 | #define TRACE_ON 0 |
Vladimir Chtchetkine | c8aa2c5 | 2012-04-05 16:22:55 -0700 | [diff] [blame] | 36 | |
| 37 | #if TRACE_ON |
| 38 | #define T(...) VERBOSE_PRINT(sdkctlsocket,__VA_ARGS__) |
| 39 | #else |
| 40 | #define T(...) |
| 41 | #endif |
| 42 | |
| 43 | /* Recycling memory descriptor. */ |
| 44 | typedef struct SDKCtlRecycled SDKCtlRecycled; |
| 45 | struct SDKCtlRecycled { |
| 46 | union { |
| 47 | /* Next recycled descriptor (while listed in recycler). */ |
| 48 | SDKCtlRecycled* next; |
| 49 | /* Allocated memory size (while outside of the recycler). */ |
| 50 | uint32_t size; |
| 51 | }; |
| 52 | }; |
| 53 | |
Vladimir Chtchetkine | c8aa2c5 | 2012-04-05 16:22:55 -0700 | [diff] [blame] | 54 | /* |
Vladimir Chtchetkine | 7136b05 | 2012-04-10 13:39:24 -0700 | [diff] [blame] | 55 | * Types of the data packets sent via SDK controller socket. |
Vladimir Chtchetkine | c8aa2c5 | 2012-04-05 16:22:55 -0700 | [diff] [blame] | 56 | */ |
| 57 | |
| 58 | /* The packet is a message. */ |
| 59 | #define SDKCTL_PACKET_MESSAGE 1 |
| 60 | /* The packet is a query. */ |
| 61 | #define SDKCTL_PACKET_QUERY 2 |
| 62 | /* The packet is a response to a query. */ |
| 63 | #define SDKCTL_PACKET_QUERY_RESPONSE 3 |
| 64 | |
Vladimir Chtchetkine | 7136b05 | 2012-04-10 13:39:24 -0700 | [diff] [blame] | 65 | /* |
| 66 | * Types of intenal port messages sent via SDK controller socket. |
| 67 | */ |
| 68 | |
| 69 | /* Port is connected. |
| 70 | * This message is sent by SDK controller when the service connects a socket with |
| 71 | * a port that provides requested emulation functionality. |
| 72 | */ |
| 73 | #define SDKCTL_MSG_PORT_CONNECTED -1 |
| 74 | /* Port is disconnected. |
| 75 | * This message is sent by SDK controller when a port that provides requested |
| 76 | * emulation functionality disconnects from the socket. |
| 77 | */ |
| 78 | #define SDKCTL_MSG_PORT_DISCONNECTED -2 |
| 79 | /* Port is enabled. |
| 80 | * This message is sent by SDK controller when a port that provides requested |
| 81 | * emulation functionality is ready to do the emulation. |
| 82 | */ |
| 83 | #define SDKCTL_MSG_PORT_ENABLED -3 |
| 84 | /* Port is disabled. |
| 85 | * This message is sent by SDK controller when a port that provides requested |
| 86 | * emulation functionality is not ready to do the emulation. |
| 87 | */ |
| 88 | #define SDKCTL_MSG_PORT_DISABLED -4 |
| 89 | |
| 90 | /* |
| 91 | * Types of internal queries sent via SDK controller socket. |
| 92 | */ |
| 93 | |
| 94 | /* Handshake query. |
| 95 | * This query is sent to SDK controller service as part of the connection |
| 96 | * protocol implementation. |
| 97 | */ |
| 98 | #define SDKCTL_QUERY_HANDSHAKE -1 |
| 99 | |
| 100 | /******************************************************************************** |
| 101 | * SDKCtlPacket declarations |
| 102 | *******************************************************************************/ |
| 103 | |
| 104 | /* Packet signature value ('SDKC'). */ |
| 105 | static const int _sdkctl_packet_sig = 0x53444B43; |
| 106 | |
Vladimir Chtchetkine | c8aa2c5 | 2012-04-05 16:22:55 -0700 | [diff] [blame] | 107 | /* Data packet descriptor. |
| 108 | * |
| 109 | * All packets, sent and received via SDK controller socket begin with this |
| 110 | * header, with packet data immediately following this header. |
| 111 | */ |
| 112 | typedef struct SDKCtlPacketHeader { |
Vladimir Chtchetkine | 7136b05 | 2012-04-10 13:39:24 -0700 | [diff] [blame] | 113 | /* Signature. */ |
| 114 | int signature; |
Vladimir Chtchetkine | c8aa2c5 | 2012-04-05 16:22:55 -0700 | [diff] [blame] | 115 | /* Total size of the data to transfer with this packet, including this |
| 116 | * header. The transferring data should immediatelly follow this header. */ |
| 117 | int size; |
| 118 | /* Encodes packet type. See SDKCTL_PACKET_XXX for the list of packet types |
| 119 | * used by SDK controller. */ |
| 120 | int type; |
| 121 | } SDKCtlPacketHeader; |
| 122 | |
| 123 | /* Packet descriptor, allocated by this API for data packets to be sent to SDK |
Vladimir Chtchetkine | 7136b05 | 2012-04-10 13:39:24 -0700 | [diff] [blame] | 124 | * controller. |
Vladimir Chtchetkine | c8aa2c5 | 2012-04-05 16:22:55 -0700 | [diff] [blame] | 125 | * |
| 126 | * When packet descriptors are allocated by this API, they are allocated large |
| 127 | * enough to contain this header, and packet data to send to the service, |
| 128 | * immediately following this descriptor. |
| 129 | */ |
Vladimir Chtchetkine | 7136b05 | 2012-04-10 13:39:24 -0700 | [diff] [blame] | 130 | typedef struct SDKCtlPacket { |
Vladimir Chtchetkine | c8aa2c5 | 2012-04-05 16:22:55 -0700 | [diff] [blame] | 131 | /* Supports recycling. Don't put anything in front: recycler expects this |
| 132 | * to be the first field in recyclable descriptor. */ |
Vladimir Chtchetkine | 7136b05 | 2012-04-10 13:39:24 -0700 | [diff] [blame] | 133 | SDKCtlRecycled recycling; |
Vladimir Chtchetkine | c8aa2c5 | 2012-04-05 16:22:55 -0700 | [diff] [blame] | 134 | |
Vladimir Chtchetkine | c8aa2c5 | 2012-04-05 16:22:55 -0700 | [diff] [blame] | 135 | /* SDK controller socket that transmits this packet. */ |
Vladimir Chtchetkine | 7136b05 | 2012-04-10 13:39:24 -0700 | [diff] [blame] | 136 | SDKCtlSocket* sdkctl; |
Vladimir Chtchetkine | c8aa2c5 | 2012-04-05 16:22:55 -0700 | [diff] [blame] | 137 | /* Number of outstanding references to the packet. */ |
Vladimir Chtchetkine | 7136b05 | 2012-04-10 13:39:24 -0700 | [diff] [blame] | 138 | int ref_count; |
Vladimir Chtchetkine | c8aa2c5 | 2012-04-05 16:22:55 -0700 | [diff] [blame] | 139 | |
| 140 | /* Common packet header. Packet data immediately follows this header, so it |
Vladimir Chtchetkine | 7136b05 | 2012-04-10 13:39:24 -0700 | [diff] [blame] | 141 | * must be the last field in SDKCtlPacket descriptor. */ |
| 142 | SDKCtlPacketHeader header; |
| 143 | } SDKCtlPacket; |
| 144 | |
| 145 | /******************************************************************************** |
| 146 | * SDKCtlDirectPacket declarations |
| 147 | *******************************************************************************/ |
| 148 | |
| 149 | /* Direct packet descriptor, allocated by this API for direct data packets to be |
| 150 | * sent to SDK controller service on the device. |
| 151 | * |
| 152 | * Direct packet (unlike SDKCtlPacket) don't contain data buffer, but rather |
| 153 | * reference data allocated by the client. This is useful when client sends large |
| 154 | * amount of data (such as framebuffer updates sent my multi-touch port), and |
| 155 | * regular packet descriptors for such large transfer cannot be obtained from the |
| 156 | * recycler. |
| 157 | */ |
| 158 | struct SDKCtlDirectPacket { |
| 159 | /* Supports recycling. Don't put anything in front: recycler expects this |
| 160 | * to be the first field in recyclable descriptor. */ |
| 161 | SDKCtlRecycled recycling; |
| 162 | |
| 163 | /* SDKCtlSocket that owns this packet. */ |
| 164 | SDKCtlSocket* sdkctl; |
| 165 | /* Packet to send. */ |
| 166 | SDKCtlPacketHeader* packet; |
| 167 | /* Callback to invoke on packet transmission events. */ |
| 168 | on_sdkctl_direct_cb on_sent; |
| 169 | /* An opaque pointer to pass to on_sent callback. */ |
| 170 | void* on_sent_opaque; |
| 171 | /* Number of outstanding references to the packet. */ |
| 172 | int ref_count; |
Vladimir Chtchetkine | c8aa2c5 | 2012-04-05 16:22:55 -0700 | [diff] [blame] | 173 | }; |
| 174 | |
| 175 | /******************************************************************************** |
| 176 | * SDKCtlQuery declarations |
| 177 | *******************************************************************************/ |
| 178 | |
Vladimir Chtchetkine | c8aa2c5 | 2012-04-05 16:22:55 -0700 | [diff] [blame] | 179 | /* Query packet descriptor. |
| 180 | * |
| 181 | * All queries, sent and received via SDK controller socket begin with this |
| 182 | * header, with query data immediately following this header. |
| 183 | */ |
| 184 | typedef struct SDKCtlQueryHeader { |
| 185 | /* Data packet header for this query. */ |
| 186 | SDKCtlPacketHeader packet; |
| 187 | /* A unique query identifier. This ID is used to track the query in the |
| 188 | * asynchronous environment in whcih SDK controller socket operates. */ |
| 189 | int query_id; |
Vladimir Chtchetkine | 7136b05 | 2012-04-10 13:39:24 -0700 | [diff] [blame] | 190 | /* Query type. */ |
Vladimir Chtchetkine | c8aa2c5 | 2012-04-05 16:22:55 -0700 | [diff] [blame] | 191 | int query_type; |
| 192 | } SDKCtlQueryHeader; |
| 193 | |
| 194 | /* Query descriptor, allocated by this API for queries to be sent to SDK |
| 195 | * controller service on the device. |
| 196 | * |
| 197 | * When query descriptors are allocated by this API, they are allocated large |
| 198 | * enough to contain this header, and query data to send to the service, |
| 199 | * immediately following this descriptor. |
| 200 | */ |
| 201 | struct SDKCtlQuery { |
| 202 | /* Supports recycling. Don't put anything in front: recycler expects this |
| 203 | * to be the first field in recyclable descriptor. */ |
| 204 | SDKCtlRecycled recycling; |
| 205 | |
Vladimir Chtchetkine | 7136b05 | 2012-04-10 13:39:24 -0700 | [diff] [blame] | 206 | /* Next query in the list of active queries. */ |
Vladimir Chtchetkine | c8aa2c5 | 2012-04-05 16:22:55 -0700 | [diff] [blame] | 207 | SDKCtlQuery* next; |
| 208 | /* A timer to run time out on this query after it has been sent. */ |
| 209 | LoopTimer timer[1]; |
| 210 | /* Absolute time for this query's deadline. This is the value that query's |
Vladimir Chtchetkine | 7136b05 | 2012-04-10 13:39:24 -0700 | [diff] [blame] | 211 | * timer is set to after query has been transmitted to the service. */ |
Vladimir Chtchetkine | c8aa2c5 | 2012-04-05 16:22:55 -0700 | [diff] [blame] | 212 | Duration deadline; |
| 213 | /* SDK controller socket that owns the query. */ |
| 214 | SDKCtlSocket* sdkctl; |
| 215 | /* A callback to invoke on query state changes. */ |
| 216 | on_sdkctl_query_cb query_cb; |
| 217 | /* An opaque pointer associated with this query. */ |
| 218 | void* query_opaque; |
| 219 | /* Points to an address of a buffer where to save query response. */ |
| 220 | void** response_buffer; |
Vladimir Chtchetkine | 7136b05 | 2012-04-10 13:39:24 -0700 | [diff] [blame] | 221 | /* Points to a variable containing size of the response buffer (on the way |
| 222 | * in), or actual query response size (when query is completed). */ |
Vladimir Chtchetkine | c8aa2c5 | 2012-04-05 16:22:55 -0700 | [diff] [blame] | 223 | uint32_t* response_size; |
| 224 | /* Internal response buffer, allocated if query creator didn't provide its |
| 225 | * own. This field is valid only if response_buffer field is NULL, or is |
| 226 | * pointing to this field. */ |
| 227 | void* internal_resp_buffer; |
Vladimir Chtchetkine | 7136b05 | 2012-04-10 13:39:24 -0700 | [diff] [blame] | 228 | /* Internal response buffer size used if query creator didn't provide its |
| 229 | * own. This field is valid only if response_size field is NULL, or is |
| 230 | * pointing to this field. */ |
Vladimir Chtchetkine | c8aa2c5 | 2012-04-05 16:22:55 -0700 | [diff] [blame] | 231 | uint32_t internal_resp_size; |
| 232 | /* Number of outstanding references to the query. */ |
| 233 | int ref_count; |
| 234 | |
Vladimir Chtchetkine | 7136b05 | 2012-04-10 13:39:24 -0700 | [diff] [blame] | 235 | /* Common query header. Query data immediately follows this header, so it |
Vladimir Chtchetkine | c8aa2c5 | 2012-04-05 16:22:55 -0700 | [diff] [blame] | 236 | * must be last field in SDKCtlQuery descriptor. */ |
| 237 | SDKCtlQueryHeader header; |
| 238 | }; |
| 239 | |
| 240 | /* Query reply descriptor. |
| 241 | * |
| 242 | * All replies to a query, sent and received via SDK controller socket begin with |
| 243 | * this header, with query reply data immediately following this header. |
| 244 | */ |
| 245 | typedef struct SDKCtlQueryReplyHeader { |
| 246 | /* Data packet header for this reply. */ |
| 247 | SDKCtlPacketHeader packet; |
| 248 | |
| 249 | /* An identifier for the query that is addressed with this reply. */ |
| 250 | int query_id; |
| 251 | } SDKCtlQueryReplyHeader; |
| 252 | |
| 253 | /******************************************************************************** |
Vladimir Chtchetkine | 7136b05 | 2012-04-10 13:39:24 -0700 | [diff] [blame] | 254 | * SDKCtlMessage declarations |
| 255 | *******************************************************************************/ |
| 256 | |
| 257 | /* Message packet descriptor. |
| 258 | * |
| 259 | * All messages, sent and received via SDK controller socket begin with this |
| 260 | * header, with message data immediately following this header. |
| 261 | */ |
| 262 | typedef struct SDKCtlMessageHeader { |
| 263 | /* Data packet header for this query. */ |
| 264 | SDKCtlPacketHeader packet; |
| 265 | /* Message type. */ |
| 266 | int msg_type; |
| 267 | } SDKCtlMessageHeader; |
| 268 | |
| 269 | /* Message packet descriptor. |
| 270 | * |
| 271 | * All messages, sent and received via SDK controller socket begin with this |
| 272 | * header, with message data immediately following this header. |
| 273 | */ |
Vladimir Chtchetkine | 4732aee | 2012-04-30 12:38:06 -0700 | [diff] [blame] | 274 | struct SDKCtlMessage { |
Vladimir Chtchetkine | 7136b05 | 2012-04-10 13:39:24 -0700 | [diff] [blame] | 275 | /* Data packet descriptor for this message. */ |
| 276 | SDKCtlPacket packet; |
| 277 | /* Message type. */ |
| 278 | int msg_type; |
Vladimir Chtchetkine | 4732aee | 2012-04-30 12:38:06 -0700 | [diff] [blame] | 279 | }; |
Vladimir Chtchetkine | 7136b05 | 2012-04-10 13:39:24 -0700 | [diff] [blame] | 280 | |
| 281 | /******************************************************************************** |
Vladimir Chtchetkine | c8aa2c5 | 2012-04-05 16:22:55 -0700 | [diff] [blame] | 282 | * SDK Control Socket declarations |
| 283 | *******************************************************************************/ |
| 284 | |
| 285 | /* Enumerates SDKCtlSocket states. */ |
| 286 | typedef enum SDKCtlSocketState { |
| 287 | /* Socket is disconnected from SDK controller. */ |
| 288 | SDKCTL_SOCKET_DISCONNECTED, |
| 289 | /* Connection to SDK controller is in progress. */ |
| 290 | SDKCTL_SOCKET_CONNECTING, |
| 291 | /* Socket is connected to an SDK controller service. */ |
| 292 | SDKCTL_SOCKET_CONNECTED |
| 293 | } SDKCtlSocketState; |
| 294 | |
| 295 | /* Enumerates SDKCtlSocket I/O dispatcher states. */ |
| 296 | typedef enum SDKCtlIODispatcherState { |
| 297 | /* I/O dispatcher expects a packet header. */ |
| 298 | SDKCTL_IODISP_EXPECT_HEADER, |
| 299 | /* I/O dispatcher expects packet data. */ |
| 300 | SDKCTL_IODISP_EXPECT_DATA, |
| 301 | /* I/O dispatcher expects query response header. */ |
| 302 | SDKCTL_IODISP_EXPECT_QUERY_REPLY_HEADER, |
| 303 | /* I/O dispatcher expects query response data. */ |
| 304 | SDKCTL_IODISP_EXPECT_QUERY_REPLY_DATA, |
| 305 | } SDKCtlIODispatcherState; |
| 306 | |
| 307 | /* SDKCtlSocket I/O dispatcher descriptor. */ |
| 308 | typedef struct SDKCtlIODispatcher { |
| 309 | /* SDKCtlSocket instance for this dispatcher. */ |
| 310 | SDKCtlSocket* sdkctl; |
| 311 | /* Dispatcher state. */ |
| 312 | SDKCtlIODispatcherState state; |
| 313 | /* Unites all types of headers used in SDK controller data exchange. */ |
| 314 | union { |
| 315 | /* Common packet header. */ |
Vladimir Chtchetkine | 7136b05 | 2012-04-10 13:39:24 -0700 | [diff] [blame] | 316 | SDKCtlPacketHeader packet_header; |
Vladimir Chtchetkine | c8aa2c5 | 2012-04-05 16:22:55 -0700 | [diff] [blame] | 317 | /* Header for a query packet. */ |
| 318 | SDKCtlQueryHeader query_header; |
Vladimir Chtchetkine | 7136b05 | 2012-04-10 13:39:24 -0700 | [diff] [blame] | 319 | /* Header for a message packet. */ |
| 320 | SDKCtlMessageHeader message_header; |
Vladimir Chtchetkine | c8aa2c5 | 2012-04-05 16:22:55 -0700 | [diff] [blame] | 321 | /* Header for a query response packet. */ |
| 322 | SDKCtlQueryReplyHeader query_reply_header; |
| 323 | }; |
Vladimir Chtchetkine | 7136b05 | 2012-04-10 13:39:24 -0700 | [diff] [blame] | 324 | /* Descriptor of a packet that is being received from SDK controller. */ |
Vladimir Chtchetkine | c8aa2c5 | 2012-04-05 16:22:55 -0700 | [diff] [blame] | 325 | SDKCtlPacket* packet; |
| 326 | /* A query for which a reply is currently being received. */ |
| 327 | SDKCtlQuery* current_query; |
| 328 | } SDKCtlIODispatcher; |
| 329 | |
| 330 | /* SDK controller socket descriptor. */ |
| 331 | struct SDKCtlSocket { |
| 332 | /* SDK controller socket state */ |
Vladimir Chtchetkine | 7136b05 | 2012-04-10 13:39:24 -0700 | [diff] [blame] | 333 | SDKCtlSocketState state; |
| 334 | /* SDK controller port status */ |
| 335 | SdkCtlPortStatus port_status; |
Vladimir Chtchetkine | c8aa2c5 | 2012-04-05 16:22:55 -0700 | [diff] [blame] | 336 | /* I/O dispatcher for the socket. */ |
Vladimir Chtchetkine | 7136b05 | 2012-04-10 13:39:24 -0700 | [diff] [blame] | 337 | SDKCtlIODispatcher io_dispatcher; |
Vladimir Chtchetkine | c8aa2c5 | 2012-04-05 16:22:55 -0700 | [diff] [blame] | 338 | /* Asynchronous socket connected to SDK Controller on the device. */ |
Vladimir Chtchetkine | 7136b05 | 2012-04-10 13:39:24 -0700 | [diff] [blame] | 339 | AsyncSocket* as; |
Vladimir Chtchetkine | c8aa2c5 | 2012-04-05 16:22:55 -0700 | [diff] [blame] | 340 | /* Client callback that monitors this socket connection. */ |
Vladimir Chtchetkine | 7136b05 | 2012-04-10 13:39:24 -0700 | [diff] [blame] | 341 | on_sdkctl_socket_connection_cb on_socket_connection; |
| 342 | /* Client callback that monitors SDK controller prt connection. */ |
| 343 | on_sdkctl_port_connection_cb on_port_connection; |
Vladimir Chtchetkine | c8aa2c5 | 2012-04-05 16:22:55 -0700 | [diff] [blame] | 344 | /* A callback to invoke when a message is received from the SDK controller. */ |
Vladimir Chtchetkine | 7136b05 | 2012-04-10 13:39:24 -0700 | [diff] [blame] | 345 | on_sdkctl_message_cb on_message; |
Vladimir Chtchetkine | c8aa2c5 | 2012-04-05 16:22:55 -0700 | [diff] [blame] | 346 | /* An opaque pointer associated with this socket. */ |
Vladimir Chtchetkine | 7136b05 | 2012-04-10 13:39:24 -0700 | [diff] [blame] | 347 | void* opaque; |
| 348 | /* Name of an SDK controller port this socket is connected to. */ |
| 349 | char* service_name; |
Vladimir Chtchetkine | c8aa2c5 | 2012-04-05 16:22:55 -0700 | [diff] [blame] | 350 | /* I/O looper for timers. */ |
Vladimir Chtchetkine | 7136b05 | 2012-04-10 13:39:24 -0700 | [diff] [blame] | 351 | Looper* looper; |
Vladimir Chtchetkine | c8aa2c5 | 2012-04-05 16:22:55 -0700 | [diff] [blame] | 352 | /* Head of the active query list. */ |
Vladimir Chtchetkine | 7136b05 | 2012-04-10 13:39:24 -0700 | [diff] [blame] | 353 | SDKCtlQuery* query_head; |
Vladimir Chtchetkine | c8aa2c5 | 2012-04-05 16:22:55 -0700 | [diff] [blame] | 354 | /* Tail of the active query list. */ |
Vladimir Chtchetkine | 7136b05 | 2012-04-10 13:39:24 -0700 | [diff] [blame] | 355 | SDKCtlQuery* query_tail; |
Vladimir Chtchetkine | c8aa2c5 | 2012-04-05 16:22:55 -0700 | [diff] [blame] | 356 | /* Query ID generator that gets incremented for each new query. */ |
Vladimir Chtchetkine | 7136b05 | 2012-04-10 13:39:24 -0700 | [diff] [blame] | 357 | int next_query_id; |
Vladimir Chtchetkine | c8aa2c5 | 2012-04-05 16:22:55 -0700 | [diff] [blame] | 358 | /* Timeout before trying to reconnect after disconnection. */ |
Vladimir Chtchetkine | 7136b05 | 2012-04-10 13:39:24 -0700 | [diff] [blame] | 359 | int reconnect_to; |
Vladimir Chtchetkine | c8aa2c5 | 2012-04-05 16:22:55 -0700 | [diff] [blame] | 360 | /* Number of outstanding references to this descriptor. */ |
Vladimir Chtchetkine | 7136b05 | 2012-04-10 13:39:24 -0700 | [diff] [blame] | 361 | int ref_count; |
Vladimir Chtchetkine | c8aa2c5 | 2012-04-05 16:22:55 -0700 | [diff] [blame] | 362 | /* Head of the recycled memory */ |
Vladimir Chtchetkine | 7136b05 | 2012-04-10 13:39:24 -0700 | [diff] [blame] | 363 | SDKCtlRecycled* recycler; |
Vladimir Chtchetkine | c8aa2c5 | 2012-04-05 16:22:55 -0700 | [diff] [blame] | 364 | /* Recyclable block size. */ |
Vladimir Chtchetkine | 7136b05 | 2012-04-10 13:39:24 -0700 | [diff] [blame] | 365 | uint32_t recycler_block_size; |
Vladimir Chtchetkine | c8aa2c5 | 2012-04-05 16:22:55 -0700 | [diff] [blame] | 366 | /* Maximum number of blocks to recycle. */ |
Vladimir Chtchetkine | 7136b05 | 2012-04-10 13:39:24 -0700 | [diff] [blame] | 367 | int recycler_max; |
Vladimir Chtchetkine | c8aa2c5 | 2012-04-05 16:22:55 -0700 | [diff] [blame] | 368 | /* Number of blocs in the recycler. */ |
Vladimir Chtchetkine | 7136b05 | 2012-04-10 13:39:24 -0700 | [diff] [blame] | 369 | int recycler_count; |
Vladimir Chtchetkine | c8aa2c5 | 2012-04-05 16:22:55 -0700 | [diff] [blame] | 370 | }; |
| 371 | |
| 372 | /******************************************************************************** |
| 373 | * SDKCtlSocket recycling management |
| 374 | *******************************************************************************/ |
| 375 | |
| 376 | /* Gets a recycled block for a given SDKCtlSocket, or allocates new memory |
| 377 | * block. */ |
| 378 | static void* |
| 379 | _sdkctl_socket_alloc_recycler(SDKCtlSocket* sdkctl, uint32_t size) |
| 380 | { |
| 381 | SDKCtlRecycled* block = NULL; |
| 382 | |
| 383 | if (sdkctl->recycler != NULL && size <= sdkctl->recycler_block_size) { |
Vladimir Chtchetkine | 7136b05 | 2012-04-10 13:39:24 -0700 | [diff] [blame] | 384 | assert(sdkctl->recycler_count > 0); |
Vladimir Chtchetkine | c8aa2c5 | 2012-04-05 16:22:55 -0700 | [diff] [blame] | 385 | /* There are blocks in the recycler, and requested size fits. */ |
| 386 | block = sdkctl->recycler; |
| 387 | sdkctl->recycler = block->next; |
| 388 | block->size = sdkctl->recycler_block_size; |
| 389 | sdkctl->recycler_count--; |
| 390 | } else if (size <= sdkctl->recycler_block_size) { |
Vladimir Chtchetkine | 7136b05 | 2012-04-10 13:39:24 -0700 | [diff] [blame] | 391 | /* There are no blocks in the recycler, but requested size fits. Lets |
| 392 | * allocate block that we can later recycle. */ |
Vladimir Chtchetkine | c8aa2c5 | 2012-04-05 16:22:55 -0700 | [diff] [blame] | 393 | block = malloc(sdkctl->recycler_block_size); |
| 394 | if (block == NULL) { |
Vladimir Chtchetkine | 7136b05 | 2012-04-10 13:39:24 -0700 | [diff] [blame] | 395 | APANIC("SDKCtl %s: Unable to allocate %d bytes block.", |
Vladimir Chtchetkine | c8aa2c5 | 2012-04-05 16:22:55 -0700 | [diff] [blame] | 396 | sdkctl->service_name, sdkctl->recycler_block_size); |
| 397 | } |
| 398 | block->size = sdkctl->recycler_block_size; |
| 399 | } else { |
| 400 | /* Requested size doesn't fit the recycler. */ |
| 401 | block = malloc(size); |
| 402 | if (block == NULL) { |
| 403 | APANIC("SDKCtl %s: Unable to allocate %d bytes block", |
| 404 | sdkctl->service_name, size); |
| 405 | } |
| 406 | block->size = size; |
| 407 | } |
| 408 | |
| 409 | return block; |
| 410 | } |
| 411 | |
| 412 | /* Recycles, or frees a block of memory for a given SDKCtlSocket. */ |
| 413 | static void |
| 414 | _sdkctl_socket_free_recycler(SDKCtlSocket* sdkctl, void* mem) |
| 415 | { |
Vladimir Chtchetkine | 7136b05 | 2012-04-10 13:39:24 -0700 | [diff] [blame] | 416 | SDKCtlRecycled* const block = (SDKCtlRecycled*)mem; |
Vladimir Chtchetkine | c8aa2c5 | 2012-04-05 16:22:55 -0700 | [diff] [blame] | 417 | |
Vladimir Chtchetkine | 7136b05 | 2012-04-10 13:39:24 -0700 | [diff] [blame] | 418 | if (block->size != sdkctl->recycler_block_size || |
| 419 | sdkctl->recycler_count == sdkctl->recycler_max) { |
| 420 | /* Recycler is full, or block cannot be recycled. Just free the memory. */ |
Vladimir Chtchetkine | c8aa2c5 | 2012-04-05 16:22:55 -0700 | [diff] [blame] | 421 | free(mem); |
Vladimir Chtchetkine | 7136b05 | 2012-04-10 13:39:24 -0700 | [diff] [blame] | 422 | } else { |
| 423 | /* Add that block to the recycler. */ |
| 424 | assert(sdkctl->recycler_count >= 0); |
| 425 | block->next = sdkctl->recycler; |
| 426 | sdkctl->recycler = block; |
| 427 | sdkctl->recycler_count++; |
Vladimir Chtchetkine | c8aa2c5 | 2012-04-05 16:22:55 -0700 | [diff] [blame] | 428 | } |
Vladimir Chtchetkine | c8aa2c5 | 2012-04-05 16:22:55 -0700 | [diff] [blame] | 429 | } |
| 430 | |
| 431 | /* Empties the recycler for a given SDKCtlSocket. */ |
| 432 | static void |
| 433 | _sdkctl_socket_empty_recycler(SDKCtlSocket* sdkctl) |
| 434 | { |
| 435 | SDKCtlRecycled* block = sdkctl->recycler; |
| 436 | while (block != NULL) { |
Vladimir Chtchetkine | 7136b05 | 2012-04-10 13:39:24 -0700 | [diff] [blame] | 437 | void* const to_free = block; |
Vladimir Chtchetkine | c8aa2c5 | 2012-04-05 16:22:55 -0700 | [diff] [blame] | 438 | block = block->next; |
| 439 | free(to_free); |
| 440 | } |
| 441 | sdkctl->recycler = NULL; |
| 442 | sdkctl->recycler_count = 0; |
| 443 | } |
| 444 | |
| 445 | /******************************************************************************** |
| 446 | * SDKCtlSocket query list management |
| 447 | *******************************************************************************/ |
| 448 | |
| 449 | /* Adds a query to the list of active queries. |
| 450 | * Param: |
| 451 | * sdkctl - SDKCtlSocket instance for the query. |
| 452 | * query - Query to add to the list. |
| 453 | */ |
| 454 | static void |
| 455 | _sdkctl_socket_add_query(SDKCtlQuery* query) |
| 456 | { |
| 457 | SDKCtlSocket* const sdkctl = query->sdkctl; |
| 458 | if (sdkctl->query_head == NULL) { |
Vladimir Chtchetkine | 7136b05 | 2012-04-10 13:39:24 -0700 | [diff] [blame] | 459 | assert(sdkctl->query_tail == NULL); |
Vladimir Chtchetkine | c8aa2c5 | 2012-04-05 16:22:55 -0700 | [diff] [blame] | 460 | sdkctl->query_head = sdkctl->query_tail = query; |
| 461 | } else { |
| 462 | sdkctl->query_tail->next = query; |
| 463 | sdkctl->query_tail = query; |
| 464 | } |
| 465 | |
| 466 | /* Keep the query referenced while it's in the list. */ |
| 467 | sdkctl_query_reference(query); |
| 468 | } |
| 469 | |
| 470 | /* Removes a query from the list of active queries. |
| 471 | * Param: |
Vladimir Chtchetkine | 7136b05 | 2012-04-10 13:39:24 -0700 | [diff] [blame] | 472 | * query - Query to remove from the list of active queries. If query has been |
| 473 | * removed from the list, it will be dereferenced to offset the reference |
| 474 | * that wad made when the query has been added to the list. |
Vladimir Chtchetkine | c8aa2c5 | 2012-04-05 16:22:55 -0700 | [diff] [blame] | 475 | * Return: |
| 476 | * Boolean: 1 if query has been removed, or 0 if query has not been found in the |
| 477 | * list of active queries. |
| 478 | */ |
| 479 | static int |
| 480 | _sdkctl_socket_remove_query(SDKCtlQuery* query) |
| 481 | { |
| 482 | SDKCtlSocket* const sdkctl = query->sdkctl; |
| 483 | SDKCtlQuery* prev = NULL; |
| 484 | SDKCtlQuery* head = sdkctl->query_head; |
| 485 | |
Vladimir Chtchetkine | 7136b05 | 2012-04-10 13:39:24 -0700 | [diff] [blame] | 486 | /* Quick check: the query could be currently handled by the dispatcher. */ |
Vladimir Chtchetkine | c8aa2c5 | 2012-04-05 16:22:55 -0700 | [diff] [blame] | 487 | if (sdkctl->io_dispatcher.current_query == query) { |
| 488 | /* Release the query from dispatcher. */ |
Vladimir Chtchetkine | c8aa2c5 | 2012-04-05 16:22:55 -0700 | [diff] [blame] | 489 | sdkctl->io_dispatcher.current_query = NULL; |
Vladimir Chtchetkine | 7136b05 | 2012-04-10 13:39:24 -0700 | [diff] [blame] | 490 | sdkctl_query_release(query); |
Vladimir Chtchetkine | c8aa2c5 | 2012-04-05 16:22:55 -0700 | [diff] [blame] | 491 | return 1; |
| 492 | } |
| 493 | |
| 494 | /* Remove query from the list. */ |
| 495 | while (head != NULL && query != head) { |
| 496 | prev = head; |
| 497 | head = head->next; |
| 498 | } |
Vladimir Chtchetkine | 7136b05 | 2012-04-10 13:39:24 -0700 | [diff] [blame] | 499 | if (head == NULL) { |
| 500 | D("SDKCtl %s: Query %p is not found in the list.", |
| 501 | sdkctl->service_name, query); |
Vladimir Chtchetkine | c8aa2c5 | 2012-04-05 16:22:55 -0700 | [diff] [blame] | 502 | return 0; |
| 503 | } |
Vladimir Chtchetkine | 7136b05 | 2012-04-10 13:39:24 -0700 | [diff] [blame] | 504 | |
| 505 | if (prev == NULL) { |
| 506 | /* Query is at the head of the list. */ |
| 507 | assert(query == sdkctl->query_head); |
| 508 | sdkctl->query_head = query->next; |
| 509 | } else { |
| 510 | /* Query is in the middle / at the end of the list. */ |
| 511 | assert(query != sdkctl->query_head); |
| 512 | prev->next = query->next; |
| 513 | } |
| 514 | if (sdkctl->query_tail == query) { |
| 515 | /* Query is at the tail of the list. */ |
| 516 | assert(query->next == NULL); |
| 517 | sdkctl->query_tail = prev; |
| 518 | } |
| 519 | query->next = NULL; |
| 520 | |
| 521 | /* Release query that is now removed from the list. Note that query |
| 522 | * passed to this routine should hold an extra reference, owned by the |
| 523 | * caller. */ |
| 524 | sdkctl_query_release(query); |
| 525 | |
| 526 | return 1; |
Vladimir Chtchetkine | c8aa2c5 | 2012-04-05 16:22:55 -0700 | [diff] [blame] | 527 | } |
| 528 | |
| 529 | /* Removes a query (based on query ID) from the list of active queries. |
| 530 | * Param: |
| 531 | * sdkctl - SDKCtlSocket instance that owns the query. |
| 532 | * query_id - Identifies the query to remove. |
| 533 | * Return: |
| 534 | * A query removed from the list of active queries, or NULL if query with the |
Vladimir Chtchetkine | 7136b05 | 2012-04-10 13:39:24 -0700 | [diff] [blame] | 535 | * given ID has not been found in the list. Note that query returned from this |
| 536 | * routine still holds the reference made when the query has been added to the |
| 537 | * list. |
Vladimir Chtchetkine | c8aa2c5 | 2012-04-05 16:22:55 -0700 | [diff] [blame] | 538 | */ |
| 539 | static SDKCtlQuery* |
| 540 | _sdkctl_socket_remove_query_id(SDKCtlSocket* sdkctl, int query_id) |
| 541 | { |
Vladimir Chtchetkine | 7136b05 | 2012-04-10 13:39:24 -0700 | [diff] [blame] | 542 | SDKCtlQuery* query = NULL; |
Vladimir Chtchetkine | c8aa2c5 | 2012-04-05 16:22:55 -0700 | [diff] [blame] | 543 | SDKCtlQuery* prev = NULL; |
| 544 | SDKCtlQuery* head = sdkctl->query_head; |
| 545 | |
| 546 | /* Quick check: the query could be currently handled by dispatcher. */ |
| 547 | if (sdkctl->io_dispatcher.current_query != NULL && |
| 548 | sdkctl->io_dispatcher.current_query->header.query_id == query_id) { |
| 549 | /* Release the query from dispatcher. */ |
Vladimir Chtchetkine | 7136b05 | 2012-04-10 13:39:24 -0700 | [diff] [blame] | 550 | query = sdkctl->io_dispatcher.current_query; |
Vladimir Chtchetkine | c8aa2c5 | 2012-04-05 16:22:55 -0700 | [diff] [blame] | 551 | sdkctl->io_dispatcher.current_query = NULL; |
| 552 | return query; |
| 553 | } |
| 554 | |
| 555 | /* Remove query from the list. */ |
| 556 | while (head != NULL && head->header.query_id != query_id) { |
| 557 | prev = head; |
| 558 | head = head->next; |
| 559 | } |
Vladimir Chtchetkine | 7136b05 | 2012-04-10 13:39:24 -0700 | [diff] [blame] | 560 | if (head == NULL) { |
| 561 | D("SDKCtl %s: Query ID %d is not found in the list.", |
Vladimir Chtchetkine | c8aa2c5 | 2012-04-05 16:22:55 -0700 | [diff] [blame] | 562 | sdkctl->service_name, query_id); |
| 563 | return NULL; |
| 564 | } |
Vladimir Chtchetkine | 7136b05 | 2012-04-10 13:39:24 -0700 | [diff] [blame] | 565 | |
| 566 | /* Query is found in the list. */ |
| 567 | query = head; |
| 568 | if (prev == NULL) { |
| 569 | /* Query is at the head of the list. */ |
| 570 | assert(query == sdkctl->query_head); |
| 571 | sdkctl->query_head = query->next; |
| 572 | } else { |
| 573 | /* Query is in the middle, or at the end of the list. */ |
| 574 | assert(query != sdkctl->query_head); |
| 575 | prev->next = query->next; |
| 576 | } |
| 577 | if (sdkctl->query_tail == query) { |
| 578 | /* Query is at the tail of the list. */ |
| 579 | assert(query->next == NULL); |
| 580 | sdkctl->query_tail = prev; |
| 581 | } |
| 582 | query->next = NULL; |
| 583 | |
| 584 | return query; |
Vladimir Chtchetkine | c8aa2c5 | 2012-04-05 16:22:55 -0700 | [diff] [blame] | 585 | } |
| 586 | |
| 587 | /* Pulls the first query from the list of active queries. |
| 588 | * Param: |
| 589 | * sdkctl - SDKCtlSocket instance that owns the query. |
| 590 | * Return: |
Vladimir Chtchetkine | 7136b05 | 2012-04-10 13:39:24 -0700 | [diff] [blame] | 591 | * A query removed from the head of the list of active queries, or NULL if query |
Vladimir Chtchetkine | c8aa2c5 | 2012-04-05 16:22:55 -0700 | [diff] [blame] | 592 | * list is empty. |
| 593 | */ |
| 594 | static SDKCtlQuery* |
| 595 | _sdkctl_socket_pull_first_query(SDKCtlSocket* sdkctl) |
| 596 | { |
| 597 | SDKCtlQuery* const query = sdkctl->query_head; |
| 598 | |
| 599 | if (query != NULL) { |
| 600 | sdkctl->query_head = query->next; |
| 601 | if (sdkctl->query_head == NULL) { |
| 602 | sdkctl->query_tail = NULL; |
| 603 | } |
| 604 | } |
| 605 | return query; |
| 606 | } |
| 607 | |
| 608 | /* Generates new query ID for the given SDKCtl. */ |
| 609 | static int |
| 610 | _sdkctl_socket_next_query_id(SDKCtlSocket* sdkctl) |
| 611 | { |
| 612 | return ++sdkctl->next_query_id; |
| 613 | } |
| 614 | |
| 615 | /******************************************************************************** |
| 616 | * SDKCtlPacket implementation |
| 617 | *******************************************************************************/ |
| 618 | |
| 619 | /* Alocates a packet. */ |
| 620 | static SDKCtlPacket* |
Vladimir Chtchetkine | 7136b05 | 2012-04-10 13:39:24 -0700 | [diff] [blame] | 621 | _sdkctl_packet_new(SDKCtlSocket* sdkctl, uint32_t size, int type) |
Vladimir Chtchetkine | c8aa2c5 | 2012-04-05 16:22:55 -0700 | [diff] [blame] | 622 | { |
Vladimir Chtchetkine | 7136b05 | 2012-04-10 13:39:24 -0700 | [diff] [blame] | 623 | /* Allocate packet descriptor large enough to contain packet data. */ |
| 624 | SDKCtlPacket* const packet = |
| 625 | _sdkctl_socket_alloc_recycler(sdkctl, sizeof(SDKCtlPacket) + size); |
Vladimir Chtchetkine | c8aa2c5 | 2012-04-05 16:22:55 -0700 | [diff] [blame] | 626 | |
Vladimir Chtchetkine | 7136b05 | 2012-04-10 13:39:24 -0700 | [diff] [blame] | 627 | packet->sdkctl = sdkctl; |
| 628 | packet->ref_count = 1; |
| 629 | packet->header.signature = _sdkctl_packet_sig; |
| 630 | packet->header.size = size; |
| 631 | packet->header.type = type; |
Vladimir Chtchetkine | c8aa2c5 | 2012-04-05 16:22:55 -0700 | [diff] [blame] | 632 | |
| 633 | /* Refence SDKCTlSocket that owns this packet. */ |
| 634 | sdkctl_socket_reference(sdkctl); |
| 635 | |
Vladimir Chtchetkine | 7136b05 | 2012-04-10 13:39:24 -0700 | [diff] [blame] | 636 | T("SDKCtl %s: Packet %p of type %d is allocated for %d bytes transfer.", |
| 637 | sdkctl->service_name, packet, type, size); |
| 638 | |
Vladimir Chtchetkine | c8aa2c5 | 2012-04-05 16:22:55 -0700 | [diff] [blame] | 639 | return packet; |
| 640 | } |
| 641 | |
| 642 | /* Frees a packet. */ |
| 643 | static void |
| 644 | _sdkctl_packet_free(SDKCtlPacket* packet) |
| 645 | { |
| 646 | SDKCtlSocket* const sdkctl = packet->sdkctl; |
| 647 | |
Vladimir Chtchetkine | 7136b05 | 2012-04-10 13:39:24 -0700 | [diff] [blame] | 648 | /* Recycle packet. */ |
Vladimir Chtchetkine | c8aa2c5 | 2012-04-05 16:22:55 -0700 | [diff] [blame] | 649 | _sdkctl_socket_free_recycler(packet->sdkctl, packet); |
| 650 | |
Vladimir Chtchetkine | 7136b05 | 2012-04-10 13:39:24 -0700 | [diff] [blame] | 651 | T("SDKCtl %s: Packet %p is freed.", sdkctl->service_name, packet); |
| 652 | |
Vladimir Chtchetkine | c8aa2c5 | 2012-04-05 16:22:55 -0700 | [diff] [blame] | 653 | /* Release SDKCTlSocket that owned this packet. */ |
| 654 | sdkctl_socket_release(sdkctl); |
| 655 | } |
| 656 | |
Vladimir Chtchetkine | 7136b05 | 2012-04-10 13:39:24 -0700 | [diff] [blame] | 657 | /* References a packet. */ |
Vladimir Chtchetkine | c8aa2c5 | 2012-04-05 16:22:55 -0700 | [diff] [blame] | 658 | int |
Vladimir Chtchetkine | 7136b05 | 2012-04-10 13:39:24 -0700 | [diff] [blame] | 659 | _sdkctl_packet_reference(SDKCtlPacket* packet) |
Vladimir Chtchetkine | c8aa2c5 | 2012-04-05 16:22:55 -0700 | [diff] [blame] | 660 | { |
| 661 | assert(packet->ref_count > 0); |
| 662 | packet->ref_count++; |
| 663 | return packet->ref_count; |
| 664 | } |
| 665 | |
Vladimir Chtchetkine | 7136b05 | 2012-04-10 13:39:24 -0700 | [diff] [blame] | 666 | /* Releases a packet. */ |
Vladimir Chtchetkine | c8aa2c5 | 2012-04-05 16:22:55 -0700 | [diff] [blame] | 667 | int |
Vladimir Chtchetkine | 7136b05 | 2012-04-10 13:39:24 -0700 | [diff] [blame] | 668 | _sdkctl_packet_release(SDKCtlPacket* packet) |
Vladimir Chtchetkine | c8aa2c5 | 2012-04-05 16:22:55 -0700 | [diff] [blame] | 669 | { |
| 670 | assert(packet->ref_count > 0); |
| 671 | packet->ref_count--; |
| 672 | if (packet->ref_count == 0) { |
| 673 | /* Last reference has been dropped. Destroy this object. */ |
| 674 | _sdkctl_packet_free(packet); |
| 675 | return 0; |
| 676 | } |
| 677 | return packet->ref_count; |
| 678 | } |
| 679 | |
Vladimir Chtchetkine | 7136b05 | 2012-04-10 13:39:24 -0700 | [diff] [blame] | 680 | /* An I/O callback invoked on packet transmission. |
| 681 | * Param: |
| 682 | * io_opaque SDKCtlPacket instance of the packet that's being sent with this I/O. |
| 683 | * asio - Write I/O descriptor. |
| 684 | * status - I/O status. |
| 685 | */ |
| 686 | static AsyncIOAction |
| 687 | _on_sdkctl_packet_send_io(void* io_opaque, |
| 688 | AsyncSocketIO* asio, |
| 689 | AsyncIOState status) |
| 690 | { |
| 691 | SDKCtlPacket* const packet = (SDKCtlPacket*)io_opaque; |
| 692 | AsyncIOAction action = ASIO_ACTION_DONE; |
| 693 | |
| 694 | /* Reference the packet while we're in this callback. */ |
| 695 | _sdkctl_packet_reference(packet); |
| 696 | |
| 697 | /* Lets see what's going on with query transmission. */ |
| 698 | switch (status) { |
| 699 | case ASIO_STATE_SUCCEEDED: |
| 700 | /* Packet has been sent to the service. */ |
| 701 | T("SDKCtl %s: Packet %p transmission has succeeded.", |
| 702 | packet->sdkctl->service_name, packet); |
| 703 | break; |
| 704 | |
| 705 | case ASIO_STATE_CANCELLED: |
| 706 | T("SDKCtl %s: Packet %p is cancelled.", |
| 707 | packet->sdkctl->service_name, packet); |
| 708 | break; |
| 709 | |
| 710 | case ASIO_STATE_FAILED: |
| 711 | T("SDKCtl %s: Packet %p has failed: %d -> %s", |
| 712 | packet->sdkctl->service_name, packet, errno, strerror(errno)); |
| 713 | break; |
| 714 | |
| 715 | case ASIO_STATE_FINISHED: |
| 716 | /* Time to disassociate the packet with the I/O. */ |
| 717 | _sdkctl_packet_release(packet); |
| 718 | break; |
| 719 | |
| 720 | default: |
| 721 | /* Transitional state. */ |
| 722 | break; |
| 723 | } |
| 724 | |
| 725 | _sdkctl_packet_release(packet); |
| 726 | |
| 727 | return action; |
| 728 | } |
| 729 | |
| 730 | /* Transmits a packet to SDK Controller. |
| 731 | * Param: |
| 732 | * packet - Packet to transmit. |
| 733 | */ |
| 734 | static void |
| 735 | _sdkctl_packet_transmit(SDKCtlPacket* packet) |
| 736 | { |
| 737 | assert(packet->header.signature == _sdkctl_packet_sig); |
| 738 | |
| 739 | /* Reference to associate with the I/O */ |
| 740 | _sdkctl_packet_reference(packet); |
| 741 | |
| 742 | /* Transmit the packet to SDK controller. */ |
| 743 | async_socket_write_rel(packet->sdkctl->as, &packet->header, packet->header.size, |
| 744 | _on_sdkctl_packet_send_io, packet, -1); |
| 745 | |
| 746 | T("SDKCtl %s: Packet %p size %d is being sent.", |
| 747 | packet->sdkctl->service_name, packet, packet->header.size); |
| 748 | } |
| 749 | |
| 750 | /******************************************************************************** |
| 751 | * SDKCtlDirectPacket implementation |
| 752 | ********************************************************************************/ |
| 753 | |
| 754 | SDKCtlDirectPacket* |
| 755 | sdkctl_direct_packet_new(SDKCtlSocket* sdkctl) |
| 756 | { |
| 757 | SDKCtlDirectPacket* const packet = |
| 758 | _sdkctl_socket_alloc_recycler(sdkctl, sizeof(SDKCtlDirectPacket)); |
| 759 | |
| 760 | packet->sdkctl = sdkctl; |
| 761 | packet->ref_count = 1; |
| 762 | |
| 763 | /* Refence SDKCTlSocket that owns this packet. */ |
| 764 | sdkctl_socket_reference(packet->sdkctl); |
| 765 | |
| 766 | T("SDKCtl %s: Direct packet %p is allocated.", sdkctl->service_name, packet); |
| 767 | |
| 768 | return packet; |
| 769 | } |
| 770 | |
| 771 | /* Frees a direct packet. */ |
| 772 | static void |
| 773 | _sdkctl_direct_packet_free(SDKCtlDirectPacket* packet) |
| 774 | { |
| 775 | SDKCtlSocket* const sdkctl = packet->sdkctl; |
| 776 | |
| 777 | /* Free allocated resources. */ |
| 778 | _sdkctl_socket_free_recycler(packet->sdkctl, packet); |
| 779 | |
| 780 | T("SDKCtl %s: Direct packet %p is freed.", sdkctl->service_name, packet); |
| 781 | |
| 782 | /* Release SDKCTlSocket that owned this packet. */ |
| 783 | sdkctl_socket_release(sdkctl); |
| 784 | } |
| 785 | |
| 786 | /* References a packet. */ |
| 787 | int |
| 788 | sdkctl_direct_packet_reference(SDKCtlDirectPacket* packet) |
| 789 | { |
| 790 | assert(packet->ref_count > 0); |
| 791 | packet->ref_count++; |
| 792 | return packet->ref_count; |
| 793 | } |
| 794 | |
| 795 | /* Releases a packet. */ |
| 796 | int |
| 797 | sdkctl_direct_packet_release(SDKCtlDirectPacket* packet) |
| 798 | { |
| 799 | assert(packet->ref_count > 0); |
| 800 | packet->ref_count--; |
| 801 | if (packet->ref_count == 0) { |
| 802 | /* Last reference has been dropped. Destroy this object. */ |
| 803 | _sdkctl_direct_packet_free(packet); |
| 804 | return 0; |
| 805 | } |
| 806 | return packet->ref_count; |
| 807 | } |
| 808 | |
| 809 | /* An I/O callback invoked on direct packet transmission. |
| 810 | * Param: |
| 811 | * io_opaque SDKCtlDirectPacket instance of the packet that's being sent with |
| 812 | * this I/O. |
| 813 | * asio - Write I/O descriptor. |
| 814 | * status - I/O status. |
| 815 | */ |
| 816 | static AsyncIOAction |
| 817 | _on_sdkctl_direct_packet_send_io(void* io_opaque, |
| 818 | AsyncSocketIO* asio, |
| 819 | AsyncIOState status) |
| 820 | { |
| 821 | SDKCtlDirectPacket* const packet = (SDKCtlDirectPacket*)io_opaque; |
| 822 | AsyncIOAction action = ASIO_ACTION_DONE; |
| 823 | |
| 824 | /* Reference the packet while we're in this callback. */ |
| 825 | sdkctl_direct_packet_reference(packet); |
| 826 | |
| 827 | /* Lets see what's going on with query transmission. */ |
| 828 | switch (status) { |
| 829 | case ASIO_STATE_SUCCEEDED: |
| 830 | /* Packet has been sent to the service. */ |
| 831 | T("SDKCtl %s: Direct packet %p transmission has succeeded.", |
| 832 | packet->sdkctl->service_name, packet); |
| 833 | packet->on_sent(packet->on_sent_opaque, packet, status); |
| 834 | break; |
| 835 | |
| 836 | case ASIO_STATE_CANCELLED: |
| 837 | T("SDKCtl %s: Direct packet %p is cancelled.", |
| 838 | packet->sdkctl->service_name, packet); |
| 839 | packet->on_sent(packet->on_sent_opaque, packet, status); |
| 840 | break; |
| 841 | |
| 842 | case ASIO_STATE_FAILED: |
| 843 | T("SDKCtl %s: Direct packet %p has failed: %d -> %s", |
| 844 | packet->sdkctl->service_name, packet, errno, strerror(errno)); |
| 845 | packet->on_sent(packet->on_sent_opaque, packet, status); |
| 846 | break; |
| 847 | |
| 848 | case ASIO_STATE_FINISHED: |
| 849 | /* Time to disassociate with the I/O. */ |
| 850 | sdkctl_direct_packet_release(packet); |
| 851 | break; |
| 852 | |
| 853 | default: |
| 854 | /* Transitional state. */ |
| 855 | break; |
| 856 | } |
| 857 | |
| 858 | sdkctl_direct_packet_release(packet); |
| 859 | |
| 860 | return action; |
| 861 | } |
| 862 | |
| 863 | void |
| 864 | sdkctl_direct_packet_send(SDKCtlDirectPacket* packet, |
| 865 | void* data, |
| 866 | on_sdkctl_direct_cb cb, |
| 867 | void* cb_opaque) |
| 868 | { |
| 869 | packet->packet = (SDKCtlPacketHeader*)data; |
| 870 | packet->on_sent = cb; |
| 871 | packet->on_sent_opaque = cb_opaque; |
| 872 | assert(packet->packet->signature == _sdkctl_packet_sig); |
| 873 | |
| 874 | /* Reference for I/O */ |
| 875 | sdkctl_direct_packet_reference(packet); |
| 876 | |
| 877 | /* Transmit the packet to SDK controller. */ |
| 878 | async_socket_write_rel(packet->sdkctl->as, packet->packet, packet->packet->size, |
| 879 | _on_sdkctl_direct_packet_send_io, packet, -1); |
| 880 | |
| 881 | T("SDKCtl %s: Direct packet %p size %d is being sent", |
| 882 | packet->sdkctl->service_name, packet, packet->packet->size); |
| 883 | } |
| 884 | |
| 885 | /******************************************************************************** |
| 886 | * SDKCtlMessage implementation |
| 887 | *******************************************************************************/ |
| 888 | |
| 889 | /* Alocates a message descriptor. */ |
| 890 | static SDKCtlMessage* |
| 891 | _sdkctl_message_new(SDKCtlSocket* sdkctl, uint32_t msg_size, int msg_type) |
| 892 | { |
| 893 | SDKCtlMessage* const msg = |
| 894 | (SDKCtlMessage*)_sdkctl_packet_new(sdkctl, |
| 895 | sizeof(SDKCtlMessageHeader) + msg_size, |
| 896 | SDKCTL_PACKET_MESSAGE); |
| 897 | msg->msg_type = msg_type; |
| 898 | |
| 899 | return msg; |
| 900 | } |
| 901 | |
| 902 | int |
| 903 | sdkctl_message_reference(SDKCtlMessage* msg) |
| 904 | { |
| 905 | return _sdkctl_packet_reference(&msg->packet); |
| 906 | } |
| 907 | |
| 908 | int |
| 909 | sdkctl_message_release(SDKCtlMessage* msg) |
| 910 | { |
| 911 | return _sdkctl_packet_release(&msg->packet); |
| 912 | } |
| 913 | |
| 914 | SDKCtlMessage* |
| 915 | sdkctl_message_send(SDKCtlSocket* sdkctl, |
| 916 | int msg_type, |
| 917 | const void* data, |
| 918 | uint32_t size) |
| 919 | { |
| 920 | SDKCtlMessage* const msg = _sdkctl_message_new(sdkctl, size, msg_type); |
| 921 | if (size != 0 && data != NULL) { |
| 922 | memcpy(msg + 1, data, size); |
| 923 | } |
| 924 | _sdkctl_packet_transmit(&msg->packet); |
| 925 | |
| 926 | return msg; |
| 927 | } |
| 928 | |
| 929 | int |
| 930 | sdkctl_message_get_header_size(void) |
| 931 | { |
| 932 | return sizeof(SDKCtlMessageHeader); |
| 933 | } |
| 934 | |
| 935 | void |
| 936 | sdkctl_init_message_header(void* msg, int msg_type, int msg_size) |
| 937 | { |
| 938 | SDKCtlMessageHeader* const msg_header = (SDKCtlMessageHeader*)msg; |
| 939 | |
| 940 | msg_header->packet.signature = _sdkctl_packet_sig; |
| 941 | msg_header->packet.size = sizeof(SDKCtlMessageHeader) + msg_size; |
| 942 | msg_header->packet.type = SDKCTL_PACKET_MESSAGE; |
| 943 | msg_header->msg_type = msg_type; |
| 944 | } |
| 945 | |
Vladimir Chtchetkine | c8aa2c5 | 2012-04-05 16:22:55 -0700 | [diff] [blame] | 946 | /******************************************************************************** |
| 947 | * SDKCtlQuery implementation |
| 948 | *******************************************************************************/ |
| 949 | |
| 950 | /* Frees query descriptor. */ |
| 951 | static void |
| 952 | _sdkctl_query_free(SDKCtlQuery* query) |
| 953 | { |
| 954 | if (query != NULL) { |
| 955 | SDKCtlSocket* const sdkctl = query->sdkctl; |
Vladimir Chtchetkine | c8aa2c5 | 2012-04-05 16:22:55 -0700 | [diff] [blame] | 956 | if (query->internal_resp_buffer != NULL && |
| 957 | (query->response_buffer == NULL || |
| 958 | query->response_buffer == &query->internal_resp_buffer)) { |
Vladimir Chtchetkine | 7136b05 | 2012-04-10 13:39:24 -0700 | [diff] [blame] | 959 | /* This query used its internal buffer to receive the response. |
| 960 | * Free it. */ |
Vladimir Chtchetkine | c8aa2c5 | 2012-04-05 16:22:55 -0700 | [diff] [blame] | 961 | free(query->internal_resp_buffer); |
| 962 | } |
| 963 | |
| 964 | loopTimer_done(query->timer); |
Vladimir Chtchetkine | 7136b05 | 2012-04-10 13:39:24 -0700 | [diff] [blame] | 965 | |
| 966 | /* Recyle the descriptor. */ |
Vladimir Chtchetkine | c8aa2c5 | 2012-04-05 16:22:55 -0700 | [diff] [blame] | 967 | _sdkctl_socket_free_recycler(sdkctl, query); |
| 968 | |
Vladimir Chtchetkine | 7136b05 | 2012-04-10 13:39:24 -0700 | [diff] [blame] | 969 | T("SDKCtl %s: Query %p is freed.", sdkctl->service_name, query); |
| 970 | |
Vladimir Chtchetkine | c8aa2c5 | 2012-04-05 16:22:55 -0700 | [diff] [blame] | 971 | /* Release socket that owned this query. */ |
| 972 | sdkctl_socket_release(sdkctl); |
| 973 | } |
| 974 | } |
| 975 | |
| 976 | /* Cancels timeout for the query. |
| 977 | * |
| 978 | * For the simplicity of implementation, the dispatcher will cancel query timer |
| 979 | * when query response data begins to flow in. If we let the timer to expire at |
| 980 | * that stage, we will end up with data flowing in without real place to |
| 981 | * accomodate it. |
| 982 | */ |
| 983 | static void |
| 984 | _sdkctl_query_cancel_timeout(SDKCtlQuery* query) |
| 985 | { |
| 986 | loopTimer_stop(query->timer); |
| 987 | |
Vladimir Chtchetkine | 7136b05 | 2012-04-10 13:39:24 -0700 | [diff] [blame] | 988 | T("SDKCtl %s: Query %p ID %d deadline %lld is cancelled.", |
| 989 | query->sdkctl->service_name, query, query->header.query_id, query->deadline); |
Vladimir Chtchetkine | c8aa2c5 | 2012-04-05 16:22:55 -0700 | [diff] [blame] | 990 | } |
| 991 | |
| 992 | /* |
| 993 | * Query I/O callbacks. |
| 994 | */ |
| 995 | |
| 996 | /* Callback that is invoked by the I/O dispatcher when query is successfuly |
| 997 | * completed (i.e. response to the query is received). |
| 998 | */ |
| 999 | static void |
| 1000 | _on_sdkctl_query_completed(SDKCtlQuery* query) |
| 1001 | { |
| 1002 | T("SDKCtl %s: Query %p ID %d is completed.", |
| 1003 | query->sdkctl->service_name, query, query->header.query_id); |
| 1004 | |
| 1005 | /* Cancel deadline, and inform the client about query completion. */ |
| 1006 | _sdkctl_query_cancel_timeout(query); |
| 1007 | query->query_cb(query->query_opaque, query, ASIO_STATE_SUCCEEDED); |
| 1008 | } |
| 1009 | |
| 1010 | /* A callback that is invoked on query cancellation. */ |
| 1011 | static void |
| 1012 | _on_sdkctl_query_cancelled(SDKCtlQuery* query) |
| 1013 | { |
| 1014 | /* |
| 1015 | * Query cancellation means that SDK controller is disconnected. In turn, |
| 1016 | * this means that SDK controller socket will handle disconnection in its |
| 1017 | * connection callback. So, at this point all we need to do here is to inform |
Vladimir Chtchetkine | 7136b05 | 2012-04-10 13:39:24 -0700 | [diff] [blame] | 1018 | * the client about query cancellation. |
Vladimir Chtchetkine | c8aa2c5 | 2012-04-05 16:22:55 -0700 | [diff] [blame] | 1019 | */ |
| 1020 | |
| 1021 | /* Cancel deadline, and inform the client about query cancellation. */ |
| 1022 | _sdkctl_query_cancel_timeout(query); |
| 1023 | query->query_cb(query->query_opaque, query, ASIO_STATE_CANCELLED); |
| 1024 | } |
| 1025 | |
| 1026 | /* A timer callback that is invoked on query timeout. |
| 1027 | * Param: |
| 1028 | * opaque - SDKCtlQuery instance. |
| 1029 | */ |
| 1030 | static void |
| 1031 | _on_skdctl_query_timeout(void* opaque) |
| 1032 | { |
| 1033 | SDKCtlQuery* const query = (SDKCtlQuery*)opaque; |
| 1034 | |
| 1035 | D("SDKCtl %s: Query %p ID %d with deadline %lld has timed out at %lld", |
| 1036 | query->sdkctl->service_name, query, query->header.query_id, |
| 1037 | query->deadline, async_socket_deadline(query->sdkctl->as, 0)); |
| 1038 | |
| 1039 | /* Reference the query while we're in this callback. */ |
| 1040 | sdkctl_query_reference(query); |
| 1041 | |
| 1042 | /* Inform the client about deadline expiration. Note that client may |
| 1043 | * extend the deadline, and retry the query. */ |
| 1044 | const AsyncIOAction action = |
| 1045 | query->query_cb(query->query_opaque, query, ASIO_STATE_TIMED_OUT); |
| 1046 | |
| 1047 | /* For actions other than retry we will destroy the query. */ |
| 1048 | if (action != ASIO_ACTION_RETRY) { |
| 1049 | _sdkctl_socket_remove_query(query); |
| 1050 | } |
| 1051 | |
| 1052 | sdkctl_query_release(query); |
| 1053 | } |
| 1054 | |
Vladimir Chtchetkine | 7136b05 | 2012-04-10 13:39:24 -0700 | [diff] [blame] | 1055 | /* A callback that is invoked when query has been sent to the SDK controller. */ |
Vladimir Chtchetkine | c8aa2c5 | 2012-04-05 16:22:55 -0700 | [diff] [blame] | 1056 | static void |
| 1057 | _on_sdkctl_query_sent(SDKCtlQuery* query) |
| 1058 | { |
Vladimir Chtchetkine | 7136b05 | 2012-04-10 13:39:24 -0700 | [diff] [blame] | 1059 | T("SDKCtl %s: Sent %d bytes of query %p ID %d of type %d", |
Vladimir Chtchetkine | c8aa2c5 | 2012-04-05 16:22:55 -0700 | [diff] [blame] | 1060 | query->sdkctl->service_name, query->header.packet.size, query, |
| 1061 | query->header.query_id, query->header.query_type); |
| 1062 | |
| 1063 | /* Inform the client about the event. */ |
| 1064 | query->query_cb(query->query_opaque, query, ASIO_STATE_CONTINUES); |
| 1065 | |
| 1066 | /* Set a timer to expire at query's deadline, and let the response to come |
| 1067 | * through the dispatcher loop. */ |
| 1068 | loopTimer_startAbsolute(query->timer, query->deadline); |
| 1069 | } |
| 1070 | |
| 1071 | /* An I/O callback invoked on query transmission. |
| 1072 | * Param: |
| 1073 | * io_opaque SDKCtlQuery instance of the query that's being sent with this I/O. |
| 1074 | * asio - Write I/O descriptor. |
| 1075 | * status - I/O status. |
| 1076 | */ |
| 1077 | static AsyncIOAction |
| 1078 | _on_sdkctl_query_send_io(void* io_opaque, |
| 1079 | AsyncSocketIO* asio, |
| 1080 | AsyncIOState status) |
| 1081 | { |
| 1082 | SDKCtlQuery* const query = (SDKCtlQuery*)io_opaque; |
| 1083 | AsyncIOAction action = ASIO_ACTION_DONE; |
| 1084 | |
| 1085 | /* Reference the query while we're in this callback. */ |
| 1086 | sdkctl_query_reference(query); |
| 1087 | |
Vladimir Chtchetkine | c8aa2c5 | 2012-04-05 16:22:55 -0700 | [diff] [blame] | 1088 | /* Lets see what's going on with query transmission. */ |
| 1089 | switch (status) { |
Vladimir Chtchetkine | 7136b05 | 2012-04-10 13:39:24 -0700 | [diff] [blame] | 1090 | case ASIO_STATE_SUCCEEDED: |
| 1091 | /* Query has been sent to the service. */ |
| 1092 | _on_sdkctl_query_sent(query); |
| 1093 | break; |
| 1094 | |
Vladimir Chtchetkine | c8aa2c5 | 2012-04-05 16:22:55 -0700 | [diff] [blame] | 1095 | case ASIO_STATE_CANCELLED: |
Vladimir Chtchetkine | 7136b05 | 2012-04-10 13:39:24 -0700 | [diff] [blame] | 1096 | T("SDKCtl %s: Query %p ID %d is cancelled in transmission.", |
| 1097 | query->sdkctl->service_name, query, query->header.query_id); |
Vladimir Chtchetkine | c8aa2c5 | 2012-04-05 16:22:55 -0700 | [diff] [blame] | 1098 | /* Remove the query from the list of active queries. */ |
| 1099 | _sdkctl_socket_remove_query(query); |
| 1100 | _on_sdkctl_query_cancelled(query); |
| 1101 | break; |
| 1102 | |
| 1103 | case ASIO_STATE_TIMED_OUT: |
Vladimir Chtchetkine | 7136b05 | 2012-04-10 13:39:24 -0700 | [diff] [blame] | 1104 | D("SDKCtl %s: Query %p ID %d with deadline %lld has timed out in transmission at %lld", |
Vladimir Chtchetkine | c8aa2c5 | 2012-04-05 16:22:55 -0700 | [diff] [blame] | 1105 | query->sdkctl->service_name, query, query->header.query_id, |
Vladimir Chtchetkine | 7136b05 | 2012-04-10 13:39:24 -0700 | [diff] [blame] | 1106 | query->deadline, async_socket_deadline(query->sdkctl->as, 0)); |
Vladimir Chtchetkine | c8aa2c5 | 2012-04-05 16:22:55 -0700 | [diff] [blame] | 1107 | /* Invoke query's callback. */ |
| 1108 | action = query->query_cb(query->query_opaque, query, status); |
| 1109 | /* For actions other than retry we need to stop the query. */ |
| 1110 | if (action != ASIO_ACTION_RETRY) { |
| 1111 | _sdkctl_socket_remove_query(query); |
| 1112 | } |
| 1113 | break; |
| 1114 | |
| 1115 | case ASIO_STATE_FAILED: |
Vladimir Chtchetkine | 7136b05 | 2012-04-10 13:39:24 -0700 | [diff] [blame] | 1116 | T("SDKCtl %s: Query %p ID %d failed in transmission: %d -> %s", |
Vladimir Chtchetkine | c8aa2c5 | 2012-04-05 16:22:55 -0700 | [diff] [blame] | 1117 | query->sdkctl->service_name, query, query->header.query_id, |
Vladimir Chtchetkine | c8aa2c5 | 2012-04-05 16:22:55 -0700 | [diff] [blame] | 1118 | errno, strerror(errno)); |
| 1119 | /* Invoke query's callback. Note that we will let the client to |
| 1120 | * decide what to do on I/O failure. */ |
| 1121 | action = query->query_cb(query->query_opaque, query, status); |
| 1122 | /* For actions other than retry we need to stop the query. */ |
| 1123 | if (action != ASIO_ACTION_RETRY) { |
| 1124 | _sdkctl_socket_remove_query(query); |
| 1125 | } |
| 1126 | break; |
| 1127 | |
| 1128 | case ASIO_STATE_FINISHED: |
| 1129 | /* Time to disassociate with the I/O. */ |
| 1130 | sdkctl_query_release(query); |
| 1131 | break; |
| 1132 | |
| 1133 | default: |
| 1134 | /* Transitional state. */ |
| 1135 | break; |
| 1136 | } |
| 1137 | |
| 1138 | sdkctl_query_release(query); |
| 1139 | |
| 1140 | return action; |
| 1141 | } |
| 1142 | |
| 1143 | /******************************************************************************** |
| 1144 | * SDKCtlQuery public API implementation |
| 1145 | ********************************************************************************/ |
| 1146 | |
| 1147 | SDKCtlQuery* |
| 1148 | sdkctl_query_new(SDKCtlSocket* sdkctl, int query_type, uint32_t in_data_size) |
| 1149 | { |
Vladimir Chtchetkine | 7136b05 | 2012-04-10 13:39:24 -0700 | [diff] [blame] | 1150 | SDKCtlQuery* const query = |
| 1151 | _sdkctl_socket_alloc_recycler(sdkctl, sizeof(SDKCtlQuery) + in_data_size); |
| 1152 | query->next = NULL; |
| 1153 | query->sdkctl = sdkctl; |
| 1154 | query->response_buffer = NULL; |
| 1155 | query->response_size = NULL; |
| 1156 | query->internal_resp_buffer = NULL; |
| 1157 | query->internal_resp_size = 0; |
| 1158 | query->query_cb = NULL; |
| 1159 | query->query_opaque = NULL; |
| 1160 | query->deadline = DURATION_INFINITE; |
| 1161 | query->ref_count = 1; |
| 1162 | query->header.packet.signature = _sdkctl_packet_sig; |
| 1163 | query->header.packet.size = sizeof(SDKCtlQueryHeader) + in_data_size; |
| 1164 | query->header.packet.type = SDKCTL_PACKET_QUERY; |
| 1165 | query->header.query_id = _sdkctl_socket_next_query_id(sdkctl); |
| 1166 | query->header.query_type = query_type; |
Vladimir Chtchetkine | c8aa2c5 | 2012-04-05 16:22:55 -0700 | [diff] [blame] | 1167 | |
| 1168 | /* Initialize timer to fire up on query deadline expiration. */ |
| 1169 | loopTimer_init(query->timer, sdkctl->looper, _on_skdctl_query_timeout, query); |
| 1170 | |
| 1171 | /* Reference socket that owns this query. */ |
| 1172 | sdkctl_socket_reference(sdkctl); |
| 1173 | |
| 1174 | T("SDKCtl %s: Query %p ID %d type %d is created for %d bytes of data.", |
| 1175 | query->sdkctl->service_name, query, query->header.query_id, |
| 1176 | query_type, in_data_size); |
| 1177 | |
| 1178 | return query; |
| 1179 | } |
| 1180 | |
| 1181 | SDKCtlQuery* |
| 1182 | sdkctl_query_new_ex(SDKCtlSocket* sdkctl, |
| 1183 | int query_type, |
| 1184 | uint32_t in_data_size, |
| 1185 | const void* in_data, |
| 1186 | void** response_buffer, |
| 1187 | uint32_t* response_size, |
| 1188 | on_sdkctl_query_cb query_cb, |
| 1189 | void* query_opaque) |
| 1190 | { |
| 1191 | SDKCtlQuery* const query = sdkctl_query_new(sdkctl, query_type, in_data_size); |
| 1192 | |
Vladimir Chtchetkine | 7136b05 | 2012-04-10 13:39:24 -0700 | [diff] [blame] | 1193 | query->response_buffer = response_buffer; |
Vladimir Chtchetkine | c8aa2c5 | 2012-04-05 16:22:55 -0700 | [diff] [blame] | 1194 | if (query->response_buffer == NULL) { |
| 1195 | /* Creator didn't supply a buffer. Use internal one instead. */ |
| 1196 | query->response_buffer = &query->internal_resp_buffer; |
Vladimir Chtchetkine | c8aa2c5 | 2012-04-05 16:22:55 -0700 | [diff] [blame] | 1197 | } |
Vladimir Chtchetkine | 7136b05 | 2012-04-10 13:39:24 -0700 | [diff] [blame] | 1198 | query->response_size = response_size; |
Vladimir Chtchetkine | c8aa2c5 | 2012-04-05 16:22:55 -0700 | [diff] [blame] | 1199 | if (query->response_size == NULL) { |
| 1200 | /* Creator didn't supply a buffer for response size. Use internal one |
| 1201 | * instead. */ |
| 1202 | query->response_size = &query->internal_resp_size; |
Vladimir Chtchetkine | c8aa2c5 | 2012-04-05 16:22:55 -0700 | [diff] [blame] | 1203 | } |
Vladimir Chtchetkine | 7136b05 | 2012-04-10 13:39:24 -0700 | [diff] [blame] | 1204 | query->query_cb = query_cb; |
| 1205 | query->query_opaque = query_opaque; |
Vladimir Chtchetkine | c8aa2c5 | 2012-04-05 16:22:55 -0700 | [diff] [blame] | 1206 | /* Init query's input buffer. */ |
| 1207 | if (in_data_size != 0 && in_data != NULL) { |
| 1208 | memcpy(query + 1, in_data, in_data_size); |
| 1209 | } |
| 1210 | |
| 1211 | return query; |
| 1212 | } |
| 1213 | |
| 1214 | void |
| 1215 | sdkctl_query_send(SDKCtlQuery* query, int to) |
| 1216 | { |
| 1217 | SDKCtlSocket* const sdkctl = query->sdkctl; |
| 1218 | |
| 1219 | /* Initialize the deadline. */ |
| 1220 | query->deadline = async_socket_deadline(query->sdkctl->as, to); |
| 1221 | |
| 1222 | /* List the query in the list of active queries. */ |
| 1223 | _sdkctl_socket_add_query(query); |
| 1224 | |
| 1225 | /* Reference query associated with write I/O. */ |
| 1226 | sdkctl_query_reference(query); |
| 1227 | |
Vladimir Chtchetkine | 7136b05 | 2012-04-10 13:39:24 -0700 | [diff] [blame] | 1228 | assert(query->header.packet.signature == _sdkctl_packet_sig); |
Vladimir Chtchetkine | c8aa2c5 | 2012-04-05 16:22:55 -0700 | [diff] [blame] | 1229 | /* Transmit the query to SDK controller. */ |
| 1230 | async_socket_write_abs(sdkctl->as, &query->header, query->header.packet.size, |
| 1231 | _on_sdkctl_query_send_io, query, query->deadline); |
| 1232 | |
Vladimir Chtchetkine | 7136b05 | 2012-04-10 13:39:24 -0700 | [diff] [blame] | 1233 | T("SDKCtl %s: Query %p ID %d type %d is being sent with deadline at %lld", |
Vladimir Chtchetkine | c8aa2c5 | 2012-04-05 16:22:55 -0700 | [diff] [blame] | 1234 | query->sdkctl->service_name, query, query->header.query_id, |
| 1235 | query->header.query_type, query->deadline); |
| 1236 | } |
| 1237 | |
| 1238 | SDKCtlQuery* |
| 1239 | sdkctl_query_build_and_send(SDKCtlSocket* sdkctl, |
| 1240 | int query_type, |
| 1241 | uint32_t in_data_size, |
| 1242 | const void* in_data, |
| 1243 | void** response_buffer, |
| 1244 | uint32_t* response_size, |
| 1245 | on_sdkctl_query_cb query_cb, |
| 1246 | void* query_opaque, |
| 1247 | int to) |
| 1248 | { |
| 1249 | SDKCtlQuery* const query = |
| 1250 | sdkctl_query_new_ex(sdkctl, query_type, in_data_size, in_data, |
| 1251 | response_buffer, response_size, query_cb, |
| 1252 | query_opaque); |
| 1253 | sdkctl_query_send(query, to); |
| 1254 | return query; |
| 1255 | } |
| 1256 | |
| 1257 | int |
| 1258 | sdkctl_query_reference(SDKCtlQuery* query) |
| 1259 | { |
| 1260 | assert(query->ref_count > 0); |
| 1261 | query->ref_count++; |
| 1262 | return query->ref_count; |
| 1263 | } |
| 1264 | |
| 1265 | int |
| 1266 | sdkctl_query_release(SDKCtlQuery* query) |
| 1267 | { |
| 1268 | assert(query->ref_count > 0); |
| 1269 | query->ref_count--; |
| 1270 | if (query->ref_count == 0) { |
| 1271 | /* Last reference has been dropped. Destroy this object. */ |
| 1272 | _sdkctl_query_free(query); |
| 1273 | return 0; |
| 1274 | } |
| 1275 | return query->ref_count; |
| 1276 | } |
| 1277 | |
Vladimir Chtchetkine | 7136b05 | 2012-04-10 13:39:24 -0700 | [diff] [blame] | 1278 | void* |
| 1279 | sdkctl_query_get_buffer_in(SDKCtlQuery* query) |
| 1280 | { |
| 1281 | /* Query buffer starts right after the header. */ |
| 1282 | return query + 1; |
| 1283 | } |
| 1284 | |
| 1285 | void* |
| 1286 | sdkctl_query_get_buffer_out(SDKCtlQuery* query) |
| 1287 | { |
| 1288 | return query->response_buffer != NULL ? *query->response_buffer : |
| 1289 | query->internal_resp_buffer; |
| 1290 | } |
| 1291 | |
Vladimir Chtchetkine | c8aa2c5 | 2012-04-05 16:22:55 -0700 | [diff] [blame] | 1292 | /******************************************************************************** |
| 1293 | * SDKCtlPacket implementation |
| 1294 | *******************************************************************************/ |
| 1295 | |
Vladimir Chtchetkine | 7136b05 | 2012-04-10 13:39:24 -0700 | [diff] [blame] | 1296 | /* A packet has been received from SDK controller. |
| 1297 | * Note that we expect the packet to be a message, since queries, and query |
| 1298 | * replies are handled separately. */ |
Vladimir Chtchetkine | c8aa2c5 | 2012-04-05 16:22:55 -0700 | [diff] [blame] | 1299 | static void |
| 1300 | _on_sdkctl_packet_received(SDKCtlSocket* sdkctl, SDKCtlPacket* packet) |
| 1301 | { |
| 1302 | T("SDKCtl %s: Received packet size: %d, type: %d", |
| 1303 | sdkctl->service_name, packet->header.size, packet->header.type); |
| 1304 | |
Vladimir Chtchetkine | 7136b05 | 2012-04-10 13:39:24 -0700 | [diff] [blame] | 1305 | assert(packet->header.signature == _sdkctl_packet_sig); |
| 1306 | if (packet->header.type == SDKCTL_PACKET_MESSAGE) { |
| 1307 | SDKCtlMessage* const msg = (SDKCtlMessage*)packet; |
| 1308 | /* Lets see if this is an internal protocol message. */ |
| 1309 | switch (msg->msg_type) { |
| 1310 | case SDKCTL_MSG_PORT_CONNECTED: |
| 1311 | sdkctl->port_status = SDKCTL_PORT_CONNECTED; |
| 1312 | sdkctl->on_port_connection(sdkctl->opaque, sdkctl, |
| 1313 | SDKCTL_PORT_CONNECTED); |
| 1314 | break; |
| 1315 | |
| 1316 | case SDKCTL_MSG_PORT_DISCONNECTED: |
| 1317 | sdkctl->port_status = SDKCTL_PORT_DISCONNECTED; |
| 1318 | sdkctl->on_port_connection(sdkctl->opaque, sdkctl, |
| 1319 | SDKCTL_PORT_DISCONNECTED); |
| 1320 | break; |
| 1321 | |
| 1322 | case SDKCTL_MSG_PORT_ENABLED: |
| 1323 | sdkctl->port_status = SDKCTL_PORT_ENABLED; |
| 1324 | sdkctl->on_port_connection(sdkctl->opaque, sdkctl, |
| 1325 | SDKCTL_PORT_ENABLED); |
| 1326 | break; |
| 1327 | |
| 1328 | case SDKCTL_MSG_PORT_DISABLED: |
| 1329 | sdkctl->port_status = SDKCTL_PORT_DISABLED; |
| 1330 | sdkctl->on_port_connection(sdkctl->opaque, sdkctl, |
| 1331 | SDKCTL_PORT_DISABLED); |
| 1332 | break; |
| 1333 | |
| 1334 | default: |
| 1335 | /* This is a higher-level message. Dispatch the message to the |
| 1336 | * client. */ |
| 1337 | sdkctl->on_message(sdkctl->opaque, sdkctl, msg, msg->msg_type, msg + 1, |
| 1338 | packet->header.size - sizeof(SDKCtlMessageHeader)); |
| 1339 | break; |
| 1340 | } |
| 1341 | } else { |
| 1342 | E("SDKCtl %s: Received unknown packet type %d size %d", |
| 1343 | sdkctl->service_name, packet->header.type, packet->header.size); |
| 1344 | } |
Vladimir Chtchetkine | c8aa2c5 | 2012-04-05 16:22:55 -0700 | [diff] [blame] | 1345 | } |
| 1346 | |
| 1347 | /******************************************************************************** |
| 1348 | * SDKCtlIODispatcher implementation |
| 1349 | *******************************************************************************/ |
| 1350 | |
| 1351 | /* An I/O callback invoked when data gets received from the socket. |
| 1352 | * Param: |
| 1353 | * io_opaque SDKCtlIODispatcher instance associated with the reader. |
| 1354 | * asio - Read I/O descriptor. |
| 1355 | * status - I/O status. |
| 1356 | */ |
| 1357 | static AsyncIOAction _on_sdkctl_io_dispatcher_io(void* io_opaque, |
| 1358 | AsyncSocketIO* asio, |
| 1359 | AsyncIOState status); |
| 1360 | |
| 1361 | /* Starts I/O dispatcher for SDK controller socket. */ |
| 1362 | static void |
| 1363 | _sdkctl_io_dispatcher_start(SDKCtlSocket* sdkctl) { |
| 1364 | SDKCtlIODispatcher* const dispatcher = &sdkctl->io_dispatcher; |
| 1365 | |
| 1366 | dispatcher->state = SDKCTL_IODISP_EXPECT_HEADER; |
| 1367 | dispatcher->sdkctl = sdkctl; |
| 1368 | dispatcher->packet = NULL; |
| 1369 | dispatcher->current_query = NULL; |
| 1370 | |
| 1371 | /* Register a packet header reader with the socket. */ |
Vladimir Chtchetkine | 7136b05 | 2012-04-10 13:39:24 -0700 | [diff] [blame] | 1372 | async_socket_read_rel(dispatcher->sdkctl->as, &dispatcher->packet_header, |
Vladimir Chtchetkine | c8aa2c5 | 2012-04-05 16:22:55 -0700 | [diff] [blame] | 1373 | sizeof(SDKCtlPacketHeader), _on_sdkctl_io_dispatcher_io, |
| 1374 | dispatcher, -1); |
| 1375 | } |
| 1376 | |
| 1377 | /* Resets I/O dispatcher for SDK controller socket. */ |
| 1378 | static void |
| 1379 | _sdkctl_io_dispatcher_reset(SDKCtlSocket* sdkctl) { |
| 1380 | SDKCtlIODispatcher* const dispatcher = &sdkctl->io_dispatcher; |
| 1381 | |
| 1382 | /* Cancel current query. */ |
| 1383 | if (dispatcher->current_query != NULL) { |
| 1384 | SDKCtlQuery* const query = dispatcher->current_query; |
| 1385 | dispatcher->current_query = NULL; |
| 1386 | _on_sdkctl_query_cancelled(query); |
| 1387 | sdkctl_query_release(query); |
| 1388 | } |
| 1389 | |
| 1390 | /* Free packet data buffer. */ |
| 1391 | if (dispatcher->packet != NULL) { |
Vladimir Chtchetkine | 7136b05 | 2012-04-10 13:39:24 -0700 | [diff] [blame] | 1392 | _sdkctl_packet_release(dispatcher->packet); |
Vladimir Chtchetkine | c8aa2c5 | 2012-04-05 16:22:55 -0700 | [diff] [blame] | 1393 | dispatcher->packet = NULL; |
| 1394 | } |
| 1395 | |
| 1396 | /* Reset dispatcher state. */ |
| 1397 | dispatcher->state = SDKCTL_IODISP_EXPECT_HEADER; |
Vladimir Chtchetkine | 7136b05 | 2012-04-10 13:39:24 -0700 | [diff] [blame] | 1398 | |
| 1399 | T("SDKCtl %s: I/O Dispatcher is reset", sdkctl->service_name); |
Vladimir Chtchetkine | c8aa2c5 | 2012-04-05 16:22:55 -0700 | [diff] [blame] | 1400 | } |
| 1401 | |
| 1402 | /* |
| 1403 | * I/O dispatcher callbacks. |
| 1404 | */ |
| 1405 | |
| 1406 | /* A callback that is invoked when a failure occurred while dispatcher was |
| 1407 | * reading data from the socket. |
| 1408 | */ |
| 1409 | static void |
| 1410 | _on_io_dispatcher_io_failure(SDKCtlIODispatcher* dispatcher, |
| 1411 | AsyncSocketIO* asio) |
| 1412 | { |
| 1413 | SDKCtlSocket* const sdkctl = dispatcher->sdkctl; |
| 1414 | |
| 1415 | D("SDKCtl %s: Dispatcher I/O failure: %d -> %s", |
| 1416 | sdkctl->service_name, errno, strerror(errno)); |
| 1417 | |
| 1418 | /* We treat all I/O failures same way we treat disconnection. Just cancel |
| 1419 | * everything, disconnect, and let the client to decide what to do next. */ |
| 1420 | sdkctl_socket_disconnect(sdkctl); |
| 1421 | |
| 1422 | /* Report disconnection to the client, and let it restore connection in this |
| 1423 | * callback. */ |
Vladimir Chtchetkine | 7136b05 | 2012-04-10 13:39:24 -0700 | [diff] [blame] | 1424 | sdkctl->on_socket_connection(sdkctl->opaque, sdkctl, ASIO_STATE_FAILED); |
Vladimir Chtchetkine | c8aa2c5 | 2012-04-05 16:22:55 -0700 | [diff] [blame] | 1425 | } |
| 1426 | |
| 1427 | /* A callback that is invoked when dispatcher's reader has been cancelled. */ |
| 1428 | static void |
| 1429 | _on_io_dispatcher_io_cancelled(SDKCtlIODispatcher* dispatcher, |
| 1430 | AsyncSocketIO* asio) |
| 1431 | { |
| 1432 | T("SDKCtl %s: Dispatcher I/O cancelled.", dispatcher->sdkctl->service_name); |
| 1433 | |
| 1434 | /* If we're in the middle of receiving query reply we need to cancel the |
| 1435 | * query. */ |
| 1436 | if (dispatcher->current_query != NULL) { |
| 1437 | SDKCtlQuery* const query = dispatcher->current_query; |
| 1438 | dispatcher->current_query = NULL; |
| 1439 | _on_sdkctl_query_cancelled(query); |
| 1440 | sdkctl_query_release(query); |
| 1441 | } |
| 1442 | |
| 1443 | /* Discard packet data we've received so far. */ |
| 1444 | if (dispatcher->packet != NULL) { |
Vladimir Chtchetkine | 7136b05 | 2012-04-10 13:39:24 -0700 | [diff] [blame] | 1445 | _sdkctl_packet_release(dispatcher->packet); |
Vladimir Chtchetkine | c8aa2c5 | 2012-04-05 16:22:55 -0700 | [diff] [blame] | 1446 | dispatcher->packet = NULL; |
| 1447 | } |
| 1448 | } |
| 1449 | |
| 1450 | /* A generic packet header has been received by I/O dispatcher. */ |
| 1451 | static AsyncIOAction |
| 1452 | _on_io_dispatcher_packet_header(SDKCtlIODispatcher* dispatcher, |
| 1453 | AsyncSocketIO* asio) |
| 1454 | { |
| 1455 | SDKCtlSocket* const sdkctl = dispatcher->sdkctl; |
| 1456 | |
| 1457 | T("SDKCtl %s: Packet header type %d, size %d is received.", |
Vladimir Chtchetkine | 7136b05 | 2012-04-10 13:39:24 -0700 | [diff] [blame] | 1458 | dispatcher->sdkctl->service_name, dispatcher->packet_header.type, |
| 1459 | dispatcher->packet_header.size); |
| 1460 | |
| 1461 | /* Make sure we have a valid packet header. */ |
| 1462 | if (dispatcher->packet_header.signature != _sdkctl_packet_sig) { |
| 1463 | E("SDKCtl %s: Invalid packet signature %x for packet type %d, size %d", |
| 1464 | sdkctl->service_name, dispatcher->packet_header.signature, |
| 1465 | dispatcher->packet_header.type, dispatcher->packet_header.size); |
| 1466 | /* This is a protocol failure. Treat it as I/O failure: disconnect, and |
| 1467 | * let the client to decide what to do next. */ |
| 1468 | errno = EINVAL; |
| 1469 | _on_io_dispatcher_io_failure(dispatcher, asio); |
| 1470 | return ASIO_ACTION_DONE; |
| 1471 | } |
Vladimir Chtchetkine | c8aa2c5 | 2012-04-05 16:22:55 -0700 | [diff] [blame] | 1472 | |
| 1473 | /* Here we have three choices for the packet, that define the rest of |
| 1474 | * the data that follow it: |
| 1475 | * - Regular packet, |
| 1476 | * - Response to a query that has been sent to SDK controller, |
| 1477 | * - A query from SDK controller. |
| 1478 | * Update the state accordingly, and initiate reading of the |
| 1479 | * remaining of the packet. |
| 1480 | */ |
Vladimir Chtchetkine | 7136b05 | 2012-04-10 13:39:24 -0700 | [diff] [blame] | 1481 | if (dispatcher->packet_header.type == SDKCTL_PACKET_QUERY_RESPONSE) { |
Vladimir Chtchetkine | c8aa2c5 | 2012-04-05 16:22:55 -0700 | [diff] [blame] | 1482 | /* This is a response to the query. Before receiving response data we |
| 1483 | * need to locate the relevant query, and use its response buffer to read |
| 1484 | * the data. For that we need to obtain query ID firts. So, initiate |
| 1485 | * reading of the remaining part of SDKCtlQueryReplyHeader. */ |
| 1486 | dispatcher->state = SDKCTL_IODISP_EXPECT_QUERY_REPLY_HEADER; |
| 1487 | async_socket_read_rel(sdkctl->as, &dispatcher->query_reply_header.query_id, |
| 1488 | sizeof(SDKCtlQueryReplyHeader) - sizeof(SDKCtlPacketHeader), |
| 1489 | _on_sdkctl_io_dispatcher_io, dispatcher, -1); |
| 1490 | } else { |
| 1491 | /* For regular packets, as well as queries, we simply allocate buffer, |
| 1492 | * that fits the entire packet, and read the remainder of the data in |
| 1493 | * there. */ |
| 1494 | dispatcher->state = SDKCTL_IODISP_EXPECT_DATA; |
| 1495 | dispatcher->packet = |
Vladimir Chtchetkine | 7136b05 | 2012-04-10 13:39:24 -0700 | [diff] [blame] | 1496 | _sdkctl_packet_new(sdkctl, dispatcher->packet_header.size, |
| 1497 | dispatcher->packet_header.type); |
Vladimir Chtchetkine | c8aa2c5 | 2012-04-05 16:22:55 -0700 | [diff] [blame] | 1498 | /* Initiate reading of the packet data. */ |
| 1499 | async_socket_read_rel(sdkctl->as, dispatcher->packet + 1, |
Vladimir Chtchetkine | 7136b05 | 2012-04-10 13:39:24 -0700 | [diff] [blame] | 1500 | dispatcher->packet_header.size - sizeof(SDKCtlPacketHeader), |
Vladimir Chtchetkine | c8aa2c5 | 2012-04-05 16:22:55 -0700 | [diff] [blame] | 1501 | _on_sdkctl_io_dispatcher_io, dispatcher, -1); |
| 1502 | } |
| 1503 | |
| 1504 | return ASIO_ACTION_DONE; |
| 1505 | } |
| 1506 | |
| 1507 | /* A generic packet has been received by I/O dispatcher. */ |
| 1508 | static AsyncIOAction |
| 1509 | _on_io_dispatcher_packet(SDKCtlIODispatcher* dispatcher, AsyncSocketIO* asio) |
| 1510 | { |
| 1511 | SDKCtlSocket* const sdkctl = dispatcher->sdkctl; |
Vladimir Chtchetkine | 7136b05 | 2012-04-10 13:39:24 -0700 | [diff] [blame] | 1512 | SDKCtlPacket* const packet = dispatcher->packet; |
| 1513 | dispatcher->packet = NULL; |
Vladimir Chtchetkine | c8aa2c5 | 2012-04-05 16:22:55 -0700 | [diff] [blame] | 1514 | |
| 1515 | T("SDKCtl %s: Packet type %d, size %d is received.", |
Vladimir Chtchetkine | 7136b05 | 2012-04-10 13:39:24 -0700 | [diff] [blame] | 1516 | dispatcher->sdkctl->service_name, dispatcher->packet_header.type, |
| 1517 | dispatcher->packet_header.size); |
Vladimir Chtchetkine | c8aa2c5 | 2012-04-05 16:22:55 -0700 | [diff] [blame] | 1518 | |
Vladimir Chtchetkine | 7136b05 | 2012-04-10 13:39:24 -0700 | [diff] [blame] | 1519 | _on_sdkctl_packet_received(sdkctl, packet); |
| 1520 | _sdkctl_packet_release(packet); |
Vladimir Chtchetkine | c8aa2c5 | 2012-04-05 16:22:55 -0700 | [diff] [blame] | 1521 | |
| 1522 | /* Get ready for the next I/O cycle. */ |
| 1523 | dispatcher->state = SDKCTL_IODISP_EXPECT_HEADER; |
Vladimir Chtchetkine | 7136b05 | 2012-04-10 13:39:24 -0700 | [diff] [blame] | 1524 | async_socket_read_rel(sdkctl->as, &dispatcher->packet_header, sizeof(SDKCtlPacketHeader), |
Vladimir Chtchetkine | c8aa2c5 | 2012-04-05 16:22:55 -0700 | [diff] [blame] | 1525 | _on_sdkctl_io_dispatcher_io, dispatcher, -1); |
| 1526 | return ASIO_ACTION_DONE; |
| 1527 | } |
| 1528 | |
| 1529 | /* A query reply header has been received by I/O dispatcher. */ |
| 1530 | static AsyncIOAction |
| 1531 | _on_io_dispatcher_query_reply_header(SDKCtlIODispatcher* dispatcher, |
| 1532 | AsyncSocketIO* asio) |
| 1533 | { |
| 1534 | SDKCtlSocket* const sdkctl = dispatcher->sdkctl; |
| 1535 | SDKCtlQuery* query; |
| 1536 | |
| 1537 | T("SDKCtl %s: Query reply header is received for query ID %d", |
| 1538 | dispatcher->sdkctl->service_name, dispatcher->query_reply_header.query_id); |
| 1539 | |
| 1540 | /* Pull the query out of the list of active queries. It's the dispatcher that |
| 1541 | * owns this query now. */ |
| 1542 | dispatcher->current_query = |
| 1543 | _sdkctl_socket_remove_query_id(sdkctl, dispatcher->query_reply_header.query_id); |
| 1544 | query = dispatcher->current_query; |
Vladimir Chtchetkine | 7136b05 | 2012-04-10 13:39:24 -0700 | [diff] [blame] | 1545 | const uint32_t query_data_size = |
| 1546 | dispatcher->packet_header.size - sizeof(SDKCtlQueryReplyHeader); |
| 1547 | dispatcher->state = SDKCTL_IODISP_EXPECT_QUERY_REPLY_DATA; |
Vladimir Chtchetkine | c8aa2c5 | 2012-04-05 16:22:55 -0700 | [diff] [blame] | 1548 | |
| 1549 | if (query == NULL) { |
| 1550 | D("%s: Query #%d is not found by dispatcher", |
| 1551 | dispatcher->sdkctl->service_name, dispatcher->query_reply_header.query_id); |
| 1552 | |
| 1553 | /* Query is not found. Just read the remainder of reply up in the air, |
| 1554 | * and then discard when it's over. */ |
| 1555 | dispatcher->state = SDKCTL_IODISP_EXPECT_QUERY_REPLY_DATA; |
| 1556 | dispatcher->packet = |
Vladimir Chtchetkine | 7136b05 | 2012-04-10 13:39:24 -0700 | [diff] [blame] | 1557 | _sdkctl_packet_new(sdkctl, dispatcher->packet_header.size, |
| 1558 | dispatcher->packet_header.type); |
Vladimir Chtchetkine | c8aa2c5 | 2012-04-05 16:22:55 -0700 | [diff] [blame] | 1559 | /* Copy query reply info to the packet. */ |
| 1560 | memcpy(&dispatcher->packet->header, &dispatcher->query_reply_header, |
| 1561 | sizeof(SDKCtlQueryReplyHeader)); |
Vladimir Chtchetkine | 7136b05 | 2012-04-10 13:39:24 -0700 | [diff] [blame] | 1562 | async_socket_read_rel(sdkctl->as, dispatcher->packet + 1, query_data_size, |
Vladimir Chtchetkine | c8aa2c5 | 2012-04-05 16:22:55 -0700 | [diff] [blame] | 1563 | _on_sdkctl_io_dispatcher_io, dispatcher, -1); |
| 1564 | } else { |
| 1565 | /* Prepare to receive query reply. For the simplicity sake, cancel query |
| 1566 | * time out, so it doesn't expire on us while we're in the middle of |
| 1567 | * receiving query's reply. */ |
| 1568 | _sdkctl_query_cancel_timeout(query); |
| 1569 | |
Vladimir Chtchetkine | c8aa2c5 | 2012-04-05 16:22:55 -0700 | [diff] [blame] | 1570 | if (*query->response_size < query_data_size) { |
| 1571 | *query->response_buffer = malloc(query_data_size); |
| 1572 | if (*query->response_buffer == NULL) { |
| 1573 | APANIC("%s: Unable to allocate %d bytes for query response", |
| 1574 | sdkctl->service_name, query_data_size); |
| 1575 | } |
| 1576 | } |
| 1577 | /* Save the actual query response size. */ |
| 1578 | *query->response_size = query_data_size; |
| 1579 | |
| 1580 | /* Start reading query response. */ |
Vladimir Chtchetkine | c8aa2c5 | 2012-04-05 16:22:55 -0700 | [diff] [blame] | 1581 | async_socket_read_rel(sdkctl->as, *query->response_buffer, |
| 1582 | *query->response_size, _on_sdkctl_io_dispatcher_io, |
| 1583 | dispatcher, -1); |
| 1584 | } |
| 1585 | |
| 1586 | return ASIO_ACTION_DONE; |
| 1587 | } |
| 1588 | |
| 1589 | /* A query reply header has been received by I/O dispatcher. */ |
| 1590 | static AsyncIOAction |
| 1591 | _on_io_dispatcher_query_reply(SDKCtlIODispatcher* dispatcher, AsyncSocketIO* asio) |
| 1592 | { |
| 1593 | SDKCtlSocket* const sdkctl = dispatcher->sdkctl; |
| 1594 | SDKCtlQuery* const query = dispatcher->current_query; |
| 1595 | dispatcher->current_query = NULL; |
| 1596 | |
| 1597 | if (query != NULL) { |
| 1598 | _ANDROID_ASSERT(query->header.query_id == dispatcher->query_reply_header.query_id, |
| 1599 | "SDKCtl %s: Query ID mismatch in I/O dispatcher", |
| 1600 | sdkctl->service_name); |
| 1601 | T("SDKCtl %s: Query reply is received for query %p ID %d. Reply size is %d", |
| 1602 | dispatcher->sdkctl->service_name, query, query->header.query_id, |
| 1603 | *query->response_size); |
| 1604 | |
| 1605 | /* Complete the query, and release it from the dispatcher. */ |
| 1606 | _on_sdkctl_query_completed(query); |
| 1607 | sdkctl_query_release(query); |
| 1608 | } else { |
| 1609 | /* This was "read up in the air" for a cancelled query. Just discard the |
| 1610 | * read data. */ |
| 1611 | if (dispatcher->packet != NULL) { |
Vladimir Chtchetkine | 7136b05 | 2012-04-10 13:39:24 -0700 | [diff] [blame] | 1612 | _sdkctl_packet_release(dispatcher->packet); |
Vladimir Chtchetkine | c8aa2c5 | 2012-04-05 16:22:55 -0700 | [diff] [blame] | 1613 | dispatcher->packet = NULL; |
| 1614 | } |
| 1615 | } |
| 1616 | |
| 1617 | /* Get ready for the next I/O cycle. */ |
| 1618 | dispatcher->state = SDKCTL_IODISP_EXPECT_HEADER; |
Vladimir Chtchetkine | 7136b05 | 2012-04-10 13:39:24 -0700 | [diff] [blame] | 1619 | async_socket_read_rel(sdkctl->as, &dispatcher->packet_header, sizeof(SDKCtlPacketHeader), |
Vladimir Chtchetkine | c8aa2c5 | 2012-04-05 16:22:55 -0700 | [diff] [blame] | 1620 | _on_sdkctl_io_dispatcher_io, dispatcher, -1); |
| 1621 | return ASIO_ACTION_DONE; |
| 1622 | } |
| 1623 | |
| 1624 | /* An I/O callback invoked when data gets received from the socket. |
| 1625 | * This is main I/O dispatcher loop. |
| 1626 | * Param: |
| 1627 | * io_opaque SDKCtlIODispatcher instance associated with the reader. |
| 1628 | * asio - Read I/O descriptor. |
| 1629 | * status - I/O status. |
| 1630 | */ |
| 1631 | static AsyncIOAction |
| 1632 | _on_sdkctl_io_dispatcher_io(void* io_opaque, |
| 1633 | AsyncSocketIO* asio, |
| 1634 | AsyncIOState status) |
| 1635 | { |
| 1636 | AsyncIOAction action = ASIO_ACTION_DONE; |
| 1637 | SDKCtlIODispatcher* const dispatcher = (SDKCtlIODispatcher*)io_opaque; |
| 1638 | SDKCtlSocket* const sdkctl = dispatcher->sdkctl; |
| 1639 | |
| 1640 | /* Reference SDKCtlSocket while we're in this callback. */ |
| 1641 | sdkctl_socket_reference(sdkctl); |
| 1642 | |
| 1643 | if (status != ASIO_STATE_SUCCEEDED) { |
| 1644 | /* Something going on with I/O other than receiving data.. */ |
| 1645 | switch (status) { |
| 1646 | case ASIO_STATE_STARTED: |
| 1647 | /* Data has started flowing in. Cancel timeout on I/O that has |
| 1648 | * started, so we can complete the current state of the |
| 1649 | * dispatcher without interruptions other than I/O failures. */ |
| 1650 | async_socket_io_cancel_time_out(asio); |
| 1651 | break; |
| 1652 | |
| 1653 | case ASIO_STATE_FAILED: |
| 1654 | /* I/O failure has occurred. Handle the failure. */ |
| 1655 | _on_io_dispatcher_io_failure(dispatcher, asio); |
| 1656 | break; |
| 1657 | |
| 1658 | case ASIO_STATE_TIMED_OUT: |
| 1659 | /* The way I/O dispatcher is implemented, this should never |
| 1660 | * happen, because dispatcher doesn't set I/O expiration time |
| 1661 | * when registering its readers. */ |
| 1662 | _ANDROID_ASSERT(0, |
| 1663 | "SDKCtl %s: We should never receive ASIO_STATE_TIMED_OUT in SDKCtl I/O dispatcher.", |
| 1664 | sdkctl->service_name); |
| 1665 | break; |
| 1666 | |
| 1667 | case ASIO_STATE_CANCELLED: |
| 1668 | /* Cancellation means that we're in the middle of handling |
| 1669 | * disconnection. Sooner or later, this dispatcher will be reset, |
| 1670 | * so we don't really care about keeping its state at this point. |
| 1671 | */ |
| 1672 | _on_io_dispatcher_io_cancelled(dispatcher, asio); |
| 1673 | break; |
| 1674 | |
| 1675 | case ASIO_STATE_FINISHED: |
| 1676 | break; |
| 1677 | |
| 1678 | default: |
| 1679 | _ANDROID_ASSERT(0, "SDKCtl %s: Unexpected I/O status %d in the dispatcher", |
| 1680 | sdkctl->service_name, status); |
| 1681 | /* Handle this as protocol failure. */ |
| 1682 | errno = EINVAL; |
| 1683 | _on_io_dispatcher_io_failure(dispatcher, asio); |
| 1684 | action = ASIO_ACTION_ABORT; |
| 1685 | break; |
| 1686 | } |
| 1687 | |
| 1688 | sdkctl_socket_release(sdkctl); |
| 1689 | |
| 1690 | return action; |
| 1691 | } |
| 1692 | |
| 1693 | /* Requested data has been read. Handle the chunk depending on dispatcher's |
| 1694 | * state. */ |
| 1695 | switch (dispatcher->state) { |
| 1696 | case SDKCTL_IODISP_EXPECT_HEADER: |
| 1697 | /* A generic packet header is received. */ |
| 1698 | action = _on_io_dispatcher_packet_header(dispatcher, asio); |
| 1699 | break; |
| 1700 | |
| 1701 | case SDKCTL_IODISP_EXPECT_QUERY_REPLY_HEADER: |
| 1702 | /* Query reply header is received. */ |
| 1703 | action = _on_io_dispatcher_query_reply_header(dispatcher, asio); |
| 1704 | break; |
| 1705 | |
| 1706 | case SDKCTL_IODISP_EXPECT_QUERY_REPLY_DATA: |
| 1707 | /* Query reply is received. Complete the query. */ |
| 1708 | action = _on_io_dispatcher_query_reply(dispatcher, asio); |
| 1709 | break; |
| 1710 | |
| 1711 | case SDKCTL_IODISP_EXPECT_DATA: |
| 1712 | /* A generic packet is received. */ |
| 1713 | action = _on_io_dispatcher_packet(dispatcher, asio); |
| 1714 | break; |
| 1715 | |
| 1716 | default: |
| 1717 | _ANDROID_ASSERT(0, "SDKCtl %s: Unexpected I/O dispacher state %d", |
| 1718 | sdkctl->service_name, dispatcher->state); |
| 1719 | break; |
| 1720 | } |
| 1721 | |
| 1722 | sdkctl_socket_release(sdkctl); |
| 1723 | |
| 1724 | return action; |
| 1725 | } |
| 1726 | |
| 1727 | /******************************************************************************** |
| 1728 | * SDKCtlSocket internals. |
| 1729 | *******************************************************************************/ |
| 1730 | |
| 1731 | /* Cancels all queries that is active on this socket. */ |
| 1732 | static void |
| 1733 | _sdkctl_socket_cancel_all_queries(SDKCtlSocket* sdkctl) |
| 1734 | { |
| 1735 | SDKCtlIODispatcher* const dispatcher = &sdkctl->io_dispatcher; |
| 1736 | SDKCtlQuery* query; |
| 1737 | |
| 1738 | /* Cancel query that is being completed in dispatcher. */ |
| 1739 | if (dispatcher->current_query != NULL) { |
| 1740 | SDKCtlQuery* const query = dispatcher->current_query; |
| 1741 | dispatcher->current_query = NULL; |
| 1742 | _on_sdkctl_query_cancelled(query); |
| 1743 | sdkctl_query_release(query); |
| 1744 | } |
| 1745 | |
| 1746 | /* One by one empty query list cancelling pulled queries. */ |
| 1747 | query = _sdkctl_socket_pull_first_query(sdkctl); |
| 1748 | while (query != NULL) { |
| 1749 | _sdkctl_query_cancel_timeout(query); |
| 1750 | query->query_cb(query->query_opaque, query, ASIO_STATE_CANCELLED); |
| 1751 | sdkctl_query_release(query); |
| 1752 | query = _sdkctl_socket_pull_first_query(sdkctl); |
| 1753 | } |
| 1754 | } |
| 1755 | |
| 1756 | /* Cancels all packets that is active on this socket. */ |
| 1757 | static void |
| 1758 | _sdkctl_socket_cancel_all_packets(SDKCtlSocket* sdkctl) |
| 1759 | { |
| 1760 | } |
| 1761 | |
| 1762 | /* Cancels all I/O that is active on this socket. */ |
| 1763 | static void |
| 1764 | _sdkctl_socket_cancel_all_io(SDKCtlSocket* sdkctl) |
| 1765 | { |
| 1766 | /* Cancel all queries, and packets that are active for this I/O. */ |
| 1767 | _sdkctl_socket_cancel_all_queries(sdkctl); |
| 1768 | _sdkctl_socket_cancel_all_packets(sdkctl); |
| 1769 | } |
| 1770 | |
| 1771 | /* Disconnects AsyncSocket for SDKCtlSocket. */ |
| 1772 | static void |
| 1773 | _sdkctl_socket_disconnect_socket(SDKCtlSocket* sdkctl) |
| 1774 | { |
| 1775 | if (sdkctl->as != NULL) { |
| 1776 | /* Disconnect the socket. This will trigger I/O cancellation callbacks. */ |
| 1777 | async_socket_disconnect(sdkctl->as); |
| 1778 | |
| 1779 | /* Cancel all I/O that is active on this socket. */ |
| 1780 | _sdkctl_socket_cancel_all_io(sdkctl); |
| 1781 | |
| 1782 | /* Reset I/O dispatcher. */ |
| 1783 | _sdkctl_io_dispatcher_reset(sdkctl); |
| 1784 | } |
| 1785 | |
| 1786 | sdkctl->state = SDKCTL_SOCKET_DISCONNECTED; |
Vladimir Chtchetkine | 7136b05 | 2012-04-10 13:39:24 -0700 | [diff] [blame] | 1787 | sdkctl->port_status = SDKCTL_PORT_DISCONNECTED; |
Vladimir Chtchetkine | c8aa2c5 | 2012-04-05 16:22:55 -0700 | [diff] [blame] | 1788 | } |
| 1789 | |
| 1790 | /* Frees SDKCtlSocket instance. */ |
| 1791 | static void |
| 1792 | _sdkctl_socket_free(SDKCtlSocket* sdkctl) |
| 1793 | { |
| 1794 | if (sdkctl != NULL) { |
Vladimir Chtchetkine | 7136b05 | 2012-04-10 13:39:24 -0700 | [diff] [blame] | 1795 | T("SDKCtl %s: descriptor is destroing.", sdkctl->service_name); |
| 1796 | |
Vladimir Chtchetkine | c8aa2c5 | 2012-04-05 16:22:55 -0700 | [diff] [blame] | 1797 | /* Disconnect, and release the socket. */ |
| 1798 | if (sdkctl->as != NULL) { |
| 1799 | async_socket_disconnect(sdkctl->as); |
| 1800 | async_socket_release(sdkctl->as); |
| 1801 | } |
| 1802 | |
| 1803 | /* Free allocated resources. */ |
| 1804 | if (sdkctl->looper != NULL) { |
| 1805 | looper_free(sdkctl->looper); |
| 1806 | } |
| 1807 | if (sdkctl->service_name != NULL) { |
| 1808 | free(sdkctl->service_name); |
| 1809 | } |
| 1810 | _sdkctl_socket_empty_recycler(sdkctl); |
| 1811 | |
| 1812 | AFREE(sdkctl); |
| 1813 | } |
| 1814 | } |
| 1815 | |
| 1816 | /******************************************************************************** |
| 1817 | * SDK Control Socket connection callbacks. |
| 1818 | *******************************************************************************/ |
| 1819 | |
| 1820 | /* Initiates handshake query when SDK controller socket is connected. */ |
| 1821 | static void _sdkctl_do_handshake(SDKCtlSocket* sdkctl); |
| 1822 | |
| 1823 | /* A socket connection is established. |
| 1824 | * Here we will start I/O dispatcher, and will initiate a handshake with |
| 1825 | * the SdkController service for this socket. */ |
| 1826 | static AsyncIOAction |
| 1827 | _on_async_socket_connected(SDKCtlSocket* sdkctl) |
| 1828 | { |
| 1829 | D("SDKCtl %s: Socket is connected.", sdkctl->service_name); |
| 1830 | |
| 1831 | /* Notify the client that connection is established. */ |
| 1832 | const AsyncIOAction action = |
Vladimir Chtchetkine | 7136b05 | 2012-04-10 13:39:24 -0700 | [diff] [blame] | 1833 | sdkctl->on_socket_connection(sdkctl->opaque, sdkctl, ASIO_STATE_SUCCEEDED); |
Vladimir Chtchetkine | c8aa2c5 | 2012-04-05 16:22:55 -0700 | [diff] [blame] | 1834 | |
| 1835 | if (action == ASIO_ACTION_DONE) { |
| 1836 | /* Initialize, and start main I/O dispatcher. */ |
| 1837 | _sdkctl_io_dispatcher_start(sdkctl); |
| 1838 | |
| 1839 | /* Initiate handshake. */ |
| 1840 | _sdkctl_do_handshake(sdkctl); |
| 1841 | |
| 1842 | return action; |
| 1843 | } else { |
| 1844 | /* Client didn't like something about this connection. */ |
| 1845 | return action; |
| 1846 | } |
| 1847 | } |
| 1848 | |
| 1849 | /* Handles lost connection with SdkController service. */ |
| 1850 | static AsyncIOAction |
| 1851 | _on_async_socket_disconnected(SDKCtlSocket* sdkctl) |
| 1852 | { |
| 1853 | D("SDKCtl %s: Socket has been disconnected.", sdkctl->service_name); |
| 1854 | |
| 1855 | _sdkctl_socket_disconnect_socket(sdkctl); |
| 1856 | |
Vladimir Chtchetkine | 7136b05 | 2012-04-10 13:39:24 -0700 | [diff] [blame] | 1857 | AsyncIOAction action = sdkctl->on_socket_connection(sdkctl->opaque, sdkctl, |
| 1858 | ASIO_STATE_FAILED); |
Vladimir Chtchetkine | c8aa2c5 | 2012-04-05 16:22:55 -0700 | [diff] [blame] | 1859 | if (action == ASIO_ACTION_DONE) { |
| 1860 | /* Default action for disconnect is to reestablish the connection. */ |
| 1861 | action = ASIO_ACTION_RETRY; |
| 1862 | } |
| 1863 | if (action == ASIO_ACTION_RETRY) { |
| 1864 | sdkctl->state = SDKCTL_SOCKET_CONNECTING; |
| 1865 | } |
| 1866 | return action; |
| 1867 | } |
| 1868 | |
| 1869 | /* An entry point for all socket connection events. |
| 1870 | * Here we will dispatch connection events to appropriate handlers. |
| 1871 | * Param: |
| 1872 | * client_opaque - SDKCtlSocket isntance. |
| 1873 | */ |
| 1874 | static AsyncIOAction |
| 1875 | _on_async_socket_connection(void* client_opaque, |
| 1876 | AsyncSocket* as, |
| 1877 | AsyncIOState status) |
| 1878 | { |
| 1879 | AsyncIOAction action = ASIO_ACTION_DONE; |
| 1880 | SDKCtlSocket* const sdkctl = (SDKCtlSocket*)client_opaque; |
| 1881 | |
| 1882 | /* Reference the socket while in this callback. */ |
| 1883 | sdkctl_socket_reference(sdkctl); |
| 1884 | |
| 1885 | switch (status) { |
| 1886 | case ASIO_STATE_SUCCEEDED: |
| 1887 | sdkctl->state = SDKCTL_SOCKET_CONNECTED; |
| 1888 | _on_async_socket_connected(sdkctl); |
| 1889 | break; |
| 1890 | |
| 1891 | case ASIO_STATE_FAILED: |
| 1892 | if (sdkctl->state == SDKCTL_SOCKET_CONNECTED) { |
| 1893 | /* This is disconnection condition. */ |
| 1894 | action = _on_async_socket_disconnected(sdkctl); |
| 1895 | } else { |
| 1896 | /* An error has occurred while attempting to connect to socket. |
| 1897 | * Lets try again... */ |
| 1898 | action = ASIO_ACTION_RETRY; |
| 1899 | } |
| 1900 | break; |
| 1901 | |
| 1902 | case ASIO_STATE_RETRYING: |
| 1903 | default: |
| 1904 | action = ASIO_ACTION_RETRY; |
| 1905 | break; |
| 1906 | } |
| 1907 | |
| 1908 | sdkctl_socket_release(sdkctl); |
| 1909 | |
| 1910 | return action; |
| 1911 | } |
| 1912 | |
| 1913 | /******************************************************************************** |
| 1914 | * SDK Control Socket public API |
| 1915 | *******************************************************************************/ |
| 1916 | |
| 1917 | SDKCtlSocket* |
| 1918 | sdkctl_socket_new(int reconnect_to, |
| 1919 | const char* service_name, |
Vladimir Chtchetkine | 7136b05 | 2012-04-10 13:39:24 -0700 | [diff] [blame] | 1920 | on_sdkctl_socket_connection_cb on_socket_connection, |
| 1921 | on_sdkctl_port_connection_cb on_port_connection, |
Vladimir Chtchetkine | c8aa2c5 | 2012-04-05 16:22:55 -0700 | [diff] [blame] | 1922 | on_sdkctl_message_cb on_message, |
| 1923 | void* opaque) |
| 1924 | { |
| 1925 | SDKCtlSocket* sdkctl; |
| 1926 | ANEW0(sdkctl); |
| 1927 | |
Vladimir Chtchetkine | 7136b05 | 2012-04-10 13:39:24 -0700 | [diff] [blame] | 1928 | sdkctl->state = SDKCTL_SOCKET_DISCONNECTED; |
| 1929 | sdkctl->port_status = SDKCTL_PORT_DISCONNECTED; |
| 1930 | sdkctl->opaque = opaque; |
| 1931 | sdkctl->service_name = ASTRDUP(service_name); |
| 1932 | sdkctl->on_socket_connection = on_socket_connection; |
| 1933 | sdkctl->on_port_connection = on_port_connection; |
| 1934 | sdkctl->on_message = on_message; |
| 1935 | sdkctl->reconnect_to = reconnect_to; |
| 1936 | sdkctl->as = NULL; |
| 1937 | sdkctl->next_query_id = 0; |
| 1938 | sdkctl->query_head = sdkctl->query_tail = NULL; |
| 1939 | sdkctl->ref_count = 1; |
| 1940 | sdkctl->recycler = NULL; |
| 1941 | sdkctl->recycler_block_size = 0; |
| 1942 | sdkctl->recycler_max = 0; |
| 1943 | sdkctl->recycler_count = 0; |
| 1944 | |
| 1945 | T("SDKCtl %s: descriptor is created.", sdkctl->service_name); |
Vladimir Chtchetkine | c8aa2c5 | 2012-04-05 16:22:55 -0700 | [diff] [blame] | 1946 | |
| 1947 | sdkctl->looper = looper_newCore(); |
| 1948 | if (sdkctl->looper == NULL) { |
| 1949 | E("Unable to create I/O looper for SDKCtl socket '%s'", |
| 1950 | service_name); |
Vladimir Chtchetkine | 7136b05 | 2012-04-10 13:39:24 -0700 | [diff] [blame] | 1951 | on_socket_connection(opaque, sdkctl, ASIO_STATE_FAILED); |
Vladimir Chtchetkine | c8aa2c5 | 2012-04-05 16:22:55 -0700 | [diff] [blame] | 1952 | _sdkctl_socket_free(sdkctl); |
| 1953 | return NULL; |
| 1954 | } |
| 1955 | |
| 1956 | return sdkctl; |
| 1957 | } |
| 1958 | |
| 1959 | int sdkctl_socket_reference(SDKCtlSocket* sdkctl) |
| 1960 | { |
| 1961 | assert(sdkctl->ref_count > 0); |
| 1962 | sdkctl->ref_count++; |
| 1963 | return sdkctl->ref_count; |
| 1964 | } |
| 1965 | |
| 1966 | int |
| 1967 | sdkctl_socket_release(SDKCtlSocket* sdkctl) |
| 1968 | { |
| 1969 | assert(sdkctl->ref_count > 0); |
| 1970 | sdkctl->ref_count--; |
| 1971 | if (sdkctl->ref_count == 0) { |
| 1972 | /* Last reference has been dropped. Destroy this object. */ |
| 1973 | _sdkctl_socket_free(sdkctl); |
| 1974 | return 0; |
| 1975 | } |
| 1976 | return sdkctl->ref_count; |
| 1977 | } |
| 1978 | |
| 1979 | void |
| 1980 | sdkctl_init_recycler(SDKCtlSocket* sdkctl, |
| 1981 | uint32_t data_size, |
| 1982 | int max_recycled_num) |
| 1983 | { |
| 1984 | if (sdkctl->recycler != NULL) { |
| 1985 | D("SDKCtl %s: Recycler is already initialized. Ignoring recycler init.", |
| 1986 | sdkctl->service_name); |
| 1987 | return; |
| 1988 | } |
| 1989 | |
| 1990 | /* SDKCtlQuery is max descriptor sizeof. */ |
| 1991 | data_size += sizeof(SDKCtlQuery); |
| 1992 | |
| 1993 | sdkctl->recycler_block_size = data_size; |
| 1994 | sdkctl->recycler_max = max_recycled_num; |
| 1995 | sdkctl->recycler_count = 0; |
| 1996 | } |
| 1997 | |
| 1998 | void |
| 1999 | sdkctl_socket_connect(SDKCtlSocket* sdkctl, int port, int retry_to) |
| 2000 | { |
| 2001 | T("SDKCtl %s: Handling connect request to port %d, retrying in %dms...", |
| 2002 | sdkctl->service_name, port, retry_to); |
| 2003 | |
| 2004 | sdkctl->state = SDKCTL_SOCKET_CONNECTING; |
| 2005 | sdkctl->as = async_socket_new(port, sdkctl->reconnect_to, |
| 2006 | _on_async_socket_connection, sdkctl, |
| 2007 | sdkctl->looper); |
| 2008 | if (sdkctl->as == NULL) { |
| 2009 | E("Unable to allocate AsyncSocket for SDKCtl socket '%s'", |
| 2010 | sdkctl->service_name); |
Vladimir Chtchetkine | 7136b05 | 2012-04-10 13:39:24 -0700 | [diff] [blame] | 2011 | sdkctl->on_socket_connection(sdkctl->opaque, sdkctl, ASIO_STATE_FAILED); |
Vladimir Chtchetkine | c8aa2c5 | 2012-04-05 16:22:55 -0700 | [diff] [blame] | 2012 | } else { |
| 2013 | async_socket_connect(sdkctl->as, retry_to); |
| 2014 | } |
| 2015 | } |
| 2016 | |
| 2017 | void |
| 2018 | sdkctl_socket_reconnect(SDKCtlSocket* sdkctl, int port, int retry_to) |
| 2019 | { |
| 2020 | T("SDKCtl %s: Handling reconnection request to port %d, retrying in %dms...", |
| 2021 | sdkctl->service_name, port, retry_to); |
| 2022 | |
| 2023 | _sdkctl_socket_disconnect_socket(sdkctl); |
| 2024 | |
| 2025 | if (sdkctl->as == NULL) { |
| 2026 | sdkctl_socket_connect(sdkctl, port, retry_to); |
| 2027 | } else { |
| 2028 | sdkctl->state = SDKCTL_SOCKET_CONNECTING; |
| 2029 | async_socket_reconnect(sdkctl->as, retry_to); |
| 2030 | } |
| 2031 | } |
| 2032 | |
| 2033 | void |
| 2034 | sdkctl_socket_disconnect(SDKCtlSocket* sdkctl) |
| 2035 | { |
| 2036 | T("SDKCtl %s: Handling disconnect request.", sdkctl->service_name); |
| 2037 | |
| 2038 | _sdkctl_socket_disconnect_socket(sdkctl); |
| 2039 | } |
| 2040 | |
Vladimir Chtchetkine | 7136b05 | 2012-04-10 13:39:24 -0700 | [diff] [blame] | 2041 | int |
| 2042 | sdkctl_socket_is_connected(SDKCtlSocket* sdkctl) |
| 2043 | { |
| 2044 | return (sdkctl->state == SDKCTL_SOCKET_CONNECTED) ? 1 : 0; |
| 2045 | } |
| 2046 | |
| 2047 | int |
| 2048 | sdkctl_socket_is_port_ready(SDKCtlSocket* sdkctl) |
| 2049 | { |
| 2050 | return (sdkctl->port_status == SDKCTL_PORT_ENABLED) ? 1 : 0; |
| 2051 | } |
| 2052 | |
| 2053 | SdkCtlPortStatus |
| 2054 | sdkctl_socket_get_port_status(SDKCtlSocket* sdkctl) |
| 2055 | { |
| 2056 | return sdkctl->port_status; |
| 2057 | } |
| 2058 | |
| 2059 | int |
| 2060 | sdkctl_socket_is_handshake_ok(SDKCtlSocket* sdkctl) |
| 2061 | { |
| 2062 | switch (sdkctl->port_status) { |
| 2063 | case SDKCTL_HANDSHAKE_DUP: |
| 2064 | case SDKCTL_HANDSHAKE_UNKNOWN_QUERY: |
| 2065 | case SDKCTL_HANDSHAKE_UNKNOWN_RESPONSE: |
| 2066 | return 0; |
| 2067 | default: |
| 2068 | return 1; |
| 2069 | } |
| 2070 | } |
Vladimir Chtchetkine | c8aa2c5 | 2012-04-05 16:22:55 -0700 | [diff] [blame] | 2071 | |
| 2072 | /******************************************************************************** |
| 2073 | * Handshake query |
| 2074 | *******************************************************************************/ |
| 2075 | |
Vladimir Chtchetkine | 7136b05 | 2012-04-10 13:39:24 -0700 | [diff] [blame] | 2076 | /* |
| 2077 | * Handshake result values. |
| 2078 | */ |
| 2079 | |
| 2080 | /* Handshake has succeeded completed, and service-side port is connected. */ |
| 2081 | #define SDKCTL_HANDSHAKE_RESP_CONNECTED 0 |
| 2082 | /* Handshake has succeeded completed, but service-side port is not connected. */ |
| 2083 | #define SDKCTL_HANDSHAKE_RESP_NOPORT 1 |
| 2084 | /* Handshake has failed due to duplicate connection request. */ |
| 2085 | #define SDKCTL_HANDSHAKE_RESP_DUP -1 |
| 2086 | /* Handshake has failed due to unknown query. */ |
| 2087 | #define SDKCTL_HANDSHAKE_RESP_QUERY_UNKNOWN -2 |
| 2088 | |
Vladimir Chtchetkine | c8aa2c5 | 2012-04-05 16:22:55 -0700 | [diff] [blame] | 2089 | /* A callback that is ivoked on handshake I/O events. */ |
| 2090 | static AsyncIOAction |
| 2091 | _on_handshake_io(void* query_opaque, |
| 2092 | SDKCtlQuery* query, |
| 2093 | AsyncIOState status) |
| 2094 | { |
| 2095 | SDKCtlSocket* const sdkctl = (SDKCtlSocket*)query_opaque; |
| 2096 | |
| 2097 | if (status == ASIO_STATE_SUCCEEDED) { |
Vladimir Chtchetkine | 7136b05 | 2012-04-10 13:39:24 -0700 | [diff] [blame] | 2098 | const int* res = (const int*)(*query->response_buffer); |
| 2099 | SdkCtlPortStatus handshake_status; |
| 2100 | switch (*res) { |
| 2101 | case SDKCTL_HANDSHAKE_RESP_CONNECTED: |
| 2102 | D("SDKCtl %s: Handshake succeeded. Port is connected", |
| 2103 | sdkctl->service_name); |
| 2104 | handshake_status = SDKCTL_HANDSHAKE_CONNECTED; |
| 2105 | break; |
Vladimir Chtchetkine | c8aa2c5 | 2012-04-05 16:22:55 -0700 | [diff] [blame] | 2106 | |
Vladimir Chtchetkine | 7136b05 | 2012-04-10 13:39:24 -0700 | [diff] [blame] | 2107 | case SDKCTL_HANDSHAKE_RESP_NOPORT: |
| 2108 | D("SDKCtl %s: Handshake succeeded. Port is not connected", |
| 2109 | sdkctl->service_name); |
| 2110 | handshake_status = SDKCTL_HANDSHAKE_NO_PORT; |
| 2111 | break; |
| 2112 | |
| 2113 | case SDKCTL_HANDSHAKE_RESP_DUP: |
| 2114 | D("SDKCtl %s: Handshake failed: duplicate connection.", |
| 2115 | sdkctl->service_name); |
| 2116 | handshake_status = SDKCTL_HANDSHAKE_DUP; |
| 2117 | break; |
| 2118 | |
| 2119 | case SDKCTL_HANDSHAKE_RESP_QUERY_UNKNOWN: |
| 2120 | D("SDKCtl %s: Handshake failed: unknown query.", |
| 2121 | sdkctl->service_name); |
| 2122 | handshake_status = SDKCTL_HANDSHAKE_UNKNOWN_QUERY; |
| 2123 | break; |
| 2124 | |
| 2125 | default: |
| 2126 | E("SDKCtl %s: Unknown handshake response: %d", |
| 2127 | sdkctl->service_name, *res); |
| 2128 | handshake_status = SDKCTL_HANDSHAKE_UNKNOWN_RESPONSE; |
| 2129 | break; |
| 2130 | } |
| 2131 | sdkctl->port_status = handshake_status; |
| 2132 | sdkctl->on_port_connection(sdkctl->opaque, sdkctl, handshake_status); |
Vladimir Chtchetkine | c8aa2c5 | 2012-04-05 16:22:55 -0700 | [diff] [blame] | 2133 | } else { |
| 2134 | /* Something is going on with the handshake... */ |
| 2135 | switch (status) { |
| 2136 | case ASIO_STATE_FAILED: |
| 2137 | case ASIO_STATE_TIMED_OUT: |
| 2138 | case ASIO_STATE_CANCELLED: |
| 2139 | D("SDKCtl %s: Handshake failed: I/O state %d. Error: %d -> %s", |
| 2140 | sdkctl->service_name, status, errno, strerror(errno)); |
Vladimir Chtchetkine | 7136b05 | 2012-04-10 13:39:24 -0700 | [diff] [blame] | 2141 | sdkctl->on_socket_connection(sdkctl->opaque, sdkctl, |
| 2142 | ASIO_STATE_FAILED); |
Vladimir Chtchetkine | c8aa2c5 | 2012-04-05 16:22:55 -0700 | [diff] [blame] | 2143 | break; |
| 2144 | |
| 2145 | default: |
| 2146 | break; |
| 2147 | } |
| 2148 | } |
| 2149 | return ASIO_ACTION_DONE; |
| 2150 | } |
| 2151 | |
| 2152 | static AsyncIOAction |
| 2153 | _on_sdkctl_endianness_io(void* io_opaque, |
| 2154 | AsyncSocketIO* asio, |
| 2155 | AsyncIOState status) { |
| 2156 | SDKCtlSocket* const sdkctl = (SDKCtlSocket*)io_opaque; |
| 2157 | |
| 2158 | if (status == ASIO_STATE_SUCCEEDED) { |
| 2159 | /* Now it's time to initiate handshake message. */ |
| 2160 | D("SDKCtl %s: Sending handshake query...", sdkctl->service_name); |
| 2161 | SDKCtlQuery* query = |
| 2162 | sdkctl_query_build_and_send(sdkctl, SDKCTL_QUERY_HANDSHAKE, |
| 2163 | strlen(sdkctl->service_name), |
| 2164 | sdkctl->service_name, NULL, NULL, |
| 2165 | _on_handshake_io, sdkctl, 3000); |
| 2166 | sdkctl_query_release(query); |
| 2167 | return ASIO_ACTION_DONE; |
| 2168 | } else { |
| 2169 | /* Something is going on with the endianness... */ |
| 2170 | switch (status) { |
| 2171 | case ASIO_STATE_FAILED: |
| 2172 | case ASIO_STATE_TIMED_OUT: |
| 2173 | case ASIO_STATE_CANCELLED: |
| 2174 | D("SDKCtl %s: endianness failed: I/O state %d. Error: %d -> %s", |
| 2175 | sdkctl->service_name, status, errno, strerror(errno)); |
Vladimir Chtchetkine | 7136b05 | 2012-04-10 13:39:24 -0700 | [diff] [blame] | 2176 | sdkctl->on_socket_connection(sdkctl->opaque, sdkctl, ASIO_STATE_FAILED); |
Vladimir Chtchetkine | c8aa2c5 | 2012-04-05 16:22:55 -0700 | [diff] [blame] | 2177 | break; |
| 2178 | |
| 2179 | default: |
| 2180 | break; |
| 2181 | } |
| 2182 | } |
| 2183 | return ASIO_ACTION_DONE; |
| 2184 | } |
| 2185 | |
| 2186 | static void |
| 2187 | _sdkctl_do_handshake(SDKCtlSocket* sdkctl) |
| 2188 | { |
| 2189 | #ifndef HOST_WORDS_BIGENDIAN |
| 2190 | static const char _host_end = 0; |
| 2191 | #else |
| 2192 | static const char _host_end = 1; |
| 2193 | #endif |
| 2194 | |
Vladimir Chtchetkine | 7136b05 | 2012-04-10 13:39:24 -0700 | [diff] [blame] | 2195 | D("SDKCtl %s: Sending endianness: %d", sdkctl->service_name, _host_end); |
Vladimir Chtchetkine | c8aa2c5 | 2012-04-05 16:22:55 -0700 | [diff] [blame] | 2196 | |
| 2197 | /* Before we can send any structured data to the SDK controller we need to |
| 2198 | * report endianness of the host. */ |
| 2199 | async_socket_write_rel(sdkctl->as, &_host_end, 1, |
| 2200 | _on_sdkctl_endianness_io, sdkctl, 3000); |
| 2201 | } |