300 lines
8.1 KiB
C
300 lines
8.1 KiB
C
/*
|
|
* Copyright (C) 2016 The Android Open Source Project
|
|
*
|
|
* Permission is hereby granted, free of charge, to any person
|
|
* obtaining a copy of this software and associated documentation
|
|
* files (the "Software"), to deal in the Software without
|
|
* restriction, including without limitation the rights to use, copy,
|
|
* modify, merge, publish, distribute, sublicense, and/or sell copies
|
|
* of the Software, and to permit persons to whom the Software is
|
|
* furnished to do so, subject to the following conditions:
|
|
*
|
|
* The above copyright notice and this permission notice shall be
|
|
* included in all copies or substantial portions of the Software.
|
|
*
|
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
|
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
|
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
|
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
|
|
* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
|
|
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
|
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
* SOFTWARE.
|
|
*/
|
|
|
|
#include <trusty/trusty_ipc.h>
|
|
#include <trusty/util.h>
|
|
|
|
#define LOCAL_LOG 0
|
|
|
|
static int sync_ipc_on_connect_complete(struct trusty_ipc_chan *chan)
|
|
{
|
|
trusty_assert(chan);
|
|
|
|
chan->complete = 1;
|
|
return TRUSTY_EVENT_HANDLED;
|
|
}
|
|
|
|
static int sync_ipc_on_message(struct trusty_ipc_chan *chan)
|
|
{
|
|
trusty_assert(chan);
|
|
|
|
chan->complete = 1;
|
|
return TRUSTY_EVENT_HANDLED;
|
|
}
|
|
|
|
static int sync_ipc_on_disconnect(struct trusty_ipc_chan *chan)
|
|
{
|
|
trusty_assert(chan);
|
|
|
|
chan->complete = TRUSTY_ERR_CHANNEL_CLOSED;
|
|
return TRUSTY_EVENT_HANDLED;
|
|
}
|
|
|
|
static int wait_for_complete(struct trusty_ipc_chan *chan)
|
|
{
|
|
int rc;
|
|
|
|
chan->complete = 0;
|
|
for (;;) {
|
|
rc = trusty_ipc_poll_for_event(chan);
|
|
if (rc < 0)
|
|
return rc;
|
|
|
|
if (chan->complete)
|
|
break;
|
|
|
|
trusty_ipc_dev_idle(chan->dev);
|
|
}
|
|
|
|
return chan->complete;
|
|
}
|
|
|
|
static int wait_for_connect(struct trusty_ipc_chan *chan)
|
|
{
|
|
trusty_debug("%s: chan %x: waiting for connect\n", __func__,
|
|
(int)chan->handle);
|
|
return wait_for_complete(chan);
|
|
}
|
|
|
|
static int wait_for_send(struct trusty_ipc_chan *chan)
|
|
{
|
|
trusty_debug("%s: chan %d: waiting for send\n", __func__, chan->handle);
|
|
return wait_for_complete(chan);
|
|
}
|
|
|
|
static int wait_for_reply(struct trusty_ipc_chan *chan)
|
|
{
|
|
trusty_debug("%s: chan %d: waiting for reply\n", __func__, chan->handle);
|
|
return wait_for_complete(chan);
|
|
}
|
|
|
|
static struct trusty_ipc_ops sync_ipc_ops = {
|
|
.on_connect_complete = sync_ipc_on_connect_complete,
|
|
.on_message = sync_ipc_on_message,
|
|
.on_disconnect = sync_ipc_on_disconnect,
|
|
};
|
|
|
|
void trusty_ipc_chan_init(struct trusty_ipc_chan *chan,
|
|
struct trusty_ipc_dev *dev)
|
|
{
|
|
trusty_assert(chan);
|
|
trusty_assert(dev);
|
|
|
|
trusty_memset(chan, 0, sizeof(*chan));
|
|
|
|
chan->handle = INVALID_IPC_HANDLE;
|
|
chan->dev = dev;
|
|
chan->ops = &sync_ipc_ops;
|
|
chan->ops_ctx = chan;
|
|
}
|
|
|
|
int trusty_ipc_connect(struct trusty_ipc_chan *chan, const char *port,
|
|
bool wait)
|
|
{
|
|
int rc;
|
|
|
|
trusty_assert(chan);
|
|
trusty_assert(chan->dev);
|
|
trusty_assert(chan->handle == INVALID_IPC_HANDLE);
|
|
trusty_assert(port);
|
|
|
|
rc = trusty_ipc_dev_connect(chan->dev, port, (uint64_t)(uintptr_t)chan);
|
|
if (rc < 0) {
|
|
trusty_error("%s: init connection failed (%d)\n", __func__, rc);
|
|
return rc;
|
|
}
|
|
chan->handle = (handle_t)rc;
|
|
trusty_debug("chan->handle: %x\n", (int)chan->handle);
|
|
|
|
/* got valid channel */
|
|
if (wait) {
|
|
rc = wait_for_connect(chan);
|
|
if (rc < 0) {
|
|
trusty_error("%s: wait for connect failed (%d)\n", __func__, rc);
|
|
trusty_ipc_close(chan);
|
|
}
|
|
}
|
|
|
|
return rc;
|
|
}
|
|
|
|
int trusty_ipc_close(struct trusty_ipc_chan *chan)
|
|
{
|
|
int rc;
|
|
|
|
trusty_assert(chan);
|
|
|
|
rc = trusty_ipc_dev_close(chan->dev, chan->handle);
|
|
chan->handle = INVALID_IPC_HANDLE;
|
|
|
|
return rc;
|
|
}
|
|
|
|
int trusty_ipc_send(struct trusty_ipc_chan *chan,
|
|
const struct trusty_ipc_iovec *iovs, size_t iovs_cnt,
|
|
bool wait)
|
|
{
|
|
int rc;
|
|
|
|
trusty_assert(chan);
|
|
trusty_assert(chan->dev);
|
|
trusty_assert(chan->handle);
|
|
|
|
Again:
|
|
rc = trusty_ipc_dev_send(chan->dev, chan->handle, iovs, iovs_cnt);
|
|
if (rc == TRUSTY_ERR_SEND_BLOCKED) {
|
|
if (wait) {
|
|
rc = wait_for_send(chan);
|
|
if (rc < 0) {
|
|
trusty_error("%s: wait to send failed (%d)\n", __func__, rc);
|
|
return rc;
|
|
}
|
|
goto Again;
|
|
}
|
|
}
|
|
return rc;
|
|
}
|
|
|
|
int trusty_ipc_recv(struct trusty_ipc_chan *chan,
|
|
const struct trusty_ipc_iovec *iovs, size_t iovs_cnt,
|
|
bool wait)
|
|
{
|
|
int rc;
|
|
trusty_assert(chan);
|
|
trusty_assert(chan->dev);
|
|
trusty_assert(chan->handle);
|
|
|
|
Again:
|
|
rc = trusty_ipc_dev_recv(chan->dev, chan->handle, iovs, iovs_cnt);
|
|
if (rc == TRUSTY_ERR_NO_MSG) {
|
|
if (wait) {
|
|
rc = wait_for_reply(chan);
|
|
if (rc < 0) {
|
|
trusty_error("%s: wait to reply failed (%d)\n", __func__, rc);
|
|
return rc;
|
|
}
|
|
goto Again;
|
|
}
|
|
}
|
|
|
|
return rc;
|
|
}
|
|
|
|
int trusty_ipc_poll_for_event(struct trusty_ipc_chan *chan)
|
|
{
|
|
int rc;
|
|
struct trusty_ipc_event evt;
|
|
trusty_assert(chan && chan->ops);
|
|
|
|
rc = trusty_ipc_dev_get_event(chan->dev, chan->handle, &evt);
|
|
if (rc) {
|
|
trusty_error("%s: get event failed (%d)\n", __func__, rc);
|
|
return rc;
|
|
}
|
|
|
|
/* check if we have an event */
|
|
if (!evt.event) {
|
|
trusty_debug("%s: no event\n", __func__);
|
|
return TRUSTY_EVENT_NONE;
|
|
}
|
|
|
|
/* check if we have raw event handler */
|
|
if (chan->ops->on_raw_event) {
|
|
/* invoke it first */
|
|
rc = chan->ops->on_raw_event(chan, &evt);
|
|
if (rc < 0) {
|
|
trusty_error("%s: chan %d: raw event cb returned (%d)\n", __func__,
|
|
chan->handle, rc);
|
|
return rc;
|
|
}
|
|
if (rc > 0)
|
|
return rc; /* handled */
|
|
}
|
|
|
|
if (evt.event & IPC_HANDLE_POLL_ERROR) {
|
|
/* something is very wrong */
|
|
trusty_error("%s: chan %d: chan in error state\n", __func__,
|
|
chan->handle);
|
|
return TRUSTY_ERR_GENERIC;
|
|
}
|
|
|
|
/* send unblocked should be handled first as it is edge truggered event */
|
|
if (evt.event & IPC_HANDLE_POLL_SEND_UNBLOCKED) {
|
|
if (chan->ops->on_send_unblocked) {
|
|
rc = chan->ops->on_send_unblocked(chan);
|
|
if (rc < 0) {
|
|
trusty_error("%s: chan %d: send unblocked cb returned (%d)\n",
|
|
__func__, chan->handle, rc);
|
|
return rc;
|
|
}
|
|
if (rc > 0)
|
|
return rc; /* handled */
|
|
}
|
|
}
|
|
|
|
/* check for connection complete */
|
|
if (evt.event & IPC_HANDLE_POLL_READY) {
|
|
if (chan->ops->on_connect_complete) {
|
|
rc = chan->ops->on_connect_complete(chan);
|
|
if (rc < 0) {
|
|
trusty_error("%s: chan %d: ready cb returned (%d)\n", __func__,
|
|
chan->handle, rc);
|
|
return rc;
|
|
}
|
|
if (rc > 0)
|
|
return rc; /* handled */
|
|
}
|
|
}
|
|
|
|
/* check for incomming messages */
|
|
if (evt.event & IPC_HANDLE_POLL_MSG) {
|
|
if (chan->ops->on_message) {
|
|
rc = chan->ops->on_message(chan);
|
|
if (rc < 0) {
|
|
trusty_error("%s: chan %d: msg cb returned (%d)\n", __func__,
|
|
chan->handle, rc);
|
|
return rc;
|
|
}
|
|
if (rc > 0)
|
|
return rc;
|
|
}
|
|
}
|
|
|
|
/* check for hangups */
|
|
if (evt.event & IPC_HANDLE_POLL_HUP) {
|
|
if (chan->ops->on_disconnect) {
|
|
rc = chan->ops->on_disconnect(chan);
|
|
if (rc < 0) {
|
|
trusty_error("%s: chan %d: hup cb returned (%d)\n", __func__,
|
|
chan->handle, rc);
|
|
return rc;
|
|
}
|
|
if (rc > 0)
|
|
return rc;
|
|
}
|
|
}
|
|
|
|
return TRUSTY_ERR_NONE;
|
|
}
|