summaryrefslogtreecommitdiff
path: root/gisi/pipe.c
diff options
context:
space:
mode:
authorRémi Denis-Courmont <remi.denis-courmont@nokia.com>2009-08-07 15:19:12 +0300
committerAki Niemi <aki.niemi@nokia.com>2009-08-14 16:56:52 +0300
commit2bfe1a24b939687d06ac22b9374468316f33eeac (patch)
tree19342c5f6a1f56e7d1b7ed85820be62abbef78e8 /gisi/pipe.c
parent2076a044ecc2e9af5e188ad2de312520d6540739 (diff)
downloadofono-2bfe1a24b939687d06ac22b9374468316f33eeac.tar.gz
Phonet: basic pipe owner support
This will be needed for GPRS support.
Diffstat (limited to 'gisi/pipe.c')
-rw-r--r--gisi/pipe.c314
1 files changed, 314 insertions, 0 deletions
diff --git a/gisi/pipe.c b/gisi/pipe.c
new file mode 100644
index 00000000..73116dc9
--- /dev/null
+++ b/gisi/pipe.c
@@ -0,0 +1,314 @@
+/*
+ * This file is part of oFono - Open Source Telephony
+ *
+ * Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+ *
+ * Contact: Rémi Denis-Courmont <remi.denis-courmont@nokia.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdint.h>
+#include <stdbool.h>
+#include <errno.h>
+#include <glib.h>
+#include "client.h"
+#include "pipe.h"
+
+#define PN_PIPE 0xd9
+
+typedef struct {
+ uint8_t dummy;
+ uint8_t cmd;
+ uint8_t state_after;
+ uint8_t priority;
+
+ uint16_t object1;
+ uint8_t type1;
+ uint8_t pad;
+
+ uint16_t object2;
+ uint8_t type2;
+ uint8_t n_sb;
+} _isi_pipe_create_req_t;
+
+typedef struct {
+ uint8_t cmd;
+ uint8_t pipe_handle;
+ uint8_t pad;
+} isi_pipe_enable_req_t;
+
+typedef struct {
+ uint8_t cmd;
+ uint8_t pipe_handle;
+ uint8_t state_after;
+} isi_pipe_reset_req_t;
+
+typedef struct {
+ uint8_t cmd;
+ uint8_t pipe_handle;
+} isi_pipe_remove_req_t;
+
+typedef struct {
+ uint8_t cmd;
+ uint8_t pipe_handle;
+ uint8_t error_code;
+
+ uint8_t error1;
+ uint8_t error2;
+} isi_pipe_resp_t;
+
+#define PN_PIPE_INVALID_HANDLE 0xff
+
+enum {
+ PNS_PIPE_CREATE_REQ,
+ PNS_PIPE_CREATE_RESP,
+ PNS_PIPE_REMOVE_REQ,
+ PNS_PIPE_REMOVE_RESP,
+ PNS_PIPE_RESET_REQ,
+ PNS_PIPE_RESET_RESP,
+ PNS_PIPE_ENABLE_REQ,
+ PNS_PIPE_ENABLE_RESP,
+ PNS_PIPE_REDIRECT_REQ,
+ PNS_PIPE_REDIRECT_RESP,
+ PNS_PIPE_DISABLE_REQ,
+ PNS_PIPE_DISABLE_RESP,
+};
+
+enum { /* error codes */
+ PN_PIPE_NO_ERROR,
+ PN_PIPE_ERR_INVALID_PARAM,
+ PN_PIPE_ERR_INVALID_HANDLE,
+ PN_PIPE_ERR_INVALID_CTRL_ID,
+ PN_PIPE_ERR_NOT_ALLOWED,
+ PN_PIPE_ERR_PEP_IN_USE,
+ PN_PIPE_ERR_OVERLOAD,
+ PN_PIPE_ERR_DEV_DISCONNECTED,
+ PN_PIPE_ERR_TIMEOUT,
+ PN_PIPE_ERR_ALL_PIPES_IN_USE,
+ PN_PIPE_ERR_GENERAL,
+ PN_PIPE_ERR_NOT_SUPPORTED,
+};
+
+enum { /* initial pipe state */
+ PN_PIPE_DISABLE,
+ PN_PIPE_ENABLE,
+};
+
+enum {
+ PN_MSG_PRIORITY_LOW = 1,
+ PN_MSG_PRIORITY_HIGH,
+};
+
+struct _GIsiPipe {
+ GIsiClient *client;
+ int error;
+ uint8_t handle;
+ bool enabled;
+ bool enabling;
+};
+
+static int g_isi_pipe_error(uint8_t code)
+{
+ static const int codes[] = {
+ [PN_PIPE_NO_ERROR] = 0,
+ [PN_PIPE_ERR_INVALID_PARAM] = -EINVAL,
+ [PN_PIPE_ERR_INVALID_HANDLE] = -EBADF,
+ [PN_PIPE_ERR_INVALID_CTRL_ID] = -ENOTSUP,
+ [PN_PIPE_ERR_NOT_ALLOWED] = -EPERM,
+ [PN_PIPE_ERR_PEP_IN_USE] = -EBUSY,
+ [PN_PIPE_ERR_OVERLOAD] = -ENOBUFS,
+ [PN_PIPE_ERR_DEV_DISCONNECTED] = -ENETDOWN,
+ [PN_PIPE_ERR_TIMEOUT] = -ETIMEDOUT,
+ [PN_PIPE_ERR_ALL_PIPES_IN_USE] = -ENFILE,
+ [PN_PIPE_ERR_GENERAL] = -EAGAIN,
+ [PN_PIPE_ERR_NOT_SUPPORTED] = -ENOSYS,
+ };
+
+ if (code == PN_PIPE_NO_ERROR ||
+ ((code < sizeof(codes) / sizeof(codes[0])) && codes[code]))
+ return codes[code];
+ return -EBADMSG;
+}
+
+static bool g_isi_pipe_created(GIsiClient *client,
+ const void *restrict data, size_t len,
+ uint16_t object, void *opaque)
+{
+ GIsiPipe *pipe = opaque;
+ const isi_pipe_resp_t *resp = data;
+
+ if (len < 5 ||
+ resp->cmd != PNS_PIPE_CREATE_RESP)
+ return false;
+
+ if (resp->pipe_handle != PN_PIPE_INVALID_HANDLE) {
+ pipe->handle = resp->pipe_handle;
+ if (pipe->enabling)
+ g_isi_pipe_start(pipe);
+ } else
+ pipe->error = g_isi_pipe_error(resp->error_code);
+ return true;
+}
+
+/**
+ * Create a Phonet pipe in disabled state and with low priority.
+ * @param obj1 Object handle of the first end point
+ * @param obj2 Object handle of the second end point
+ * @param type1 Type of the first end point
+ * @param type2 Type of the second end point
+ * @return a pipe object on success, NULL on error.
+ */
+GIsiPipe *g_isi_pipe_create(uint16_t obj1, uint16_t obj2,
+ uint8_t type1, uint8_t type2)
+{
+ _isi_pipe_create_req_t msg = {
+ .cmd = PNS_PIPE_CREATE_REQ,
+ .state_after = PN_PIPE_DISABLE,
+ .priority = PN_MSG_PRIORITY_LOW,
+ .object1 = obj1,
+ .type1 = type1,
+ .object2 = obj2,
+ .type2 = type2,
+ .n_sb = 0,
+ };
+ GIsiPipe *pipe = g_malloc(sizeof(*pipe));
+
+ pipe->client = g_isi_client_create(PN_PIPE);
+ pipe->error = 0;
+ pipe->enabling = false;
+ pipe->enabled = false;
+ pipe->handle = PN_PIPE_INVALID_HANDLE;
+
+ if (pipe->client == NULL ||
+ g_isi_request_make(pipe->client, &msg.cmd, sizeof(msg) - 1, 3,
+ g_isi_pipe_created, pipe) == NULL)
+ goto error;
+
+ return pipe;
+
+error:
+ if (pipe->client)
+ g_isi_client_destroy(pipe->client);
+ g_free(pipe);
+ return NULL;
+}
+
+static const isi_pipe_resp_t *
+g_isi_pipe_check_resp(const GIsiPipe *pipe, uint8_t cmd,
+ const void *restrict data, size_t len)
+{
+ const isi_pipe_resp_t *resp = data;
+
+ if ((len < 5) || (resp->cmd != cmd) ||
+ (resp->pipe_handle != pipe->handle))
+ return NULL;
+ return resp;
+}
+
+static bool g_isi_pipe_enabled(GIsiClient *client,
+ const void *restrict data, size_t len,
+ uint16_t object, void *opaque)
+{
+ GIsiPipe *pipe = opaque;
+ const isi_pipe_resp_t *resp;
+
+ resp = g_isi_pipe_check_resp(pipe, PNS_PIPE_ENABLE_RESP, data, len);
+ if (!resp)
+ return false;
+
+ pipe->error = g_isi_pipe_error(resp->error_code);
+ pipe->enabling = false;
+ if (!pipe->error)
+ pipe->enabled = true;
+ return true;
+}
+
+static GIsiRequest *g_isi_pipe_enable(GIsiPipe *pipe)
+{
+ isi_pipe_enable_req_t msg = {
+ .cmd = PNS_PIPE_ENABLE_REQ,
+ .pipe_handle = pipe->handle,
+ };
+ const size_t len = 3;
+
+ return g_isi_request_make(pipe->client, &msg, len, 5,
+ g_isi_pipe_enabled, pipe);
+}
+
+/**
+ * Enable a pipe, i.e. turn on data transfer between the two end points.
+ * @param pipe pipe as returned from g_isi_pipe_create()
+ * @return 0 on success or an error code
+ */
+int g_isi_pipe_start(GIsiPipe *pipe)
+{
+ if (pipe->error)
+ return pipe->error;
+ if (pipe->enabling || pipe->enabled)
+ return 0;
+
+ if (pipe->handle != PN_PIPE_INVALID_HANDLE)
+ g_isi_pipe_enable(pipe);
+ else
+ pipe->enabling = true;
+}
+
+/* Not very useful, it will never have time to trigger */
+static bool g_isi_pipe_removed(GIsiClient *client,
+ const void *restrict data, size_t len,
+ uint16_t object, void *opaque)
+{
+ GIsiPipe *pipe = opaque;
+ const isi_pipe_resp_t *resp;
+
+ resp = g_isi_pipe_check_resp(pipe, PNS_PIPE_REMOVE_RESP, data, len);
+ if (!resp)
+ return false;
+
+ pipe->handle = PN_PIPE_INVALID_HANDLE;
+ pipe->error = -EPIPE;
+ return true;
+}
+
+
+static GIsiRequest *g_isi_pipe_remove(GIsiPipe *pipe)
+{
+ isi_pipe_remove_req_t msg = {
+ .cmd = PNS_PIPE_REMOVE_REQ,
+ .pipe_handle = pipe->handle,
+ };
+ const size_t len = 3;
+
+ return g_isi_request_make(pipe->client, &msg, len, 5,
+ g_isi_pipe_removed, pipe);
+}
+
+/**
+ * Destroy a pipe. If it was connected, it is removed.
+ * @param pipe pipe as returned from g_isi_pipe_create()
+ */
+void g_isi_pipe_destroy(GIsiPipe *pipe)
+{
+ if (!pipe->error)
+ g_isi_pipe_remove(pipe);
+ g_isi_client_destroy(pipe->client);
+ g_free(pipe);
+}