ofono/drivers/rilmodem/gprs.c

488 lines
12 KiB
C

/*
*
* oFono - Open Source Telephony
*
* Copyright (C) 2008-2011 Intel Corporation. All rights reserved.
* Copyright (C) 2010 ST-Ericsson AB.
* Copyright (C) 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
#define _GNU_SOURCE
#include <string.h>
#include <stdlib.h>
#include <stdio.h>
#include <errno.h>
#include <glib.h>
#include <ofono/log.h>
#include <ofono/modem.h>
#include <ofono/gprs.h>
#include <ofono/types.h>
#include "gril.h"
#include "grilutil.h"
#include "common.h"
#include "rilmodem.h"
#include "grilreply.h"
#include "grilrequest.h"
#include "grilunsol.h"
#include "gprs.h"
/* Time between get data status retries */
#define GET_STATUS_TIMER_MS 5000
/*
* This module is the ofono_gprs_driver implementation for rilmodem.
*
* Notes:
*
* 1. ofono_gprs_suspend/resume() are not used by this module, as
* the concept of suspended GPRS is not exposed by RILD.
*/
static int ril_tech_to_bearer_tech(int ril_tech)
{
/*
* This code handles the mapping between the RIL_RadioTechnology
* and packet bearer values ( see <curr_bearer> values - 27.007
* Section 7.29 ).
*/
switch (ril_tech) {
case RADIO_TECH_GSM:
case RADIO_TECH_UNKNOWN:
return PACKET_BEARER_NONE;
case RADIO_TECH_GPRS:
return PACKET_BEARER_GPRS;
case RADIO_TECH_EDGE:
return PACKET_BEARER_EGPRS;
case RADIO_TECH_UMTS:
return PACKET_BEARER_UMTS;
case RADIO_TECH_HSDPA:
return PACKET_BEARER_HSDPA;
case RADIO_TECH_HSUPA:
return PACKET_BEARER_HSUPA;
case RADIO_TECH_HSPAP:
case RADIO_TECH_HSPA:
/*
* HSPAP is HSPA+; which ofono doesn't define;
* so, if differentiating HSPA and HSPA+ is
* important, then ofono needs to be patched,
* and we probably also need to introduce a
* new indicator icon.
*/
return PACKET_BEARER_HSUPA_HSDPA;
case RADIO_TECH_LTE:
return PACKET_BEARER_EPS;
default:
return PACKET_BEARER_NONE;
}
}
static void ril_gprs_state_change(struct ril_msg *message, gpointer user_data)
{
struct ofono_gprs *gprs = user_data;
struct ril_gprs_data *gd = ofono_gprs_get_data(gprs);
g_ril_print_unsol_no_args(gd->ril, message);
/*
* We just want to track network data status if ofono
* itself is attached, so we avoid unnecessary data state requests.
*/
if (gd->ofono_attached == TRUE)
ril_gprs_registration_status(gprs, NULL, NULL);
}
gboolean ril_gprs_set_attached_cb(gpointer user_data)
{
struct cb_data *cbd = user_data;
ofono_gprs_cb_t cb = cbd->cb;
DBG("");
CALLBACK_WITH_SUCCESS(cb, cbd->data);
g_free(cbd);
/* Run once per g_idle_add() call */
return FALSE;
}
static void ril_gprs_set_attached(struct ofono_gprs *gprs, int attached,
ofono_gprs_cb_t cb, void *data)
{
struct cb_data *cbd = cb_data_new(cb, data, NULL);
struct ril_gprs_data *gd = ofono_gprs_get_data(gprs);
DBG("attached: %d", attached);
/*
* As RIL offers no actual control over the GPRS 'attached'
* state, we save the desired state, and use it to override
* the actual modem's state in the 'attached_status' function.
* This is similar to the way the core ofono gprs code handles
* data roaming ( see src/gprs.c gprs_netreg_update().
*
* The core gprs code calls driver->set_attached() when a netreg
* notificaiton is received and any configured roaming conditions
* are met.
*/
gd->ofono_attached = attached;
/*
* Call from idle loop, so core can set driver_attached before
* the callback is invoked.
*/
g_idle_add(ril_gprs_set_attached_cb, cbd);
}
static gboolean ril_get_status_retry(gpointer user_data)
{
struct ofono_gprs *gprs = user_data;
struct ril_gprs_data *gd = ofono_gprs_get_data(gprs);
gd->status_retry_cb_id = 0;
ril_gprs_registration_status(gprs, NULL, NULL);
return FALSE;
}
static void ril_data_reg_cb(struct ril_msg *message, gpointer user_data)
{
struct cb_data *cbd = user_data;
ofono_gprs_status_cb_t cb = cbd->cb;
struct ofono_gprs *gprs = cbd->user;
struct ril_gprs_data *gd = ofono_gprs_get_data(gprs);
struct reply_data_reg_state *reply;
gboolean attached = FALSE;
gboolean notify_status = FALSE;
int old_status;
old_status = gd->rild_status;
if (message->error != RIL_E_SUCCESS) {
ofono_error("%s: DATA_REGISTRATION_STATE reply failure: %s",
__func__,
ril_error_to_string(message->error));
goto error;
}
reply = g_ril_reply_parse_data_reg_state(gd->ril, message);
if (reply == NULL)
goto error;
/*
* There are three cases that can result in this callback
* running:
*
* 1) The driver's probe() method was called, and thus an
* internal call to ril_gprs_registration_status() is
* generated. No ofono cb exists.
*
* 2) ril_gprs_state_change() is called due to an unsolicited
* event from RILD. No ofono cb exists.
*
* 3) The ofono code code calls the driver's attached_status()
* function. A valid ofono cb exists.
*/
if (gd->rild_status != reply->reg_state.status) {
gd->rild_status = reply->reg_state.status;
if (cb == NULL)
notify_status = TRUE;
}
/*
* Override the actual status based upon the desired
* attached status set by the core GPRS code ( controlled
* by the ConnnectionManager's 'Powered' property ).
*/
attached = (reply->reg_state.status ==
NETWORK_REGISTRATION_STATUS_REGISTERED ||
reply->reg_state.status ==
NETWORK_REGISTRATION_STATUS_ROAMING);
if (attached && gd->ofono_attached == FALSE) {
DBG("attached=true; ofono_attached=false; return !REGISTERED");
reply->reg_state.status =
NETWORK_REGISTRATION_STATUS_NOT_REGISTERED;
/*
* Further optimization so that if ril_status ==
* NOT_REGISTERED, ofono_attached == false, and status ==
* ROAMING | REGISTERED, then notify gets cleared...
*
* As is, this results in unecessary status notify calls
* when nothing has changed.
*/
if (notify_status && reply->reg_state.status == old_status)
notify_status = FALSE;
}
if (old_status == -1) {
ofono_gprs_register(gprs);
/* Different rild implementations use different events here */
g_ril_register(gd->ril,
gd->state_changed_unsol,
ril_gprs_state_change, gprs);
if (reply->max_cids == 0)
gd->max_cids = RIL_MAX_NUM_ACTIVE_DATA_CALLS;
else if (reply->max_cids < RIL_MAX_NUM_ACTIVE_DATA_CALLS)
gd->max_cids = reply->max_cids;
else
gd->max_cids = RIL_MAX_NUM_ACTIVE_DATA_CALLS;
DBG("Setting max cids to %d", gd->max_cids);
ofono_gprs_set_cid_range(gprs, 1, gd->max_cids);
/*
* This callback is a result of the inital call
* to probe(), so should return after registration.
*/
g_free(reply);
return;
}
/* Just need to notify ofono if it's already attached */
if (notify_status) {
/*
* If network disconnect has occurred, call detached_notify()
* instead of status_notify().
*/
if (!attached &&
(old_status == NETWORK_REGISTRATION_STATUS_REGISTERED ||
old_status ==
NETWORK_REGISTRATION_STATUS_ROAMING)) {
DBG("calling ofono_gprs_detached_notify()");
ofono_gprs_detached_notify(gprs);
reply->reg_state.tech = RADIO_TECH_UNKNOWN;
} else {
DBG("calling ofono_gprs_status_notify()");
ofono_gprs_status_notify(gprs, reply->reg_state.status);
}
}
if (gd->tech != reply->reg_state.tech) {
gd->tech = reply->reg_state.tech;
ofono_gprs_bearer_notify(gprs,
ril_tech_to_bearer_tech(reply->reg_state.tech));
}
if (cb)
CALLBACK_WITH_SUCCESS(cb, reply->reg_state.status, cbd->data);
g_free(reply);
return;
error:
/*
* For some modems DATA_REGISTRATION_STATE will return an error until we
* are registered in the voice network.
*/
if (old_status == -1 && message->error == RIL_E_GENERIC_FAILURE)
gd->status_retry_cb_id =
g_timeout_add(GET_STATUS_TIMER_MS,
ril_get_status_retry, gprs);
if (cb)
CALLBACK_WITH_FAILURE(cb, -1, cbd->data);
}
void ril_gprs_registration_status(struct ofono_gprs *gprs,
ofono_gprs_status_cb_t cb, void *data)
{
struct ril_gprs_data *gd = ofono_gprs_get_data(gprs);
struct cb_data *cbd = cb_data_new(cb, data, gprs);
DBG("");
if (g_ril_send(gd->ril, RIL_REQUEST_DATA_REGISTRATION_STATE, NULL,
ril_data_reg_cb, cbd, g_free) == 0) {
ofono_error("%s: send "
"RIL_REQUEST_DATA_REGISTRATION_STATE failed",
__func__);
g_free(cbd);
if (cb != NULL)
CALLBACK_WITH_FAILURE(cb, -1, data);
}
}
static void drop_data_call_cb(struct ril_msg *message, gpointer user_data)
{
struct ofono_gprs *gprs = user_data;
struct ril_gprs_data *gd = ofono_gprs_get_data(gprs);
if (message->error == RIL_E_SUCCESS)
g_ril_print_response_no_args(gd->ril, message);
else
ofono_error("%s: RIL error %s", __func__,
ril_error_to_string(message->error));
if (--(gd->pending_deact_req) == 0)
ril_gprs_registration_status(gprs, NULL, NULL);
}
static int drop_data_call(struct ofono_gprs *gprs, int cid)
{
struct ril_gprs_data *gd = ofono_gprs_get_data(gprs);
struct req_deactivate_data_call request;
struct parcel rilp;
struct ofono_error error;
request.cid = cid;
request.reason = RIL_DEACTIVATE_DATA_CALL_NO_REASON;
g_ril_request_deactivate_data_call(gd->ril, &request, &rilp, &error);
if (g_ril_send(gd->ril, RIL_REQUEST_DEACTIVATE_DATA_CALL,
&rilp, drop_data_call_cb, gprs, NULL) == 0) {
ofono_error("%s: send failed", __func__);
return -1;
}
return 0;
}
static void get_active_data_calls_cb(struct ril_msg *message,
gpointer user_data)
{
struct ofono_gprs *gprs = user_data;
struct ril_gprs_data *gd = ofono_gprs_get_data(gprs);
struct ril_data_call_list *call_list = NULL;
GSList *iterator;
struct ril_data_call *call;
if (message->error != RIL_E_SUCCESS) {
ofono_error("%s: RIL error %s", __func__,
ril_error_to_string(message->error));
goto end;
}
/* reply can be NULL when there are no existing data calls */
call_list = g_ril_unsol_parse_data_call_list(gd->ril, message);
if (call_list == NULL)
goto end;
/*
* We disconnect from previous calls here, which might be needed
* because of a previous ofono abort, as some rild implementations do
* not disconnect the calls even after the ril socket is closed.
*/
for (iterator = call_list->calls; iterator; iterator = iterator->next) {
call = iterator->data;
DBG("Standing data call with cid %d", call->cid);
if (drop_data_call(gprs, call->cid) == 0)
++(gd->pending_deact_req);
}
g_ril_unsol_free_data_call_list(call_list);
end:
if (gd->pending_deact_req == 0)
ril_gprs_registration_status(gprs, NULL, NULL);
}
static void get_active_data_calls(struct ofono_gprs *gprs)
{
struct ril_gprs_data *gd = ofono_gprs_get_data(gprs);
if (g_ril_send(gd->ril, RIL_REQUEST_DATA_CALL_LIST, NULL,
get_active_data_calls_cb, gprs, NULL) == 0)
ofono_error("%s: send failed", __func__);
}
void ril_gprs_start(struct ril_gprs_driver_data *driver_data,
struct ofono_gprs *gprs, struct ril_gprs_data *gd)
{
gd->ril = g_ril_clone(driver_data->gril);
gd->modem = driver_data->modem;
gd->ofono_attached = FALSE;
gd->max_cids = 0;
gd->rild_status = -1;
gd->tech = RADIO_TECH_UNKNOWN;
/* AOSP RILD tracks data network state together with voice */
gd->state_changed_unsol =
RIL_UNSOL_RESPONSE_VOICE_NETWORK_STATE_CHANGED;
ofono_gprs_set_data(gprs, gd);
get_active_data_calls(gprs);
}
int ril_gprs_probe(struct ofono_gprs *gprs, unsigned int vendor, void *data)
{
struct ril_gprs_driver_data *driver_data = data;
struct ril_gprs_data *gd;
gd = g_try_new0(struct ril_gprs_data, 1);
if (gd == NULL)
return -ENOMEM;
ril_gprs_start(driver_data, gprs, gd);
return 0;
}
void ril_gprs_remove(struct ofono_gprs *gprs)
{
struct ril_gprs_data *gd = ofono_gprs_get_data(gprs);
DBG("");
if (gd->status_retry_cb_id != 0)
g_source_remove(gd->status_retry_cb_id);
ofono_gprs_set_data(gprs, NULL);
g_ril_unref(gd->ril);
g_free(gd);
}
static struct ofono_gprs_driver driver = {
.name = RILMODEM,
.probe = ril_gprs_probe,
.remove = ril_gprs_remove,
.set_attached = ril_gprs_set_attached,
.attached_status = ril_gprs_registration_status,
};
void ril_gprs_init(void)
{
ofono_gprs_driver_register(&driver);
}
void ril_gprs_exit(void)
{
ofono_gprs_driver_unregister(&driver);
}