blob: 61d7029bf532baff31143e6fb10cb29f1d223179 [file] [log] [blame]
/******************************************************************************
*(C) Copyright 2015 Marvell International Ltd.
* All Rights Reserved
******************************************************************************/
#include <errno.h>
#include <unistd.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <libubox/blob.h>
#include <libubox/blobmsg.h>
#include <libubox/ustream.h>
#include <libubus.h>
#include <ubusmsg.h>
#include <include/log.h>
#include <cm.h>
#include "ril.h"
#include "rilutil.h"
#include "chl.h"
#include "chl_main.h"
#include "chl_ril.h"
#include "chl_ubus.h"
#include "chl_util.h"
#define RIL_NAME "ril"
#define RIL_REQ "ril_request"
#define RIL_MAX_RETRIES 20
#define SIM_MAX_RETRIES 10
#define RIL_REQ_TIMEOUT 20000
#define RIL_NUM_SETUP_DATA_CALL_PARAMS 7
struct chl_ril_context {
int ril_err;
int dcl_num;
Ubus_Data_Call_Response *dcr;
int pin_status;
int ps_attach_status;
};
static const char *ril_unsol_events[] = {
"ril.unsol.mm",
"ril.unsol.ps",
};
static bool ps_attached = false;
static bool ril_stub = false;
static unsigned int ril_id;
static struct blob_buf blob;
static struct ubus_subscriber ril_subscriber;
static struct chl_ril_context ril_ctx;
static struct chl_ril_context *chl_ril_context_get(void)
{
memset(&ril_ctx, 0, sizeof(struct chl_ril_context));
return &ril_ctx;
}
static void chl_ril_context_done(struct chl_ril_context *rctx)
{
if (rctx->dcr)
free(rctx->dcr);
}
static char *ril_ip_type_to_str(enum ip_type type)
{
switch(type) {
case IPV4:
return IPV4_STR;
case IPV6:
return IPV6_STR;
case IPV4V6:
return IPV4V6_STR;
default:
return "";
}
}
char *data_call_fail_to_str(RIL_DataCallFailCause fail)
{
switch(fail) {
case PDP_FAIL_NONE:
return "PDP_FAIL_NONE";
case PDP_FAIL_OPERATOR_BARRED:
return "PDP_FAIL_OPERATOR_BARRED";
case PDP_FAIL_INSUFFICIENT_RESOURCES:
return "PDP_FAIL_INSUFFICIENT_RESOURCES";
case PDP_FAIL_MISSING_UKNOWN_APN:
return "PDP_FAIL_MISSING_UKNOWN_APN";
case PDP_FAIL_UNKNOWN_PDP_ADDRESS_TYPE:
return "PDP_FAIL_UNKNOWN_PDP_ADDRESS_TYPE";
case PDP_FAIL_USER_AUTHENTICATION:
return "PDP_FAIL_USER_AUTHENTICATION";
case PDP_FAIL_ACTIVATION_REJECT_GGSN:
return "PDP_FAIL_ACTIVATION_REJECT_GGSN";
case PDP_FAIL_ACTIVATION_REJECT_UNSPECIFIED:
return "PDP_FAIL_ACTIVATION_REJECT_UNSPECIFIED";
case PDP_FAIL_SERVICE_OPTION_NOT_SUPPORTED:
return "PDP_FAIL_SERVICE_OPTION_NOT_SUPPORTED";
case PDP_FAIL_SERVICE_OPTION_NOT_SUBSCRIBED:
return "PDP_FAIL_SERVICE_OPTION_NOT_SUBSCRIBED";
case PDP_FAIL_SERVICE_OPTION_OUT_OF_ORDER:
return "PDP_FAIL_SERVICE_OPTION_OUT_OF_ORDER";
case PDP_FAIL_NSAPI_IN_USE:
return "PDP_FAIL_NSAPI_IN_USE";
case PDP_FAIL_REGULAR_DEACTIVATION:
return "PDP_FAIL_REGULAR_DEACTIVATION";
case PDP_FAIL_ONLY_IPV4_ALLOWED:
return "PDP_FAIL_ONLY_IPV4_ALLOWED";
case PDP_FAIL_ONLY_IPV6_ALLOWED:
return "PDP_FAIL_ONLY_IPV6_ALLOWED";
case PDP_FAIL_ONLY_SINGLE_BEARER_ALLOWED:
return "PDP_FAIL_ONLY_SINGLE_BEARER_ALLOWED";
case PDP_FAIL_PROTOCOL_ERRORS:
return "PDP_FAIL_PROTOCOL_ERRORS";
case PDP_FAIL_VOICE_REGISTRATION_FAIL:
return "PDP_FAIL_VOICE_REGISTRATION_FAIL";
case PDP_FAIL_DATA_REGISTRATION_FAIL:
return "PDP_FAIL_DATA_REGISTRATION_FAIL";
case PDP_FAIL_SIGNAL_LOST:
return "PDP_FAIL_SIGNAL_LOST";
case PDP_FAIL_PREF_RADIO_TECH_CHANGED:
return "PDP_FAIL_PREF_RADIO_TECH_CHANGED";
case PDP_FAIL_RADIO_POWER_OFF:
return "PDP_FAIL_RADIO_POWER_OFF";
case PDP_FAIL_TETHERED_CALL_ACTIVE:
return "PDP_FAIL_TETHERED_CALL_ACTIVE";
case PDP_FAIL_ERROR_UNSPECIFIED:
return "PDP_FAIL_ERROR_UNSPECIFIED";
default:
return "Unknown";
}
}
static char *ril_req_to_str(int req_num)
{
switch(req_num) {
case RIL_REQUEST_GET_SIM_STATUS:
return "RIL_REQUEST_GET_SIM_STATUS";
break;
case RIL_REQUEST_DATA_CALL_LIST:
return "RIL_REQUEST_DATA_CALL_LIST";
break;
case RIL_REQUEST_SETUP_DATA_CALL:
return "RIL_REQUEST_SETUP_DATA_CALL";
break;
case RIL_REQUEST_DEACTIVATE_DATA_CALL:
return "RIL_REQUEST_DEACTIVATE_DATA_CALL";
break;
case RIL_REQUEST_SET_INITIAL_ATTACH_APN:
return "RIL_REQUEST_SET_INITIAL_ATTACH_APN";
break;
default:
return "unknown";
}
}
int chl_ril_type_to_int(char *type)
{
if (!strcmp(type, IPV4_STR))
return IPV4;
else if (!strcmp(type, IPV6_STR))
return IPV6;
else if (!strcmp(type, IPV4V6_STR))
return IPV4V6;
else
{
CHL_ERR("bad ip type in response, setting ipv4v6\n");
return IPV4V6;
}
}
static void chl_ril_setup_data_call_stub(struct chl_pdp *pdp, bool activate,
Ubus_Data_Call_Response *rsp)
{
static int cid_n = 1;
static int ip = 1;
static int ip6 = 1;
if (!activate)
return;
CHL_INFO("generating PDP info\n");
rsp->status = PDP_FAIL_NONE;
rsp->cid = cid_n;
switch (pdp->type) {
case IPV4:
strcpy(rsp->type, IPV4_STR);
sprintf(rsp->addresses, "%d.%d.%d.%d", ip, ip, ip, ip);
sprintf(rsp->dnses, "%d.%d.%d.100 %d.%d.%d.200",
ip, ip, ip, ip, ip, ip);
break;
case IPV6:
strcpy(rsp->type, IPV6_STR);
sprintf(rsp->addresses, "fe80:%d%d%d::1", ip6, ip6, ip6);
sprintf(rsp->dnses, "2002:%d%d%d::2 2002:%d%d%d::3",
ip, ip, ip, ip, ip, ip);
break;
case IPV4V6:
default:
strcpy(rsp->type, IPV4V6_STR);
sprintf(rsp->addresses, "%d.%d.%d.%d fe80:%d%d%d::3",
ip, ip, ip, ip, ip6, ip6, ip6);
sprintf(rsp->dnses, "%d.%d.%d.100 2002:%d%d%d::2",
ip, ip, ip, ip6, ip6, ip6);
break;
}
cid_n++;
ip++;
ip6++;
}
static void chl_ps_attach_status_change(int status)
{
if (!status) {
CHL_INFO("PS DETACHED\n");
ps_attached = false;
} else if (status == 1) {
ps_attached = true;
chl_retry_open_all();
}
}
static void chl_ril_dcl_changed_cb(Ubus_Data_Call_Response *dcr, int num)
{
CHL_INFO("Data call list changed. Pdp num [%d]\n", num);
chl_handle_dcl_changed(dcr, num);
}
static int chl_ril_indication_cb(struct ubus_context *uctx,
struct ubus_object *obj, struct ubus_request_data *req,
const char *method, struct blob_attr *msg)
{
unsigned int requestid;
unsigned int rilerrno;
void *data = NULL;
int len, ret, num;
ret = rilutil_parseResponse(msg, &requestid, &rilerrno, &data, &len);
if (ret) {
CHL_ERR("parse msg error\n");
if(data)
free(data);
return ret;
}
if (rilerrno) {
CHL_ERR("unsolicited id [%d], error code [%d]\n", requestid, rilerrno);
if(data)
free(data);
return rilerrno;
}
switch (requestid) {
case RIL_UNSOL_VOICE_RADIO_TECH_CHANGED:
break;
case RIL_UNSOL_DATA_CALL_LIST_CHANGED: {
num = len / sizeof(Ubus_Data_Call_Response);
chl_ril_dcl_changed_cb(data, num);
break;
}
case RIL_UNSOL_RESPONSE_RADIO_STATE_CHANGED:
break;
case RIL_UNSOL_RESPONSE_SIM_STATUS_CHANGED:
break;
case RIL_UNSOL_SIGNAL_STRENGTH: {
RIL_SignalStrength_v6 *sig = (RIL_SignalStrength_v6 *)data;
if ((sig->GW_SignalStrength.bitErrorRate == 89) &&
(sig->GW_SignalStrength.signalStrength == 67)) {
CHL_INFO("CP assert! - close all\n");
chl_ps_attach_status_change(0);
chl_close_all();
}
break;
}
case RIL_UNSOL_PS_ATTACH_STATUS: {
chl_ps_attach_status_change(*(int *)data);
break;
}
default:
break;
}
if (data)
free(data);
return ret;
}
static int chl_ril_subscribe_indications(void)
{
int i = 0;
int ret = 0;
for (i = 0; i < ARRAY_SIZE(ril_unsol_events); i++) {
ret = chl_ubus_subscribe_event(&ril_subscriber,
ril_unsol_events[i]);
if (ret) {
CHL_ERR("event [%s] subscribe failed\n",
ril_unsol_events[i]);
goto unsubscribe;
}
}
return ret;
unsubscribe:
for (i = i-1 ; i > 0; i--)
chl_ubus_unsubscribe_event(&ril_subscriber, ril_unsol_events[i]);
return ret;
}
static int chl_ril_unsubscribe_indications(void)
{
int i;
for (i = 0; i < ARRAY_SIZE(ril_unsol_events); i++)
chl_ubus_unsubscribe_event(&ril_subscriber, ril_unsol_events[i]);
CHL_INFO("unsubscribe ril.unsol events done\n");
return 0;
}
int chl_ril_enable_indications(void)
{
int ret = 0;
ret = chl_ubus_register_subscriber(&ril_subscriber);
if (ret) {
CHL_ERR("Failed to register subscriber [%s]\n", ubus_strerror(ret));
return ret;
}
ril_subscriber.cb = chl_ril_indication_cb;
ret = chl_ril_subscribe_indications();
if (ret) {
CHL_ERR("Failed to enable indications [%s]\n", ubus_strerror(ret));
return ret;
}
return ret;
}
int chl_ril_disable_indications(void)
{
int ret = 0;
chl_ril_unsubscribe_indications();
ret = chl_ubus_unregister_subscriber(&ril_subscriber);
if (ret)
CHL_ERR("Failed to unregister subscriber [%s]\n", ubus_strerror(ret));
return ret;
}
static void chl_ril_data_cb(struct ubus_request *req, int type,
struct blob_attr *msg)
{
struct chl_ril_context *rctx;
unsigned int requestid, rilerrno = 0;
void *data = NULL;
int len = 0;
if (rilutil_parseResponse(msg, &requestid, &rilerrno, &data, &len)) {
CHL_ERR("Parse failed\n");
return;
}
rctx = req->priv;
if (rilerrno) {
rctx->ril_err = rilerrno;
CHL_ERR("ril request [%s] failed with [%d]\n",
ril_req_to_str(requestid), rilerrno);
if(data)
free(data);
return;
}
switch (requestid) {
case RIL_REQUEST_SETUP_DATA_CALL: {
rctx->dcr = data;
break;
}
case RIL_REQUEST_GET_PS_ATTACH_STATUS: {
chl_ps_attach_status_change(*(int *)data);
free(data);
break;
}
case RIL_REQUEST_GET_SIM_STATUS: {
RIL_CardStatus_v6 *status = data;
rctx->pin_status = status->applications[0].app_state;
free(data);
break;
}
case RIL_REQUEST_DATA_CALL_LIST: {
rctx->dcl_num = len / sizeof(Ubus_Data_Call_Response);
rctx->dcr = data;
break;
}
case RIL_REQUEST_SET_INITIAL_ATTACH_APN:
case RIL_REQUEST_DEACTIVATE_DATA_CALL:
default:
if (data)
free(data);
}
}
static void chl_ril_complete_cb(struct ubus_request *req, int ret)
{
free(req);
}
static int chl_send_to_ril(bool sync, unsigned int request, const void * data,
int data_len, void *priv)
{
int ret = UBUS_STATUS_OK;
ret = blob_buf_init(&blob, 0);
if (ret) {
CHL_ERR("blob_buf_init failed: [%d] [%s]\n", ret,
ril_req_to_str(request));
return ret;
}
ret = rilutil_makeRequestBlob(&blob, request, data, data_len);
if (ret) {
CHL_ERR("rilutil_makeRequestBlob failed: [%d] [%s]", ret,
ril_req_to_str(request));
return ret;
}
if (sync) {
if ((ret = chl_ubus_invoke(&blob, ril_id,
RIL_REQ, chl_ril_data_cb, priv,
RIL_REQ_TIMEOUT)))
CHL_ERR("ubus_invoke [%s] failed: [%s]\n",
ril_req_to_str(request) ,ubus_strerror(ret));
} else {
if ((ret = chl_ubus_invoke_async(&blob, ril_id,
RIL_REQ, chl_ril_data_cb,
chl_ril_complete_cb, NULL)))
CHL_ERR("ubus_invoke_async [%s] failed: [%s]\n",
ril_req_to_str(request) ,ubus_strerror(ret));
}
return ret;
}
int chl_ril_setup_data_call(struct chl_pdp *pdp, bool activate,
Ubus_Data_Call_Response *response, int *ril_err)
{
char buf[4] = {0};
char auth_buf[4] = {0};
char radio_buf[4] = {0};
struct chl_ril_context *rctx;
rilutilstrings data_call;
int ret = 0;
data_call.str = NULL;
if (ril_stub) {
*ril_err = 0;
chl_ril_setup_data_call_stub(pdp, activate, response);
return 0;
}
if (!ps_attached) {
*ril_err = RIL_E_OP_NOT_ALLOWED_BEFORE_REG_TO_NW;
return 0;
}
rctx = chl_ril_context_get();
if (activate) {
data_call.num = RIL_NUM_SETUP_DATA_CALL_PARAMS;
data_call.str = (char **)malloc(data_call.num * sizeof(char *));
if (!data_call.str) {
CHL_ERR("string allocation failed\n");
ret = -1;
goto done;
}
if (pdp->extra.flags & EXTP_PROF)
sprintf(auth_buf, "%u", pdp->extra.auth_type);
if (pdp->extra.flags & EXTP_RADIO)
sprintf(radio_buf, "%u", pdp->extra.radio_tech);
data_call.str[0] = radio_buf;
data_call.str[1] = pdp->extra.data_profile;
data_call.str[2] = pdp->apn;
data_call.str[3] = pdp->extra.username;
data_call.str[4] = pdp->extra.password;
data_call.str[5] = auth_buf;
data_call.str[6] = ril_ip_type_to_str(pdp->type);
ret = chl_send_to_ril(true, RIL_REQUEST_SETUP_DATA_CALL,
&data_call, 0, rctx);
if (ret) {
CHL_ERR("chl_send_to_ril failed\n");
goto done;
}
if (rctx->ril_err) {
*ril_err= rctx->ril_err;
goto done;
}
if (!rctx->dcr) {
CHL_ERR("no setup data call rsp received!\n");
ret = -1;
goto done;
}
memcpy(response, rctx->dcr, sizeof (Ubus_Data_Call_Response));
} else {
data_call.num = 2;
data_call.str = (char **)malloc(data_call.num * sizeof(char *));
if (!data_call.str) {
CHL_ERR("string allocation failed\n");
ret = -1;
goto done;
}
sprintf(buf, "%d", pdp->cid);
data_call.str[0] = buf;
data_call.str[1] = "0";
ret = chl_send_to_ril(true, RIL_REQUEST_DEACTIVATE_DATA_CALL,
&data_call, 0, rctx);
if (ret) {
CHL_ERR("chl_send_to_ril failed\n");
ret = - 1;
goto done;
}
if (rctx->ril_err) {
*ril_err = rctx->ril_err;
goto done;
}
}
done:
chl_ril_context_done(rctx);
if(data_call.str)
free(data_call.str);
return ret;
}
void chl_ril_request_dcl(void)
{
int ret;
struct chl_ril_context *rctx;
rctx = chl_ril_context_get();
ret = chl_send_to_ril(true, RIL_REQUEST_DATA_CALL_LIST, NULL, 0, rctx);
if (ret) {
CHL_ERR("chl_send_to_ril failed\n");
return;
}
if (rctx->ril_err) {
CHL_ERR("failed to get data call list with [%d]\n", rctx->ril_err);
return;
}
chl_ril_dcl_changed_cb(rctx->dcr, rctx->dcl_num);
chl_ril_context_done(rctx);
}
void chl_ril_request_ps_status(void)
{
int ret;
struct chl_ril_context *rctx;
rctx = chl_ril_context_get();
ret = chl_send_to_ril(true, RIL_REQUEST_GET_PS_ATTACH_STATUS, NULL, 0,
rctx);
if (ret) {
CHL_ERR("chl_send_to_ril failed\n");
return;
}
if (rctx->ril_err) {
CHL_ERR("failed to get data ps attach status with [%d]\n",
rctx->ril_err);
return;
}
chl_ril_context_done(rctx);
}
int chl_ril_set_lte_dflt(struct reg_param *rp, int *ril_err)
{
struct chl_ril_context *rctx;
RIL_InitialAttachApn data;
int ret = 1, len = 0;
memset(&data, 0, sizeof(RIL_InitialAttachApn));
CHL_INFO("trying to register lte default [%s]\n", rp->apn);
data.apn = rp->apn;
len += strlen(data.apn) + 1;
data.protocol = ril_ip_type_to_str(rp->ip_type);
len += strlen(data.protocol) + 1;
if (!rp->ext)
goto send;
data.authtype = rp->ext->auth_type;
len += sizeof(data.authtype);
if (rp->ext->flags & EXTP_UNAME) {
data.username = rp->ext->username;
len += strlen(data.username) + 1;
}
if (rp->ext->flags & EXTP_PWD) {
data.password = rp->ext->password;
len += strlen(data.password) + 1;
}
send:
rctx = chl_ril_context_get();
ret = chl_send_to_ril(true, RIL_REQUEST_SET_INITIAL_ATTACH_APN, &data,
len, rctx);
if (ret) {
CHL_ERR("chl_send_to_ril failed\n");
goto done;
}
if (rctx->ril_err) {
CHL_ERR("lte default req failed with [%d]\n", rctx->ril_err);
*ril_err = rctx->ril_err;
goto done;
}
ret = 0;
done:
chl_ril_context_done(rctx);
return ret;
}
void chl_ril_toggle_stub(void)
{
ril_stub = !ril_stub;
CHL_INFO("ril stub is [%s]\n", ril_stub ? "on" : "off");
}
static int ril_is_ready(void)
{
if (!chl_ubus_lookup_id(RIL_NAME, &ril_id))
return 1;
return 0;
}
int chl_ril_register(void)
{
int retries = 0;
do {
if (ril_is_ready())
goto ready;
sleep(1);
} while (retries++ < RIL_MAX_RETRIES);
CHL_ERR("Failed to look up RIL object\n");
return -1;
ready:
return 0;
}
static int chl_sim_is_ready(void)
{
int ret = 0;
struct chl_ril_context *rctx;
rctx = chl_ril_context_get();
ret = chl_send_to_ril(true, RIL_REQUEST_GET_SIM_STATUS, NULL, 0, rctx);
if (ret) {
CHL_ERR("chl_send_to_ril failed\n");
goto done;
}
if (rctx->ril_err) {
CHL_ERR("sim request failed with [%d]\n", rctx->ril_err);
goto done;
}
if (rctx->pin_status < 0 || rctx->pin_status != RIL_APPSTATE_READY) {
CHL_ERR("sim not ready [%d]\n", rctx->pin_status);
goto done;
}
ret = 1;
done:
chl_ril_context_done(rctx);
return ret;
}
int chl_wait_for_sim(void)
{
int retries = 0;
do {
if (chl_sim_is_ready())
goto ready;
sleep(1);
} while (retries++ < SIM_MAX_RETRIES);
CHL_ERR("failed to wait for sim\n");
return -1;
ready:
return 0;
}