ofono/gisi/pipe.c

315 lines
7.0 KiB
C
Raw Normal View History

/*
* 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(NULL, 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);
}