blob: 691c61e73b0aa582b9bccdf337e9898257ccec0b [file] [log] [blame]
/* ST-Ericsson U300 RIL
*
* Copyright (C) ST-Ericsson AB 2008-2010
* Copyright 2006, The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
* Based on reference-ril by The Android Open Source Project.
*
* Heavily modified for ST-Ericsson U300 modems.
* Author: Christian Bejram <christian.bejram@stericsson.com>
*/
#include <stdio.h>
#include <pthread.h>
#include <string.h>
#include <assert.h>
#include <cutils/properties.h>
#include "u300-ril-information.h"
#include "u300-ril-network.h"
#include "u300-ril.h"
#include "at_tok.h"
#define LOG_TAG "RILV"
#include <utils/Log.h>
bool g_shutdownCompleted; /* Static allowed - uncontrolled */
/*****************************************************/
/* Controlled static state variables - section start */
/*****************************************************/
/*****************************************************/
/* Controlled static state variables - section end */
/*****************************************************/
/**
* RIL_REQUEST_GET_IMSI
*/
void requestGetIMSI(void *data, size_t datalen, RIL_Token t)
{
ATResponse *atresponse = NULL;
int err;
err = at_send_command_numeric("AT+CIMI", &atresponse);
if (err < 0 || atresponse->success == 0)
RIL_onRequestComplete(t, RIL_E_GENERIC_FAILURE, NULL, 0);
else {
RIL_onRequestComplete(t, RIL_E_SUCCESS,
atresponse->p_intermediates->line,
sizeof(char *));
}
at_response_free(atresponse);
return;
}
/* RIL_REQUEST_DEVICE_IDENTITY
*
* Request the device ESN / MEID / IMEI / IMEISV.
*
*/
void requestDeviceIdentity(void *data, size_t datalen, RIL_Token t)
{
ATResponse *atresponse = NULL;
char *response[4];
char *line = NULL;
int svn;
int err;
response[0] = NULL;
response[1] = NULL;
response[2] = ""; /* CDMA not supported */
response[3] = ""; /* CDMA not supported */
/* IMEI */
err = at_send_command_numeric("AT+CGSN", &atresponse);
if (err < 0 || atresponse->success == 0)
goto error;
asprintf(&response[0], "%s", atresponse->p_intermediates->line);
at_response_free(atresponse);
atresponse = NULL;
/* IMEISV */
err = at_send_command_singleline("AT*ESVN?", "*ESVN:", &atresponse);
if (err < 0 || atresponse->success == 0)
goto finally;
line = atresponse->p_intermediates->line;
err = at_tok_start(&line);
if (err < 0)
goto finally;
err = at_tok_nextint(&line, &svn);
if (err == 0)
asprintf(&response[1], "%02d", svn);
finally:
RIL_onRequestComplete(t, RIL_E_SUCCESS, &response, sizeof(response));
goto exit;
error:
RIL_onRequestComplete(t, RIL_E_GENERIC_FAILURE, NULL, 0);
exit:
at_response_free(atresponse);
if (response[0] != NULL)
free(response[0]);
if (response[1] != NULL)
free(response[1]);
return;
}
/**
* Deprecated!
* RIL_REQUEST_GET_IMEI
*
* Get the device IMEI, including check digit.
*/
void requestGetIMEI(void *data, size_t datalen, RIL_Token t)
{
ATResponse *atresponse = NULL;
int err;
err = at_send_command_numeric("AT+CGSN", &atresponse);
if (err < 0 || atresponse->success == 0)
RIL_onRequestComplete(t, RIL_E_GENERIC_FAILURE, NULL, 0);
else {
RIL_onRequestComplete(t, RIL_E_SUCCESS,
atresponse->p_intermediates->line,
sizeof(char *));
}
at_response_free(atresponse);
return;
}
/**
* Deprecated!
* RIL_REQUEST_GET_IMEISV
*
* Get the device IMEISV, which should be two decimal digits.
*/
void requestGetIMEISV(void *data, size_t datalen, RIL_Token t)
{
ATResponse *atresponse = NULL;
char *response = NULL;
char *line = NULL;
int svn;
int err;
err = at_send_command_singleline("AT*ESVN?", "*ESVN:", &atresponse);
if (err < 0 || atresponse->success == 0)
goto error;
line = atresponse->p_intermediates->line;
err = at_tok_start(&line);
if (err < 0)
goto error;
err = at_tok_nextint(&line, &svn);
if (err < 0 )
goto error;
asprintf(&response, "%02d", svn);
RIL_onRequestComplete(t, RIL_E_SUCCESS, response, sizeof(char *));
goto exit;
error:
RIL_onRequestComplete(t, RIL_E_GENERIC_FAILURE, NULL, 0);
exit:
at_response_free(atresponse);
if (response != NULL)
free(response);
return;
}
/**
* RIL_REQUEST_RADIO_POWER
*
* Toggle radio on and off (for "airplane" mode).
*/
void requestRadioPower(void *data, size_t datalen, RIL_Token t)
{
int onOff;
int err;
ATResponse *atresponse = NULL;
const char *property = "sys.shutdown.requested";
char systemShutdown[PROPERTY_VALUE_MAX + 1];
int getRet = -1;
if(datalen < sizeof(int))
goto error;
onOff = ((int *) data)[0];
if (onOff == 0 && getCurrentState() != RADIO_STATE_OFF) {
/*
* Check Android system property if system is shutting down.
* Note: Android sets sys.shutdown.requested=0 when shutting down.
*/
getRet = property_get(property, systemShutdown, NULL);
if (getRet > 0 && !strncmp(systemShutdown, "0", 1)) {
/* Modem shutdown */
LOGI("Android shutdown received. Shutting down modem.");
err = at_send_command("AT+CFUN=100", &atresponse);
if (err < 0 || atresponse->success == 0)
LOGE("Failed to perform modem shutdown");
/* loop until MID reports that modem is shut down */
while (g_shutdownCompleted == false) {
usleep(100*1000);
}
} else {
/* Turn radio off */
err = at_send_command("AT+CFUN=4", &atresponse);
}
if (err < 0 || atresponse->success == 0)
goto error;
setRadioState(RADIO_STATE_OFF);
} else if (onOff > 0 && getCurrentState() == RADIO_STATE_OFF) {
err = at_send_command("AT+CFUN=99", &atresponse);
if (err < 0 || atresponse->success == 0) {
LOGW("AT+CFUN=99 failed, falling back to AT+CFUN=1");
at_response_free(atresponse);
err = at_send_command("AT+CFUN=1", &atresponse);
if (err < 0 || atresponse->success == 0)
goto error;
}
setRadioState(RADIO_STATE_SIM_NOT_READY);
} else {
LOGE("Erroneous input to requestRadioPower()!");
goto error;
}
RIL_onRequestComplete(t, RIL_E_SUCCESS, NULL, 0);
finally:
at_response_free(atresponse);
return;
error:
RIL_onRequestComplete(t, RIL_E_GENERIC_FAILURE, NULL, 0);
goto finally;
}
/**
* Queries the signal strength and sends the signal strength
* as Unsolicited response to Android.
*/
void pollAndDispatchSignalStrength(void *param)
{
RIL_SignalStrength signalStrength;
if (querySignalStrength(&signalStrength))
RIL_onUnsolicitedResponse(RIL_UNSOL_SIGNAL_STRENGTH,
&signalStrength, sizeof(RIL_SignalStrength));
}
bool changeScreenState(int screenState)
{
bool res = true;
getScreenStateLock();
setScreenState(screenState);
if (screenState == 1) {
/* Screen is on - be sure to enable all unsolicited notifications. */
#ifdef LTE_COMMAND_SET_ENABLED
if (at_send_command("AT+CEREG=2", NULL) < 0)
goto error;
if (at_send_command("AT+CREG=2", NULL) < 0)
#else
if (at_send_command("AT*EREG=2", NULL) < 0)
#endif
goto error;
if (at_send_command("AT+CGREG=2", NULL) < 0)
goto error;
if (at_send_command("AT*EPSB=1", NULL) < 0)
goto error;
if (at_send_command("AT+CMER=3,0,0,1", NULL) < 0)
goto error;
/*
* Android will not poll for update of signal strength after switch of
* screen state, we need to poll to update screen signal strength bar.
*/
enqueueRILEvent(CMD_QUEUE_AUXILIARY, pollAndDispatchSignalStrength,
NULL, NULL);
/* Trigger a rehash of network values, just to be sure. */
RIL_onUnsolicitedResponse(RIL_UNSOL_RESPONSE_NETWORK_STATE_CHANGED,
NULL, 0);
} else if (screenState == 0) {
/* Screen is off - disable all unsolicited notifications. */
#ifdef LTE_COMMAND_SET_ENABLED
if (at_send_command("AT+CEREG=0", NULL) < 0)
#include <stdlib.h>
LOGI("Failed to disable CEREG notifications");
if (at_send_command("AT+CREG=0", NULL) < 0)
LOGI("Failed to disable CREG notifications");
#else
if (at_send_command("AT*EREG=0", NULL) < 0)
LOGI("Failed to disable EREG notifications");
#endif
if (at_send_command("AT+CGREG=0", NULL) < 0)
LOGI("Failed to disable CGREG notifications");
if (at_send_command("AT*EPSB=0", NULL) < 0)
LOGI("Failed to disable EPSB notifications");
if (at_send_command("AT+CMER=3,0,0,0", NULL) < 0)
LOGI("Failed to disable CMER notifications");
} else {
/* Not a defined value - error. */
goto error;
}
goto exit;
error:
LOGE("%s failed to change screen state subscriptions", __func__);
res = false;
exit:
releaseScreenStateLock();
return res;
}
void requestScreenState(void *data, size_t datalen, RIL_Token t)
{
int screenState;
if (datalen < sizeof(int))
goto error;
screenState = ((int *) data)[0];
if(!changeScreenState(screenState))
goto error;
RIL_onRequestComplete(t, RIL_E_SUCCESS, NULL, 0);
goto exit;
error:
LOGE("ERROR: requestScreenState failed");
RIL_onRequestComplete(t, RIL_E_GENERIC_FAILURE, NULL, 0);
exit:
return;
}
/**
* RIL_REQUEST_BASEBAND_VERSION
*
* Return string value indicating baseband version, eg
* response from AT+CGMR.
*/
void requestBasebandVersion(void *data, size_t datalen, RIL_Token t)
{
int err;
ATResponse *atresponse = NULL;
ATLine *atline;
char *line;
err = at_send_command_with_echo("AT+CGMR", &atresponse);
if (err < 0 ||
atresponse->success == 0 || atresponse->p_intermediates == NULL)
goto error;
/* When local echo is enabled, first line contains "AT+CGMR" echoed
* back by the modem. This line needs to be skipped.
*/
for (atline = atresponse->p_intermediates;
atline->p_next; atline = atline->p_next) {
LOGW("CGMR: Skipping local echo.");
}
line = atline->line;
/* The returned value is used by Android in a system property.
* The RIL should have no knowledge about this, but since Android
* system properties only allow values with length < 90 and causes an
* exception if the length of the returned string is > 90 this needs to
* be checked here.
* Todo: Until Android implements limit handling on the string we need
* to have a workaround in the RIL to chop the string.
*/
if (strlen(line) > 90)
line[90] = '\0';
RIL_onRequestComplete(t, RIL_E_SUCCESS, line, sizeof(char *));
finally:
at_response_free(atresponse);
return;
error:
LOGE("Error in requestBasebandVersion()");
RIL_onRequestComplete(t, RIL_E_GENERIC_FAILURE, NULL, 0);
goto finally;
}
void onResetModemStateInformation(int resetState)
{
/* NOTE: Function is called in DEFAULT queueRunner context! */
LOGI("%s() starting", __func__);
switch (resetState) {
case RESET_START:
/*
* Issued prior to AT channels are recreated and shall therefore NOT
* initiate modem communication!
* -> Reset any internal static state variables and report to Android if
* nessasary.
*/
break;
case RESET_AT_INITIALIZED:
/*
* Issued when AT channels are available and before SIM is booted and
* unlocked.
* -> Re-setup modem and Android with internal static state variables
* which cannot be reset after modem restart.
*/
break;
case RESET_SIM_READY:
/*
* Issued after SIM is unlocked.
* -> Re-setup modem with internal static state variables which
* cannot be reset before SIM is unlocked.
*/
break;
default:
LOGE("%s() received unknown resetState. Fatal error!", __func__);
assert(0);
}
}