ofono/gril/gril.c

1289 lines
26 KiB
C

/*
*
* RIL library with GLib integration
*
* Copyright (C) 2008-2011 Intel Corporation. All rights reserved.
* Copyright (C) 2012-2013 Canonical Ltd.
* Copyright (C) 2013 Jolla Ltd.
*
* 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 <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <ctype.h>
#include <errno.h>
#include <sys/socket.h>
#include <sys/un.h>
#include <netinet/in.h>
#include <unistd.h>
#include <glib.h>
#include <ofono/log.h>
#include "ringbuffer.h"
#include "gril.h"
#include "grilutil.h"
#define RIL_TRACE(ril, fmt, arg...) do { \
if (ril->trace == TRUE) \
ofono_debug(fmt, ## arg); \
} while (0)
struct ril_request {
gchar *data;
guint data_len;
gint req;
gint id;
guint gid;
GRilResponseFunc callback;
gpointer user_data;
GDestroyNotify notify;
};
struct ril_notify_node {
guint id;
guint gid;
GRilNotifyFunc callback;
gpointer user_data;
gboolean destroyed;
};
typedef gboolean (*node_remove_func)(struct ril_notify_node *node,
gpointer user_data);
struct ril_notify {
GSList *nodes;
};
struct ril_s {
gint ref_count; /* Ref count */
gint next_cmd_id; /* Next command id */
guint next_notify_id; /* Next notify id */
guint next_gid; /* Next group id */
GRilIO *io; /* GRil IO */
GQueue *command_queue; /* Command queue */
GQueue *out_queue; /* Commands sent/been sent */
guint req_bytes_written; /* bytes written from req */
GHashTable *notify_list; /* List of notification reg */
GRilDisconnectFunc user_disconnect; /* user disconnect func */
gpointer user_disconnect_data; /* user disconnect data */
guint read_so_far; /* Number of bytes processed */
gboolean suspended; /* Are we suspended? */
gboolean debug;
gboolean trace;
gint timeout_source;
gboolean destroyed; /* Re-entrancy guard */
gboolean in_read_handler; /* Re-entrancy guard */
gboolean in_notify;
enum ofono_ril_vendor vendor;
int slot;
GRilMsgIdToStrFunc req_to_string;
GRilMsgIdToStrFunc unsol_to_string;
};
struct _GRil {
gint ref_count;
struct ril_s *parent;
guint group;
};
struct req_hdr {
/* Warning: length is stored in network order */
uint32_t length;
uint32_t reqid;
uint32_t serial;
};
#define RIL_PRINT_BUF_SIZE 8096
char print_buf[RIL_PRINT_BUF_SIZE] __attribute__((used));
static void ril_wakeup_writer(struct ril_s *ril);
static const char *request_id_to_string(struct ril_s *ril, int req)
{
const char *str = NULL;
if (ril->req_to_string)
str = ril->req_to_string(req);
if (str == NULL)
str = ril_request_id_to_string(req);
return str;
}
static const char *unsol_request_to_string(struct ril_s *ril, int req)
{
const char *str = NULL;
if (ril->unsol_to_string)
str = ril->unsol_to_string(req);
if (str == NULL)
str = ril_unsol_request_to_string(req);
return str;
}
static void ril_notify_node_destroy(gpointer data, gpointer user_data)
{
struct ril_notify_node *node = data;
g_free(node);
}
static void ril_notify_destroy(gpointer user_data)
{
struct ril_notify *notify = user_data;
g_slist_foreach(notify->nodes, ril_notify_node_destroy, NULL);
g_slist_free(notify->nodes);
g_free(notify);
}
static gint ril_notify_node_compare_by_id(gconstpointer a, gconstpointer b)
{
const struct ril_notify_node *node = a;
guint id = GPOINTER_TO_UINT(b);
if (node->id < id)
return -1;
if (node->id > id)
return 1;
return 0;
}
static gboolean ril_unregister_all(struct ril_s *ril,
gboolean mark_only,
node_remove_func func,
gpointer userdata)
{
GHashTableIter iter;
struct ril_notify *notify;
struct ril_notify_node *node;
gpointer key, value;
GSList *p;
GSList *c;
GSList *t;
if (ril->notify_list == NULL)
return FALSE;
g_hash_table_iter_init(&iter, ril->notify_list);
while (g_hash_table_iter_next(&iter, &key, &value)) {
notify = value;
p = NULL;
c = notify->nodes;
while (c) {
node = c->data;
if (func(node, userdata) != TRUE) {
p = c;
c = c->next;
continue;
}
if (mark_only) {
node->destroyed = TRUE;
p = c;
c = c->next;
continue;
}
if (p)
p->next = c->next;
else
notify->nodes = c->next;
ril_notify_node_destroy(node, NULL);
t = c;
c = c->next;
g_slist_free_1(t);
}
if (notify->nodes == NULL)
g_hash_table_iter_remove(&iter);
}
return TRUE;
}
/*
* This function creates a RIL request. For a good reference on
* the layout of RIL requests, responses, and unsolicited requests
* see:
*
* https://wiki.mozilla.org/B2G/RIL
*/
static struct ril_request *ril_request_create(struct ril_s *ril,
guint gid,
const gint req,
const gint id,
struct parcel *rilp,
GRilResponseFunc func,
gpointer user_data,
GDestroyNotify notify,
gboolean wakeup)
{
struct ril_request *r;
struct req_hdr header;
guint data_len = 0;
if (rilp != NULL)
data_len = rilp->size;
r = g_try_new0(struct ril_request, 1);
if (r == NULL) {
ofono_error("%s Out of memory", __func__);
return NULL;
}
/* Full request size: header size plus buffer length */
r->data_len = data_len + sizeof(header);
r->data = g_try_new(char, r->data_len);
if (r->data == NULL) {
ofono_error("ril_request: can't allocate new request.");
g_free(r);
return NULL;
}
/* Length does not include the length field. Network order. */
header.length = htonl(r->data_len - sizeof(header.length));
header.reqid = req;
header.serial = id;
/* copy header */
memcpy(r->data, &header, sizeof(header));
/* copy request data */
if (data_len)
memcpy(r->data + sizeof(header), rilp->data, data_len);
r->req = req;
r->gid = gid;
r->id = id;
r->callback = func;
r->user_data = user_data;
r->notify = notify;
return r;
}
static void ril_request_destroy(struct ril_request *req)
{
if (req->notify)
req->notify(req->user_data);
g_free(req->data);
g_free(req);
}
static void ril_cleanup(struct ril_s *p)
{
/* Cleanup pending commands */
if (p->command_queue) {
g_queue_free(p->command_queue);
p->command_queue = NULL;
}
if (p->out_queue) {
g_queue_free(p->out_queue);
p->out_queue = NULL;
}
/* Cleanup registered notifications */
if (p->notify_list) {
g_hash_table_destroy(p->notify_list);
p->notify_list = NULL;
}
if (p->timeout_source) {
g_source_remove(p->timeout_source);
p->timeout_source = 0;
}
}
void g_ril_set_disconnect_function(GRil *ril, GRilDisconnectFunc disconnect,
gpointer user_data)
{
ril->parent->user_disconnect = disconnect;
ril->parent->user_disconnect_data = user_data;
}
static void io_disconnect(gpointer user_data)
{
struct ril_s *ril = user_data;
ofono_error("%s: disconnected from rild", __func__);
ril_cleanup(ril);
g_ril_io_unref(ril->io);
ril->io = NULL;
if (ril->user_disconnect)
ril->user_disconnect(ril->user_disconnect_data);
}
static void handle_response(struct ril_s *p, struct ril_msg *message)
{
guint count = g_queue_get_length(p->command_queue);
struct ril_request *req;
gboolean found = FALSE;
guint i, len;
gint id;
for (i = 0; i < count; i++) {
req = g_queue_peek_nth(p->command_queue, i);
if (req->id == message->serial_no) {
found = TRUE;
message->req = req->req;
if (message->error != RIL_E_SUCCESS)
RIL_TRACE(p, "[%d,%04d]< %s failed %s",
p->slot, message->serial_no,
request_id_to_string(p, message->req),
ril_error_to_string(message->error));
req = g_queue_pop_nth(p->command_queue, i);
if (req->callback)
req->callback(message, req->user_data);
/* gril may have been destroyed in the request callback */
if (p->destroyed) {
ril_request_destroy(req);
return;
}
len = g_queue_get_length(p->out_queue);
for (i = 0; i < len; i++) {
id = GPOINTER_TO_INT(g_queue_peek_nth(
p->out_queue, i));
if (id == req->id) {
g_queue_pop_nth(p->out_queue, i);
break;
}
}
ril_request_destroy(req);
if (g_queue_peek_head(p->command_queue))
ril_wakeup_writer(p);
break;
}
}
if (found == FALSE)
ofono_error("No matching request for reply: %s serial_no: %d!",
request_id_to_string(p, message->req),
message->serial_no);
}
static gboolean node_check_destroyed(struct ril_notify_node *node,
gpointer userdata)
{
gboolean val = GPOINTER_TO_UINT(userdata);
if (node->destroyed == val)
return TRUE;
return FALSE;
}
static void handle_unsol_req(struct ril_s *p, struct ril_msg *message)
{
struct ril_notify *notify;
if (p->notify_list == NULL)
return;
p->in_notify = TRUE;
notify = g_hash_table_lookup(p->notify_list, &message->req);
if (notify != NULL) {
GSList *list_item;
for (list_item = notify->nodes; list_item;
list_item = g_slist_next(list_item)) {
struct ril_notify_node *node = list_item->data;
if (node->destroyed)
continue;
node->callback(message, node->user_data);
}
} else {
/* Only log events not being listended for... */
DBG("RIL Event slot %d: %s\n",
p->slot, unsol_request_to_string(p, message->req));
}
p->in_notify = FALSE;
/* Now destroy nodes possibly removed by callbacks */
if (notify != NULL)
ril_unregister_all(p, FALSE, node_check_destroyed,
GUINT_TO_POINTER(TRUE));
}
static void dispatch(struct ril_s *p, struct ril_msg *message)
{
int32_t *unsolicited_field, *id_num_field;
gchar *bufp = message->buf;
gchar *datap;
gsize data_len;
/* This could be done with a struct/union... */
unsolicited_field = (int32_t *) (void *) bufp;
if (*unsolicited_field)
message->unsolicited = TRUE;
else
message->unsolicited = FALSE;
bufp += 4;
id_num_field = (int32_t *) (void *) bufp;
if (message->unsolicited) {
message->req = (int) *id_num_field;
/*
* A RIL Unsolicited Event is two UINT32 fields ( unsolicited,
* and req/ev ), so subtract the length of the header from the
* overall length to calculate the length of the Event Data.
*/
data_len = message->buf_len - 8;
} else {
message->serial_no = (int) *id_num_field;
bufp += 4;
message->error = *((int32_t *) (void *) bufp);
/*
* A RIL Solicited Response is three UINT32 fields ( unsolicied,
* serial_no and error ), so subtract the length of the header
* from the overall length to calculate the length of the Event
* Data.
*/
data_len = message->buf_len - 12;
}
/* advance to start of data.. */
bufp += 4;
/*
* Now, use buffer for event data if present
*/
if (data_len) {
datap = g_try_malloc(data_len);
if (datap == NULL)
goto error;
/* Copy event bytes from message->buf into new buffer */
memcpy(datap, bufp, data_len);
/* Free buffer that includes header */
g_free(message->buf);
/* ...and replace with new buffer */
message->buf = datap;
message->buf_len = data_len;
} else {
/* Free buffer that includes header */
g_free(message->buf);
/* To know if there was no data when parsing */
message->buf = NULL;
message->buf_len = 0;
}
if (message->unsolicited == TRUE)
handle_unsol_req(p, message);
else
handle_response(p, message);
error:
g_free(message->buf);
g_free(message);
}
static struct ril_msg *read_fixed_record(struct ril_s *p,
const guchar *bytes, gsize *len)
{
struct ril_msg *message;
unsigned message_len, plen;
/* First four bytes are length in TCP byte order (Big Endian) */
plen = ntohl(*((uint32_t *) (void *) bytes));
bytes += 4;
/*
* TODO: Verify that 8k is the max message size from rild.
*
* This condition shouldn't happen. If it does
* there are three options:
*
* 1) Exit; ofono will restart via DBus (this is what we do now)
* 2) Consume the bytes & continue
* 3) force a disconnect
*/
if (plen > GRIL_BUFFER_SIZE - 4) {
ofono_error("ERROR RIL parcel bigger than buffer (%u), exiting",
plen);
exit(1);
}
/*
* If we don't have the whole fixed record in the ringbuffer
* then return NULL & leave ringbuffer as is.
*/
message_len = *len - 4;
if (message_len < plen)
return NULL;
message = g_malloc(sizeof(struct ril_msg));
/* allocate ril_msg->buffer */
message->buf_len = plen;
message->buf = g_malloc(plen);
/* Copy bytes into message buffer */
memmove(message->buf, (const void *) bytes, plen);
/* Indicate to caller size of record we extracted */
*len = plen + 4;
return message;
}
static void new_bytes(struct ring_buffer *rbuf, gpointer user_data)
{
struct ril_msg *message;
struct ril_s *p = user_data;
unsigned int len = ring_buffer_len(rbuf);
unsigned int wrap = ring_buffer_len_no_wrap(rbuf);
guchar *buf = ring_buffer_read_ptr(rbuf, p->read_so_far);
p->in_read_handler = TRUE;
while (p->suspended == FALSE && (p->read_so_far < len)) {
gsize rbytes = MIN(len - p->read_so_far, wrap - p->read_so_far);
if (rbytes < 4) {
DBG("Not enough bytes for header length: len: %d", len);
return;
}
/*
* This function attempts to read the next full length
* fixed message from the stream. if not all bytes are
* available, it returns NULL. otherwise it allocates
* and returns a ril_message with the copied bytes, and
* drains those bytes from the ring_buffer
*/
message = read_fixed_record(p, buf, &rbytes);
/* wait for the rest of the record... */
if (message == NULL)
break;
buf += rbytes;
p->read_so_far += rbytes;
/* TODO: need to better understand how wrap works! */
if (p->read_so_far == wrap) {
buf = ring_buffer_read_ptr(rbuf, p->read_so_far);
wrap = len;
}
dispatch(p, message);
ring_buffer_drain(rbuf, p->read_so_far);
len -= p->read_so_far;
wrap -= p->read_so_far;
p->read_so_far = 0;
}
p->in_read_handler = FALSE;
if (p->destroyed)
g_free(p);
}
/*
* This function is a GIOFunc and may be called directly or via an IO watch.
* The return value controls whether the watch stays active ( TRUE ), or is
* removed ( FALSE ).
*/
static gboolean can_write_data(gpointer data)
{
struct ril_s *ril = data;
struct ril_request *req;
gsize bytes_written, towrite, len;
guint qlen, oqlen;
gint id;
gboolean written = TRUE;
guint i, j;
qlen = g_queue_get_length(ril->command_queue);
if (qlen < 1)
return FALSE;
/* if the whole request was not written */
if (ril->req_bytes_written != 0) {
for (i = 0; i < qlen; i++) {
req = g_queue_peek_nth(ril->command_queue, i);
if (req) {
id = GPOINTER_TO_INT(g_queue_peek_head(
ril->out_queue));
if (req->id == id)
goto out;
} else {
return FALSE;
}
}
}
/* if no requests already sent */
oqlen = g_queue_get_length(ril->out_queue);
if (oqlen < 1) {
req = g_queue_peek_head(ril->command_queue);
if (req == NULL)
return FALSE;
g_queue_push_head(ril->out_queue, GINT_TO_POINTER(req->id));
goto out;
}
for (i = 0; i < qlen; i++) {
req = g_queue_peek_nth(ril->command_queue, i);
if (req == NULL)
return FALSE;
for (j = 0; j < oqlen; j++) {
id = GPOINTER_TO_INT(
g_queue_peek_nth(ril->out_queue, j));
if (req->id == id) {
written = TRUE;
break;
} else {
written = FALSE;
}
}
if (written == FALSE)
break;
}
/* watcher fired though requests already written */
if (written == TRUE)
return FALSE;
g_queue_push_head(ril->out_queue, GINT_TO_POINTER(req->id));
out:
len = req->data_len;
towrite = len - ril->req_bytes_written;
#ifdef WRITE_SCHEDULER_DEBUG
if (towrite > 5)
towrite = 5;
#endif
bytes_written = g_ril_io_write(ril->io,
req->data + ril->req_bytes_written,
towrite);
if (bytes_written == 0)
return FALSE;
ril->req_bytes_written += bytes_written;
if (bytes_written < towrite)
return TRUE;
else
ril->req_bytes_written = 0;
return FALSE;
}
static void ril_wakeup_writer(struct ril_s *ril)
{
g_ril_io_set_write_handler(ril->io, can_write_data, ril);
}
static void ril_suspend(struct ril_s *ril)
{
ril->suspended = TRUE;
g_ril_io_set_write_handler(ril->io, NULL, NULL);
g_ril_io_set_read_handler(ril->io, NULL, NULL);
g_ril_io_set_debug(ril->io, NULL, NULL);
}
static gboolean ril_set_debug(struct ril_s *ril,
GRilDebugFunc func, gpointer user_data)
{
if (ril->io == NULL)
return FALSE;
g_ril_io_set_debug(ril->io, func, user_data);
return TRUE;
}
static void ril_unref(struct ril_s *ril)
{
gboolean is_zero;
is_zero = g_atomic_int_dec_and_test(&ril->ref_count);
if (is_zero == FALSE)
return;
if (ril->io) {
ril_suspend(ril);
g_ril_io_unref(ril->io);
ril->io = NULL;
ril_cleanup(ril);
}
if (ril->in_read_handler)
ril->destroyed = TRUE;
else
g_free(ril);
}
static gboolean node_compare_by_group(struct ril_notify_node *node,
gpointer userdata)
{
guint group = GPOINTER_TO_UINT(userdata);
if (node->gid == group)
return TRUE;
return FALSE;
}
static struct ril_s *create_ril(const char *sock_path, unsigned int uid,
unsigned int gid)
{
struct ril_s *ril;
struct sockaddr_un addr;
int sk;
int r;
GIOChannel *io;
ril = g_try_new0(struct ril_s, 1);
if (ril == NULL)
return ril;
ril->ref_count = 1;
ril->next_cmd_id = 1;
ril->next_notify_id = 1;
ril->next_gid = 0;
ril->req_bytes_written = 0;
ril->trace = FALSE;
/* sock_path is allowed to be NULL for unit tests */
if (sock_path == NULL)
return ril;
sk = socket(AF_UNIX, SOCK_STREAM, 0);
if (sk < 0) {
ofono_error("create_ril: can't create unix socket: %s (%d)\n",
strerror(errno), errno);
goto error;
}
memset(&addr, 0, sizeof(addr));
addr.sun_family = AF_UNIX;
strncpy(addr.sun_path, sock_path, sizeof(addr.sun_path) - 1);
/* Drop root user last, otherwise we won't be able to change egid */
if (gid != 0 && setegid(gid) < 0)
ofono_error("%s: setegid(%d) failed: %s (%d)",
__func__, gid, strerror(errno), errno);
if (uid != 0 && seteuid(uid) < 0)
ofono_error("%s: seteuid(%d) failed: %s (%d)",
__func__, uid, strerror(errno), errno);
r = connect(sk, (struct sockaddr *) &addr, sizeof(addr));
/* Switch back to root as needed */
if (uid && seteuid(0) < 0)
ofono_error("%s: seteuid(0) failed: %s (%d)",
__func__, strerror(errno), errno);
if (gid && setegid(0) < 0)
ofono_error("%s: setegid(0) failed: %s (%d)",
__func__, strerror(errno), errno);
if (r < 0) {
ofono_error("create_ril: can't connect to RILD: %s (%d)\n",
strerror(errno), errno);
goto error;
}
io = g_io_channel_unix_new(sk);
if (io == NULL) {
ofono_error("create_ril: can't open RILD io channel: %s (%d)\n",
strerror(errno), errno);
goto error;
}
g_io_channel_set_close_on_unref(io, TRUE);
g_io_channel_set_flags(io, G_IO_FLAG_NONBLOCK, NULL);
ril->io = g_ril_io_new(io);
g_io_channel_unref(io);
if (ril->io == NULL) {
ofono_error("create_ril: can't create ril->io");
goto error;
}
g_ril_io_set_disconnect_function(ril->io, io_disconnect, ril);
ril->command_queue = g_queue_new();
if (ril->command_queue == NULL) {
ofono_error("create_ril: Couldn't create command_queue.");
goto error;
}
ril->out_queue = g_queue_new();
if (ril->out_queue == NULL) {
ofono_error("create_ril: Couldn't create out_queue.");
goto error;
}
ril->notify_list = g_hash_table_new_full(g_int_hash, g_int_equal,
g_free,
ril_notify_destroy);
g_ril_io_set_read_handler(ril->io, new_bytes, ril);
return ril;
error:
ril_unref(ril);
return NULL;
}
static struct ril_notify *ril_notify_create(struct ril_s *ril,
const int req)
{
struct ril_notify *notify;
int *key;
notify = g_try_new0(struct ril_notify, 1);
if (notify == NULL)
return 0;
key = g_try_new0(int, 1);
if (key == NULL)
return 0;
*key = req;
g_hash_table_insert(ril->notify_list, key, notify);
return notify;
}
static void ril_cancel_group(struct ril_s *ril, guint group)
{
int n = 0;
guint len, i;
struct ril_request *req;
gboolean sent;
if (ril->command_queue == NULL)
return;
while ((req = g_queue_peek_nth(ril->command_queue, n)) != NULL) {
if (req->id == 0 || req->gid != group) {
n += 1;
continue;
}
req->callback = NULL;
sent = FALSE;
len = g_queue_get_length(ril->out_queue);
for (i = 0; i < len; i++) {
if (GPOINTER_TO_INT(
g_queue_peek_nth(ril->out_queue, i))
== req->id) {
n += 1;
sent = TRUE;
break;
}
}
if (sent)
continue;
g_queue_remove(ril->command_queue, req);
ril_request_destroy(req);
}
}
static guint ril_register(struct ril_s *ril, guint group,
const int req, GRilNotifyFunc func,
gpointer user_data)
{
struct ril_notify *notify;
struct ril_notify_node *node;
if (ril->notify_list == NULL)
return 0;
if (func == NULL)
return 0;
notify = g_hash_table_lookup(ril->notify_list, &req);
if (notify == NULL)
notify = ril_notify_create(ril, req);
if (notify == NULL)
return 0;
node = g_try_new0(struct ril_notify_node, 1);
if (node == NULL)
return 0;
node->id = ril->next_notify_id++;
node->gid = group;
node->callback = func;
node->user_data = user_data;
notify->nodes = g_slist_prepend(notify->nodes, node);
return node->id;
}
static gboolean ril_unregister(struct ril_s *ril, gboolean mark_only,
guint group, guint id)
{
GHashTableIter iter;
struct ril_notify *notify;
struct ril_notify_node *node;
gpointer key, value;
GSList *l;
if (ril->notify_list == NULL)
return FALSE;
g_hash_table_iter_init(&iter, ril->notify_list);
while (g_hash_table_iter_next(&iter, &key, &value)) {
notify = value;
l = g_slist_find_custom(notify->nodes, GUINT_TO_POINTER(id),
ril_notify_node_compare_by_id);
if (l == NULL)
continue;
node = l->data;
if (node->gid != group)
return FALSE;
if (mark_only) {
node->destroyed = TRUE;
return TRUE;
}
ril_notify_node_destroy(node, NULL);
notify->nodes = g_slist_remove(notify->nodes, node);
if (notify->nodes == NULL)
g_hash_table_iter_remove(&iter);
return TRUE;
}
return FALSE;
}
void g_ril_init_parcel(const struct ril_msg *message, struct parcel *rilp)
{
/* Set up Parcel struct for proper parsing */
rilp->data = message->buf;
rilp->size = message->buf_len;
rilp->capacity = message->buf_len;
rilp->offset = 0;
rilp->malformed = 0;
}
GRil *g_ril_new_with_ucred(const char *sock_path, enum ofono_ril_vendor vendor,
unsigned int uid, unsigned int gid)
{
GRil *ril;
ril = g_try_new0(GRil, 1);
if (ril == NULL)
return NULL;
ril->parent = create_ril(sock_path, uid, gid);
if (ril->parent == NULL) {
g_free(ril);
return NULL;
}
ril->group = ril->parent->next_gid++;
ril->ref_count = 1;
ril->parent->vendor = vendor;
return ril;
}
GRil *g_ril_new(const char *sock_path, enum ofono_ril_vendor vendor)
{
return g_ril_new_with_ucred(sock_path, vendor, 0, 0);
}
GRil *g_ril_clone(GRil *clone)
{
GRil *ril;
if (clone == NULL)
return NULL;
ril = g_try_new0(GRil, 1);
if (ril == NULL)
return NULL;
ril->parent = clone->parent;
ril->group = ril->parent->next_gid++;
ril->ref_count = 1;
g_atomic_int_inc(&ril->parent->ref_count);
return ril;
}
GIOChannel *g_ril_get_channel(GRil *ril)
{
if (ril == NULL || ril->parent->io == NULL)
return NULL;
return g_ril_io_get_channel(ril->parent->io);
}
GRilIO *g_ril_get_io(GRil *ril)
{
if (ril == NULL)
return NULL;
return ril->parent->io;
}
GRil *g_ril_ref(GRil *ril)
{
if (ril == NULL)
return NULL;
g_atomic_int_inc(&ril->ref_count);
return ril;
}
gint g_ril_send(GRil *ril, const gint reqid, struct parcel *rilp,
GRilResponseFunc func, gpointer user_data,
GDestroyNotify notify)
{
struct ril_request *r;
struct ril_s *p;
if (ril == NULL
|| ril->parent == NULL
|| ril->parent->command_queue == NULL)
return 0;
p = ril->parent;
r = ril_request_create(p, ril->group, reqid, p->next_cmd_id, rilp,
func, user_data, notify, FALSE);
if (rilp != NULL)
parcel_free(rilp);
if (r == NULL)
return 0;
p->next_cmd_id++;
g_queue_push_tail(p->command_queue, r);
ril_wakeup_writer(p);
if (rilp == NULL)
g_ril_print_request_no_args(ril, r->id, reqid);
else
g_ril_print_request(ril, r->id, reqid);
return r->id;
}
void g_ril_unref(GRil *ril)
{
gboolean is_zero;
if (ril == NULL)
return;
is_zero = g_atomic_int_dec_and_test(&ril->ref_count);
if (is_zero == FALSE)
return;
ril_cancel_group(ril->parent, ril->group);
g_ril_unregister_all(ril);
ril_unref(ril->parent);
g_free(ril);
}
gboolean g_ril_get_trace(GRil *ril)
{
if (ril == NULL || ril->parent == NULL)
return FALSE;
return ril->parent->trace;
}
gboolean g_ril_set_trace(GRil *ril, gboolean trace)
{
if (ril == NULL || ril->parent == NULL)
return FALSE;
return ril->parent->trace = trace;
}
gboolean g_ril_set_slot(GRil *ril, int slot)
{
if (ril == NULL || ril->parent == NULL)
return FALSE;
ril->parent->slot = slot;
return TRUE;
}
int g_ril_get_slot(GRil *ril)
{
if (ril == NULL)
return 0;
return ril->parent->slot;
}
gboolean g_ril_set_debugf(GRil *ril,
GRilDebugFunc func, gpointer user_data)
{
if (ril == NULL || ril->group != 0)
return FALSE;
return ril_set_debug(ril->parent, func, user_data);
}
gboolean g_ril_set_vendor_print_msg_id_funcs(GRil *ril,
GRilMsgIdToStrFunc req_to_string,
GRilMsgIdToStrFunc unsol_to_string)
{
if (ril == NULL || ril->parent == NULL)
return FALSE;
ril->parent->req_to_string = req_to_string;
ril->parent->unsol_to_string = unsol_to_string;
return TRUE;
}
guint g_ril_register(GRil *ril, const int req,
GRilNotifyFunc func, gpointer user_data)
{
if (ril == NULL)
return 0;
return ril_register(ril->parent, ril->group, req,
func, user_data);
}
gboolean g_ril_unregister(GRil *ril, guint id)
{
if (ril == NULL)
return FALSE;
return ril_unregister(ril->parent, ril->parent->in_notify,
ril->group, id);
}
gboolean g_ril_unregister_all(GRil *ril)
{
if (ril == NULL)
return FALSE;
return ril_unregister_all(ril->parent,
ril->parent->in_notify,
node_compare_by_group,
GUINT_TO_POINTER(ril->group));
}
enum ofono_ril_vendor g_ril_vendor(GRil *ril)
{
if (ril == NULL)
return OFONO_RIL_VENDOR_AOSP;
return ril->parent->vendor;
}
const char *g_ril_request_id_to_string(GRil *ril, int req)
{
return request_id_to_string(ril->parent, req);
}
const char *g_ril_unsol_request_to_string(GRil *ril, int req)
{
return unsol_request_to_string(ril->parent, req);
}