/* * * 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 #endif #include #include #include #include #include #include #include #include #include #include #include #include #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); }