/*
 * Copyright (C) 2008 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#ifndef ANDROID_INCLUDE_HARDWARE_QEMUD_H
#define ANDROID_INCLUDE_HARDWARE_QEMUD_H

#include <cutils/sockets.h>

/* the following is helper code that is used by the QEMU-specific
 * hardware HAL modules to communicate with the emulator program
 * through the 'qemud' multiplexing daemon.
 *
 * see the documentation comments for details in
 * development/emulator/qemud/qemud.c
 *
 * all definitions here are built into the HAL module to avoid
 * having to write a tiny shared library for this.
 */

/* we expect the D macro to be defined to a function macro
 * that sends its formatted string argument(s) to the log.
 * If not, ignore the traces.
 */
#ifndef D
#  define  D(...)  ((void)0)
#endif

static __inline__ int
qemud_fd_write(int  fd, const void*  buff, int  len)
{
    int  len2;
    do {
        len2 = write(fd, buff, len);
    } while (len2 < 0 && errno == EINTR);
    return len2;
}

static __inline__ int
qemud_fd_read(int  fd, void*  buff, int  len)
{
    int  len2;
    do {
        len2 = read(fd, buff, len);
    } while (len2 < 0 && errno == EINTR);
    return len2;
}

static __inline__ int
qemud_channel_open(const char*  name)
{
    int  fd;
    int  namelen = strlen(name);
    char answer[2];

    /* connect to qemud control socket */
    fd = socket_local_client( "qemud",
                              ANDROID_SOCKET_NAMESPACE_RESERVED,
                              SOCK_STREAM );
    if (fd < 0) {
        D("no qemud control socket: %s", strerror(errno));
        return -1;
    }

    /* send service name to connect */
    if (qemud_fd_write(fd, name, namelen) != namelen) {
        D("can't send service name to qemud: %s",
           strerror(errno));
        close(fd);
        return -1;
    }

    /* read answer from daemon */
    if (qemud_fd_read(fd, answer, 2) != 2 ||
        answer[0] != 'O' || answer[1] != 'K') {
        D("cant' connect to %s service through qemud", name);
        close(fd);
        return -1;
    }
    return fd;
}

static __inline__ int
qemud_channel_send(int  fd, const void*  msg, int  msglen)
{
    char  header[5];

    if (msglen < 0)
        msglen = strlen((const char*)msg);

    if (msglen == 0)
        return 0;

    snprintf(header, sizeof header, "%04x", msglen);
    if (qemud_fd_write(fd, header, 4) != 4) {
        D("can't write qemud frame header: %s", strerror(errno));
        return -1;
    }

    if (qemud_fd_write(fd, msg, msglen) != msglen) {
        D("can4t write qemud frame payload: %s", strerror(errno));
        return -1;
    }
    return 0;
}

static __inline__ int
qemud_channel_recv(int  fd, void*  msg, int  msgsize)
{
    char  header[5];
    int   size, avail;

    if (qemud_fd_read(fd, header, 4) != 4) {
        D("can't read qemud frame header: %s", strerror(errno));
        return -1;
    }
    header[4] = 0;
    if (sscanf(header, "%04x", &size) != 1) {
        D("malformed qemud frame header: '%.*s'", 4, header);
        return -1;
    }
    if (size > msgsize)
        return -1;

    if (qemud_fd_read(fd, msg, size) != size) {
        D("can't read qemud frame payload: %s", strerror(errno));
        return -1;
    }
    return size;
}

#endif /* ANDROID_INCLUDE_HARDWARE_QEMUD_H */
