| /* |
| * 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; |
| } |