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