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
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"
28#include "android/iolooper.h"
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
35#define TRACE_ON    0
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. */
44typedef struct SDKCtlRecycled SDKCtlRecycled;
45struct 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
54/*
55 * Types of the data packets sent via SDK controller socket.
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
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'). */
105static const int _sdkctl_packet_sig = 0x53444B43;
106
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 */
112typedef struct SDKCtlPacketHeader {
113    /* Signature. */
114    int     signature;
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
124 * controller.
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 */
130typedef struct SDKCtlPacket {
131    /* Supports recycling. Don't put anything in front: recycler expects this
132     * to be the first field in recyclable descriptor. */
133    SDKCtlRecycled          recycling;
134
135    /* SDK controller socket that transmits this packet. */
136    SDKCtlSocket*           sdkctl;
137    /* Number of outstanding references to the packet. */
138    int                     ref_count;
139
140    /* Common packet header. Packet data immediately follows this header, so it
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 */
158struct 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;
173};
174
175/********************************************************************************
176 *                      SDKCtlQuery declarations
177 *******************************************************************************/
178
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 */
184typedef 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;
190    /* Query type. */
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 */
201struct 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
206    /* Next query in the list of active queries. */
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
211     * timer is set to after query has been transmitted to the service. */
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;
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). */
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;
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. */
231    uint32_t                internal_resp_size;
232    /* Number of outstanding references to the query. */
233    int                     ref_count;
234
235    /* Common query header. Query data immediately follows this header, so it
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 */
245typedef 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/********************************************************************************
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 */
262typedef 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 */
274struct SDKCtlMessage {
275    /* Data packet descriptor for this message. */
276    SDKCtlPacket  packet;
277    /* Message type. */
278    int           msg_type;
279};
280
281/********************************************************************************
282 *                      SDK Control Socket declarations
283 *******************************************************************************/
284
285/* Enumerates SDKCtlSocket states. */
286typedef 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. */
296typedef 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. */
308typedef 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. */
316        SDKCtlPacketHeader      packet_header;
317        /* Header for a query packet. */
318        SDKCtlQueryHeader       query_header;
319        /* Header for a message packet. */
320        SDKCtlMessageHeader     message_header;
321        /* Header for a query response packet. */
322        SDKCtlQueryReplyHeader  query_reply_header;
323    };
324    /* Descriptor of a packet that is being received from SDK controller. */
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. */
331struct SDKCtlSocket {
332    /* SDK controller socket state */
333    SDKCtlSocketState               state;
334    /* SDK controller port status */
335    SdkCtlPortStatus                port_status;
336    /* I/O dispatcher for the socket. */
337    SDKCtlIODispatcher              io_dispatcher;
338    /* Asynchronous socket connected to SDK Controller on the device. */
339    AsyncSocket*                    as;
340    /* Client callback that monitors this socket connection. */
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;
344    /* A callback to invoke when a message is received from the SDK controller. */
345    on_sdkctl_message_cb            on_message;
346    /* An opaque pointer associated with this socket. */
347    void*                           opaque;
348    /* Name of an SDK controller port this socket is connected to. */
349    char*                           service_name;
350    /* I/O looper for timers. */
351    Looper*                         looper;
352    /* Head of the active query list. */
353    SDKCtlQuery*                    query_head;
354    /* Tail of the active query list. */
355    SDKCtlQuery*                    query_tail;
356    /* Query ID generator that gets incremented for each new query. */
357    int                             next_query_id;
358    /* Timeout before trying to reconnect after disconnection. */
359    int                             reconnect_to;
360    /* Number of outstanding references to this descriptor. */
361    int                             ref_count;
362    /* Head of the recycled memory */
363    SDKCtlRecycled*                 recycler;
364    /* Recyclable block size. */
365    uint32_t                        recycler_block_size;
366    /* Maximum number of blocks to recycle. */
367    int                             recycler_max;
368    /* Number of blocs in the recycler. */
369    int                             recycler_count;
370};
371
372/********************************************************************************
373 *                      SDKCtlSocket recycling management
374 *******************************************************************************/
375
376/* Gets a recycled block for a given SDKCtlSocket, or allocates new memory
377 * block. */
378static 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) {
384        assert(sdkctl->recycler_count > 0);
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) {
391        /* There are no blocks in the recycler, but requested size fits. Lets
392         * allocate block that we can later recycle. */
393        block = malloc(sdkctl->recycler_block_size);
394        if (block == NULL) {
395            APANIC("SDKCtl %s: Unable to allocate %d bytes block.",
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. */
413static void
414_sdkctl_socket_free_recycler(SDKCtlSocket* sdkctl, void* mem)
415{
416    SDKCtlRecycled* const block = (SDKCtlRecycled*)mem;
417
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. */
421        free(mem);
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++;
428    }
429}
430
431/* Empties the recycler for a given SDKCtlSocket. */
432static void
433_sdkctl_socket_empty_recycler(SDKCtlSocket* sdkctl)
434{
435    SDKCtlRecycled* block = sdkctl->recycler;
436    while (block != NULL) {
437        void* const to_free = block;
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 */
454static void
455_sdkctl_socket_add_query(SDKCtlQuery* query)
456{
457    SDKCtlSocket* const sdkctl = query->sdkctl;
458    if (sdkctl->query_head == NULL) {
459        assert(sdkctl->query_tail == NULL);
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:
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.
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 */
479static 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
486    /* Quick check: the query could be currently handled by the dispatcher. */
487    if (sdkctl->io_dispatcher.current_query == query) {
488        /* Release the query from dispatcher. */
489        sdkctl->io_dispatcher.current_query = NULL;
490        sdkctl_query_release(query);
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    }
499    if (head == NULL) {
500        D("SDKCtl %s: Query %p is not found in the list.",
501          sdkctl->service_name, query);
502        return 0;
503    }
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;
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
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.
538 */
539static SDKCtlQuery*
540_sdkctl_socket_remove_query_id(SDKCtlSocket* sdkctl, int query_id)
541{
542    SDKCtlQuery* query = NULL;
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. */
550        query = sdkctl->io_dispatcher.current_query;
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    }
560    if (head == NULL) {
561        D("SDKCtl %s: Query ID %d is not found in the list.",
562          sdkctl->service_name, query_id);
563        return NULL;
564    }
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;
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:
591 *  A query removed from the head of the list of active queries, or NULL if query
592 *  list is empty.
593 */
594static 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. */
609static 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. */
620static SDKCtlPacket*
621_sdkctl_packet_new(SDKCtlSocket* sdkctl, uint32_t size, int type)
622{
623    /* Allocate packet descriptor large enough to contain packet data. */
624    SDKCtlPacket* const packet =
625        _sdkctl_socket_alloc_recycler(sdkctl, sizeof(SDKCtlPacket) + size);
626
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;
632
633    /* Refence SDKCTlSocket that owns this packet. */
634    sdkctl_socket_reference(sdkctl);
635
636    T("SDKCtl %s: Packet %p of type %d is allocated for %d bytes transfer.",
637          sdkctl->service_name, packet, type, size);
638
639    return packet;
640}
641
642/* Frees a packet. */
643static void
644_sdkctl_packet_free(SDKCtlPacket* packet)
645{
646    SDKCtlSocket* const sdkctl = packet->sdkctl;
647
648    /* Recycle packet. */
649    _sdkctl_socket_free_recycler(packet->sdkctl, packet);
650
651    T("SDKCtl %s: Packet %p is freed.", sdkctl->service_name, packet);
652
653    /* Release SDKCTlSocket that owned this packet. */
654    sdkctl_socket_release(sdkctl);
655}
656
657/* References a packet. */
658int
659_sdkctl_packet_reference(SDKCtlPacket* packet)
660{
661    assert(packet->ref_count > 0);
662    packet->ref_count++;
663    return packet->ref_count;
664}
665
666/* Releases a packet. */
667int
668_sdkctl_packet_release(SDKCtlPacket* packet)
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
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 */
686static 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 */
734static 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
754SDKCtlDirectPacket*
755sdkctl_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. */
772static 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. */
787int
788sdkctl_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. */
796int
797sdkctl_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 */
816static 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
863void
864sdkctl_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. */
890static 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
902int
903sdkctl_message_reference(SDKCtlMessage* msg)
904{
905    return _sdkctl_packet_reference(&msg->packet);
906}
907
908int
909sdkctl_message_release(SDKCtlMessage* msg)
910{
911    return _sdkctl_packet_release(&msg->packet);
912}
913
914SDKCtlMessage*
915sdkctl_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
929int
930sdkctl_message_get_header_size(void)
931{
932    return sizeof(SDKCtlMessageHeader);
933}
934
935void
936sdkctl_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
946/********************************************************************************
947 *                    SDKCtlQuery implementation
948 *******************************************************************************/
949
950/* Frees query descriptor. */
951static void
952_sdkctl_query_free(SDKCtlQuery* query)
953{
954    if (query != NULL) {
955        SDKCtlSocket* const sdkctl = query->sdkctl;
956        if (query->internal_resp_buffer != NULL &&
957            (query->response_buffer == NULL ||
958             query->response_buffer == &query->internal_resp_buffer)) {
959            /* This query used its internal buffer to receive the response.
960             * Free it. */
961            free(query->internal_resp_buffer);
962        }
963
964        loopTimer_done(query->timer);
965
966        /* Recyle the descriptor. */
967        _sdkctl_socket_free_recycler(sdkctl, query);
968
969        T("SDKCtl %s: Query %p is freed.", sdkctl->service_name, query);
970
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 */
983static void
984_sdkctl_query_cancel_timeout(SDKCtlQuery* query)
985{
986    loopTimer_stop(query->timer);
987
988    T("SDKCtl %s: Query %p ID %d deadline %lld is cancelled.",
989      query->sdkctl->service_name, query, query->header.query_id, query->deadline);
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 */
999static 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. */
1011static 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
1018     * the client about query cancellation.
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 */
1030static 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
1055/* A callback that is invoked when query has been sent to the SDK controller. */
1056static void
1057_on_sdkctl_query_sent(SDKCtlQuery* query)
1058{
1059    T("SDKCtl %s: Sent %d bytes of query %p ID %d of type %d",
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 */
1077static 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
1088    /* Lets see what's going on with query transmission. */
1089    switch (status) {
1090        case ASIO_STATE_SUCCEEDED:
1091            /* Query has been sent to the service. */
1092            _on_sdkctl_query_sent(query);
1093            break;
1094
1095        case ASIO_STATE_CANCELLED:
1096            T("SDKCtl %s: Query %p ID %d is cancelled in transmission.",
1097              query->sdkctl->service_name, query, query->header.query_id);
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:
1104            D("SDKCtl %s: Query %p ID %d with deadline %lld has timed out in transmission at %lld",
1105              query->sdkctl->service_name, query, query->header.query_id,
1106              query->deadline,  async_socket_deadline(query->sdkctl->as, 0));
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:
1116            T("SDKCtl %s: Query %p ID %d failed in transmission: %d -> %s",
1117              query->sdkctl->service_name, query, query->header.query_id,
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
1147SDKCtlQuery*
1148sdkctl_query_new(SDKCtlSocket* sdkctl, int query_type, uint32_t in_data_size)
1149{
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;
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
1181SDKCtlQuery*
1182sdkctl_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
1193    query->response_buffer = response_buffer;
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;
1197    }
1198    query->response_size = response_size;
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;
1203    }
1204    query->query_cb = query_cb;
1205    query->query_opaque = query_opaque;
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
1214void
1215sdkctl_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
1228    assert(query->header.packet.signature == _sdkctl_packet_sig);
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
1233    T("SDKCtl %s: Query %p ID %d type %d is being sent with deadline at %lld",
1234      query->sdkctl->service_name, query, query->header.query_id,
1235      query->header.query_type, query->deadline);
1236}
1237
1238SDKCtlQuery*
1239sdkctl_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
1257int
1258sdkctl_query_reference(SDKCtlQuery* query)
1259{
1260    assert(query->ref_count > 0);
1261    query->ref_count++;
1262    return query->ref_count;
1263}
1264
1265int
1266sdkctl_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
1278void*
1279sdkctl_query_get_buffer_in(SDKCtlQuery* query)
1280{
1281    /* Query buffer starts right after the header. */
1282    return query + 1;
1283}
1284
1285void*
1286sdkctl_query_get_buffer_out(SDKCtlQuery* query)
1287{
1288    return query->response_buffer != NULL ? *query->response_buffer :
1289                                            query->internal_resp_buffer;
1290}
1291
1292/********************************************************************************
1293 *                      SDKCtlPacket implementation
1294 *******************************************************************************/
1295
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. */
1299static 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
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    }
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 */
1357static 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. */
1362static 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. */
1372    async_socket_read_rel(dispatcher->sdkctl->as, &dispatcher->packet_header,
1373                          sizeof(SDKCtlPacketHeader), _on_sdkctl_io_dispatcher_io,
1374                          dispatcher, -1);
1375}
1376
1377/* Resets I/O dispatcher for SDK controller socket. */
1378static 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) {
1392        _sdkctl_packet_release(dispatcher->packet);
1393        dispatcher->packet = NULL;
1394    }
1395
1396    /* Reset dispatcher state. */
1397    dispatcher->state = SDKCTL_IODISP_EXPECT_HEADER;
1398
1399    T("SDKCtl %s: I/O Dispatcher is reset", sdkctl->service_name);
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 */
1409static 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. */
1424    sdkctl->on_socket_connection(sdkctl->opaque, sdkctl, ASIO_STATE_FAILED);
1425}
1426
1427/* A callback that is invoked when dispatcher's reader has been cancelled. */
1428static 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) {
1445        _sdkctl_packet_release(dispatcher->packet);
1446        dispatcher->packet = NULL;
1447    }
1448}
1449
1450/* A generic packet header has been received by I/O dispatcher. */
1451static 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.",
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    }
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     */
1481     if (dispatcher->packet_header.type == SDKCTL_PACKET_QUERY_RESPONSE) {
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 =
1496            _sdkctl_packet_new(sdkctl, dispatcher->packet_header.size,
1497                               dispatcher->packet_header.type);
1498        /* Initiate reading of the packet data. */
1499        async_socket_read_rel(sdkctl->as, dispatcher->packet + 1,
1500                              dispatcher->packet_header.size - sizeof(SDKCtlPacketHeader),
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. */
1508static AsyncIOAction
1509_on_io_dispatcher_packet(SDKCtlIODispatcher* dispatcher, AsyncSocketIO* asio)
1510{
1511    SDKCtlSocket* const sdkctl = dispatcher->sdkctl;
1512    SDKCtlPacket* const packet = dispatcher->packet;
1513    dispatcher->packet = NULL;
1514
1515    T("SDKCtl %s: Packet type %d, size %d is received.",
1516      dispatcher->sdkctl->service_name, dispatcher->packet_header.type,
1517      dispatcher->packet_header.size);
1518
1519    _on_sdkctl_packet_received(sdkctl, packet);
1520    _sdkctl_packet_release(packet);
1521
1522    /* Get ready for the next I/O cycle. */
1523    dispatcher->state = SDKCTL_IODISP_EXPECT_HEADER;
1524    async_socket_read_rel(sdkctl->as, &dispatcher->packet_header, sizeof(SDKCtlPacketHeader),
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. */
1530static 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;
1545    const uint32_t query_data_size =
1546        dispatcher->packet_header.size - sizeof(SDKCtlQueryReplyHeader);
1547    dispatcher->state = SDKCTL_IODISP_EXPECT_QUERY_REPLY_DATA;
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 =
1557            _sdkctl_packet_new(sdkctl, dispatcher->packet_header.size,
1558                               dispatcher->packet_header.type);
1559        /* Copy query reply info to the packet. */
1560        memcpy(&dispatcher->packet->header, &dispatcher->query_reply_header,
1561               sizeof(SDKCtlQueryReplyHeader));
1562        async_socket_read_rel(sdkctl->as, dispatcher->packet + 1, query_data_size,
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
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. */
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. */
1590static 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) {
1612            _sdkctl_packet_release(dispatcher->packet);
1613            dispatcher->packet = NULL;
1614        }
1615    }
1616
1617    /* Get ready for the next I/O cycle. */
1618    dispatcher->state = SDKCTL_IODISP_EXPECT_HEADER;
1619    async_socket_read_rel(sdkctl->as, &dispatcher->packet_header, sizeof(SDKCtlPacketHeader),
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 */
1631static 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. */
1732static 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. */
1757static void
1758_sdkctl_socket_cancel_all_packets(SDKCtlSocket* sdkctl)
1759{
1760}
1761
1762/* Cancels all I/O that is active on this socket. */
1763static 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. */
1772static 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;
1787    sdkctl->port_status = SDKCTL_PORT_DISCONNECTED;
1788}
1789
1790/* Frees SDKCtlSocket instance. */
1791static void
1792_sdkctl_socket_free(SDKCtlSocket* sdkctl)
1793{
1794    if (sdkctl != NULL) {
1795        T("SDKCtl %s: descriptor is destroing.", sdkctl->service_name);
1796
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. */
1821static 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. */
1826static 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 =
1833        sdkctl->on_socket_connection(sdkctl->opaque, sdkctl, ASIO_STATE_SUCCEEDED);
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. */
1850static 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
1857    AsyncIOAction action = sdkctl->on_socket_connection(sdkctl->opaque, sdkctl,
1858                                                        ASIO_STATE_FAILED);
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 */
1874static 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
1917SDKCtlSocket*
1918sdkctl_socket_new(int reconnect_to,
1919                  const char* service_name,
1920                  on_sdkctl_socket_connection_cb on_socket_connection,
1921                  on_sdkctl_port_connection_cb on_port_connection,
1922                  on_sdkctl_message_cb on_message,
1923                  void* opaque)
1924{
1925    SDKCtlSocket* sdkctl;
1926    ANEW0(sdkctl);
1927
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);
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);
1951        on_socket_connection(opaque, sdkctl, ASIO_STATE_FAILED);
1952        _sdkctl_socket_free(sdkctl);
1953        return NULL;
1954    }
1955
1956    return sdkctl;
1957}
1958
1959int sdkctl_socket_reference(SDKCtlSocket* sdkctl)
1960{
1961    assert(sdkctl->ref_count > 0);
1962    sdkctl->ref_count++;
1963    return sdkctl->ref_count;
1964}
1965
1966int
1967sdkctl_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
1979void
1980sdkctl_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
1998void
1999sdkctl_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);
2011        sdkctl->on_socket_connection(sdkctl->opaque, sdkctl, ASIO_STATE_FAILED);
2012    } else {
2013        async_socket_connect(sdkctl->as, retry_to);
2014    }
2015}
2016
2017void
2018sdkctl_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
2033void
2034sdkctl_socket_disconnect(SDKCtlSocket* sdkctl)
2035{
2036    T("SDKCtl %s: Handling disconnect request.", sdkctl->service_name);
2037
2038    _sdkctl_socket_disconnect_socket(sdkctl);
2039}
2040
2041int
2042sdkctl_socket_is_connected(SDKCtlSocket* sdkctl)
2043{
2044    return (sdkctl->state == SDKCTL_SOCKET_CONNECTED) ? 1 : 0;
2045}
2046
2047int
2048sdkctl_socket_is_port_ready(SDKCtlSocket* sdkctl)
2049{
2050    return (sdkctl->port_status == SDKCTL_PORT_ENABLED) ? 1 : 0;
2051}
2052
2053SdkCtlPortStatus
2054sdkctl_socket_get_port_status(SDKCtlSocket* sdkctl)
2055{
2056    return sdkctl->port_status;
2057}
2058
2059int
2060sdkctl_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}
2071
2072/********************************************************************************
2073 *                       Handshake query
2074 *******************************************************************************/
2075
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
2089/* A callback that is ivoked on handshake I/O events. */
2090static 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) {
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;
2106
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);
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));
2141                sdkctl->on_socket_connection(sdkctl->opaque, sdkctl,
2142                                             ASIO_STATE_FAILED);
2143                break;
2144
2145            default:
2146                break;
2147        }
2148    }
2149    return ASIO_ACTION_DONE;
2150}
2151
2152static 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));
2176                    sdkctl->on_socket_connection(sdkctl->opaque, sdkctl, ASIO_STATE_FAILED);
2177                    break;
2178
2179                default:
2180                    break;
2181        }
2182    }
2183    return ASIO_ACTION_DONE;
2184}
2185
2186static void
2187_sdkctl_do_handshake(SDKCtlSocket* sdkctl)
2188{
2189#ifndef HOST_WORDS_BIGENDIAN
2190static const char _host_end = 0;
2191#else
2192static const char _host_end = 1;
2193#endif
2194
2195    D("SDKCtl %s: Sending endianness: %d", sdkctl->service_name, _host_end);
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}
2202