OSDN Git Service

add huaweigeneric-ril
authorChih-Wei Huang <cwhuang@linux.org.tw>
Sun, 29 Apr 2012 17:58:05 +0000 (01:58 +0800)
committerChih-Wei Huang <cwhuang@linux.org.tw>
Fri, 11 Jul 2014 06:45:52 +0000 (14:45 +0800)
Originates from https://github.com/DerArtem/huaweigeneric-ril
and a modified version in wetab tree.

24 files changed:
huaweigeneric-ril/Android.mk [new file with mode: 0644]
huaweigeneric-ril/MODULE_LICENSE_APACHE2 [new file with mode: 0644]
huaweigeneric-ril/NOTICE [new file with mode: 0644]
huaweigeneric-ril/README [new file with mode: 0644]
huaweigeneric-ril/at_error.h [new file with mode: 0644]
huaweigeneric-ril/at_tok.c [new file with mode: 0644]
huaweigeneric-ril/at_tok.h [new file with mode: 0644]
huaweigeneric-ril/atchannel.c [new file with mode: 0644]
huaweigeneric-ril/atchannel.h [new file with mode: 0644]
huaweigeneric-ril/audiochannel.cpp [new file with mode: 0644]
huaweigeneric-ril/audiochannel.h [new file with mode: 0644]
huaweigeneric-ril/fcp_parser.c [new file with mode: 0644]
huaweigeneric-ril/fcp_parser.h [new file with mode: 0644]
huaweigeneric-ril/gsm.c [new file with mode: 0644]
huaweigeneric-ril/gsm.h [new file with mode: 0644]
huaweigeneric-ril/huaweigeneric-ril.c [new file with mode: 0644]
huaweigeneric-ril/misc.c [new file with mode: 0644]
huaweigeneric-ril/misc.h [new file with mode: 0644]
huaweigeneric-ril/requestdatahandler.c [new file with mode: 0644]
huaweigeneric-ril/requestdatahandler.h [new file with mode: 0644]
huaweigeneric-ril/sms.c [new file with mode: 0644]
huaweigeneric-ril/sms_gsm.c [new file with mode: 0644]
huaweigeneric-ril/sms_gsm.h [new file with mode: 0644]
libril/ril.cpp

diff --git a/huaweigeneric-ril/Android.mk b/huaweigeneric-ril/Android.mk
new file mode 100644 (file)
index 0000000..6321da7
--- /dev/null
@@ -0,0 +1,34 @@
+# Copyright 2012 The Android-x86 Open Source Project
+
+LOCAL_PATH := $(call my-dir)
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES := \
+    at_tok.c \
+    atchannel.c \
+    fcp_parser.c \
+    gsm.c \
+    huaweigeneric-ril.c \
+    misc.c \
+    requestdatahandler.c \
+    sms.c \
+    sms_gsm.c \
+
+LOCAL_SHARED_LIBRARIES := \
+    libcutils \
+    libutils \
+    libril \
+    libmedia
+
+# for asprinf
+LOCAL_CFLAGS := -D_GNU_SOURCE
+#build shared library
+LOCAL_CFLAGS += -DRIL_SHLIB
+
+LOCAL_C_INCLUDES := \
+    hardware/ril/libril \
+
+LOCAL_MODULE := libhuaweigeneric-ril
+LOCAL_MODULE_TAGS := optional
+
+include $(BUILD_SHARED_LIBRARY)
diff --git a/huaweigeneric-ril/MODULE_LICENSE_APACHE2 b/huaweigeneric-ril/MODULE_LICENSE_APACHE2
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/huaweigeneric-ril/NOTICE b/huaweigeneric-ril/NOTICE
new file mode 100644 (file)
index 0000000..f7bd78d
--- /dev/null
@@ -0,0 +1,189 @@
+
+   Copyright (c) 2005-2008, 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.
+
+   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.
+
+
+                                 Apache License
+                           Version 2.0, January 2004
+                        http://www.apache.org/licenses/
+
+   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+   1. Definitions.
+
+      "License" shall mean the terms and conditions for use, reproduction,
+      and distribution as defined by Sections 1 through 9 of this document.
+
+      "Licensor" shall mean the copyright owner or entity authorized by
+      the copyright owner that is granting the License.
+
+      "Legal Entity" shall mean the union of the acting entity and all
+      other entities that control, are controlled by, or are under common
+      control with that entity. For the purposes of this definition,
+      "control" means (i) the power, direct or indirect, to cause the
+      direction or management of such entity, whether by contract or
+      otherwise, or (ii) ownership of fifty percent (50%) or more of the
+      outstanding shares, or (iii) beneficial ownership of such entity.
+
+      "You" (or "Your") shall mean an individual or Legal Entity
+      exercising permissions granted by this License.
+
+      "Source" form shall mean the preferred form for making modifications,
+      including but not limited to software source code, documentation
+      source, and configuration files.
+
+      "Object" form shall mean any form resulting from mechanical
+      transformation or translation of a Source form, including but
+      not limited to compiled object code, generated documentation,
+      and conversions to other media types.
+
+      "Work" shall mean the work of authorship, whether in Source or
+      Object form, made available under the License, as indicated by a
+      copyright notice that is included in or attached to the work
+      (an example is provided in the Appendix below).
+
+      "Derivative Works" shall mean any work, whether in Source or Object
+      form, that is based on (or derived from) the Work and for which the
+      editorial revisions, annotations, elaborations, or other modifications
+      represent, as a whole, an original work of authorship. For the purposes
+      of this License, Derivative Works shall not include works that remain
+      separable from, or merely link (or bind by name) to the interfaces of,
+      the Work and Derivative Works thereof.
+
+      "Contribution" shall mean any work of authorship, including
+      the original version of the Work and any modifications or additions
+      to that Work or Derivative Works thereof, that is intentionally
+      submitted to Licensor for inclusion in the Work by the copyright owner
+      or by an individual or Legal Entity authorized to submit on behalf of
+      the copyright owner. For the purposes of this definition, "submitted"
+      means any form of electronic, verbal, or written communication sent
+      to the Licensor or its representatives, including but not limited to
+      communication on electronic mailing lists, source code control systems,
+      and issue tracking systems that are managed by, or on behalf of, the
+      Licensor for the purpose of discussing and improving the Work, but
+      excluding communication that is conspicuously marked or otherwise
+      designated in writing by the copyright owner as "Not a Contribution."
+
+      "Contributor" shall mean Licensor and any individual or Legal Entity
+      on behalf of whom a Contribution has been received by Licensor and
+      subsequently incorporated within the Work.
+
+   2. Grant of Copyright License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      copyright license to reproduce, prepare Derivative Works of,
+      publicly display, publicly perform, sublicense, and distribute the
+      Work and such Derivative Works in Source or Object form.
+
+   3. Grant of Patent License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      (except as stated in this section) patent license to make, have made,
+      use, offer to sell, sell, import, and otherwise transfer the Work,
+      where such license applies only to those patent claims licensable
+      by such Contributor that are necessarily infringed by their
+      Contribution(s) alone or by combination of their Contribution(s)
+      with the Work to which such Contribution(s) was submitted. If You
+      institute patent litigation against any entity (including a
+      cross-claim or counterclaim in a lawsuit) alleging that the Work
+      or a Contribution incorporated within the Work constitutes direct
+      or contributory patent infringement, then any patent licenses
+      granted to You under this License for that Work shall terminate
+      as of the date such litigation is filed.
+
+   4. Redistribution. You may reproduce and distribute copies of the
+      Work or Derivative Works thereof in any medium, with or without
+      modifications, and in Source or Object form, provided that You
+      meet the following conditions:
+
+      (a) You must give any other recipients of the Work or
+          Derivative Works a copy of this License; and
+
+      (b) You must cause any modified files to carry prominent notices
+          stating that You changed the files; and
+
+      (c) You must retain, in the Source form of any Derivative Works
+          that You distribute, all copyright, patent, trademark, and
+          attribution notices from the Source form of the Work,
+          excluding those notices that do not pertain to any part of
+          the Derivative Works; and
+
+      (d) If the Work includes a "NOTICE" text file as part of its
+          distribution, then any Derivative Works that You distribute must
+          include a readable copy of the attribution notices contained
+          within such NOTICE file, excluding those notices that do not
+          pertain to any part of the Derivative Works, in at least one
+          of the following places: within a NOTICE text file distributed
+          as part of the Derivative Works; within the Source form or
+          documentation, if provided along with the Derivative Works; or,
+          within a display generated by the Derivative Works, if and
+          wherever such third-party notices normally appear. The contents
+          of the NOTICE file are for informational purposes only and
+          do not modify the License. You may add Your own attribution
+          notices within Derivative Works that You distribute, alongside
+          or as an addendum to the NOTICE text from the Work, provided
+          that such additional attribution notices cannot be construed
+          as modifying the License.
+
+      You may add Your own copyright statement to Your modifications and
+      may provide additional or different license terms and conditions
+      for use, reproduction, or distribution of Your modifications, or
+      for any such Derivative Works as a whole, provided Your use,
+      reproduction, and distribution of the Work otherwise complies with
+      the conditions stated in this License.
+
+   5. Submission of Contributions. Unless You explicitly state otherwise,
+      any Contribution intentionally submitted for inclusion in the Work
+      by You to the Licensor shall be under the terms and conditions of
+      this License, without any additional terms or conditions.
+      Notwithstanding the above, nothing herein shall supersede or modify
+      the terms of any separate license agreement you may have executed
+      with Licensor regarding such Contributions.
+
+   6. Trademarks. This License does not grant permission to use the trade
+      names, trademarks, service marks, or product names of the Licensor,
+      except as required for reasonable and customary use in describing the
+      origin of the Work and reproducing the content of the NOTICE file.
+
+   7. Disclaimer of Warranty. Unless required by applicable law or
+      agreed to in writing, Licensor provides the Work (and each
+      Contributor provides its Contributions) on an "AS IS" BASIS,
+      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+      implied, including, without limitation, any warranties or conditions
+      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+      PARTICULAR PURPOSE. You are solely responsible for determining the
+      appropriateness of using or redistributing the Work and assume any
+      risks associated with Your exercise of permissions under this License.
+
+   8. Limitation of Liability. In no event and under no legal theory,
+      whether in tort (including negligence), contract, or otherwise,
+      unless required by applicable law (such as deliberate and grossly
+      negligent acts) or agreed to in writing, shall any Contributor be
+      liable to You for damages, including any direct, indirect, special,
+      incidental, or consequential damages of any character arising as a
+      result of this License or out of the use or inability to use the
+      Work (including but not limited to damages for loss of goodwill,
+      work stoppage, computer failure or malfunction, or any and all
+      other commercial damages or losses), even if such Contributor
+      has been advised of the possibility of such damages.
+
+   9. Accepting Warranty or Additional Liability. While redistributing
+      the Work or Derivative Works thereof, You may choose to offer,
+      and charge a fee for, acceptance of support, warranty, indemnity,
+      or other liability obligations and/or rights consistent with this
+      License. However, in accepting such obligations, You may act only
+      on Your own behalf and on Your sole responsibility, not on behalf
+      of any other Contributor, and only if You agree to indemnify,
+      defend, and hold each Contributor harmless for any liability
+      incurred by, or claims asserted against, such Contributor by reason
+      of your accepting any such warranty or additional liability.
+
+   END OF TERMS AND CONDITIONS
diff --git a/huaweigeneric-ril/README b/huaweigeneric-ril/README
new file mode 100644 (file)
index 0000000..e9ace04
--- /dev/null
@@ -0,0 +1,36 @@
+
+The modules has still a few problems:
+
+* required modify init.rc add:
+       on device-added-/dev/ttyUSB0
+       chmod 0777 /dev/ttyUSB0
+       on device-added-/dev/ttyUSB1
+       chmod 0777 /dev/ttyUSB1
+       on device-added-/dev/ttyUSB2
+       chmod 0777 /dev/ttyUSB2
+       on device-added-/dev/ttyUSB3
+       chmod 0777 /dev/ttyUSB3
+       on device-added-/dev/ttyUSB4
+       chmod 0777 /dev/ttyUSB4
+       on device-added-/dev/ttyUSB5
+       chmod 0777 /dev/ttyUSB5
+
+
+       on boot
+       service ril-daemon /system/bin/rild
+       socket rild stream 660 root radio
+       socket rild-debug stream 660 radio system
+       user root
+       group radio cache inet misc audio
+
+
+* Edit system/build.prop ,add:
+       # for 3g
+       rild.libpath=/system/lib/libhuaweigeneric-ril.so
+       rild.libargs=-d /dev/ttyUSB2 -v /dev/ttyUSB1
+       keyguard.no_require_sim=1
+
+
+----
+
+       pppd must be suid ROOT, and kernel must include ppp support compiled in
diff --git a/huaweigeneric-ril/at_error.h b/huaweigeneric-ril/at_error.h
new file mode 100644 (file)
index 0000000..965c6b6
--- /dev/null
@@ -0,0 +1,236 @@
+#ifndef ATERROR_H
+#define ATERROR_H 1
+
+#define mbm_error \
+    at_error \
+    cme_error \
+    cms_error \
+    generic_error \
+
+#define at_error \
+    aterror(AT, NOERROR, 0) \
+    aterror(AT, ERROR_GENERIC, 1) \
+    aterror(AT, ERROR_COMMAND_PENDING, 2) \
+    aterror(AT, ERROR_CHANNEL_CLOSED, 3) \
+    aterror(AT, ERROR_TIMEOUT, 4) \
+    aterror(AT, ERROR_INVALID_THREAD, 5) \
+    aterror(AT, ERROR_INVALID_RESPONSE, 6) \
+    aterror(AT, ERROR_MEMORY_ALLOCATION, 7) \
+    aterror(AT, ERROR_STRING_CREATION, 8) \
+
+#define cme_error \
+    aterror(CME, MODULE_FAILURE, 0) \
+    aterror(CME, NO_MODULE_CONNECTION, 1) \
+    aterror(CME, PHONE_ADAPTER_RESERVED, 2) \
+    aterror(CME, OPERATION_NOT_ALLOWED, 3) \
+    aterror(CME, OPERATION_NOT_SUPPORTED, 4) \
+    aterror(CME, PH_SIM_PIN, 5) \
+    aterror(CME, PH_FSIM_PIN, 6) \
+    aterror(CME, PH_FSIM_PUK, 7) \
+    aterror(CME, SIM_NOT_INSERTED, 10) \
+    aterror(CME, SIM_PIN_REQUIRED, 11) \
+    aterror(CME, SIM_PUK_REQUIRED, 12) \
+    aterror(CME, FAILURE, 13) \
+    aterror(CME, SIM_BUSY, 14) \
+    aterror(CME, SIM_WRONG, 15) \
+    aterror(CME, INCORRECT_PASSWORD, 16) \
+    aterror(CME, SIM_PIN2_REQUIRED, 17) \
+    aterror(CME, SIM_PUK2_REQUIRED, 18) \
+    aterror(CME, MEMORY_FULL, 20) \
+    aterror(CME, INVALID_INDEX, 21) \
+    aterror(CME, NOT_FOUND, 22) \
+    aterror(CME, MEMORY_FAILURE, 23) \
+    aterror(CME, STRING_TO_LONG, 24) \
+    aterror(CME, INVALID_CHAR, 25) \
+    aterror(CME, DIALSTR_TO_LONG, 26) \
+    aterror(CME, INVALID_DIALCHAR, 27) \
+    aterror(CME, NO_NETWORK_SERVICE, 30) \
+    aterror(CME, NETWORK_TIMEOUT, 31) \
+    aterror(CME, NETWORK_NOT_ALLOWED, 32) \
+    aterror(CME, NETWORK_PERSONALIZATION_PIN_REQUIRED, 40) \
+    aterror(CME, NETWORK_PERSONALIZATION_PUK_REQUIRED, 41) \
+    aterror(CME, NETWORK_SUBSET_PERSONALIZATION_PIN_REQUIRED, 42) \
+    aterror(CME, NETWORK_SUBSET_PERSONALIZATION_PUK_REQUIRED, 43) \
+    aterror(CME, SERVICE_PROVIDER_PERSONALIZATION_PIN_REQUIRED, 44) \
+    aterror(CME, SERVICE_PROVIDER_PERSONALIZATION_PUK_REQUIRED, 45) \
+    aterror(CME, CORPORATE_PERSONALIZATION_PIN_REQUIRED, 46) \
+    aterror(CME, CORPORATE_PERSONALIZATION_PUK_REQUIRED, 47) \
+    aterror(CME, HIDDEN_KEY, 48) \
+    aterror(CME, EAP_NOT_SUPORTED, 49) \
+    aterror(CME, INCORRECT_PARAMETERS, 50) \
+    aterror(CME, UNKNOWN, 100) \
+    aterror(CME, ILLEGAL_MS, 103) \
+    aterror(CME, ILLEGAL_ME, 106) \
+    aterror(CME, PLMN_NOT_ALLOWED, 111) \
+    aterror(CME, LOCATION_AREA_NOT_ALLOWED, 112) \
+    aterror(CME, ROAMING_AREA_NOT_ALLOWED, 113) \
+    aterror(CME, SERVICE_NOT_SUPPORTED, 132) \
+    aterror(CME, SERVICE_NOT_SUBSCRIBED, 133) \
+    aterror(CME, SERVICE_TEMPORARILY_OUT, 134) \
+    aterror(CME, UNSPECIFIED_GPRS_ERROR, 148) \
+    aterror(CME, PDP_AUTH_FAILURE, 149) \
+    aterror(CME, INVALID_MOBILE_CLASS, 150) \
+    aterror(CME, PH_SIMLOCK_PIN_REQUIRED, 200) \
+    aterror(CME, SYNTAX_ERROR, 257) \
+    aterror(CME, INVALID_PARAMETER, 258) \
+    aterror(CME, LENGTH_ERROR, 259) \
+    aterror(CME, SIM_AUTH_FAILURE, 260) \
+    aterror(CME, SIM_FILE_ERROR, 261) \
+    aterror(CME, FILE_SYSTEM_ERROR, 262) \
+    aterror(CME, SERVICE_UNAVIABLE, 263) \
+    aterror(CME, PHONEBOOK_NOT_READY, 264) \
+    aterror(CME, PHONEBOOK_NOT_SUPPORTED, 265) \
+    aterror(CME, COMMAND_TO_LONG, 266) \
+    aterror(CME, PARAMETER_OUT_OF_RANGE, 267) \
+    aterror(CME, BAND_NOT_ALLOWED, 268) \
+    aterror(CME, SUPPLEMENTARY_SERIVEC_FAILURE, 269) \
+    aterror(CME, COMMAND_ABORTED, 270) \
+    aterror(CME, ACTION_ALREADY_IN_PROGRESS, 271) \
+    aterror(CME, WAN_DISABLED, 272) \
+    aterror(CME, GPS_DISABLE_DUE_TO_TEMP, 273) \
+    aterror(CME, RADIO_NOT_ACTIVATED, 274) \
+    aterror(CME, USB_NOT_CONFIGURED, 275) \
+    aterror(CME, NOT_CONNECTED, 276) \
+    aterror(CME, NOT_DISCONNECTED, 277) \
+    aterror(CME, TOO_MANY_CONNECTIONS, 278) \
+    aterror(CME, TOO_MANY_USERS, 279) \
+    aterror(CME, FDN_RESTRICITONS, 280) \
+
+#define cms_error \
+    aterror(CMS, UNASSIGNED_NUMBER, 1) \
+    aterror(CMS, BARRING, 8) \
+    aterror(CMS, CALL_BARRED, 10) \
+    aterror(CMS, SHORT_MESSAGE_REJECTED, 21) \
+    aterror(CMS, DESTINATION_OUT_OF_SERVICE, 27) \
+    aterror(CMS, UNIDENTIFIED_SUBSCRIBER, 28) \
+    aterror(CMS, FACILITY_REJECTED, 29) \
+    aterror(CMS, UNKNOWN_SUBSCRIBER, 30) \
+    aterror(CMS, NETWORK_OUT_OF_ORDER, 38) \
+    aterror(CMS, TEMP_FAILURE, 41) \
+    aterror(CMS, SMS_CONGESTION, 42) \
+    aterror(CMS, RESOURCE_UNAVAIBLE, 47) \
+    aterror(CMS, REQUESTED_FACILITY_NOT_SUBSCRIBED, 50) \
+    aterror(CMS, REQUESTED_FACILITY_NOT_IMPLEMENTED, 69) \
+    aterror(CMS, INVALID_SMS_REF, 81) \
+    aterror(CMS, INVALID_MESSAGE, 95) \
+    aterror(CMS, INVALID_MANDATORY_INFORMATION, 96) \
+    aterror(CMS, MESSAGE_TYPE_NOT_IMPLEMENTED, 97) \
+    aterror(CMS, MESSAGE_NOT_COMPATIBLE, 98) \
+    aterror(CMS, INFORMATION_ELEMENT_NOT_IMPLEMENTED, 99) \
+    aterror(CMS, PROTOCOL_ERROR, 111) \
+    aterror(CMS, INTERWORKING_UNSPECIFIED, 127) \
+    aterror(CMS, TELEMATIC_INTERWORKING_NOT_SUPPORTED, 128) \
+    aterror(CMS, SHORT_MESSAGE_TYPE_0_NOT_SUPPORTED, 129) \
+    aterror(CMS, CANNOT_REPLACE_SHORT_MESSAGE, 130) \
+    aterror(CMS, UNSPECIFIED_TP_PID_ERROR, 143) \
+    aterror(CMS, DATA_SCHEME_NOT_SUPPORTED, 144) \
+    aterror(CMS, MESSAGE_CLASS_NOT_SUPPORTED, 145) \
+    aterror(CMS, UNSPECIFIED_TP_DCS_ERROR, 159) \
+    aterror(CMS, COMMAND_CANT_BE_ACTIONED, 160) \
+    aterror(CMS, COMMAND_UNSUPPORTED, 161) \
+    aterror(CMS, UNSPECIFIED_TP_COMMAND, 175) \
+    aterror(CMS, TPDU_NOT_SUPPORTED, 176) \
+    aterror(CMS, SC_BUSY, 192) \
+    aterror(CMS, NO_SC_SUBSCRIPTINO, 193) \
+    aterror(CMS, SC_FAILURE, 194) \
+    aterror(CMS, INVALID_SME_ADDRESS, 195) \
+    aterror(CMS, SME_BARRIED, 196) \
+    aterror(CMS, SM_DUPLICATE_REJECTED, 197) \
+    aterror(CMS, TP_VPF_NOT_SUPPORTED, 198) \
+    aterror(CMS, TP_VP_NOT_SUPPORTED, 199) \
+    aterror(CMS, SIM_SMS_FULL, 208) \
+    aterror(CMS, NO_SMS_STORAGE_CAPABILITY, 209) \
+    aterror(CMS, ERROR_IN_MS, 210) \
+    aterror(CMS, MEMORY_CAPACITY_EXCEEDED, 211) \
+    aterror(CMS, STK_BUSY, 212) \
+    aterror(CMS, UNSPECIFIED_ERROR, 255) \
+    aterror(CMS, ME_FAILURE, 300) \
+    aterror(CMS, SMS_OF_ME_RESERVED, 301) \
+    aterror(CMS, SERVICE_OPERATION_NOT_ALLOWED, 302) \
+    aterror(CMS, SERVICE_OPERATION_NOT_SUPPORTED, 303) \
+    aterror(CMS, INVALID_PDU_PARAMETER, 304) \
+    aterror(CMS, INVALID_TEXT_PARAMETER, 305) \
+    aterror(CMS, SERVICE_SIM_NOT_INSERTED, 310) \
+    aterror(CMS, SERVICE_SIM_PIN_REQUIRED, 311) \
+    aterror(CMS, PH_SIM_PIN_REQUIRED, 312) \
+    aterror(CMS, SIM_FAILURE, 313) \
+    aterror(CMS, SERVICE_SIM_BUSY, 314) \
+    aterror(CMS, SERVICE_SIM_WRONG, 315) \
+    aterror(CMS, SIM_PUK_REQUIRED, 316) \
+    aterror(CMS, SERVICE_SIM_PIN2_REQUIRED, 317) \
+    aterror(CMS, SERVICE_SIM_PUK2_REQUIRED, 318) \
+    aterror(CMS, SERVICE_MEMORY_FAILURE, 320) \
+    aterror(CMS, INVALID_MEMORY_INDEX, 321) \
+    aterror(CMS, SERVICE_MEMORY_FULL, 322) \
+    aterror(CMS, SMSC_ADDR_UNKNOWN, 330) \
+    aterror(CMS, NO_NETWORK_SERVICE, 331) \
+    aterror(CMS, NETWORK_TIMEOUT, 332) \
+    aterror(CMS, NO_CNMA, 340) \
+    aterror(CMS, UNKNOWN_ERROR, 500) \
+
+#define generic_error \
+    aterror(GENERIC, ERROR_RESPONSE, 1) \
+    aterror(GENERIC, NO_CARRIER_RESPONSE, 2) \
+    aterror(GENERIC, NO_ANSWER_RESPONSE, 3) \
+    aterror(GENERIC, NO_DIALTONE_RESPONSE, 4) \
+    aterror(GENERIC, ERROR_UNSPECIFIED, 5) \
+
+#define aterror(group, name, num) group(name, num)
+
+typedef enum {
+    CME_ERROR_NON_CME = -1,
+#define CME(name, num) CME_ ## name = num,
+    cme_error
+#undef CME
+} AT_CME_Error;
+
+typedef enum {
+    CMS_ERROR_NON_CMS = -1,
+#define CMS(name, num) CMS_ ## name = num,
+    cms_error
+#undef CMS
+} AT_CMS_Error;
+
+typedef enum {
+    GENERIC_ERROR_NON_GENERIC = -1,
+#define GENERIC(name, num) GENERIC_ ## name = num,
+    generic_error
+#undef GENERIC
+} AT_Generic_Error;
+
+typedef enum {
+    AT_ERROR_NON_AT = -1,
+    /* AT ERRORS are enumerated by MBM_Error below */
+} AT_Error;
+
+#define AT_ERROR_BASE          0 /* see also _TOP */
+#define CME_ERROR_BASE      1000 /* see also _TOP */
+#define CMS_ERROR_BASE      2000 /* see also _TOP */
+#define GENERIC_ERROR_BASE  3000 /* see also _TOP */
+
+#define AT_ERROR_TOP       (CME_ERROR_BASE - 1) /* see also _BASE */
+#define CME_ERROR_TOP      (CMS_ERROR_BASE - 1) /* see also _BASE */
+#define CMS_ERROR_TOP      (GENERIC_ERROR_BASE - 1) /* see also _BASE */
+#define GENERIC_ERROR_TOP  (GENERIC_ERROR_BASE + 999) /* see also _BASE */
+
+typedef enum {
+#define AT(name, num) AT_ ## name = num + AT_ERROR_BASE,
+#define CME(name, num) AT_CME_ ## name = num + CME_ERROR_BASE,
+#define CMS(name, num) AT_CMS_ ## name = num + CMS_ERROR_BASE,
+#define GENERIC(name, num) AT_GENERIC_ ## name = num + GENERIC_ERROR_BASE,
+    mbm_error
+#undef CME
+#undef CMS
+#undef GENERIC
+#undef AT
+} MBM_Error;
+
+typedef enum {
+    NONE_ERROR,
+    AT_ERROR,
+    CME_ERROR,
+    CMS_ERROR,
+    GENERIC_ERROR,
+    UNKNOWN_ERROR,
+} AT_Error_type;
+#endif
diff --git a/huaweigeneric-ril/at_tok.c b/huaweigeneric-ril/at_tok.c
new file mode 100644 (file)
index 0000000..aef475f
--- /dev/null
@@ -0,0 +1,252 @@
+/* //device/system/reference-ril/at_tok.c
+**
+** 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.
+*/
+
+#include "at_tok.h"
+#include <string.h>
+#include <ctype.h>
+#include <stdlib.h>
+
+/**
+ * Starts tokenizing an AT response string.
+ * Returns -1 if this is not a valid response string, 0 on success.
+ * Updates *p_cur with current position.
+ */
+int at_tok_start(char **p_cur)
+{
+    if (*p_cur == NULL)
+        return -1;
+
+    /* Skip prefix,
+       consume "^[^:]:". */
+
+    *p_cur = strchr(*p_cur, ':');
+
+    if (*p_cur == NULL)
+        return -1;
+
+    (*p_cur)++;
+
+    return 0;
+}
+
+static void skipWhiteSpace(char **p_cur)
+{
+    if (*p_cur == NULL)
+        return;
+
+    while (**p_cur != '\0' && isspace(**p_cur))
+        (*p_cur)++;
+    }
+
+static void skipNextComma(char **p_cur)
+{
+    if (*p_cur == NULL)
+        return;
+
+    while (**p_cur != '\0' && **p_cur != ',')
+        (*p_cur)++;
+
+    if (**p_cur == ',')
+        (*p_cur)++;
+    }
+
+/**
+ * If the first none space character is a quotation mark, returns the string
+ * between two quotation marks, else returns the content before the first comma.
+ * Updates *p_cur.
+ */
+static char * nextTok(char **p_cur)
+{
+    char *ret = NULL;
+
+    skipWhiteSpace(p_cur);
+
+    if (*p_cur == NULL) {
+        ret = NULL;
+    } else if (**p_cur == '"') {
+        enum State {END, NORMAL, ESCAPE} state = NORMAL;
+
+        (*p_cur)++;
+        ret = *p_cur;
+
+        while (state != END) {
+            switch (state) {
+            case NORMAL:
+                switch (**p_cur) {
+                case '\\':
+                    state = ESCAPE;
+                    break;
+                case '"':
+                    state = END;
+                    break;
+                case '\0':
+                    /*
+                     * Error case, parsing string is not quoted by ending
+                     * double quote, e.g. "bla bla, this function expects input
+                     * string to be NULL terminated, so that the loop can exit.
+                     */
+                    ret = NULL;
+                    goto exit;
+                default:
+                    /* Stays in normal case. */
+                    break;
+                }
+                break;
+
+            case ESCAPE:
+                state = NORMAL;
+                break;
+
+            default:
+                /* This should never happen. */
+                break;
+            }
+
+            if (state == END) {
+                **p_cur = '\0';
+            }
+
+        (*p_cur)++;
+        }
+        skipNextComma(p_cur);
+    } else {
+        ret = strsep(p_cur, ",");
+    }
+exit:
+    return ret;
+}
+
+/**
+ * Parses the next integer in the AT response line and places it in *p_out.
+ * Returns 0 on success and -1 on fail.
+ * Updates *p_cur.
+ * "base" is the same as the base param in strtol.
+ */
+static int at_tok_nextint_base(char **p_cur, int *p_out, int base, int  uns)
+{
+    char *ret;
+
+    if (*p_cur == NULL)
+        return -1;
+
+    if (p_out == NULL)
+        return -1;
+
+    ret = nextTok(p_cur);
+
+    if (ret == NULL)
+        return -1;
+    else {
+        long l;
+        char *end;
+
+        if (uns)
+            l = strtoul(ret, &end, base);
+        else
+            l = strtol(ret, &end, base);
+
+        *p_out = (int)l;
+
+        if (end == ret)
+            return -1;
+    }
+
+    return 0;
+}
+
+/**
+ * Parses the next base 10 integer in the AT response line
+ * and places it in *p_out.
+ * Returns 0 on success and -1 on fail.
+ * Updates *p_cur.
+ */
+int at_tok_nextint(char **p_cur, int *p_out)
+{
+    return at_tok_nextint_base(p_cur, p_out, 10, 0);
+}
+
+/**
+ * Parses the next base 16 integer in the AT response line
+ * and places it in *p_out.
+ * Returns 0 on success and -1 on fail.
+ * Updates *p_cur.
+ */
+int at_tok_nexthexint(char **p_cur, int *p_out)
+{
+    return at_tok_nextint_base(p_cur, p_out, 16, 1);
+}
+
+int at_tok_nextbool(char **p_cur, char *p_out)
+{
+    int ret;
+    int result;
+
+    ret = at_tok_nextint(p_cur, &result);
+
+    if (ret < 0)
+        return -1;
+
+    /* Booleans should be 0 or 1. */
+    if (!(result == 0 || result == 1))
+        return -1;
+
+    if (p_out != NULL)
+        *p_out = (char)result;
+    else
+        return -1;
+
+    return ret;
+}
+
+int at_tok_nextstr(char **p_cur, char **p_out)
+{
+    if (*p_cur == NULL)
+        return -1;
+
+    *p_out = nextTok(p_cur);
+    if (*p_out == NULL)
+        return -1;
+
+    return 0;
+}
+
+/** Returns 1 on "has more tokens" and 0 if not. */
+int at_tok_hasmore(char **p_cur)
+{
+    return ! (*p_cur == NULL || **p_cur == '\0');
+}
+
+/** *p_out returns count of given character (needle) in given string (p_in). */
+int at_tok_charcounter(char *p_in, char needle, int *p_out)
+{
+    char *p_cur = p_in;
+    int num_found = 0;
+
+    if (p_in == NULL)
+        return -1;
+
+    while (*p_cur != '\0') {
+        if (*p_cur == needle) {
+            num_found++;
+        }
+
+        p_cur++;
+    }
+
+    *p_out = num_found;
+    return 0;
+}
diff --git a/huaweigeneric-ril/at_tok.h b/huaweigeneric-ril/at_tok.h
new file mode 100644 (file)
index 0000000..16fdcbd
--- /dev/null
@@ -0,0 +1,31 @@
+/* //device/system/reference-ril/at_tok.h
+**
+** 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.
+*/
+
+#ifndef AT_TOK_H
+#define AT_TOK_H 1
+
+int at_tok_start(char **p_cur);
+int at_tok_nextint(char **p_cur, int *p_out);
+int at_tok_nexthexint(char **p_cur, int *p_out);
+
+int at_tok_nextbool(char **p_cur, char *p_out);
+int at_tok_nextstr(char **p_cur, char **out);
+
+int at_tok_hasmore(char **p_cur);
+
+int at_tok_charcounter(char *p_in, char needle, int *p_out);
+#endif
diff --git a/huaweigeneric-ril/atchannel.c b/huaweigeneric-ril/atchannel.c
new file mode 100644 (file)
index 0000000..2382723
--- /dev/null
@@ -0,0 +1,1377 @@
+/* //device/system/reference-ril/atchannel.c
+**
+** 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.
+*/
+
+#include "atchannel.h"
+#include "at_tok.h"
+
+#include <stdio.h>
+#include <string.h>
+#include <pthread.h>
+#include <ctype.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <sys/time.h>
+#include <time.h>
+#include <unistd.h>
+#include <stdarg.h>
+
+#include <poll.h>
+
+#define LOG_NDEBUG 0
+#define LOG_TAG "AT"
+#include <utils/Log.h>
+
+#ifdef HAVE_ANDROID_OS
+/* For IOCTL's */
+#include <linux/omap_csmi.h>
+#endif /*HAVE_ANDROID_OS*/
+
+#include "misc.h"
+
+#define MAX_AT_RESPONSE (8 * 1024)
+#define HANDSHAKE_RETRY_COUNT 8
+#define HANDSHAKE_TIMEOUT_MSEC 250
+#define DEFAULT_AT_TIMEOUT_MSEC (3 * 60 * 1000)
+#define BUFFSIZE 512
+
+struct atcontext {
+    pthread_t tid_reader;
+    int fd;                  /* fd of the AT channel. */
+    int readerCmdFds[2];
+    int isInitialized;
+    ATUnsolHandler unsolHandler;
+
+    /* For input buffering. */
+    char ATBuffer[MAX_AT_RESPONSE+1];
+    char *ATBufferCur;
+
+    int readCount;
+
+    /*
+     * For current pending command, these are protected by commandmutex.
+     *
+     * The mutex and cond struct is memset in the getAtChannel() function,
+     * so no initializer should be needed.
+     */
+    pthread_mutex_t requestmutex;
+    pthread_mutex_t commandmutex;
+    pthread_cond_t requestcond;
+    pthread_cond_t commandcond;
+
+    ATCommandType type;
+    const char *responsePrefix;
+    const char *smsPDU;
+    ATResponse *response;
+
+    void (*onTimeout)(void);
+    void (*onReaderClosed)(void);
+    int readerClosed;
+
+    int timeoutMsec;
+};
+
+static struct atcontext *s_defaultAtContext = NULL;
+static va_list empty = {0};
+
+static pthread_key_t key;
+static pthread_once_t key_once = PTHREAD_ONCE_INIT;
+
+static int writeCtrlZ (const char *s);
+static int writeline (const char *s);
+static void onReaderClosed(void);
+
+static void make_key(void)
+{
+    (void) pthread_key_create(&key, NULL);
+}
+
+/**
+ * Set the atcontext pointer. Useful for sub-threads that needs to hold
+ * the same state information.
+ *
+ * The caller IS responsible for freeing any memory already allocated
+ * for any previous atcontexts.
+ */
+static void setAtContext(struct atcontext *ac)
+{
+    (void) pthread_once(&key_once, make_key);
+    (void) pthread_setspecific(key, ac);
+}
+
+static void ac_free(void)
+{
+    struct atcontext *ac = NULL;
+    (void) pthread_once(&key_once, make_key);
+    if ((ac = (struct atcontext *) pthread_getspecific(key)) != NULL) {
+        free(ac);
+        ALOGD("%s() freed current thread AT context", __func__);
+    } else {
+        ALOGW("%s() No AT context exist for current thread, cannot free it",
+            __func__);
+    }
+}
+
+static int initializeAtContext(void)
+{
+    struct atcontext *ac = NULL;
+
+    if (pthread_once(&key_once, make_key)) {
+        ALOGE("%s() Pthread_once failed!", __func__);
+        goto error;
+    }
+
+    ac = (struct atcontext *)pthread_getspecific(key);
+
+    if (ac == NULL) {
+        ac = (struct atcontext *) malloc(sizeof(struct atcontext));
+        if (ac == NULL) {
+            ALOGE("%s() Failed to allocate memory", __func__);
+            goto error;
+        }
+
+        memset(ac, 0, sizeof(struct atcontext));
+
+        ac->fd = -1;
+        ac->readerCmdFds[0] = -1;
+        ac->readerCmdFds[1] = -1;
+        ac->ATBufferCur = ac->ATBuffer;
+
+        if (pipe(ac->readerCmdFds)) {
+            ALOGE("%s() Failed to create pipe: %s", __func__, strerror(errno));
+            goto error;
+        }
+
+        pthread_mutex_init(&ac->commandmutex, NULL);
+        pthread_mutex_init(&ac->requestmutex, NULL);
+        pthread_cond_init(&ac->requestcond, NULL);
+        pthread_cond_init(&ac->commandcond, NULL);
+
+        ac->timeoutMsec = DEFAULT_AT_TIMEOUT_MSEC;
+
+        if (pthread_setspecific(key, ac)) {
+            ALOGE("%s() Calling pthread_setspecific failed!", __func__);
+            goto error;
+        }
+    }
+
+    ALOGI("Initialized new AT Context!");
+
+    return 0;
+
+error:
+    ALOGE("%s() Failed initializing new AT Context!", __func__);
+    free(ac);
+    return -1;
+}
+
+static struct atcontext *getAtContext(void)
+{
+    struct atcontext *ac = NULL;
+
+    (void) pthread_once(&key_once, make_key);
+
+    if ((ac = (struct atcontext *) pthread_getspecific(key)) == NULL) {
+        if (s_defaultAtContext) {
+            ALOGW("WARNING! external thread use default AT Context");
+            ac = s_defaultAtContext;
+        } else {
+            ALOGE("WARNING! %s() called from external thread with "
+                 "no defaultAtContext set!! This IS a bug! "
+                 "A crash is probably nearby!", __func__);
+        }
+    }
+
+    return ac;
+}
+
+/**
+ * This function will make the current at thread the default channel,
+ * meaning that calls from a thread that is not a queuerunner will
+ * be executed in this context.
+ */
+void at_make_default_channel(void)
+{
+    struct atcontext *ac = getAtContext();
+
+    if (ac->isInitialized)
+        s_defaultAtContext = ac;
+}
+
+#if AT_DEBUG
+void  AT_DUMP(const char*  prefix, const char*  buff, int  len)
+{
+    if (len < 0)
+        len = strlen(buff);
+    ALOGD("%.*s", len, buff);
+}
+#endif
+
+#ifndef HAVE_ANDROID_OS
+int pthread_cond_timeout_np(pthread_cond_t *cond,
+                            pthread_mutex_t * mutex,
+                            unsigned msecs)
+{
+    struct timespec ts;
+    clock_gettime(CLOCK_REALTIME, &ts);
+
+    ts.tv_sec += msecs / 1000;
+    ts.tv_nsec += (msecs % 1000) * 1000000;
+    return pthread_cond_timedwait(cond, mutex, &ts);
+}
+#endif /*HAVE_ANDROID_OS*/
+
+static void sleepMsec(long long msec)
+{
+    struct timespec ts;
+    int err;
+
+    ts.tv_sec = (msec / 1000);
+    ts.tv_nsec = (msec % 1000) * 1000 * 1000;
+
+    do {
+        err = nanosleep (&ts, &ts);
+    } while (err < 0 && errno == EINTR);
+}
+
+
+
+/** Add an intermediate response to sp_response. */
+static void addIntermediate(const char *line)
+{
+    ATLine *p_new;
+    struct atcontext *ac = getAtContext();
+
+    p_new = (ATLine  *) malloc(sizeof(ATLine));
+
+    p_new->line = strdup(line);
+
+    /* Note: This adds to the head of the list, so the list will
+       be in reverse order of lines received. the order is flipped
+       again before passing on to the command issuer. */
+    p_new->p_next = ac->response->p_intermediates;
+    ac->response->p_intermediates = p_new;
+}
+
+
+/**
+ * Returns 1 if line is a final response indicating error.
+ * See 27.007 annex B.
+ * WARNING: NO CARRIER and others are sometimes unsolicited.
+ */
+static const char * s_finalResponsesError[] = {
+    "ERROR",
+    "+CMS ERROR:",
+    "+CME ERROR:",
+    "NO CARRIER",      /* Sometimes! */
+    "NO ANSWER",
+    "NO DIALTONE",
+};
+
+static int isFinalResponseError(const char *line)
+{
+    size_t i;
+
+    for (i = 0 ; i < NUM_ELEMS(s_finalResponsesError) ; i++) {
+        if (strStartsWith(line, s_finalResponsesError[i])) {
+            return 1;
+        }
+    }
+
+    return 0;
+}
+
+/**
+ * Returns 1 if line is a final response indicating success.
+ * See 27.007 annex B.
+ * WARNING: NO CARRIER and others are sometimes unsolicited.
+ */
+static const char * s_finalResponsesSuccess[] = {
+    "OK",
+    "CONNECT"       /* Some stacks start up data on another channel. */
+};
+static int isFinalResponseSuccess(const char *line)
+{
+    size_t i;
+
+    for (i = 0 ; i < NUM_ELEMS(s_finalResponsesSuccess) ; i++) {
+        if (strStartsWith(line, s_finalResponsesSuccess[i])) {
+            return 1;
+        }
+    }
+
+    return 0;
+}
+
+/**
+ * Returns 1 if line is the first line in (what will be) a two-line
+ * SMS unsolicited response.
+ */
+static const char * s_smsUnsoliciteds[] = {
+    "+CMT:",
+    "+CDS:",
+    "+CBM:"
+};
+static int isSMSUnsolicited(const char *line)
+{
+    size_t i;
+
+    for (i = 0 ; i < NUM_ELEMS(s_smsUnsoliciteds) ; i++) {
+        if (strStartsWith(line, s_smsUnsoliciteds[i])) {
+            return 1;
+        }
+    }
+
+    return 0;
+}
+
+
+/** Assumes s_commandmutex is held. */
+static void handleFinalResponse(const char *line)
+{
+    struct atcontext *ac = getAtContext();
+
+    ac->response->finalResponse = strdup(line);
+
+    pthread_cond_signal(&ac->commandcond);
+}
+
+static void handleUnsolicited(const char *line)
+{
+    struct atcontext *ac = getAtContext();
+
+    if (ac->unsolHandler != NULL) {
+        ac->unsolHandler(line, NULL);
+    }
+}
+
+static void processLine(const char *line)
+{
+    struct atcontext *ac = getAtContext();
+    pthread_mutex_lock(&ac->commandmutex);
+
+    if (ac->response == NULL) {
+        /* No command pending. */
+        handleUnsolicited(line);
+    } else if (isFinalResponseSuccess(line)) {
+        ac->response->success = 1;
+        handleFinalResponse(line);
+    } else if (isFinalResponseError(line)) {
+        ac->response->success = 0;
+        handleFinalResponse(line);
+    } else if (ac->smsPDU != NULL && 0 == strcmp(line, "> ")) {
+        /* See eg. TS 27.005 4.3.
+           Commands like AT+CMGS have a "> " prompt. */
+        writeCtrlZ(ac->smsPDU);
+        ac->smsPDU = NULL;
+    } else switch (ac->type) {
+        case NO_RESULT:
+            handleUnsolicited(line);
+            break;
+        case NUMERIC:
+            if (ac->response->p_intermediates == NULL
+                && isdigit(line[0])) {
+                addIntermediate(line);
+            } else {
+                /* Either we already have an intermediate response or
+                   the line doesn't begin with a digit. */
+                handleUnsolicited(line);
+            }
+            break;
+        case SINGLELINE:
+            if (ac->response->p_intermediates == NULL
+                && strStartsWith (line, ac->responsePrefix)) {
+                addIntermediate(line);
+            } else {
+                /* We already have an intermediate response. */
+                handleUnsolicited(line);
+            }
+            break;
+        case MULTILINE:
+            if (strStartsWith (line, ac->responsePrefix)) {
+                addIntermediate(line);
+            } else {
+                handleUnsolicited(line);
+            }
+        break;
+
+        default: /* This should never be reached */
+            ALOGE("%s() Unsupported AT command type %d", __func__, ac->type);
+            handleUnsolicited(line);
+        break;
+    }
+
+    pthread_mutex_unlock(&ac->commandmutex);
+}
+
+
+/**
+ * Returns a pointer to the end of the next line,
+ * special-cases the "> " SMS prompt.
+ *
+ * returns NULL if there is no complete line.
+ */
+static char * findNextEOL(char *cur)
+{
+    if (cur[0] == '>' && cur[1] == ' ' && cur[2] == '\0') {
+        /* SMS prompt character...not \r terminated */
+        return cur+2;
+    }
+
+    /* Find next newline */
+    while (*cur != '\0' && *cur != '\r' && *cur != '\n') cur++;
+
+    return *cur == '\0' ? NULL : cur;
+}
+
+
+/**
+ * Reads a line from the AT channel, returns NULL on timeout.
+ * Assumes it has exclusive read access to the FD.
+ *
+ * This line is valid only until the next call to readline.
+ *
+ * This function exists because as of writing, android libc does not
+ * have buffered stdio.
+ */
+
+static const char *readline(void)
+{
+    ssize_t count;
+
+    char *p_read = NULL;
+    char *p_eol = NULL;
+    char *ret = NULL;
+
+    struct atcontext *ac = getAtContext();
+    read(ac->fd,NULL,0);
+
+    /* This is a little odd. I use *s_ATBufferCur == 0 to mean
+     * "buffer consumed completely". If it points to a character,
+     * then the buffer continues until a \0.
+     */
+    if (*ac->ATBufferCur == '\0') {
+        /* Empty buffer. */
+        ac->ATBufferCur = ac->ATBuffer;
+        *ac->ATBufferCur = '\0';
+        p_read = ac->ATBuffer;
+    } else {   /* *s_ATBufferCur != '\0' */
+        /* There's data in the buffer from the last read. */
+
+        /* skip over leading newlines */
+        while (*ac->ATBufferCur == '\r' || *ac->ATBufferCur == '\n')
+            ac->ATBufferCur++;
+
+        p_eol = findNextEOL(ac->ATBufferCur);
+
+        if (p_eol == NULL) {
+            /* A partial line. Move it up and prepare to read more. */
+            size_t len;
+
+            len = strlen(ac->ATBufferCur);
+
+            memmove(ac->ATBuffer, ac->ATBufferCur, len + 1);
+            p_read = ac->ATBuffer + len;
+            ac->ATBufferCur = ac->ATBuffer;
+        }
+        /* Otherwise, (p_eol !- NULL) there is a complete line
+           that will be returned from the while () loop below. */
+    }
+
+    while (p_eol == NULL) {
+        int err;
+        struct pollfd pfds[2];
+
+        /* This condition should be synchronized with the read function call
+         * size argument below.
+         */
+        if (0 >= MAX_AT_RESPONSE - (p_read - ac->ATBuffer) - 2) {
+            ALOGE("%s() ERROR: Input line exceeded buffer", __func__);
+            /* Ditch buffer and start over again. */
+            ac->ATBufferCur = ac->ATBuffer;
+            *ac->ATBufferCur = '\0';
+            p_read = ac->ATBuffer;
+        }
+
+        /* If our fd is invalid, we are probably closed. Return. */
+        if (ac->fd < 0)
+            return NULL;
+
+        pfds[0].fd = ac->fd;
+        pfds[0].events = POLLIN | POLLERR;
+
+        pfds[1].fd = ac->readerCmdFds[0];
+        pfds[1].events = POLLIN;
+
+        err = poll(pfds, 2, -1);
+
+        if (err < 0) {
+            ALOGE("%s() poll: error: %s", __func__, strerror(errno));
+            return NULL;
+        }
+
+        if (pfds[1].revents & POLLIN) {
+            char buf[10];
+
+            /* Just drain it. We don't care, this is just for waking up. */
+            read(pfds[1].fd, &buf, 1);
+            continue;
+        }
+
+        if (pfds[0].revents & POLLERR) {
+            ALOGE("%s() POLLERR event! Returning...", __func__);
+            return NULL;
+        }
+
+        if (!(pfds[0].revents & POLLIN))
+            continue;
+
+        do
+            /* The size argument should be synchronized to the ditch buffer
+             * condition above.
+             */
+            count = read(ac->fd, p_read,
+                         MAX_AT_RESPONSE - (p_read - ac->ATBuffer) - 2);
+
+        while (count < 0 && errno == EINTR);
+
+        if (count > 0) {
+            AT_DUMP( "<< ", p_read, count );
+            ac->readCount += count;
+
+            /* Implementation requires extra EOS or EOL to get it right if
+             * there are no trailing newlines in the read buffer. Adding extra
+             * EOS does not harm even if there actually were trailing EOLs.
+             */
+            p_read[count] = '\0';
+            p_read[count+1] = '\0';
+
+            /* Skip over leading newlines. */
+            while (*ac->ATBufferCur == '\r' || *ac->ATBufferCur == '\n')
+                ac->ATBufferCur++;
+
+            p_eol = findNextEOL(ac->ATBufferCur);
+            p_read += count;
+        } else if (count <= 0) {
+            /* Read error encountered or EOF reached. */
+            if (count == 0)
+                ALOGD("%s() atchannel: EOF reached.", __func__);
+            else
+                ALOGD("%s() atchannel: read error %s", __func__, strerror(errno));
+
+            return NULL;
+        }
+    }
+
+    /* A full line in the buffer. Place a \0 over the \r and return. */
+
+    ret = ac->ATBufferCur;
+    *p_eol = '\0';
+
+    /* The extra EOS added after read takes care of the case when there is no
+     * valid data after p_eol.
+     */
+    ac->ATBufferCur = p_eol + 1;     /* This will always be <= p_read,
+                                        and there will be a \0 at *p_read. */
+
+    ALOGI("AT(%d)< %s", ac->fd, ret);
+    return ret;
+}
+
+static void onReaderClosed(void)
+{
+    struct atcontext *ac = getAtContext();
+    if (ac->onReaderClosed != NULL && ac->readerClosed == 0) {
+
+        pthread_mutex_lock(&ac->commandmutex);
+
+        ac->readerClosed = 1;
+
+        pthread_cond_signal(&ac->commandcond);
+
+        pthread_mutex_unlock(&ac->commandmutex);
+
+        ac->onReaderClosed();
+    }
+}
+
+static void *readerLoop(void *arg)
+{
+    struct atcontext *ac = NULL;
+
+    ALOGI("Entering readerloop!");
+
+    setAtContext((struct atcontext *) arg);
+    ac = getAtContext();
+
+    for (;;) {
+        const char * line;
+
+        line = readline();
+
+        if (line == NULL)
+            break;
+
+        if(isSMSUnsolicited(line)) {
+            char *line1;
+            const char *line2;
+
+            /* The scope of string returned by 'readline()' is valid only
+               until next call to 'readline()' hence making a copy of line
+               before calling readline again. */
+            line1 = strdup(line);
+            line2 = readline();
+
+            if (line2 == NULL) {
+                free(line1);
+                break;
+            }
+
+            if (ac->unsolHandler != NULL)
+                ac->unsolHandler(line1, line2);
+
+            free(line1);
+        } else
+            processLine(line);
+        }
+
+    onReaderClosed();
+    ALOGI("Exiting readerloop!");
+    return NULL;
+}
+
+/**
+ * Sends string s to the radio with a \r appended.
+ * Returns AT_ERROR_* on error, 0 on success.
+ *
+ * This function exists because as of writing, android libc does not
+ * have buffered stdio.
+ */
+static int writeline (const char *s)
+{
+    size_t cur = 0;
+    size_t len = strlen(s);
+    ssize_t written;
+
+    struct atcontext *ac = getAtContext();
+
+    if (ac->fd < 0 || ac->readerClosed > 0) {
+        return AT_ERROR_CHANNEL_CLOSED;
+    }
+
+    ALOGD("AT(%d)> %s", ac->fd, s);
+
+    AT_DUMP( ">> ", s, strlen(s) );
+
+    /* The main string. */
+    while (cur < len) {
+        do {
+            written = write (ac->fd, s + cur, len - cur);
+        } while (written < 0 && errno == EINTR);
+
+        if (written < 0) {
+            return AT_ERROR_GENERIC;
+        }
+
+        cur += written;
+    }
+
+    /* The \r  */
+
+    do {
+        written = write (ac->fd, "\r" , 1);
+    } while ((written < 0 && errno == EINTR) || (written == 0));
+
+    if (written < 0) {
+        return AT_ERROR_GENERIC;
+    }
+
+    return 0;
+}
+
+static int writeCtrlZ (const char *s)
+{
+    size_t cur = 0;
+    size_t len = strlen(s);
+    ssize_t written;
+
+    struct atcontext *ac = getAtContext();
+
+    if (ac->fd < 0 || ac->readerClosed > 0)
+        return AT_ERROR_CHANNEL_CLOSED;
+
+    ALOGD("AT> %s^Z\n", s);
+
+    AT_DUMP( ">* ", s, strlen(s) );
+
+    /* The main string. */
+    while (cur < len) {
+        do {
+            written = write (ac->fd, s + cur, len - cur);
+        } while (written < 0 && errno == EINTR);
+
+        if (written < 0)
+            return AT_ERROR_GENERIC;
+
+        cur += written;
+    }
+
+    /* the ^Z  */
+    do {
+        written = write (ac->fd, "\032" , 1);
+    } while ((written < 0 && errno == EINTR) || (written == 0));
+
+    if (written < 0)
+        return AT_ERROR_GENERIC;
+
+    return 0;
+}
+
+static void clearPendingCommand(void)
+{
+    struct atcontext *ac = getAtContext();
+
+    if (ac->response != NULL)
+        at_response_free(ac->response);
+
+    ac->response = NULL;
+    ac->responsePrefix = NULL;
+    ac->smsPDU = NULL;
+    }
+
+static AT_Error merror(int type, int error)
+{
+    switch(type) {
+    case AT_ERROR :
+        return (AT_Error)(AT_ERROR_BASE + error);
+    case CME_ERROR :
+        return (AT_Error)(CME_ERROR_BASE + error);
+    case CMS_ERROR:
+        return (AT_Error)(CMS_ERROR_BASE + error);
+    case GENERIC_ERROR:
+        return (AT_Error)(GENERIC_ERROR_BASE + error);
+    default:
+        return (AT_Error)(GENERIC_ERROR_UNSPECIFIED);
+    }
+}
+
+static AT_Error at_get_error(const ATResponse *p_response)
+{
+    int ret;
+    int err;
+    char *p_cur;
+
+    if (p_response == NULL)
+        return merror(GENERIC_ERROR, GENERIC_ERROR_UNSPECIFIED);
+
+    if (p_response->success > 0)
+        return AT_NOERROR;
+
+    if (p_response->finalResponse == NULL)
+        return AT_ERROR_INVALID_RESPONSE;
+
+    if (isFinalResponseSuccess(p_response->finalResponse))
+        return AT_NOERROR;
+
+    p_cur = p_response->finalResponse;
+    err = at_tok_start(&p_cur);
+    if (err < 0)
+        return merror(GENERIC_ERROR, GENERIC_ERROR_UNSPECIFIED);
+
+    err = at_tok_nextint(&p_cur, &ret);
+    if (err < 0)
+        return merror(GENERIC_ERROR, GENERIC_ERROR_UNSPECIFIED);
+
+    if(strStartsWith(p_response->finalResponse, "+CME ERROR:"))
+        return merror(CME_ERROR, ret);
+    else if (strStartsWith(p_response->finalResponse, "+CMS ERROR:"))
+        return merror(CMS_ERROR, ret);
+    else if (strStartsWith(p_response->finalResponse, "ERROR:"))
+        return merror(GENERIC_ERROR, GENERIC_ERROR_RESPONSE);
+    else if (strStartsWith(p_response->finalResponse, "+NO CARRIER:"))
+        return merror(GENERIC_ERROR, GENERIC_NO_CARRIER_RESPONSE);
+    else if (strStartsWith(p_response->finalResponse, "+NO ANSWER:"))
+        return merror(GENERIC_ERROR, GENERIC_NO_ANSWER_RESPONSE);
+    else if (strStartsWith(p_response->finalResponse, "+NO DIALTONE:"))
+        return merror(GENERIC_ERROR, GENERIC_NO_DIALTONE_RESPONSE);
+    else
+        return merror(GENERIC_ERROR, GENERIC_ERROR_UNSPECIFIED);
+}
+
+/**
+ * Starts AT handler on stream "fd'.
+ * returns 0 on success, -1 on error.
+ */
+int at_open(int fd, ATUnsolHandler h)
+{
+    int ret;
+    pthread_attr_t attr;
+
+    struct atcontext *ac = NULL;
+
+    if (initializeAtContext()) {
+        ALOGE("%s() InitializeAtContext failed!", __func__);
+        goto error;
+    }
+
+    ac = getAtContext();
+
+    ac->fd = fd;
+    ac->isInitialized = 1;
+    ac->unsolHandler = h;
+    ac->readerClosed = 0;
+
+    ac->responsePrefix = NULL;
+    ac->smsPDU = NULL;
+    ac->response = NULL;
+
+    pthread_attr_init (&attr);
+    pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
+
+    ret = pthread_create(&ac->tid_reader, &attr, readerLoop, ac);
+
+    if (ret < 0) {
+        perror ("pthread_create");
+        goto error;
+    }
+
+
+    return 0;
+error:
+    ac_free();
+    return -1;
+}
+
+/* FIXME is it ok to call this from the reader and the command thread? */
+void at_close(void)
+{
+    struct atcontext *ac = getAtContext();
+
+    if (ac->fd >= 0) {
+        if (close(ac->fd) != 0)
+            ALOGE("%s() FAILED to close fd %d!", __func__, ac->fd);
+    }
+    ac->fd = -1;
+
+    pthread_mutex_lock(&ac->commandmutex);
+
+    ac->readerClosed = 1;
+
+    pthread_cond_signal(&ac->commandcond);
+
+    pthread_mutex_unlock(&ac->commandmutex);
+
+    /* Kick readerloop. */
+    write(ac->readerCmdFds[1], "x", 1);
+}
+
+static ATResponse *at_response_new(void)
+{
+    return (ATResponse *) calloc(1, sizeof(ATResponse));
+}
+
+void at_response_free(ATResponse *p_response)
+{
+    ATLine *p_line;
+
+    if (p_response == NULL) return;
+
+    p_line = p_response->p_intermediates;
+
+    while (p_line != NULL) {
+        ATLine *p_toFree;
+
+        p_toFree = p_line;
+        p_line = p_line->p_next;
+
+        free(p_toFree->line);
+        free(p_toFree);
+    }
+
+    free (p_response->finalResponse);
+    free (p_response);
+}
+
+/**
+ * The line reader places the intermediate responses in reverse order,
+ * here we flip them back.
+ */
+static void reverseIntermediates(ATResponse *p_response)
+{
+    ATLine *pcur,*pnext;
+
+    pcur = p_response->p_intermediates;
+    p_response->p_intermediates = NULL;
+
+    while (pcur != NULL) {
+        pnext = pcur->p_next;
+        pcur->p_next = p_response->p_intermediates;
+        p_response->p_intermediates = pcur;
+        pcur = pnext;
+    }
+}
+
+/**
+ * Internal send_command implementation.
+ * Doesn't lock or call the timeout callback.
+ *
+ * timeoutMsec == 0 means infinite timeout.
+ */
+static int at_send_command_full_nolock (const char *command, ATCommandType type,
+                    const char *responsePrefix, const char *smspdu,
+                    long long timeoutMsec, ATResponse **pp_outResponse)
+{
+    int err = AT_NOERROR;
+
+    struct atcontext *ac = getAtContext();
+
+    /* Default to NULL, to allow caller to free securely even if
+     * no response will be set below */
+    if (pp_outResponse != NULL)
+        *pp_outResponse = NULL;
+
+    /* FIXME This is to prevent future problems due to calls from other threads; should be revised. */
+    while (pthread_mutex_trylock(&ac->requestmutex) == EBUSY)
+        pthread_cond_wait(&ac->requestcond, &ac->commandmutex);
+
+    if(ac->response != NULL) {
+        err = AT_ERROR_COMMAND_PENDING;
+        goto finally;
+    }
+
+    ac->type = type;
+    ac->responsePrefix = responsePrefix;
+    ac->smsPDU = smspdu;
+    ac->response = at_response_new();
+    if (ac->response == NULL) {
+        err = AT_ERROR_MEMORY_ALLOCATION;
+        goto finally;
+    }
+
+    err = writeline (command);
+
+    if (err != AT_NOERROR)
+        goto finally;
+
+    while (ac->response->finalResponse == NULL && ac->readerClosed == 0) {
+        if (timeoutMsec != 0)
+            err = pthread_cond_timeout_np(&ac->commandcond, &ac->commandmutex, timeoutMsec);
+        else
+            err = pthread_cond_wait(&ac->commandcond, &ac->commandmutex);
+
+        if (err == ETIMEDOUT) {
+            err = AT_ERROR_TIMEOUT;
+            goto finally;
+        }
+        }
+
+    if (ac->response->success == 0) {
+        err = at_get_error(ac->response);
+    }
+
+    if (pp_outResponse == NULL)
+        at_response_free(ac->response);
+    else {
+        /* Line reader stores intermediate responses in reverse order. */
+        reverseIntermediates(ac->response);
+        *pp_outResponse = ac->response;
+    }
+
+    ac->response = NULL;
+
+    if(ac->readerClosed > 0) {
+        err = AT_ERROR_CHANNEL_CLOSED;
+        goto finally;
+    }
+
+finally:
+    clearPendingCommand();
+
+    pthread_cond_broadcast(&ac->requestcond);
+    pthread_mutex_unlock(&ac->requestmutex);
+
+    return err;
+}
+
+/**
+ * Internal send_command implementation.
+ *
+ * timeoutMsec == 0 means infinite timeout.
+ */
+static int at_send_command_full (const char *command, ATCommandType type,
+                    const char *responsePrefix, const char *smspdu,
+                    long long timeoutMsec, ATResponse **pp_outResponse, int useap, va_list ap)
+{
+    int err;
+
+    struct atcontext *ac = getAtContext();
+    static char strbuf[BUFFSIZE];
+    const char *ptr;
+
+    if (0 != pthread_equal(ac->tid_reader, pthread_self()))
+        /* Cannot be called from reader thread. */
+        return AT_ERROR_INVALID_THREAD;
+
+    pthread_mutex_lock(&ac->commandmutex);
+    if (useap) {
+        if (!vsnprintf(strbuf, BUFFSIZE, command, ap)) {
+           pthread_mutex_unlock(&ac->commandmutex);
+           return AT_ERROR_STRING_CREATION;
+    }
+        ptr = strbuf;
+    } else
+        ptr = command;
+
+    err = at_send_command_full_nolock(ptr, type,
+                    responsePrefix, smspdu,
+                    timeoutMsec, pp_outResponse);
+
+    pthread_mutex_unlock(&ac->commandmutex);
+
+    if (err == AT_ERROR_TIMEOUT && ac->onTimeout != NULL)
+        ac->onTimeout();
+
+    return err;
+}
+
+/* Only call this from onTimeout, since we're not locking or anything. */
+void at_send_escape (void)
+{
+    struct atcontext *ac = getAtContext();
+    int written;
+
+    do
+        written = write (ac->fd, "\033" , 1);
+    while ((written < 0 && errno == EINTR) || (written == 0));
+}
+
+/**
+ * Issue a single normal AT command with no intermediate response expected.
+ *
+ * "command" should not include \r.
+ */
+int at_send_command (const char *command, ...)
+{
+    int err;
+
+    struct atcontext *ac = getAtContext();
+    va_list ap;
+    va_start(ap, command);
+
+    err = at_send_command_full (command, NO_RESULT, NULL,
+            NULL, ac->timeoutMsec, NULL, 1, ap);
+    va_end(ap);
+
+    if (err != AT_NOERROR)
+        ALOGI(" --- %s", at_str_err(-err));
+
+    return -err;
+}
+
+int at_send_command_raw (const char *command, ATResponse **pp_outResponse)
+{
+    struct atcontext *ac = getAtContext();
+    int err;
+
+    err = at_send_command_full (command, MULTILINE, "",
+            NULL, ac->timeoutMsec, pp_outResponse, 0, empty);
+
+    /* Don't check for intermediate responses as it is unknown if any
+     * intermediate responses are expected. Don't free the response, instead,
+     * let calling function free the allocated response.
+     */
+
+    if (err != AT_NOERROR)
+        ALOGI(" --- %s", at_str_err(-err));
+
+    return -err;
+}
+
+int at_send_command_singleline (const char *command,
+                                const char *responsePrefix,
+                                 ATResponse **pp_outResponse, ...)
+{
+    int err;
+
+    struct atcontext *ac = getAtContext();
+    va_list ap;
+    va_start(ap, pp_outResponse);
+
+    err = at_send_command_full (command, SINGLELINE, responsePrefix,
+                                    NULL, ac->timeoutMsec, pp_outResponse, 1, ap);
+
+    if (err == AT_NOERROR && pp_outResponse != NULL
+            && (*pp_outResponse) != NULL
+            && (*pp_outResponse)->p_intermediates == NULL)
+        /* Command with pp_outResponse must have an intermediate response */
+        err = AT_ERROR_INVALID_RESPONSE;
+
+    /* Free response in case of error */
+    if (err != AT_NOERROR && pp_outResponse != NULL
+            && (*pp_outResponse) != NULL) {
+        at_response_free(*pp_outResponse);
+        *pp_outResponse = NULL;
+    }
+
+    va_end(ap);
+
+    if (err != AT_NOERROR)
+        ALOGI(" --- %s", at_str_err(-err));
+
+    return -err;
+}
+
+int at_send_command_numeric (const char *command,
+                                 ATResponse **pp_outResponse)
+{
+    int err;
+
+    struct atcontext *ac = getAtContext();
+
+    err = at_send_command_full (command, NUMERIC, NULL,
+                                NULL, ac->timeoutMsec, pp_outResponse, 0, empty);
+
+    if (err == AT_NOERROR && pp_outResponse != NULL
+            && (*pp_outResponse) != NULL
+            && (*pp_outResponse)->p_intermediates == NULL)
+        /* Command with pp_outResponse must have an intermediate response */
+        err = AT_ERROR_INVALID_RESPONSE;
+
+    /* Free response in case of error */
+    if (err != AT_NOERROR && pp_outResponse != NULL
+            && (*pp_outResponse) != NULL) {
+        at_response_free(*pp_outResponse);
+        *pp_outResponse = NULL;
+    }
+
+    if (err != AT_NOERROR)
+        ALOGI(" --- %s", at_str_err(-err));
+
+    return -err;
+}
+
+
+int at_send_command_sms (const char *command,
+                                const char *pdu,
+                                const char *responsePrefix,
+                                 ATResponse **pp_outResponse)
+{
+    int err;
+
+    struct atcontext *ac = getAtContext();
+
+    err = at_send_command_full (command, SINGLELINE, responsePrefix,
+                                    pdu, ac->timeoutMsec, pp_outResponse, 0, empty);
+
+    if (err == AT_NOERROR && pp_outResponse != NULL
+            && (*pp_outResponse) != NULL
+            && (*pp_outResponse)->p_intermediates == NULL)
+        /* Command with pp_outResponse must have an intermediate response */
+        err = AT_ERROR_INVALID_RESPONSE;
+
+    /* Free response in case of error */
+    if (err != AT_NOERROR && pp_outResponse != NULL
+            && (*pp_outResponse) != NULL) {
+        at_response_free(*pp_outResponse);
+        *pp_outResponse = NULL;
+    }
+
+    if (err != AT_NOERROR)
+        ALOGI(" --- %s", at_str_err(-err));
+
+    return -err;
+}
+
+
+int at_send_command_multiline (const char *command,
+                                const char *responsePrefix,
+                                 ATResponse **pp_outResponse, ...)
+{
+    int err;
+
+    struct atcontext *ac = getAtContext();
+    va_list ap;
+    va_start(ap, pp_outResponse);
+
+    err = at_send_command_full (command, MULTILINE, responsePrefix,
+                                    NULL, ac->timeoutMsec, pp_outResponse, 1, ap);
+    va_end(ap);
+
+    /* Free response in case of error */
+    if (err != AT_NOERROR && pp_outResponse != NULL
+            && (*pp_outResponse) != NULL) {
+        at_response_free(*pp_outResponse);
+        *pp_outResponse = NULL;
+    }
+
+    if (err != AT_NOERROR)
+        ALOGI(" --- %s", at_str_err(-err));
+
+    return -err;
+}
+
+/**
+ * Set the default timeout. Let it be reasonably high, some commands
+ * take their time. Default is 10 minutes.
+ */
+void at_set_timeout_msec(int timeout)
+{
+    struct atcontext *ac = getAtContext();
+
+    ac->timeoutMsec = timeout;
+}
+
+/** This callback is invoked on the command thread. */
+void at_set_on_timeout(void (*onTimeout)(void))
+{
+    struct atcontext *ac = getAtContext();
+
+    ac->onTimeout = onTimeout;
+}
+
+
+/*
+ * This callback is invoked on the reader thread (like ATUnsolHandler), when the
+ * input stream closes before you call at_close (not when you call at_close()).
+ * You should still call at_close(). It may also be invoked immediately from the
+ * current thread if the read channel is already closed.
+ */
+void at_set_on_reader_closed(void (*onClose)(void))
+{
+    struct atcontext *ac = getAtContext();
+
+    ac->onReaderClosed = onClose;
+}
+
+
+/**
+ * Periodically issue an AT command and wait for a response.
+ * Used to ensure channel has start up and is active.
+ */
+int at_handshake(void)
+{
+    int i;
+    int err = 0;
+
+    struct atcontext *ac = getAtContext();
+
+    if (0 != pthread_equal(ac->tid_reader, pthread_self()))
+        /* Cannot be called from reader thread. */
+        return AT_ERROR_INVALID_THREAD;
+
+    pthread_mutex_lock(&ac->commandmutex);
+
+    for (i = 0 ; i < HANDSHAKE_RETRY_COUNT ; i++) {
+        /* Some stacks start with verbose off. */
+        err = at_send_command_full_nolock ("ATE0Q0V1", NO_RESULT,
+                    NULL, NULL, HANDSHAKE_TIMEOUT_MSEC, NULL);
+
+        if (err == 0)
+            break;
+    }
+
+    if (err == 0) {
+        /* Pause for a bit to let the input buffer drain any unmatched OK's
+           (they will appear as extraneous unsolicited responses). */
+        ALOGD("%s() pausing %d ms to drain unmatched OK's...",
+             __func__, HANDSHAKE_TIMEOUT_MSEC);
+        sleepMsec(HANDSHAKE_TIMEOUT_MSEC);
+    }
+
+    pthread_mutex_unlock(&ac->commandmutex);
+
+    return err;
+}
+
+AT_Error at_get_at_error(int error)
+{
+    error = -error;
+    if (error >= AT_ERROR_BASE && error < AT_ERROR_TOP)
+        return (AT_Error)(error - AT_ERROR_BASE);
+    else
+        return AT_ERROR_NON_AT;
+    }
+
+AT_CME_Error at_get_cme_error(int error)
+{
+    error = -error;
+    if (error >= CME_ERROR_BASE && error < CME_ERROR_TOP)
+        return (AT_CME_Error)(error - CME_ERROR_BASE);
+    else
+        return CME_ERROR_NON_CME;
+    }
+
+AT_CMS_Error at_get_cms_error(int error)
+{
+    error = -error;
+    if (error >= CMS_ERROR_BASE && error < CMS_ERROR_TOP)
+        return (AT_CMS_Error)(error - CMS_ERROR_BASE);
+    else
+        return CMS_ERROR_NON_CMS;
+}
+
+AT_Generic_Error at_get_generic_error(int error)
+{
+    error = -error;
+    if (error >= GENERIC_ERROR_BASE && error < GENERIC_ERROR_TOP)
+        return (AT_Generic_Error)(error - GENERIC_ERROR_BASE);
+    else
+        return GENERIC_ERROR_NON_GENERIC;
+    }
+
+AT_Error_type at_get_error_type(int error)
+{
+    error = -error;
+    if (error == AT_NOERROR)
+        return NONE_ERROR;
+
+    if (error > AT_ERROR_BASE && error <= AT_ERROR_TOP)
+        return AT_ERROR;
+
+    if (error >= CME_ERROR_BASE && error <= CME_ERROR_TOP)
+        return CME_ERROR;
+
+    if (error >= CMS_ERROR_BASE && error <= CMS_ERROR_TOP)
+        return CMS_ERROR;
+
+    if (error >= GENERIC_ERROR_BASE && error <= GENERIC_ERROR_TOP)
+        return GENERIC_ERROR;
+
+    return UNKNOWN_ERROR;
+    }
+
+#define quote(x) #x
+
+const char *at_str_err(int error)
+{
+    const char *s = "--UNKNOWN--";
+
+    error = -error;
+    switch(error) {
+#define AT(name, num) case num + AT_ERROR_BASE: s = quote(AT_##name); break;
+#define CME(name, num) case num + CME_ERROR_BASE: s = quote(CME_##name); break;
+#define CMS(name, num) case num + CMS_ERROR_BASE: s = quote(CMS_##name); break;
+#define GENERIC(name, num) case num + GENERIC_ERROR_BASE: s = quote(GENERIC_##name); break;
+    mbm_error
+#undef AT
+#undef CME
+#undef CMS
+#undef GENERIC
+}
+
+    return s;
+}
diff --git a/huaweigeneric-ril/atchannel.h b/huaweigeneric-ril/atchannel.h
new file mode 100644 (file)
index 0000000..135d195
--- /dev/null
@@ -0,0 +1,141 @@
+/* //device/system/reference-ril/atchannel.h
+**
+** 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.
+*/
+
+#ifndef ATCHANNEL_H
+#define ATCHANNEL_H 1
+#include "at_error.h"
+
+/* Define AT_DEBUG to send AT traffic to "/tmp/radio-at.log" */
+#define AT_DEBUG  0
+
+#if AT_DEBUG
+extern void  AT_DUMP(const char* prefix, const char*  buff, int  len);
+#else
+#define  AT_DUMP(prefix,buff,len)  do{}while(0)
+#endif
+
+typedef enum {
+    DEFAULT_VALUE = -1,
+    GENERAL = 0,
+    AUTHENTICATION_FAILURE = 1,
+    IMSI_UNKNOWN_IN_HLR = 2,
+    ILLEGAL_MS = 3,
+    ILLEGAL_ME = 4,
+    PLMN_NOT_ALLOWED = 5,
+    LOCATION_AREA_NOT_ALLOWED = 6,
+    ROAMING_NOT_ALLOWED = 7,
+    NO_SUITABLE_CELL_IN_LOCATION_AREA = 8,
+    NETWORK_FAILURE = 9,
+    PERSISTEN_LOCATION_UPDATE_REJECT = 10
+} Reg_Deny_DetailReason;
+
+typedef enum {
+    NO_RESULT,      /* No intermediate response expected. */
+    NUMERIC,        /* A single intermediate response starting with a 0-9. */
+    SINGLELINE,     /* A single intermediate response starting with a prefix. */
+    MULTILINE       /* Multiple line intermediate response
+                       starting with a prefix. */
+} ATCommandType;
+
+/** A singly-linked list of intermediate responses. */
+typedef struct ATLine  {
+    struct ATLine *p_next;
+    char *line;
+} ATLine;
+
+/** Free this with at_response_free(). */
+typedef struct {
+    int success;              /* True if final response indicates
+                                 success (eg "OK"). */
+    char *finalResponse;      /* Eg OK, ERROR */
+    ATLine  *p_intermediates; /* Any intermediate responses. */
+} ATResponse;
+
+/**
+ * A user-provided unsolicited response handler function.
+ * This will be called from the reader thread, so do not block.
+ * "s" is the line, and "sms_pdu" is either NULL or the PDU response
+ * for multi-line TS 27.005 SMS PDU responses (eg +CMT:).
+ */
+typedef void (*ATUnsolHandler)(const char *s, const char *sms_pdu);
+
+int at_open(int fd, ATUnsolHandler h);
+void at_close(void);
+
+/*
+ * Set default timeout for at commands. Let it be reasonable high
+ * since some commands take their time. Default is 10 minutes.
+ */
+void at_set_timeout_msec(int timeout);
+
+/*
+ * This callback is invoked on the command thread.
+ * You should reset or handshake here to avoid getting out of sync.
+ */
+void at_set_on_timeout(void (*onTimeout)(void));
+
+/*
+ * This callback is invoked on the reader thread (like ATUnsolHandler), when the
+ * input stream closes before you call at_close (not when you call at_close()).
+ * You should still call at_close(). It may also be invoked immediately from the
+ * current thread if the read channel is already closed.
+ */
+void at_set_on_reader_closed(void (*onClose)(void));
+
+void at_send_escape(void);
+
+int at_send_command_singleline (const char *command,
+                                const char *responsePrefix,
+                                ATResponse **pp_outResponse,
+                                ...);
+
+int at_send_command_numeric (const char *command,
+                                 ATResponse **pp_outResponse);
+
+int at_send_command_multiline (const char *command,
+                                const char *responsePrefix,
+                               ATResponse **pp_outResponse,
+                               ...);
+
+
+int at_handshake(void);
+
+int at_send_command (const char *command, ...);
+
+/* at_send_command_raw do allow missing intermediate response(s) without an
+ * error code in the return value. Besides that, the response is not freed.
+ * This requires the caller to handle freeing of the response, even in the
+ * case that there was an error.
+ */
+int at_send_command_raw (const char *command, ATResponse **pp_outResponse);
+
+int at_send_command_sms (const char *command, const char *pdu,
+                            const char *responsePrefix,
+                            ATResponse **pp_outResponse);
+
+void at_response_free(ATResponse *p_response);
+
+void at_make_default_channel(void);
+
+AT_Error get_at_error(int error);
+AT_CME_Error at_get_cme_error(int error);
+AT_CMS_Error at_get_cms_error(int error);
+AT_Generic_Error at_get_generic_error(int error);
+AT_Error_type at_get_error_type(int error);
+const char *at_str_err(int error);
+
+#endif
diff --git a/huaweigeneric-ril/audiochannel.cpp b/huaweigeneric-ril/audiochannel.cpp
new file mode 100644 (file)
index 0000000..0211ed0
--- /dev/null
@@ -0,0 +1,481 @@
+/**
+ * Copyright (C) 2010 Eduardo José Tagle <ejtagle@tutopia.com>
+ *
+ * Since deeply inspired from portaudio dev port:
+ * Copyright (C) 2009-2010 r3gis (http://www.r3gis.fr)
+ * Copyright (C) 2008-2009 Teluu Inc. (http://www.teluu.com)
+ * Copyright (C) 2003-2008 Benny Prijono <benny@prijono.org>
+ *
+ * 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.
+ */
+
+
+#include <termios.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <sys/types.h>
+#include "audiochannel.h"
+
+#define LOG_NDEBUG 0
+#define LOG_TAG "RILAudioCh"
+#include <utils/Log.h>
+
+#include <system/audio.h>
+#include <media/AudioRecord.h>
+#include <media/AudioSystem.h>
+#include <media/AudioTrack.h>
+
+// ---- Android sound streaming ----
+
+/* modemAudioOut:
+    Output an audio frame (160 samples) to the 3G audio port of the cell modem
+
+        data = Pointer to audio data to write
+
+*/
+static int modemAudioOut(struct GsmAudioTunnel* ctx,const void* data)
+{
+    if (ctx->fd == -1)
+        return 0;
+
+    // Write audio to the 3G modem audio port in 320 bytes chunks... This is
+    //  required by huawei modems...
+
+    // Write audio chunk
+    int res = write(ctx->fd, data, ctx->frame_size * (ctx->bits_per_sample/8));
+    if (res < 0)
+        return -1;
+
+    // Write a 0 length frame to post data
+    res = write(ctx->fd, data, 0);
+    if (res < 0)
+        return -1;
+
+    return 0;
+}
+
+/* modemAudioIn:
+    Input an audio frame (160 samples) from the 3G audio port of the cell modem
+
+        data = Pointer to buffer where data must be stored
+*/
+static int modemAudioIn(struct GsmAudioTunnel* ctx, void* data)
+{
+    int res = 0;
+    if (ctx->fd == -1)
+        return 0;
+
+
+    while (1) {
+        res = read(ctx->fd, data, ctx->frame_size * (ctx->bits_per_sample/8));
+        if (res == -1) {
+
+            if (errno != EAGAIN && errno != EINTR) {
+                // A real error, not something that trying again will fix
+                break;
+            }
+        } else {
+            break;
+        }
+    }
+
+    /* Failure means 0 bytes */
+    if (res < 0)
+        res = 0;
+
+    /* If some samples missing, complete them with silence */
+    if (res < (int) (ctx->frame_size * (ctx->bits_per_sample/8) )) {
+
+        /* Output silence */
+        memset( (char*)data + res, 0, (ctx->frame_size * (ctx->bits_per_sample/8) - res));
+    }
+
+    return 0;
+}
+
+static void AndroidRecorderCallback(int event, void* userData, void* info)
+{
+    struct GsmAudioTunnel *ctx = (struct GsmAudioTunnel*) userData;
+    android::AudioRecord::Buffer* uinfo = (android::AudioRecord::Buffer*) info;
+    unsigned nsamples;
+    void *input;
+
+    if(!ctx || !uinfo)
+        return;
+
+    if (ctx->quit_flag)
+        goto on_break;
+
+    input = (void *) uinfo->raw;
+
+    // Calculate number of total samples we've got
+    nsamples = uinfo->frameCount + ctx->rec_buf_count;
+
+    if (nsamples >= ctx->frame_size) {
+
+        /* If buffer is not empty, combine the buffer with the just incoming
+         * samples, then call put_frame.
+         */
+        if (ctx->rec_buf_count) {
+
+            unsigned chunk_count = ctx->frame_size - ctx->rec_buf_count;
+            memcpy( (char*)ctx->rec_buf + ctx->rec_buf_count * (ctx->bits_per_sample/8), input, chunk_count * (ctx->bits_per_sample/8));
+
+            /* Send the audio to the modem */
+            modemAudioOut(ctx, ctx->rec_buf);
+
+            input = (char*) input + chunk_count * (ctx->bits_per_sample/8);
+            nsamples -= ctx->frame_size;
+            ctx->rec_buf_count = 0;
+        }
+
+        // Give all frames we have
+        while (nsamples >= ctx->frame_size) {
+
+            /* Send the audio to the modem */
+            modemAudioOut(ctx, input);
+
+            input = (char*) input + ctx->frame_size * (ctx->bits_per_sample/8);
+            nsamples -= ctx->frame_size;
+        }
+
+        // Store the remaining samples into the buffer
+        if (nsamples) {
+            ctx->rec_buf_count = nsamples;
+            memcpy(ctx->rec_buf, input, nsamples * (ctx->bits_per_sample/8));
+        }
+
+    } else {
+        // Not enough samples, let's just store them in the buffer
+        memcpy((char*)ctx->rec_buf + ctx->rec_buf_count * (ctx->bits_per_sample/8), input, uinfo->frameCount * (ctx->bits_per_sample/8));
+        ctx->rec_buf_count += uinfo->frameCount;
+    }
+
+    return;
+
+on_break:
+    ALOGD("Record thread stopped");
+    ctx->rec_thread_exited = 1;
+    return;
+}
+
+static void AndroidPlayerCallback( int event, void* userData, void* info)
+{
+    unsigned nsamples_req;
+    void *output;
+    struct GsmAudioTunnel *ctx = (struct GsmAudioTunnel*) userData;
+    android::AudioTrack::Buffer* uinfo = (android::AudioTrack::Buffer*) info;
+
+    if (!ctx || !uinfo)
+        return;
+
+    if (ctx->quit_flag)
+        goto on_break;
+
+    nsamples_req = uinfo->frameCount;
+    output = (void*) uinfo->raw;
+
+    // Check if any buffered samples
+    if (ctx->play_buf_count) {
+
+        // samples buffered >= requested by sound device
+        if (ctx->play_buf_count >= nsamples_req) {
+
+            memcpy(output, ctx->play_buf, nsamples_req * (ctx->bits_per_sample/8));
+            ctx->play_buf_count -= nsamples_req;
+
+            memmove(ctx->play_buf,
+                (char*)ctx->play_buf + nsamples_req * (ctx->bits_per_sample/8), ctx->play_buf_count * (ctx->bits_per_sample/8));
+            nsamples_req = 0;
+            return;
+        }
+
+        // samples buffered < requested by sound device
+        memcpy(output, ctx->play_buf,
+                        ctx->play_buf_count * (ctx->bits_per_sample/8));
+        nsamples_req -= ctx->play_buf_count;
+        output = (char*)output + ctx->play_buf_count * (ctx->bits_per_sample/8);
+        ctx->play_buf_count = 0;
+    }
+
+    // Fill output buffer as requested in chunks
+    while (nsamples_req) {
+        if (nsamples_req >= ctx->frame_size) {
+
+            /* get a frame from the modem */
+            modemAudioIn(ctx, output);
+
+            nsamples_req -= ctx->frame_size;
+            output = (char*)output + ctx->frame_size * (ctx->bits_per_sample/8);
+
+        } else {
+
+            /* get a frame from the modem */
+            modemAudioIn(ctx, ctx->play_buf);
+
+            memcpy(output, ctx->play_buf,
+                            nsamples_req * (ctx->bits_per_sample/8));
+            ctx->play_buf_count = ctx->frame_size - nsamples_req;
+            memmove(ctx->play_buf,
+                            (char*)ctx->play_buf + nsamples_req * (ctx->bits_per_sample/8),
+                            ctx->play_buf_count * (ctx->bits_per_sample/8));
+            nsamples_req = 0;
+        }
+    }
+
+    return;
+
+on_break:
+    ALOGD("Play thread stopped");
+    ctx->play_thread_exited = 1;
+    return;
+}
+
+ //AT^DDSETEX=2
+
+int gsm_audio_tunnel_start(struct GsmAudioTunnel *ctx,const char* gsmvoicechannel,unsigned int sampling_rate,unsigned int frame_size,unsigned int bits_per_sample)
+{
+    struct termios newtio;
+    int format = (bits_per_sample > 8)
+        ? AUDIO_FORMAT_PCM_16_BIT
+        : AUDIO_FORMAT_PCM_8_BIT;
+
+    /* If already running, dont do it again */
+    if (ctx->running)
+        return 0;
+
+    memset(ctx,0,sizeof(struct GsmAudioTunnel));
+
+    ctx->sampling_rate = sampling_rate;
+    ctx->frame_size = frame_size;
+    ctx->bits_per_sample = bits_per_sample;
+
+    ALOGD("Opening GSM voice channel '%s', sampling_rate:%u hz, frame_size:%u, bits_per_sample:%u  ...",
+        gsmvoicechannel,sampling_rate,frame_size,bits_per_sample);
+
+    // Open the device(com port) to be non-blocking (read will return immediately)
+    ctx->fd = open(gsmvoicechannel, O_RDWR | O_NOCTTY | O_NDELAY | O_NONBLOCK);
+    if (ctx->fd < 0) {
+        ALOGE("Could not open '%s'",gsmvoicechannel);
+        return -1;
+    }
+
+    // Configure it to get data as raw as possible
+    tcgetattr(ctx->fd, &newtio );
+    newtio.c_cflag = B115200 | CS8 | CLOCAL | CREAD;
+    newtio.c_iflag = IGNPAR | IGNBRK | IGNCR | IXOFF;
+    newtio.c_oflag = 0;
+    newtio.c_lflag = 0;
+    newtio.c_cc[VMIN]=0;
+    newtio.c_cc[VTIME]=5;   // You may want to tweak this; 5 = 1/2 second, 10 = 1 second
+    tcsetattr(ctx->fd,TCSANOW, &newtio);
+
+    ALOGD("Creating streams....");
+    ctx->rec_buf = malloc(ctx->frame_size * (ctx->bits_per_sample/8));
+    if (!ctx->rec_buf) {
+        close(ctx->fd);
+        return -1;
+    }
+
+    ctx->play_buf = malloc(ctx->frame_size * (ctx->bits_per_sample/8));
+    if (!ctx->play_buf) {
+        free(ctx->rec_buf);
+        close(ctx->fd);
+        return -1;
+    }
+
+    // Compute buffer size
+    size_t inputBuffSize = 0;
+    android::AudioSystem::getInputBufferSize(
+                    ctx->sampling_rate, // Samples per second
+                    format,
+                    AUDIO_CHANNEL_IN_MONO,
+                    &inputBuffSize);
+
+    // We use 2* size of input buffer for ping pong use of record buffer.
+    inputBuffSize = 2 * inputBuffSize;
+
+    // Create audio record channel
+    ctx->rec_strm = new android::AudioRecord();
+    if(!ctx->rec_strm) {
+        ALOGE("fail to create audio record");
+        free(ctx->play_buf);
+        free(ctx->rec_buf);
+        close(ctx->fd);
+        return -1;
+    }
+
+    // Unmute microphone
+    // android::AudioSystem::muteMicrophone(false);
+    int create_result = ((android::AudioRecord*)ctx->rec_strm)->set(
+                    AUDIO_SOURCE_MIC,
+                    ctx->sampling_rate,
+                    format,
+                    AUDIO_CHANNEL_IN_MONO,
+                    inputBuffSize,
+                    0,                  //flags
+                    &AndroidRecorderCallback,
+                    (void *) ctx,
+                    (inputBuffSize / 2), // Notification frames
+                    false,
+                    0);
+
+    if(create_result != android::NO_ERROR){
+        ALOGE("fail to check audio record : error code %d", create_result);
+        delete ((android::AudioRecord*)ctx->rec_strm);
+        free(ctx->play_buf);
+        free(ctx->rec_buf);
+        close(ctx->fd);
+        return -1;
+    }
+
+    if(((android::AudioRecord*)ctx->rec_strm)->initCheck() != android::NO_ERROR) {
+        ALOGE("fail to check audio record : buffer size is : %d, error code : %d", inputBuffSize, ((android::AudioRecord*)ctx->rec_strm)->initCheck() );
+        delete ((android::AudioRecord*)ctx->rec_strm);
+        free(ctx->play_buf);
+        free(ctx->rec_buf);
+        close(ctx->fd);
+        return -1;
+    }
+
+    // Create audio playback channel
+    ctx->play_strm = new android::AudioTrack();
+    if(!ctx->play_strm) {
+        ALOGE("Failed to create AudioTrack");
+        delete ((android::AudioRecord*)ctx->rec_strm);
+        free(ctx->play_buf);
+        free(ctx->rec_buf);
+        close(ctx->fd);
+        return -1;
+    }
+
+    // android::AudioSystem::setMasterMute(false);
+    create_result = ((android::AudioTrack*)ctx->play_strm)->set(
+                    AUDIO_STREAM_VOICE_CALL,
+                    ctx->sampling_rate, //this is sample rate in Hz (16000 Hz for example)
+                    format,
+                    AUDIO_CHANNEL_OUT_MONO, //For now this is mono (we expect 1)
+                    inputBuffSize,
+                    0, //flags
+                    &AndroidPlayerCallback,
+                    (void *) ctx,
+                    (inputBuffSize / 2),
+                    0,
+                    false,
+                    0);
+
+    if(create_result != android::NO_ERROR){
+        ALOGE("fail to check audio record : error code %d", create_result);
+        delete ((android::AudioTrack*)ctx->play_strm);
+        delete ((android::AudioRecord*)ctx->rec_strm);
+        free(ctx->play_buf);
+        free(ctx->rec_buf);
+        close(ctx->fd);
+        return -1;
+    }
+
+    if(((android::AudioTrack*)ctx->play_strm)->initCheck() != android::NO_ERROR) {
+        ALOGE("fail to check audio playback : buffer size is : %d, error code : %d", inputBuffSize, ((android::AudioTrack*)ctx->play_strm)->initCheck() );
+        delete ((android::AudioTrack*)ctx->play_strm);
+        delete ((android::AudioRecord*)ctx->rec_strm);
+        free(ctx->play_buf);
+        free(ctx->rec_buf);
+        close(ctx->fd);
+        return -1;
+    }
+
+    /* Save the current audio routing setting, then switch it to earpiece. */
+    // android::AudioSystem::getMode(&ctx->saved_audio_mode);
+    // android::AudioSystem::getRouting(ctx->saved_audio_mode, &ctx->saved_audio_routing);
+    // android::AudioSystem::setRouting(ctx->saved_audio_mode,
+    //                      android::AudioSystem::ROUTE_EARPIECE,
+    //                      android::AudioSystem::ROUTE_ALL);
+
+    ALOGD("Starting streaming...");
+
+    if (ctx->play_strm) {
+        ((android::AudioTrack*)ctx->play_strm)->start();
+    }
+
+    if (ctx->rec_strm) {
+        ((android::AudioRecord*)ctx->rec_strm)->start();
+    }
+
+    ALOGD("Done");
+
+    ctx->running = 1;
+
+    // OK, done
+    return 0;
+}
+
+/* API: destroy ctx. */
+int gsm_audio_tunnel_stop(struct GsmAudioTunnel *ctx)
+{
+    int i = 0;
+
+    /* If not running, dont do it again */
+    if (!ctx->running)
+        return 0;
+
+
+    ALOGD("Will Stop ctx, wait for all audio callback clean");
+    ctx->quit_flag = 1;
+    for (i=0; !ctx->rec_thread_exited && i<100; ++i){
+        usleep(100000);
+    }
+    for (i=0; !ctx->play_thread_exited && i<100; ++i){
+        usleep(100000);
+    }
+
+    // After all sleep for 0.1 seconds since android device can be slow
+    usleep(100000);
+
+    ALOGD("Stopping ctx..");
+    if (ctx->rec_strm) {
+        ((android::AudioRecord*)ctx->rec_strm)->stop();
+    }
+
+    if (ctx->play_strm) {
+        ((android::AudioTrack*)ctx->play_strm)->stop();
+    }
+
+    // Restore the audio routing setting
+    //      android::AudioSystem::setRouting(ctx->saved_audio_mode,
+    //                      ctx->saved_audio_routing,
+    //                      android::AudioSystem::ROUTE_ALL);
+
+
+    ALOGD("Closing streaming");
+
+    if (ctx->play_strm) {
+        delete ((android::AudioTrack*)ctx->play_strm);
+        ctx->play_strm = NULL;
+    }
+
+    if (ctx->rec_strm) {
+        delete ((android::AudioRecord*)ctx->rec_strm);
+        ctx->rec_strm = NULL;
+    }
+
+    free(ctx->play_buf);
+    free(ctx->rec_buf);
+
+    close(ctx->fd);
+    memset(ctx,0,sizeof(struct GsmAudioTunnel));
+
+    ALOGD("Done");
+    return 0;
+}
diff --git a/huaweigeneric-ril/audiochannel.h b/huaweigeneric-ril/audiochannel.h
new file mode 100644 (file)
index 0000000..6cc59b5
--- /dev/null
@@ -0,0 +1,65 @@
+/*
+**
+** Copyright (C) 2010 Eduardo José Tagle <ejtagle@tutopia.com>
+**
+** 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.
+**
+** Author: Christian Bejram <christian.bejram@stericsson.com>
+*/
+#ifndef _AUDIOCHANNEL_H
+#define _AUDIOCHANNEL_H 1
+
+struct GsmAudioTunnel {
+    int running;                    // If running
+
+    // 3G voice modem
+    int fd;                         // Voice data serial port handler
+
+    // Common properties
+    volatile int quit_flag;         // If threads should quit
+    unsigned int frame_size;        // Frame size
+    unsigned int sampling_rate;     // Sampling rate
+    unsigned int bits_per_sample;   // Bits per sample. valid values = 16/8
+
+    // Playback
+    void* play_strm;                // Playback stream
+    volatile int play_thread_exited;// If play thread has exited
+    void* play_buf;                 // Pointer to the playback buffer
+    unsigned int play_buf_count;    // Count of already stored samples in the playback buffer
+
+    // Record
+    void* rec_strm;                 // Record stream
+    volatile int rec_thread_exited; // If record thread has exited
+    void* rec_buf;                  // Pointer to the recording buffer
+    unsigned int rec_buf_count;     // Count of already stored samples in the recording buffer
+};
+
+#define GSM_AUDIO_CHANNEL_STATIC_INIT { 0, 0, 0,0,0,0, 0,0,0,0 ,0,0,0,0 }
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+int gsm_audio_tunnel_start(struct GsmAudioTunnel *stream,
+    const char* gsmvoicechannel,
+    unsigned int sampling_rate,
+    unsigned int frame_size,
+    unsigned int bits_per_sample);
+
+int gsm_audio_tunnel_stop(struct GsmAudioTunnel *stream);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/huaweigeneric-ril/fcp_parser.c b/huaweigeneric-ril/fcp_parser.c
new file mode 100644 (file)
index 0000000..65cbf34
--- /dev/null
@@ -0,0 +1,129 @@
+/*
+**
+** 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: Dmitry Tarnyagin <dmitry.tarnyagin@stericsson.com>
+*/
+
+#include <memory.h>
+#include <errno.h>
+#include <stdio.h>
+
+#define LOG_TAG "RILV"
+#include <utils/Log.h>
+
+#include "fcp_parser.h"
+#include "misc.h"
+
+int fcp_to_ts_51011(/*in*/ const char *stream, /*in*/ size_t len,
+        /*out*/ struct ts_51011_921_resp *out)
+{
+    const char *end = &stream[len];
+    struct tlv fcp;
+    int ret = parseTlv(stream, end, &fcp);
+    const char *what = NULL;
+#define FCP_CVT_THROW(_ret, _what)  \
+    do {                    \
+        ret = _ret;         \
+        what = _what;       \
+        goto except;        \
+    } while (0)
+
+    if (ret < 0)
+        FCP_CVT_THROW(ret, "ETSI TS 102 221, 11.1.1.3: FCP template TLV structure");
+    if (fcp.tag != 0x62)
+        FCP_CVT_THROW(-EINVAL, "ETSI TS 102 221, 11.1.1.3: FCP template tag");
+
+    /*
+     * NOTE: Following fields do not exist in FCP template:
+     * - file_acc
+     * - file_status
+     */
+
+    memset(out, 0, sizeof(*out));
+    while (fcp.data < fcp.end) {
+        unsigned char fdbyte;
+        size_t property_size;
+        struct tlv tlv;
+        ret = parseTlv(fcp.data, end, &tlv);
+        if (ret < 0)
+            FCP_CVT_THROW(ret, "ETSI TS 102 221, 11.1.1.3: FCP property TLV structure");
+        property_size = (tlv.end - tlv.data) / 2;
+
+        switch (tlv.tag) {
+            case 0x80: /* File size, ETSI TS 102 221, 11.1.1.4.1 */
+                /* File size > 0xFFFF is not supported by ts_51011 */
+                if (property_size != 2)
+                    FCP_CVT_THROW(-ENOTSUP, "3GPP TS 51 011, 9.2.1: Unsupported file size");
+                /* be16 on both sides */
+                ((char*)&out->file_size)[0] = TLV_DATA(tlv, 0);
+                ((char*)&out->file_size)[1] = TLV_DATA(tlv, 1);
+                break;
+            case 0x83: /* File identifier, ETSI TS 102 221, 11.1.1.4.4 */
+                /* Sanity check */
+                if (property_size != 2)
+                    FCP_CVT_THROW(-EINVAL, "ETSI TS 102 221, 11.1.1.4.4: Invalid file identifier");
+                /* be16 on both sides */
+                ((char*)&out->file_id)[0] = TLV_DATA(tlv, 0);
+                ((char*)&out->file_id)[1] = TLV_DATA(tlv, 1);
+                break;
+            case 0x82: /* File descriptior, ETSI TS 102 221, 11.1.1.4.3 */
+                /* Sanity check */
+                if (property_size < 2)
+                    FCP_CVT_THROW(-EINVAL, "ETSI TS 102 221, 11.1.1.4.3: Invalid file descriptor");
+                fdbyte = TLV_DATA(tlv, 0);
+                /* ETSI TS 102 221, Table 11.5 for FCP fields */
+                /* 3GPP TS 51 011, 9.2.1 and 9.3 for 'out' fields */
+                if ((fdbyte & 0xBF) == 0x38) {
+                    out->file_type = 2; /* DF of ADF */
+                } else if ((fdbyte & 0xB0) == 0x00) {
+                    out->file_type = 4; /* EF */
+                    out->file_status = 1; /* Not invalidated */
+                    ++out->data_size; /* file_structure field is valid */
+                    if ((fdbyte & 0x07) == 0x01) {
+                        out->file_structure = 0; /* Transparent */
+                    } else {
+                        if (property_size < 5)
+                            FCP_CVT_THROW(-EINVAL, "ETSI TS 102 221, 11.1.1.4.3: Invalid non-transparent file descriptor");
+                        ++out->data_size; /* record_size field is valid */
+                        out->record_size = TLV_DATA(tlv, 3);
+                        if ((fdbyte & 0x07) == 0x06) {
+                            out->file_structure = 3; /* Cyclic */
+                        } else if ((fdbyte & 0x07) == 0x02) {
+                            out->file_structure = 1; /* Linear fixed */
+                        } else {
+                            FCP_CVT_THROW(-EINVAL, "ETSI TS 102 221, 11.1.1.4.3: Invalid file structure");
+                        }
+                    }
+                } else {
+                    out->file_type = 0; /* RFU */
+                }
+                break;
+        }
+        fcp.data = tlv.end;
+    }
+
+ finally:
+    return ret;
+
+ except:
+ #undef FCP_CVT_THROW
+    ALOGE("%s() FCP to TS 510 11: Specification violation: %s.", __func__, what);
+    goto finally;
+}
diff --git a/huaweigeneric-ril/fcp_parser.h b/huaweigeneric-ril/fcp_parser.h
new file mode 100644 (file)
index 0000000..a7e5db8
--- /dev/null
@@ -0,0 +1,47 @@
+/* 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: Dmitry Tarnyagin <dmitry.tarnyagin@stericsson.com>
+*/
+
+#ifndef FCP_PARSER_H
+#define FCP_PARSER_H
+
+#include <stdint.h>
+#include <endian.h>
+
+struct ts_51011_921_resp {
+    uint8_t   rfu_1[2];
+    uint16_t  file_size; /* be16 */
+    uint16_t  file_id;   /* be16 */
+    uint8_t   file_type;
+    uint8_t   rfu_2;
+    uint8_t   file_acc[3];
+    uint8_t   file_status;
+    uint8_t   data_size;
+    uint8_t   file_structure;
+    uint8_t   record_size;
+} __attribute__((packed));
+
+int fcp_to_ts_51011(/*in*/ const char *stream,
+                    /*in*/ size_t len,
+                    /*out*/ struct ts_51011_921_resp *out);
+
+#endif
diff --git a/huaweigeneric-ril/gsm.c b/huaweigeneric-ril/gsm.c
new file mode 100644 (file)
index 0000000..274e7db
--- /dev/null
@@ -0,0 +1,1153 @@
+#include "gsm.h"
+#include <stdlib.h>
+
+/** UTILITIES
+ **/
+byte_t
+gsm_int_to_bcdi( int  value )
+{
+    return (byte_t)((value / 10) | ((value % 10) << 4));
+}
+
+int
+gsm_int_from_bcdi( byte_t  val )
+{
+    int  ret = 0;
+
+    if ((val & 0xf0) <= 0x90)
+        ret = (val >> 4);
+
+    if ((val & 0x0f) <= 0x90)
+        ret |= (val % 0xf)*10;
+
+    return ret;
+}
+
+
+static int
+gsm_bcdi_to_ascii( cbytes_t  bcd, int  bcdlen, bytes_t  dst )
+{
+    static byte_t  bcdichars[14] = "0123456789*#,N";
+
+    int  result = 0;
+    int  shift  = 0;
+
+    while (bcdlen > 0) {
+        int  c = (bcd[0] >> shift) & 0xf;
+
+        if (c == 0xf && bcdlen == 1)
+            break;
+
+        if (c < 14) {
+            if (dst) dst[result] = bcdichars[c];
+            result += 1;
+        }
+        bcdlen --;
+        shift += 4;
+        if (shift == 8) {
+            bcd++;
+            shift = 0;
+        }
+    }
+    return result;
+}
+
+
+static int
+gsm_bcdi_from_ascii( cbytes_t  ascii, int  asciilen, bytes_t  dst )
+{
+    cbytes_t  end    = ascii + asciilen;
+    int       result = 0;
+    int       phase  = 0x01;
+
+    while (ascii < end) {
+        int  c = *ascii++;
+        int  d;
+
+        if (c == '*')
+            c = 11;
+        else if (c == '#')
+            c = 12;
+        else if (c == ',')
+            c = 13;
+        else if (c == 'N')
+            c = 14;
+        else {
+            c -= '0';
+            if ((unsigned)c >= 10)
+                break;
+        }
+        phase = (phase << 4) | c;
+        if (phase & 0x100) {
+            if (dst) dst[result] = (byte_t) phase;
+            result += 1;
+            phase   = 0x01;
+        }
+    }
+    if (phase != 0x01) {
+        if (dst) dst[result] = (byte_t)( phase | 0xf0 );
+        result += 1;
+    }
+    return  result;
+}
+
+
+int
+gsm_hexchar_to_int( char  c )
+{
+    if ((unsigned)(c - '0') < 10)
+        return c - '0';
+    if ((unsigned)(c - 'a') < 6)
+        return 10 + (c - 'a');
+    if ((unsigned)(c - 'A') < 6)
+        return 10 + (c - 'A');
+    return -1;
+}
+
+int
+gsm_hexchar_to_int0( char  c )
+{
+    int  ret = gsm_hexchar_to_int(c);
+
+    return (ret < 0) ? 0 : ret;
+}
+
+int
+gsm_hex2_to_byte( const char*  hex )
+{
+    int  hi = gsm_hexchar_to_int(hex[0]);
+    int  lo = gsm_hexchar_to_int(hex[1]);
+
+    if (hi < 0 || lo < 0)
+        return -1;
+
+    return ( (hi << 4) | lo );
+}
+
+int
+gsm_hex4_to_short( const char*  hex )
+{
+    int  hi = gsm_hex2_to_byte(hex);
+    int  lo = gsm_hex2_to_byte(hex+2);
+
+    if (hi < 0 || lo < 0)
+        return -1;
+
+    return ((hi << 8) | lo);
+}
+
+int
+gsm_hex2_to_byte0( const char*  hex )
+{
+    int  hi = gsm_hexchar_to_int0(hex[0]);
+    int  lo = gsm_hexchar_to_int0(hex[1]);
+
+    return (byte_t)( (hi << 4) | lo );
+}
+
+void
+gsm_hex_from_byte( char*  hex, int val )
+{
+    static const char  hexdigits[] = "0123456789ABCDEF";
+
+    hex[0] = hexdigits[(val >> 4) & 15];
+    hex[1] = hexdigits[val & 15];
+}
+
+void
+gsm_hex_from_short( char*  hex, int  val )
+{
+    gsm_hex_from_byte( hex,   (val >> 8) );
+    gsm_hex_from_byte( hex+2, val );
+}
+
+
+
+/** HEX
+ **/
+void
+gsm_hex_to_bytes( cbytes_t  hex, int  hexlen, bytes_t  dst )
+{
+    int  nn;
+
+    for (nn = 0; nn < hexlen/2; nn++ ) {
+        dst[nn] = (byte_t) gsm_hex2_to_byte0( hex+2*nn );
+    }
+    if (hexlen & 1) {
+        dst[nn] = gsm_hexchar_to_int0( hex[2*nn] ) << 4;
+    }
+}
+
+void
+gsm_hex_from_bytes( char*  hex, cbytes_t  src, int  srclen )
+{
+    int  nn;
+
+    for (nn = 0; nn < srclen; nn++) {
+        gsm_hex_from_byte( hex + 2*nn, src[nn] );
+    }
+}
+
+/** ROPES
+ **/
+
+void
+gsm_rope_init( GsmRope  rope )
+{
+    rope->data  = NULL;
+    rope->pos   = 0;
+    rope->max   = 0;
+    rope->error = 0;
+}
+
+void
+gsm_rope_init_alloc( GsmRope  rope, int  count )
+{
+    rope->data  = rope->data0;
+    rope->pos   = 0;
+    rope->max   = sizeof(rope->data0);
+    rope->error = 0;
+
+    if (count > 0) {
+        rope->data = calloc( count, 1 );
+        rope->max  = count;
+
+        if (rope->data == NULL) {
+            rope->error = 1;
+            rope->max   = 0;
+        }
+    }
+}
+
+int
+gsm_rope_done( GsmRope  rope )
+{
+    int  result = rope->error;
+
+    if (rope->data && rope->data != rope->data0)
+        free(rope->data);
+
+    rope->data  = NULL;
+    rope->pos   = 0;
+    rope->max   = 0;
+    rope->error = 0;
+
+    return result;
+}
+
+
+bytes_t
+gsm_rope_done_acquire( GsmRope  rope, int  *psize )
+{
+    bytes_t  result = rope->data;
+
+    *psize = rope->pos;
+    if (result == rope->data0) {
+        result = malloc(  rope->pos );
+        if (result != NULL)
+            memcpy( result, rope->data, rope->pos );
+    }
+    return result;
+}
+
+
+int
+gsm_rope_ensure( GsmRope  rope, int  new_count )
+{
+    if (rope->data != NULL) {
+        int       old_max  = rope->max;
+        bytes_t   old_data = rope->data == rope->data0 ? NULL : rope->data;
+        int       new_max  = old_max;
+        bytes_t   new_data;
+
+        while (new_max < new_count) {
+            new_max += (new_max >> 1) + 4;
+        }
+        new_data = realloc( old_data, new_max );
+        if (new_data == NULL) {
+            rope->error = 1;
+            return -1;
+        }
+        rope->data = new_data;
+        rope->max  = new_max;
+    } else {
+        rope->max = new_count;
+    }
+    return 0;
+}
+
+static int
+gsm_rope_can_grow( GsmRope  rope, int  count )
+{
+    if (!rope->data || rope->error)
+        return 0;
+
+    if (rope->pos + count > rope->max) {
+        if (rope->data == NULL)
+            rope->max = rope->pos + count;
+
+        else if (rope->error ||
+                 gsm_rope_ensure( rope, rope->pos + count ) < 0)
+            return 0;
+    }
+    return 1;
+}
+
+void
+gsm_rope_add_c( GsmRope  rope,  char  c )
+{
+    if (gsm_rope_can_grow(rope, 1)) {
+        rope->data[ rope->pos ] = (byte_t) c;
+    }
+    rope->pos += 1;
+}
+
+void
+gsm_rope_add( GsmRope  rope, const void*  buf, int  buflen )
+{
+    if (gsm_rope_can_grow(rope, buflen)) {
+        memcpy( rope->data + rope->pos, (const char*)buf, buflen );
+    }
+    rope->pos += buflen;
+}
+
+void*
+gsm_rope_reserve( GsmRope  rope, int  count )
+{
+    void*  result = NULL;
+
+    if (gsm_rope_can_grow(rope, count)) {
+        if (rope->data != NULL)
+            result = rope->data + rope->pos;
+    }
+    rope->pos += count;
+
+    return result;
+}
+
+/* skip a given number of Unicode characters in a utf-8 byte string */
+cbytes_t
+utf8_skip( cbytes_t   utf8,
+           cbytes_t   utf8end,
+           int        count)
+{
+    cbytes_t  p   = utf8;
+    cbytes_t  end = utf8end;
+
+    for ( ; count > 0; count-- ) {
+        int  c;
+
+        if (p > end)
+            break;
+
+        c = *p++;
+        if (c > 128) {
+            while (p < end && (p[0] & 0xc0) == 0x80)
+                p++;
+        }
+    }
+    return  p;
+}
+
+static __inline__ int
+utf8_next( cbytes_t  *pp, cbytes_t  end )
+{
+    cbytes_t  p      = *pp;
+    int       result = -1;
+
+    if (p < end) {
+        int  c= *p++;
+        if (c >= 128) {
+            if ((c & 0xe0) == 0xc0)
+                c &= 0x1f;
+            else if ((c & 0xf0) == 0xe0)
+                c &= 0x0f;
+            else
+                c &= 0x07;
+
+            while (p < end && (p[0] & 0xc0) == 0x80) {
+                c = (c << 6) | (p[0] & 0x3f);
+            }
+        }
+        result = c;
+        *pp    = p;
+    }
+Exit:
+    return result;
+}
+
+
+__inline__ int
+utf8_write( bytes_t  utf8, int  offset, int  v )
+{
+    int  result;
+
+    if (v < 128) {
+        result = 1;
+        if (utf8)
+            utf8[offset] = (byte_t) v;
+    } else if (v < 0x800) {
+        result = 2;
+        if (utf8) {
+            utf8[offset+0] = (byte_t)( 0xc0 | (v >> 6) );
+            utf8[offset+1] = (byte_t)( 0x80 | (v & 0x3f) );
+        }
+    } else if (v < 0x10000) {
+        result = 3;
+        if (utf8) {
+            utf8[offset+0] = (byte_t)( 0xe0 |  (v >> 12) );
+            utf8[offset+1] = (byte_t)( 0x80 | ((v >> 6) & 0x3f) );
+            utf8[offset+2] = (byte_t)( 0x80 |  (v & 0x3f) );
+        }
+    } else {
+        result = 4;
+        if (utf8) {
+            utf8[offset+0] = (byte_t)( 0xf0 | ((v >> 18) & 0x7) );
+            utf8[offset+1] = (byte_t)( 0x80 | ((v >> 12) & 0x3f) );
+            utf8[offset+2] = (byte_t)( 0x80 | ((v >> 6) & 0x3f) );
+            utf8[offset+3] = (byte_t)( 0x80 |  (v & 0x3f) );
+        }
+    }
+    return  result;
+}
+
+static __inline__ int
+ucs2_write( bytes_t  ucs2, int  offset, int  v )
+{
+    if (ucs2) {
+        ucs2[offset+0] = (byte_t) (v >> 8);
+        ucs2[offset+1] = (byte_t) (v);
+    }
+    return 2;
+}
+
+int
+utf8_check( cbytes_t   p, int  utf8len )
+{
+    cbytes_t  end    = p + utf8len;
+    int       result = 0;
+
+    if (p) {
+        while (p < end) {
+            int  c = *p++;
+            if (c >= 128) {
+                int  len;
+                if ((c & 0xe0) == 0xc0) {
+                    len = 1;
+                }
+                else if ((c & 0xf0) == 0xe0) {
+                    len = 2;
+                }
+                else if ((c & 0xf8) == 0xf0) {
+                    len = 3;
+                }
+                else
+                    goto Exit;  /* malformed utf-8 */
+
+                if (p+len > end) /* string too short */
+                    goto Exit;
+
+                for ( ; len > 0; len--, p++ ) {
+                    if ((p[0] & 0xc0) != 0x80)
+                        goto Exit;
+                }
+            }
+        }
+        result = 1;
+    }
+Exit:
+    return result;
+}
+
+/** UCS2 to UTF8
+ **/
+
+/* convert a UCS2 string into a UTF8 byte string, assumes 'buf' is correctly sized */
+int
+ucs2_to_utf8( cbytes_t  ucs2,
+              int       ucs2len,
+              bytes_t   buf )
+{
+    int  nn;
+    int  result = 0;
+
+    for (nn = 0; nn < ucs2len; ucs2 += 2, nn++) {
+        int  c= (ucs2[0] << 8) | ucs2[1];
+        result += utf8_write(buf, result, c);
+    }
+    return result;
+}
+
+/* count the number of UCS2 chars contained in a utf8 byte string */
+int
+utf8_to_ucs2( cbytes_t  utf8,
+              int       utf8len,
+              bytes_t   ucs2 )
+{
+    cbytes_t  p      = utf8;
+    cbytes_t  end    = p + utf8len;
+    int       result = 0;
+
+    while (p < end) {
+        int  c = utf8_next(&p, end);
+
+        if (c < 0)
+            break;
+
+        result += ucs2_write(ucs2, result, c);
+    }
+    return result/2;
+}
+
+
+
+/** GSM ALPHABET
+ **/
+
+#define  GSM_7BITS_ESCAPE   0x1b
+#define  GSM_7BITS_UNKNOWN  0
+
+static const unsigned short   gsm7bits_to_unicode[128] = {
+  '@', 0xa3,  '$', 0xa5, 0xe8, 0xe9, 0xf9, 0xec, 0xf2, 0xc7, '\n', 0xd8, 0xf8, '\r', 0xc5, 0xe5,
+0x394,  '_',0x3a6,0x393,0x39b,0x3a9,0x3a0,0x3a8,0x3a3,0x398,0x39e,    0, 0xc6, 0xe6, 0xdf, 0xc9,
+  ' ',  '!',  '"',  '#', 0xa4,  '%',  '&', '\'',  '(',  ')',  '*',  '+',  ',',  '-',  '.',  '/',
+  '0',  '1',  '2',  '3',  '4',  '5',  '6',  '7',  '8',  '9',  ':',  ';',  '<',  '=',  '>',  '?',
+ 0xa1,  'A',  'B',  'C',  'D',  'E',  'F',  'G',  'H',  'I',  'J',  'K',  'L',  'M',  'N',  'O',
+  'P',  'Q',  'R',  'S',  'T',  'U',  'V',  'W',  'X',  'Y',  'Z', 0xc4, 0xd6,0x147, 0xdc, 0xa7,
+ 0xbf,  'a',  'b',  'c',  'd',  'e',  'f',  'g',  'h',  'i',  'j',  'k',  'l',  'm',  'n',  'o',
+  'p',  'q',  'r',  's',  't',  'u',  'v',  'w',  'x',  'y',  'z', 0xe4, 0xf6, 0xf1, 0xfc, 0xe0,
+};
+
+static const unsigned short  gsm7bits_extend_to_unicode[128] = {
+    0,   0,   0,   0,   0,   0,   0,   0,   0,   0,'\f',   0,   0,   0,   0,   0,
+    0,   0,   0,   0, '^',   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
+    0,   0,   0,   0,   0,   0,   0,   0, '{', '}',   0,   0,   0,   0,   0,'\\',
+    0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0, '[', '~', ']',   0,
+  '|',   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
+    0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
+    0,   0,   0,   0,   0,0x20ac, 0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
+    0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
+};
+
+
+static int
+unichar_to_gsm7( int  unicode )
+{
+    int  nn;
+    for (nn = 0; nn < 128; nn++) {
+        if (gsm7bits_to_unicode[nn] == unicode) {
+            return nn;
+        }
+    }
+    return -1;
+}
+
+static int
+unichar_to_gsm7_extend( int  unichar )
+{
+    int  nn;
+    for (nn = 0; nn < 128; nn++) {
+        if (gsm7bits_extend_to_unicode[nn] == unichar) {
+            return nn;
+        }
+    }
+    return -1;
+}
+
+
+/* return the number of septets needed to encode a unicode charcode */
+static int
+unichar_to_gsm7_count( int  unicode )
+{
+    int  nn;
+
+    nn = unichar_to_gsm7(unicode);
+    if (nn >= 0)
+        return 1;
+
+    nn = unichar_to_gsm7_extend(unicode);
+    if (nn >= 0)
+        return 2;
+
+    return 0;
+}
+
+
+int
+utf8_check_gsm7( cbytes_t  utf8,
+                 int       utf8len )
+{
+    cbytes_t  utf8end = utf8 + utf8len;
+
+    while (utf8 < utf8end) {
+        int  c = utf8_next( &utf8, utf8end );
+        if (unichar_to_gsm7_count(c) == 0)
+            return 0;
+    }
+    return 1;
+}
+
+
+int
+utf8_from_gsm7( cbytes_t  src,
+                int       septet_offset,
+                int       septet_count,
+                bytes_t   utf8 )
+{
+    int  shift   = (septet_offset & 7);
+    int  escaped = 0;
+    int  result  = 0;
+
+    src += (septet_offset >> 3);
+    for ( ; septet_count > 0; septet_count-- ) {
+        int  c = (src[0] >> shift) & 0x7f;
+        int  v;
+
+        if (shift > 1) {
+            c = ((src[1] << (8-shift)) | c) & 0x7f;
+        }
+
+        if (escaped) {
+            v = gsm7bits_extend_to_unicode[c];
+        } else if (c == GSM_7BITS_ESCAPE) {
+            escaped = 1;
+            goto NextSeptet;
+        } else {
+            v = gsm7bits_to_unicode[c];
+        }
+
+        result += utf8_write( utf8, result, v );
+
+    NextSeptet:
+        shift += 7;
+        if (shift >= 8) {
+            shift -= 8;
+            src   += 1;
+        }
+    }
+    return  result;
+}
+
+
+int
+utf8_from_gsm8( cbytes_t  src, int  count, bytes_t  utf8 )
+{
+    int  result  = 0;
+    int  escaped = 0;
+
+
+    for ( ; count > 0; count-- ) {
+        int  c = *src++;
+        int  v;
+
+        if (c == 0xff)
+            break;
+
+        if (c == GSM_7BITS_ESCAPE) {
+            if (escaped) { /* two escape characters => one space */
+                c = 0x20;
+                escaped = 0;
+            } else {
+                escaped = 1;
+                continue;
+            }
+        } else {
+            if (c >= 0x80) {
+                c       = 0x20;
+                escaped = 0;
+            } else if (escaped) {
+                c = gsm7bits_extend_to_unicode[c];
+            } else
+                c = gsm7bits_to_unicode[c];
+        }
+
+        result += utf8_write( utf8, result, c );
+    }
+    return  result;
+}
+
+/* convert a GSM 7-bit message into a unicode character array
+ * the 'dst' array must contain at least 160 chars. the function
+ * returns the number of characters decoded
+ *
+ * assumes the 'dst' array has at least septet_count items, returns the
+ * number of unichars really written
+ */
+int
+ucs2_from_gsm7( bytes_t   ucs2,
+                cbytes_t  src,
+                int       septet_offset,
+                int       septet_count )
+{
+    const unsigned char*  p     = src + (septet_offset >> 3);
+    int                   shift = (septet_offset & 7);
+    int                   escaped = 0;
+    int                   result  = 0;
+
+    for ( ; septet_count > 0; septet_count-- ) {
+        unsigned  val  = (p[0] >> shift) & 0x7f;
+
+        if (shift > 1)
+            val = (val | (p[1] << (8-shift))) & 0x7f;
+
+        if (escaped) {
+            int  c = gsm7bits_to_unicode[val];
+
+            result += ucs2_write(ucs2, result, c);
+            escaped = 0;
+        } else if (val == GSM_7BITS_ESCAPE) {
+            escaped = 1;
+        } else {
+            val = gsm7bits_extend_to_unicode[val];
+            if (val == 0)
+                val = 0x20;
+
+            result += ucs2_write( ucs2, result, val );
+        }
+    }
+    return result/2;
+}
+
+
+/* count the number of septets required to write a utf8 string */
+static int
+utf8_to_gsm7_count( cbytes_t  utf8, int  utf8len )
+{
+    cbytes_t  utf8end = utf8 + utf8len;
+    int       result  = 0;
+
+    while ( utf8 < utf8end ) {
+        int  len;
+        int  c = utf8_next( &utf8, utf8end );
+
+        if (c < 0)
+            break;
+
+        len = unichar_to_gsm7_count(c);
+        if (len == 0)    /* replace non-representables with space */
+            len = 1;
+
+        result += len;
+    }
+    return result;
+}
+
+typedef struct {
+    bytes_t   dst;
+    unsigned  pad;
+    int       bits;
+    int       offset;
+} BWriterRec, *BWriter;
+
+static void
+bwriter_init( BWriter  writer, bytes_t  dst, int  start )
+{
+    int  shift = start & 7;
+
+    writer->dst    = dst + (start >> 3);
+    writer->pad    = 0;
+    writer->bits   = shift;
+    writer->offset = start;
+
+    if (shift > 0) {
+        writer->pad  = writer->dst[0] & ~(0xFF << shift);
+    }
+}
+
+static void
+bwriter_add7( BWriter  writer, unsigned  value )
+{
+    writer->pad  |= (unsigned)(value << writer->bits);
+    writer->bits += 7;
+    if (writer->bits >= 8) {
+        writer->dst[0] = (byte_t)writer->pad;
+        writer->bits  -= 8;
+        writer->pad  >>= 8;
+        writer->dst   += 1;
+    }
+    writer->offset += 7;
+}
+
+static int
+bwriter_done( BWriter  writer )
+{
+    if (writer->bits > 0) {
+        writer->dst[0] = (byte_t)writer->pad;
+        writer->pad    = 0;
+        writer->bits   = 0;
+        writer->dst   += 1;
+    }
+    return writer->offset;
+}
+
+/* convert a utf8 string to a gsm7 byte string - return the number of septets written */
+int
+utf8_to_gsm7( cbytes_t  utf8, int  utf8len, bytes_t  dst, int offset )
+{
+    const unsigned char*  utf8end = utf8 + utf8len;
+    BWriterRec            writer[1];
+
+    if (dst == NULL)
+        return utf8_to_gsm7_count(utf8, utf8len);
+
+    bwriter_init( writer, dst, offset );
+    while ( utf8 < utf8end ) {
+        int  c = utf8_next( &utf8, utf8end );
+        int  nn;
+
+        if (c < 0)
+            break;
+
+        nn = unichar_to_gsm7(c);
+        if (nn >= 0) {
+            bwriter_add7( writer, nn );
+            continue;
+        }
+
+        nn = unichar_to_gsm7_extend(c);
+        if (nn >= 0) {
+            bwriter_add7( writer, GSM_7BITS_ESCAPE );
+            bwriter_add7( writer, nn );
+            continue;
+        }
+
+        /* unknown => replaced by space */
+        bwriter_add7( writer, 0x20 );
+    }
+    return  bwriter_done( writer );
+}
+
+
+int
+utf8_to_gsm8( cbytes_t  utf8, int  utf8len, bytes_t  dst )
+{
+    const unsigned char*  utf8end = utf8 + utf8len;
+    int                   result  = 0;
+
+    while ( utf8 < utf8end ) {
+        int  c = utf8_next( &utf8, utf8end );
+        int  nn;
+
+        if (c < 0)
+            break;
+
+        nn = unichar_to_gsm7(c);
+        if (nn >= 0) {
+            if (dst)
+                dst[result] = (byte_t)nn;
+            result += 1;
+            continue;
+        }
+
+        nn = unichar_to_gsm7_extend(c);
+        if (nn >= 0) {
+            if (dst) {
+                dst[result+0] = (byte_t) GSM_7BITS_ESCAPE;
+                dst[result+1] = (byte_t) nn;
+            }
+            result += 2;
+            continue;
+        }
+
+        /* unknown => space */
+        if (dst)
+            dst[result] = 0x20;
+        result += 1;
+    }
+    return  result;
+}
+
+
+int
+ucs2_to_gsm7( cbytes_t  ucs2, int  ucs2len, bytes_t  dst, int offset )
+{
+    const unsigned char*  ucs2end = ucs2 + ucs2len*2;
+    BWriterRec            writer[1];
+
+    bwriter_init( writer, dst, offset );
+    while ( ucs2 < ucs2end ) {
+        int  c = *ucs2++;
+        int  nn;
+
+        for (nn = 0; nn < 128; nn++) {
+            if ( gsm7bits_to_unicode[nn] == c ) {
+                bwriter_add7( writer, nn );
+                goto NextUnicode;
+            }
+        }
+        for (nn = 0; nn < 128; nn++) {
+            if ( gsm7bits_extend_to_unicode[nn] == c ) {
+                bwriter_add7( writer, GSM_7BITS_ESCAPE );
+                bwriter_add7( writer, nn );
+                goto NextUnicode;
+            }
+        }
+
+        /* unknown */
+        bwriter_add7( writer, 0x20 );
+
+    NextUnicode:
+        ;
+    }
+    return  bwriter_done( writer );
+}
+
+
+int
+ucs2_to_gsm8( cbytes_t  ucs2, int  ucs2len, bytes_t  dst )
+{
+    const unsigned char*  ucs2end = ucs2 + ucs2len*2;
+    bytes_t               dst0    = dst;
+
+    while ( ucs2 < ucs2end ) {
+        int  c = *ucs2++;
+        int  nn;
+
+        for (nn = 0; nn < 128; nn++) {
+            if ( gsm7bits_to_unicode[nn] == c ) {
+                *dst++ = (byte_t)nn;
+                goto NextUnicode;
+            }
+        }
+        for (nn = 0; nn < 128; nn++) {
+            if ( gsm7bits_extend_to_unicode[nn] == c ) {
+                dst[0] = (byte_t) GSM_7BITS_ESCAPE;
+                dst[1] = (byte_t) nn;
+                dst   += 2;
+                goto NextUnicode;
+            }
+        }
+
+        /* unknown */
+        *dst++ = 0x20;
+
+    NextUnicode:
+        ;
+    }
+    return (dst - dst0);
+}
+
+int
+gsm_bcdnum_to_ascii( cbytes_t  bcd, int  count, bytes_t  dst )
+{
+    int  result = 0;
+    int  shift  = 0;
+
+    while (count > 0) {
+        int  c = (bcd[0] >> shift) & 0xf;
+
+        if (c == 15 && count == 1)  /* ignore trailing 0xf */
+            break;
+
+        if (c >= 14)
+            c = 0;
+
+        if (dst) dst[result] = "0123456789*#,N"[c];
+        result += 1;
+
+        shift += 4;
+        if (shift == 8) {
+            shift = 0;
+            bcd += 1;
+        }
+    }
+    return  result;
+}
+
+
+int
+gsm_bcdnum_from_ascii( cbytes_t  ascii, int  asciilen, bytes_t  dst )
+{
+    cbytes_t  end = ascii + asciilen;
+    int  result = 0;
+    int  phase = 0x01;
+
+    while (ascii < end) {
+        int  c = *ascii++;
+
+        if (c == '*')
+            c = 10;
+        else if (c == '#')
+            c = 11;
+        else if (c == ',')
+            c = 12;
+        else if (c == 'N')
+            c = 13;
+        else {
+            c -= '0';
+            if ((unsigned)c >= 10U)
+                return -1;
+        }
+        phase   = (phase << 4) | c;
+        result += 1;
+        if (phase & 0x100) {
+            if (dst) dst[result/2] = (byte_t) phase;
+            phase   = 0x01;
+        }
+    }
+
+    if (result & 1) {
+        if (dst) dst[result/2] = (byte_t)(phase | 0xf0);
+    }
+    return result;
+}
+
+/** ADN: Abbreviated Dialing Number
+ **/
+
+#define  ADN_FOOTER_SIZE     14
+#define  ADN_OFFSET_NUMBER_LENGTH   0
+#define  ADN_OFFSET_TON_NPI         1
+#define  ADN_OFFSET_NUMBER_START    2
+#define  ADN_OFFSET_NUMBER_END      11
+#define  ADN_OFFSET_CAPABILITY_ID   12
+#define  ADN_OFFSET_EXTENSION_ID    13
+
+/* see 10.5.1 of 3GPP 51.011 */
+static int
+sim_adn_alpha_to_utf8( cbytes_t  alpha, cbytes_t  end, bytes_t  dst )
+{
+    int  result = 0;
+    int  c, is_ucs2 = 0;
+
+    /* ignore trailing 0xff */
+    while (alpha < end && end[-1] == 0xff)
+        end--;
+
+    if (alpha >= end)
+        return 0;
+
+    if (alpha[0] == 0x80) { /* UCS/2 source encoding */
+        int  count, len;
+
+        alpha += 1;
+        result = ucs2_to_utf8( alpha, (end-alpha)/2, dst );
+    } else {
+        int  is_ucs2 = 0;
+        int  len = 0, base = 0;
+
+        if (alpha+3 <= end && alpha[0] == 0x81) {
+            is_ucs2 = 1;
+            len     = alpha[1];
+            base    = alpha[2] << 7;
+            alpha  += 3;
+            if (len > end-alpha)
+                len = end-alpha;
+        } else if (alpha+4 <= end && alpha[0] == 0x82) {
+            is_ucs2 = 1;
+            len     = alpha[1];
+            base    = (alpha[2] << 8) | alpha[3];
+            alpha  += 4;
+            if (len > end-alpha)
+                len = end-alpha;
+        }
+
+        if (is_ucs2) {
+            end = alpha + len;
+            while (alpha < end) {
+                int  c = alpha[0];
+                if (c >= 0x80) {
+                    result += utf8_write(dst, result, base + (c & 0x7f));
+                    alpha  += 1;
+                } else {
+                    /* GSM character set */
+                    int   count;
+                    for (count = 0; alpha+count < end && alpha[count] < 128; count++)
+                        ;
+                    result += utf8_from_gsm8(alpha, count, (dst ? dst+result : NULL));
+                    alpha  += count;
+                }
+            }
+        } else {
+            result = utf8_from_gsm8(alpha, end-alpha, dst);
+        }
+    }
+    return result;
+}
+
+static int
+sim_adn_alpha_from_utf8( cbytes_t  utf8, int  utf8len, bytes_t  dst )
+{
+    int   result = 0;
+
+    if (utf8_check_gsm7(utf8, utf8len)) {
+        /* GSM 7-bit compatible, encode directly as 8-bit string */
+        result = utf8_to_gsm8(utf8, utf8len, dst);
+    } else {
+        /* otherwise, simply try UCS-2 encoding, nothing more serious at the moment */
+        if (dst) {
+            dst[0] = 0x80;
+        }
+        result = 1 + utf8_to_ucs2(utf8, utf8len, dst ? (dst+1) : NULL)*2;
+    }
+    return  result;
+}
+
+int
+sim_adn_record_from_bytes( SimAdnRecord  rec, cbytes_t  data, int  len )
+{
+    cbytes_t  end    = data + len;
+    cbytes_t  footer = end - ADN_FOOTER_SIZE;
+    int       num_len;
+
+    rec->adn.alpha[0]  = 0;
+    rec->adn.number[0] = 0;
+    rec->ext_record    = 0xff;
+
+    if (len < ADN_FOOTER_SIZE)
+        return -1;
+
+    /* alpha is optional */
+    if (len > ADN_FOOTER_SIZE) {
+        cbytes_t  dataend = data + len - ADN_FOOTER_SIZE;
+        int       count   = sim_adn_alpha_to_utf8(data, dataend, NULL);
+
+        if (count > sizeof(rec->adn.alpha)-1)  /* too long */
+            return -1;
+
+        sim_adn_alpha_to_utf8(data, dataend, rec->adn.alpha);
+        rec->adn.alpha[count] = 0;
+    }
+
+    num_len = footer[ADN_OFFSET_NUMBER_LENGTH];
+    if (num_len > 11)
+        return -1;
+
+    /* decode TON and number to ASCII, NOTE: this is lossy !! */
+    {
+        int      ton    = footer[ADN_OFFSET_TON_NPI];
+        bytes_t  number = rec->adn.number;
+        int      len    = sizeof(rec->adn.number)-1;
+        int      count;
+
+        if (ton != 0x81 && ton != 0x91)
+            return -1;
+
+        if (ton == 0x91) {
+            *number++ = '+';
+            len      -= 1;
+        }
+
+        count = gsm_bcdnum_to_ascii( footer + ADN_OFFSET_NUMBER_START,
+                                     num_len*2, number );
+        number[count] = 0;
+    }
+    return 0;
+}
+
+int
+sim_adn_record_to_bytes( SimAdnRecord  rec, bytes_t   data, int  datalen )
+{
+    bytes_t   end    = data + datalen;
+    bytes_t   footer = end - ADN_FOOTER_SIZE;
+    int       ton    = 0x81;
+    cbytes_t  number = rec->adn.number;
+    int       len;
+
+    if (number[0] == '+') {
+        ton     = 0x91;
+        number += 1;
+    }
+    footer[0] = (strlen(number)+1)/2 + 1;
+    return 0;
+}
diff --git a/huaweigeneric-ril/gsm.h b/huaweigeneric-ril/gsm.h
new file mode 100644 (file)
index 0000000..4076308
--- /dev/null
@@ -0,0 +1,177 @@
+#ifndef _android_gsm_h
+#define _android_gsm_h
+
+/** USEFUL TYPES
+ **/
+
+typedef unsigned char  byte_t;
+typedef byte_t*        bytes_t;
+typedef const byte_t*  cbytes_t;
+
+/** BCD
+ **/
+
+/* convert a 8-bit value into the corresponding nibble-bcd byte */
+extern byte_t   gsm_int_to_bcdi( int  value );
+
+/* convert a nibble-bcd byte into an int, invalid nibbles are silently converted to 0 */
+extern int      gsm_int_from_bcdi( byte_t  value );
+
+/** HEX
+ **/
+
+/* convert a hex string into a byte string, assumes 'dst' is properly sized, and hexlen is even */
+extern void     gsm_hex_to_bytes  ( cbytes_t  hex, int  hexlen, bytes_t  dst );
+
+/* convert a byte string into a hex string, assumes 'hex' is properly sized */
+extern void     gsm_hex_from_bytes( char*  hex, cbytes_t  src, int  srclen );
+
+/* convert a hexchar to an int, returns -1 on error */
+extern int      gsm_hexchar_to_int( char  c );
+
+/* convert a hexchar to an int, returns 0 on error */
+extern int      gsm_hexchar_to_int0( char  c );
+
+/* convert a 2-char hex value into an int, returns -1 on error */
+extern int      gsm_hex2_to_byte( const char*  hex );
+
+/* convert a 2-char hex value into an int, returns 0 on error */
+extern int      gsm_hex2_to_byte0( const char*  hex );
+
+/* convert a 4-char hex value into an int, returns -1 on error */
+extern int      gsm_hex4_to_short( const char*  hex );
+
+/* convert a 4-char hex value into an int, returns 0 on error */
+extern int      gsm_hex4_to_short0( const char*  hex );
+
+/* write a byte to a 2-byte hex string */
+extern void     gsm_hex_from_byte( char*  hex, int  val );
+
+extern void     gsm_hex_from_short( char*  hex, int  val );
+
+/** UTF-8 and GSM Alphabet
+ **/
+
+/* check that a given utf8 string is well-formed, returns 1 on success, 0 otherwise */
+extern int      utf8_check( cbytes_t  utf8, int  utf8len );
+
+/* check that all characters in a given utf8 string can be encoded into the GSM alphabet.
+   returns 1 if TRUE, 0 otherwise */
+extern int      utf8_check_gsm7( cbytes_t  utf8, int  utf8len );
+
+/* convert a utf-8 string into a GSM septet string, assumes 'dst' is NULL or is properly sized,
+   and that all characters are representable. 'offset' is the starting bit offset in 'dst'.
+   non-representable characters are replaced by spaces.
+   returns the number of septets, */
+extern int      utf8_to_gsm7( cbytes_t  utf8, int  utf8len, bytes_t  dst, int  offset );
+
+/* convert a utf8 string into an array of 8-bit unpacked GSM septets,
+ * assumes 'dst' is NULL or is properly sized, returns the number of GSM bytes */
+extern int      utf8_to_gsm8( cbytes_t  utf8, int  utf8len, bytes_t  dst );
+
+/* convert a GSM septets string into a utf-8 byte string. assumes that 'utf8' is NULL or properly
+   sized. 'offset' is the starting bit offset in 'src', 'count' is the number of input septets.
+   return the number of utf8 bytes. */
+extern int      utf8_from_gsm7( cbytes_t  src, int  offset, int  count, bytes_t  utf8 );
+
+/* convert an unpacked 8-bit GSM septets string into a utf-8 byte string. assumes that 'utf8'
+   is NULL or properly sized. 'count' is the number of input bytes.
+   returns the number of utf8 bytes */
+extern int      utf8_from_gsm8( cbytes_t  src, int  count, bytes_t  utf8 );
+
+
+/** UCS-2 and GSM Alphabet
+ **
+ ** Note that here, 'ucs2' really refers to non-aligned UCS2-BE, as used by the GSM standard
+ **/
+
+/* check that all characters in a given ucs2 string can be encoded into the GSM alphabet.
+   returns 1 if TRUE, 0 otherwise */
+extern int      ucs2_check_gsm7( cbytes_t  ucs2, int  ucs2len );
+
+/* convert a ucs2 string into a GSM septet string, assumes 'dst' is NULL or properly sized,
+   'offset' is the starting bit offset in 'dst'. non-representable characters are replaced
+   by spaces. returns the number of septets */
+extern int      ucs2_to_gsm7( cbytes_t  ucs2, int  ucs2len, bytes_t  dst, int  offset );
+
+/* convert a ucs2 string into a GSM septet string, assumes 'dst' is NULL or properly sized,
+   non-representable characters are replaced by spaces. returns the number of bytes */
+extern int      ucs2_to_gsm8( cbytes_t  ucs2, int  ucs2len, bytes_t  dst );
+
+/* convert a GSM septets string into a ucs2 string. assumes that 'ucs2' is NULL or
+   properly sized. 'offset' is the starting bit offset in 'src', 'count' is the number
+   of input septets. return the number of ucs2 characters (not bytes) */
+extern int      ucs2_from_gsm7( bytes_t   ucs2, cbytes_t  src, int  offset, int  count );
+
+/* convert an 8-bit unpacked GSM septets string into a ucs2 string. assumes that 'ucs2'
+   is NULL or properly sized. 'count' is the number of input septets. return the number
+   of ucs2 characters (not bytes) */
+extern int      ucs2_from_gsm8( bytes_t   ucs2, cbytes_t  src, int  count );
+
+
+/** UCS2 to/from UTF8
+ **/
+
+/* convert a ucs2 string into a utf8 byte string, assumes 'utf8' NULL or properly sized.
+   returns the number of utf8 bytes*/
+extern int      ucs2_to_utf8( cbytes_t  ucs2, int  ucs2len, bytes_t  utf8 );
+
+/* convert a utf8 byte string into a ucs2 string, assumes 'ucs2' NULL or properly sized.
+   returns the number of ucs2 chars */
+extern int      utf8_to_ucs2( cbytes_t  utf8, int  utf8len, bytes_t  ucs2 );
+
+/* try to skip a given number of characters in a utf-8 byte string, return new position */
+extern cbytes_t  utf8_skip( cbytes_t   utf8, cbytes_t   utf8end, int  count);
+
+/** Dial Numbers: TON byte + 'count' bcd numbers
+ **/
+
+/* convert a bcd-coded GSM dial number into an ASCII string (not zero-terminated)
+   assumes 'dst' is NULL or properly sized, returns 0 in case of success, -1 in case of error.
+   'num_digits' is the number of digits, not input bytes. a trailing 0xf0 is ignored automatically
+   return the number of ASCII chars */
+extern int  gsm_bcdnum_to_ascii  ( cbytes_t  bcd, int  num_digits, bytes_t  dst );
+
+/* convert an ASCII dial-number into a bcd-coded string, returns the number of 4-bit nibbles written, */
+extern int  gsm_bcdnum_from_ascii( cbytes_t  ascii, int  asciilen, bytes_t  dst );
+
+/** ADN: Abbreviated Dialing Numbers
+ **/
+#define  SIM_ADN_MAX_ALPHA        20  /* maximum number of characters in ADN alpha tag */
+#define  SIM_ADN_MAX_NUMBER       20  /* maximum digits in ADN number */
+
+typedef struct {
+    byte_t  alpha [ SIM_ADN_MAX_ALPHA*3+1 ];  /* alpha tag in zero-terminated utf-8      */
+    char    number[ SIM_ADN_MAX_NUMBER+1 ];   /* dialing number in zero-terminated ASCII */
+}
+SimAdnRec, *SimAdn;
+
+typedef struct {
+    SimAdnRec       adn;
+    byte_t          ext_record;  /* 0 or 0xFF means no extension */
+}
+SimAdnRecordRec, *SimAdnRecord;
+
+extern int  sim_adn_record_from_bytes( SimAdnRecord  rec, cbytes_t  data, int  datalen );
+extern int  sim_adn_record_to_bytes  ( SimAdnRecord  rec, bytes_t   data, int  datalen );
+
+/** ROPES
+ **/
+
+typedef struct {
+    bytes_t         data;
+    int             max;
+    int             pos;
+    int             error;
+    unsigned char   data0[16];
+} GsmRopeRec, *GsmRope;
+
+extern void      gsm_rope_init( GsmRope  rope );
+extern void      gsm_rope_init_alloc( GsmRope  rope, int  alloc );
+extern int       gsm_rope_done( GsmRope  rope );
+extern bytes_t   gsm_rope_done_acquire( GsmRope  rope, int  *psize );
+extern void      gsm_rope_add_c( GsmRope  rope, char  c );
+extern void      gsm_rope_add( GsmRope  rope, const void*  str, int  len );
+extern void*     gsm_rope_reserve( GsmRope  rope, int  len );
+
+#endif /* _android_gsm_h */
diff --git a/huaweigeneric-ril/huaweigeneric-ril.c b/huaweigeneric-ril/huaweigeneric-ril.c
new file mode 100644 (file)
index 0000000..b016881
--- /dev/null
@@ -0,0 +1,6571 @@
+/* //device/system/htcgeneric-ril/htcgeneric-ril.c
+ **
+ ** Copyright 2006, The Android Open Source Project
+ ** Copyright 2012 Eduardo Jos[e Tagle <ejtagle@tutopia.com>
+ **
+ ** 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.
+ */
+
+/*
+AT^CURC=0
+ATQ0V1
+
+^MODE:
+^RSSI:
+^SRVST:
++CCCM:
+^CONN:
+^CEND:
++CDSI:
+^SMMEMFULL:
++CSSU:
+^DCONN
+^DEND
+^SIMST      Â¡
++CRING:
+AT^SYSCFG=2,3,%s,1,2
+^RSSI
+^SMMEMFULL
+*/
+
+/*
+AT^SYSCFG=2,1,3FFFFFFF,1,2 for GPRS/EDGE Preferred
+AT^SYSCFG=2,2,3FFFFFFF,1,2 for 3G Preferred
+AT^SYSCFG=13,1,3FFFFFFF,1,2 for GPRS/EDGE Only
+AT^SYSCFG=14,2,3FFFFFFF,1,2 for 3G Only
+
+The third parameter, 0x3FFFFFFF tells the card to use all bands. A value of 0x400380 here means GSM900/1800/WCDMA2100 only and a value of 0x200000 here means GSM1900 only.
+
+I don't know what the final "1,2" parameter is for. But for some it has to be "2,4" instead for some reason.
+
+The status updates from the second port are prefixed with a caret and are of these forms:
+
+^MODE:3,2 indicates GPRS
+^MODE:3,3 indicates EDGE
+^MODE:5,4 indicates 3G
+^MODE:5,5 indicates HSDPA
+
+^DSFLOWRPT:n,n,n,n,n,n,n
+n1 is the duration of the connection in seconds
+n2 is transmit (upload) speed in bytes per second (n2 *8 / 1000 will give you kbps)
+n3 is receive (download) speed in bytes per second (n3 *8 / 1000 will give you kbps)
+n4 is the total bytes transmitted during this session
+n5 is the total bytes transmitted during this session
+n6 no idea, but I always get 0x1F40 (8000) here
+n7 no idea, but I always get 0x7530 (30000) here
+
+You can reset the connection statistics by sending AT^DSFLOWCLR.
+
+---
+If anyone is interested, sending DTMF signals in modem-mode (+FCLASS=0) is possible. But it is device-dependent feature.
+For Huawei E1550 it is AT^DTMF=<n> command.
+
+----
+
+AT^CVOICE=0 Enable Voice
+AT^CVOICE=1 Disable Voice
+AT^CVOICE=? Check status
+
+AT^CVOICE? [len=10]
+        [len=0]
+        <LF>^CVOICE:0,8000,16,20 [len=21] This answer tells you voice parameters (8 khz,16 bits, 20 ms)
+        <LF>
+
+----
+That need to be tested but voice activation should work with:
+AT^U2DIAG=5
+
+then the modem become 0x12d1:0x140c a flash drive.
+
+then I plug in on my Linux Ubuntu computer, use minicom and type AT^U2DIAG=0 to deactivate the damn flash drive.
+
+Maybe it's better to first type AT^U2DIAG=0 and then AT^U2DIAG=5 ...
+
+Regards, Phil.
+*/
+
+#define LOG_NDEBUG 0
+#define LOG_TAG "RIL"
+#include <utils/Log.h>
+
+#include <telephony/ril.h>
+#include <stdio.h>
+#include <assert.h>
+#include <string.h>
+#include <errno.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <pthread.h>
+#include <alloca.h>
+#include "atchannel.h"
+#include "at_tok.h"
+#include "misc.h"
+#include "gsm.h"
+#include "requestdatahandler.h"
+#include "fcp_parser.h"
+#include "audiochannel.h"
+#include <getopt.h>
+#include <sys/socket.h>
+#include <cutils/sockets.h>
+#include <termios.h>
+#include <cutils/properties.h>
+#include <stdbool.h>
+
+#define D ALOGD
+
+#define RIL_VERSION_STRING "Huawei-ril 1.0.0.0"
+
+#define timespec_cmp(a, b, op)         \
+        ((a).tv_sec == (b).tv_sec    \
+        ? (a).tv_nsec op (b).tv_nsec \
+        : (a).tv_sec op (b).tv_sec)
+
+#define TIMEOUT_SEARCH_FOR_TTY 5 /* Poll every Xs for the port*/
+#define TIMEOUT_EMRDY 10 /* Module should respond at least within 10s */
+#define MAX_BUF 1024
+
+/* pathname returned from RIL_REQUEST_SETUP_DATA_CALL / RIL_REQUEST_SETUP_DEFAULT_PDP */
+#define PPP_TTY_PATH "ppp0"
+
+
+/** declarations */
+static const char *getVersion();
+static void signalCloseQueues(void);
+static void onRequest (int request, void *data, size_t datalen, RIL_Token t);
+static int onSupports (int requestCode);
+static void onCancel (RIL_Token t);
+extern const char *requestToString(int request);
+
+static RIL_RadioState getRadioState();
+static int isRadioOn();
+static int getCardStatus(RIL_CardStatus_v6 **pp_card_status);
+static void freeCardStatus(RIL_CardStatus_v6 *p_card_status);
+static void onDataCallListChanged(void *param);
+static int killConn(const char * cid);
+static int wait_for_property(const char *name, const char *desired_value, int maxwait);
+static void checkMessageStorageReady(void *p);
+static void onSIMReady(void *p);
+static void pollSIMState(void *param);
+static void checkMessageStorageReady(void *p);
+
+extern const char * requestToString(int request);
+
+/*** Static Variables ***/
+static const RIL_RadioFunctions s_callbacks = {
+    RIL_VERSION,
+    onRequest,
+    getRadioState,
+    onSupports,
+    onCancel,
+    getVersion
+};
+
+static RIL_RadioState sState = RADIO_STATE_UNAVAILABLE;
+static pthread_mutex_t s_state_mutex = PTHREAD_MUTEX_INITIALIZER;
+static const struct timespec TIMEVAL_SIMPOLL = {1,0};
+
+
+#ifdef RIL_SHLIB
+static const struct RIL_Env *s_rilenv;
+#define RIL_onRequestComplete(t, e, response, responselen) s_rilenv->OnRequestComplete(t,e, response, responselen)
+#define RIL_onUnsolicitedResponse(a,b,c) s_rilenv->OnUnsolicitedResponse(a,b,c)
+#define RIL_requestTimedCallback(a,b,c) s_rilenv->RequestTimedCallback(a,b,c)
+#endif
+
+typedef struct RILRequest {
+    int request;
+    void *data;
+    size_t datalen;
+    RIL_Token token;
+    struct RILRequest *next;
+} RILRequest;
+
+typedef struct RILEvent {
+    void (*eventCallback) (void *param);
+    void *param;
+    struct timespec abstime;
+    struct RILEvent *next;
+    struct RILEvent *prev;
+} RILEvent;
+
+typedef struct RequestQueue {
+    pthread_mutex_t queueMutex;
+    pthread_cond_t cond;
+    RILRequest *requestList;
+    RILEvent *eventList;
+    char enabled;
+    char closed;
+} RequestQueue;
+
+static RequestQueue s_requestQueue = {
+    PTHREAD_MUTEX_INITIALIZER,
+    PTHREAD_COND_INITIALIZER,
+    NULL,
+    NULL,
+    1,
+    1
+};
+
+static RequestQueue *s_requestQueues[] = {
+    &s_requestQueue
+};
+
+/* The Audio channel */
+static struct GsmAudioTunnel sAudioChannel = GSM_AUDIO_CHANNEL_STATIC_INIT;
+static char  sAudioDevice[64] = {0};
+
+/* Starts the audio tunnel channel */
+static void startAudioTunnel(void)
+{
+#if 0
+    ATResponse *atResponse = NULL;
+    int err;
+    char *line;
+    int voice_disabled = 0, sampling_rate = 8000,
+        bits_per_sample = 16, framesize_ms = 20;
+
+    if (sAudioChannel.running)
+        return;
+
+    // Enable voice function
+    err = at_send_command("AT^CVOICE=0");
+    if (err != AT_NOERROR)
+        goto error;
+
+    // Check status.
+    err = at_send_command_singleline("AT^CVOICE=?", "^CVOICE:", &atResponse);
+    if (err != AT_NOERROR)
+        goto error;
+
+    line = atResponse->p_intermediates->line;
+    err = at_tok_start(&line);
+    if (err < 0) goto error;
+
+    err = at_tok_nextint(&line, &voice_disabled);
+    if (err < 0) goto error;
+
+    err = at_tok_nextint(&line, &sampling_rate);
+    if (err < 0) goto error;
+
+    err = at_tok_nextint(&line, &bits_per_sample);
+    if (err < 0) goto error;
+
+    err = at_tok_nextint(&line, &framesize_ms);
+    if (err < 0) goto error;
+
+    /* Switch to audio from USB1 */
+    err = at_send_command("AT^DDSETEX=2");
+    if (err != AT_NOERROR)
+        goto error;
+
+    /* Start the audio channel */
+    err = gsm_audio_tunnel_start(&sAudioChannel,sAudioDevice,
+                sampling_rate, (sampling_rate * framesize_ms) / 1000, bits_per_sample );
+    if (err)
+        goto error;
+
+    ALOGE("Audio Tunnel enabled");
+    at_response_free(atResponse);
+    return;
+
+error:
+    ALOGE("Failed to start audio tunnel");
+
+    at_response_free(atResponse);
+    return;
+#endif
+}
+
+/* Stops the audio tunnel channel */
+static void stopAudioTunnel(void)
+{
+#if 0
+    if (!sAudioChannel.running)
+        return;
+
+    // Stop the audio tunnel
+    gsm_audio_tunnel_stop(&sAudioChannel);
+
+    D("Audio Tunnel disabled");
+#endif
+}
+
+/**
+ * Enqueue a RILEvent to the request queue.
+ */
+static void enqueueRILEvent(void (*callback) (void *param),
+                     void *param, const struct timespec *relativeTime)
+{
+    int err;
+    struct timespec ts;
+    char done = 0;
+    RequestQueue *q = NULL;
+
+    RILEvent *e = (RILEvent *) malloc(sizeof(RILEvent));
+    memset(e, 0, sizeof(RILEvent));
+
+    e->eventCallback = callback;
+    e->param = param;
+
+    if (relativeTime == NULL) {
+        relativeTime = (const struct timespec *) alloca(sizeof(struct timespec));
+        memset((struct timespec *) relativeTime, 0, sizeof(struct timespec));
+    }
+
+    clock_gettime(CLOCK_MONOTONIC, &ts);
+
+    e->abstime.tv_sec = ts.tv_sec + relativeTime->tv_sec;
+    e->abstime.tv_nsec = ts.tv_nsec + relativeTime->tv_nsec;
+
+    if (e->abstime.tv_nsec > 1000000000) {
+        e->abstime.tv_sec++;
+        e->abstime.tv_nsec -= 1000000000;
+    }
+
+    q = &s_requestQueue;
+
+again:
+    if ((err = pthread_mutex_lock(&q->queueMutex)) != 0)
+        ALOGE("%s() failed to take queue mutex: %s!", __func__, strerror(err));
+
+    if (q->eventList == NULL) {
+        q->eventList = e;
+    } else {
+        if (timespec_cmp(q->eventList->abstime, e->abstime, > )) {
+            e->next = q->eventList;
+            q->eventList->prev = e;
+            q->eventList = e;
+        } else {
+            RILEvent *tmp = q->eventList;
+            do {
+                if (timespec_cmp(tmp->abstime, e->abstime, > )) {
+                    tmp->prev->next = e;
+                    e->prev = tmp->prev;
+                    tmp->prev = e;
+                    e->next = tmp;
+                    break;
+                } else if (tmp->next == NULL) {
+                    tmp->next = e;
+                    e->prev = tmp;
+                    break;
+                }
+                tmp = tmp->next;
+            } while (tmp);
+        }
+    }
+
+    if ((err = pthread_cond_broadcast(&q->cond)) != 0)
+        ALOGE("%s() failed to take broadcast queue update: %s!",
+            __func__, strerror(err));
+
+    if ((err = pthread_mutex_unlock(&q->queueMutex)) != 0)
+        ALOGE("%s() failed to release queue mutex: %s!",
+            __func__, strerror(err));
+
+}
+
+/** returns 1 if on, 0 if off, and -1 on error */
+static int isRadioOn()
+{
+    ATResponse *atResponse = NULL;
+    int err,ison;
+    char *line;
+    char ret1,ret2;
+
+    err = at_send_command_singleline("AT^RFSWITCH?", "^RFSWITCH:", &atResponse);
+
+    if (err != AT_NOERROR) {
+        // assume radio is off
+        goto error;
+    }
+
+    line = atResponse->p_intermediates->line;
+
+    err = at_tok_start(&line);
+    if (err < 0) goto error;
+
+    err = at_tok_nextbool(&line, &ret1);
+    if (err < 0) goto error;
+
+    err = at_tok_nextbool(&line, &ret2);
+    if (err < 0) goto error;
+
+    at_response_free(atResponse);
+
+    /* 1 is ON, 0 is OFF */
+    ison = (ret1 == 1 && ret2 == 1);
+
+    ALOGD("Radio is %s", ison ? "On" : "Off");
+
+    return ison;
+
+error:
+
+    at_response_free(atResponse);
+    return -1;
+}
+
+
+static const char *radioStateToString(RIL_RadioState radioState)
+{
+    const char *state;
+
+    switch (radioState) {
+    case RADIO_STATE_OFF:
+        state = "RADIO_STATE_OFF";
+        break;
+    case RADIO_STATE_UNAVAILABLE:
+        state = "RADIO_STATE_UNAVAILABLE";
+        break;
+    case RADIO_STATE_SIM_NOT_READY:
+        state = "RADIO_STATE_SIM_NOT_READY";
+        break;
+    case RADIO_STATE_SIM_LOCKED_OR_ABSENT:
+        state = "RADIO_STATE_SIM_LOCKED_OR_ABSENT";
+        break;
+    case RADIO_STATE_SIM_READY:
+        state = "RADIO_STATE_SIM_READY";
+        break;
+    case RADIO_STATE_RUIM_NOT_READY:
+        state = "RADIO_STATE_RUIM_NOT_READY";
+        break;
+    case RADIO_STATE_RUIM_READY:
+        state = "RADIO_STATE_RUIM_READY";
+        break;
+    case RADIO_STATE_RUIM_LOCKED_OR_ABSENT:
+        state = "RADIO_STATE_RUIM_READY";
+        break;
+    case RADIO_STATE_NV_NOT_READY:
+        state = "RADIO_STATE_NV_NOT_READY";
+        break;
+    case RADIO_STATE_NV_READY:
+        state = "RADIO_STATE_NV_READY";
+        break;
+    default:
+        state = "RADIO_STATE_<> Unknown!";
+        break;
+    }
+
+    return state;
+}
+
+
+/**
+ * Synchronous call from the RIL to us to return current radio state.
+ * RADIO_STATE_UNAVAILABLE should be the initial state.
+ */
+static RIL_RadioState getRadioState()
+{
+    return sState;
+}
+
+
+static void setRadioState(RIL_RadioState newState)
+{
+    RIL_RadioState oldState;
+    int err;
+
+    if ((err = pthread_mutex_lock(&s_state_mutex)) != 0)
+        ALOGE("%s() failed to take state mutex: %s!", __func__, strerror(err));
+
+    oldState = sState;
+
+    ALOGI("%s() oldState=%s newState=%s", __func__, radioStateToString(oldState),
+         radioStateToString(newState));
+
+    sState = newState;
+
+    if ((err = pthread_mutex_unlock(&s_state_mutex)) != 0)
+        ALOGE("%s() failed to release state mutex: %s!", __func__, strerror(err));
+
+    /* Do these outside of the mutex. */
+    if (sState != oldState || sState == RADIO_STATE_SIM_LOCKED_OR_ABSENT) {
+        RIL_onUnsolicitedResponse(RIL_UNSOL_RESPONSE_RADIO_STATE_CHANGED,
+                                  NULL, 0);
+
+        if (sState == RADIO_STATE_SIM_READY) {
+            enqueueRILEvent(checkMessageStorageReady, NULL, NULL);
+            enqueueRILEvent(onSIMReady, NULL, NULL);
+        } else if (sState == RADIO_STATE_SIM_NOT_READY)
+            enqueueRILEvent(pollSIMState, NULL, NULL);
+    }
+}
+
+typedef enum {
+    SIM_ABSENT = 0,                     /* SIM card is not inserted */
+    SIM_NOT_READY = 1,                  /* SIM card is not ready */
+    SIM_READY = 2,                      /* radiostate = RADIO_STATE_SIM_READY */
+    SIM_PIN = 3,                        /* SIM PIN code lock */
+    SIM_PUK = 4,                        /* SIM PUK code lock */
+    SIM_NETWORK_PERSO = 5,              /* Network Personalization lock */
+    SIM_PIN2 = 6,                       /* SIM PIN2 lock */
+    SIM_PUK2 = 7,                       /* SIM PUK2 lock */
+    SIM_NETWORK_SUBSET_PERSO = 8,       /* Network Subset Personalization */
+    SIM_SERVICE_PROVIDER_PERSO = 9,     /* Service Provider Personalization */
+    SIM_CORPORATE_PERSO = 10,           /* Corporate Personalization */
+    SIM_SIM_PERSO = 11,                 /* SIM/USIM Personalization */
+    SIM_STERICSSON_LOCK = 12,           /* ST-Ericsson Extended SIM */
+    SIM_BLOCKED = 13,                   /* SIM card is blocked */
+    SIM_PERM_BLOCKED = 14,              /* SIM card is permanently blocked */
+    SIM_NETWORK_PERSO_PUK = 15,         /* Network Personalization PUK */
+    SIM_NETWORK_SUBSET_PERSO_PUK = 16,  /* Network Subset Perso. PUK */
+    SIM_SERVICE_PROVIDER_PERSO_PUK = 17,/* Service Provider Perso. PUK */
+    SIM_CORPORATE_PERSO_PUK = 18,       /* Corporate Personalization PUK */
+    SIM_SIM_PERSO_PUK = 19,             /* SIM Personalization PUK (unused) */
+    SIM_PUK2_PERM_BLOCKED = 20          /* PUK2 is permanently blocked */
+} SIM_Status;
+
+typedef enum {
+    UICC_TYPE_UNKNOWN,
+    UICC_TYPE_SIM,
+    UICC_TYPE_USIM,
+    UICC_TYPE_UIM,
+} UICC_Type;
+
+/** Returns one of SIM_*. Returns SIM_NOT_READY on error. */
+static SIM_Status getSIMStatus(void)
+{
+    ATResponse *atResponse = NULL;
+    int err;
+    SIM_Status ret = SIM_ABSENT;
+    char *cpinLine;
+    char *cpinResult;
+
+    if (getRadioState() == RADIO_STATE_OFF ||
+        getRadioState() == RADIO_STATE_UNAVAILABLE) {
+        return SIM_NOT_READY;
+    }
+
+    err = at_send_command_singleline("AT+CPIN?", "+CPIN:", &atResponse);
+
+    if (err != AT_NOERROR) {
+        if (at_get_error_type(err) == AT_ERROR) {
+            ret = SIM_NOT_READY;
+            goto done;
+        }
+
+        switch (at_get_cme_error(err)) {
+        case CME_SIM_NOT_INSERTED:
+            ret = SIM_ABSENT;
+            break;
+        case CME_SIM_PIN_REQUIRED:
+            ret = SIM_PIN;
+            break;
+        case CME_SIM_PUK_REQUIRED:
+            ret = SIM_PUK;
+            break;
+        case CME_SIM_PIN2_REQUIRED:
+            ret = SIM_PIN2;
+            break;
+        case CME_SIM_PUK2_REQUIRED:
+            ret = SIM_PUK2;
+            break;
+        case CME_NETWORK_PERSONALIZATION_PIN_REQUIRED:
+            ret = SIM_NETWORK_PERSO;
+            break;
+        case CME_NETWORK_PERSONALIZATION_PUK_REQUIRED:
+            ret = SIM_NETWORK_PERSO_PUK;
+            break;
+        case CME_NETWORK_SUBSET_PERSONALIZATION_PIN_REQUIRED:
+            ret = SIM_NETWORK_SUBSET_PERSO;
+            break;
+        case CME_NETWORK_SUBSET_PERSONALIZATION_PUK_REQUIRED:
+            ret = SIM_NETWORK_SUBSET_PERSO_PUK;
+            break;
+        case CME_SERVICE_PROVIDER_PERSONALIZATION_PIN_REQUIRED:
+            ret = SIM_SERVICE_PROVIDER_PERSO;
+            break;
+        case CME_SERVICE_PROVIDER_PERSONALIZATION_PUK_REQUIRED:
+            ret = SIM_SERVICE_PROVIDER_PERSO_PUK;
+            break;
+        case CME_PH_SIMLOCK_PIN_REQUIRED: /* PUK not in use by modem */
+            ret = SIM_SIM_PERSO;
+            break;
+        case CME_CORPORATE_PERSONALIZATION_PIN_REQUIRED:
+            ret = SIM_CORPORATE_PERSO;
+            break;
+        case CME_CORPORATE_PERSONALIZATION_PUK_REQUIRED:
+            ret = SIM_CORPORATE_PERSO_PUK;
+            break;
+        default:
+            ret = SIM_NOT_READY;
+            break;
+        }
+        return ret;
+    }
+
+    /* CPIN? has succeeded, now look at the result. */
+
+    cpinLine = atResponse->p_intermediates->line;
+    err = at_tok_start(&cpinLine);
+
+    if (err < 0) {
+        ret = SIM_NOT_READY;
+        goto done;
+    }
+
+    err = at_tok_nextstr(&cpinLine, &cpinResult);
+
+    if (err < 0) {
+        ret = SIM_NOT_READY;
+        goto done;
+    }
+
+    if (0 == strcmp(cpinResult, "READY")) {
+        ret = SIM_READY;
+    } else if (0 == strcmp(cpinResult, "SIM PIN")) {
+        ret = SIM_PIN;
+    } else if (0 == strcmp(cpinResult, "SIM PUK")) {
+        ret = SIM_PUK;
+    } else if (0 == strcmp(cpinResult, "SIM PIN2")) {
+        ret = SIM_PIN2;
+    } else if (0 == strcmp(cpinResult, "SIM PUK2")) {
+        ret = SIM_PUK2;
+    } else if (0 == strcmp(cpinResult, "PH-NET PIN")) {
+        ret = SIM_NETWORK_PERSO;
+    } else if (0 == strcmp(cpinResult, "PH-NETSUB PIN")) {
+        ret = SIM_NETWORK_SUBSET_PERSO;
+    } else if (0 == strcmp(cpinResult, "PH-SP PIN")) {
+        ret = SIM_SERVICE_PROVIDER_PERSO;
+    } else if (0 == strcmp(cpinResult, "PH-CORP PIN")) {
+        ret = SIM_CORPORATE_PERSO;
+    } else if (0 == strcmp(cpinResult, "PH-SIMLOCK PIN")) {
+        ret = SIM_SIM_PERSO;
+    } else if (0 == strcmp(cpinResult, "PH-ESL PIN")) {
+        ret = SIM_STERICSSON_LOCK;
+    } else if (0 == strcmp(cpinResult, "BLOCKED")) {
+        int numRetries = 3;
+        if (numRetries == -1 || numRetries == 0)
+            ret = SIM_PERM_BLOCKED;
+        else
+            ret = SIM_PUK2_PERM_BLOCKED;
+    } else if (0 == strcmp(cpinResult, "PH-SIM PIN")) {
+        /*
+         * Should not happen since lock must first be set from the phone.
+         * Setting this lock is not supported by Android.
+         */
+        ret = SIM_BLOCKED;
+    } else {
+        /* Unknown locks should not exist. Defaulting to "sim absent" */
+        ret = SIM_ABSENT;
+    }
+done:
+    at_response_free(atResponse);
+    return ret;
+}
+
+
+/**
+ * SIM ready means any commands that access the SIM will work, including:
+ *  AT+CPIN, AT+CSMS, AT+CNMI, AT+CRSM
+ *  (all SMS-related commands).
+ */
+static void pollSIMState(void *param)
+{
+    if (((int) param) != 1 &&
+        getRadioState() != RADIO_STATE_SIM_NOT_READY &&
+        getRadioState() != RADIO_STATE_SIM_LOCKED_OR_ABSENT)
+        /* No longer valid to poll. */
+        return;
+
+    switch (getSIMStatus()) {
+    case SIM_NOT_READY:
+        ALOGI("SIM_NOT_READY, poll for sim state.");
+        enqueueRILEvent(pollSIMState, NULL, &TIMEVAL_SIMPOLL);
+        return;
+
+    case SIM_PIN2:
+    case SIM_PUK2:
+    case SIM_PUK2_PERM_BLOCKED:
+    case SIM_READY:
+        setRadioState(RADIO_STATE_SIM_READY);
+        return;
+    case SIM_ABSENT:
+    case SIM_PIN:
+    case SIM_PUK:
+    case SIM_NETWORK_PERSO:
+    case SIM_NETWORK_SUBSET_PERSO:
+    case SIM_SERVICE_PROVIDER_PERSO:
+    case SIM_CORPORATE_PERSO:
+    case SIM_SIM_PERSO:
+    case SIM_STERICSSON_LOCK:
+    case SIM_BLOCKED:
+    case SIM_PERM_BLOCKED:
+    case SIM_NETWORK_PERSO_PUK:
+    case SIM_NETWORK_SUBSET_PERSO_PUK:
+    case SIM_SERVICE_PROVIDER_PERSO_PUK:
+    case SIM_CORPORATE_PERSO_PUK:
+    /* pass through, do not break */
+    default:
+        setRadioState(RADIO_STATE_SIM_LOCKED_OR_ABSENT);
+        return;
+    }
+}
+
+
+
+static void sendCallStateChanged(void *param)
+{
+    RIL_onUnsolicitedResponse (
+            RIL_UNSOL_RESPONSE_CALL_STATE_CHANGED,
+            NULL, 0);
+}
+
+
+static int clccStateToRILState(int state, RIL_CallState *p_state)
+
+{
+    switch(state) {
+        case 0: *p_state = RIL_CALL_ACTIVE;   return 0;
+        case 1: *p_state = RIL_CALL_HOLDING;  return 0;
+        case 2: *p_state = RIL_CALL_DIALING;  return 0;
+        case 3: *p_state = RIL_CALL_ALERTING; return 0;
+        case 4: *p_state = RIL_CALL_INCOMING; return 0;
+        case 5: *p_state = RIL_CALL_WAITING;  return 0;
+        default:
+            ALOGE("Unsupported call state: %d", state);
+            return -1;
+    }
+}
+
+/**
+ * Note: directly modified line and has *p_call point directly into
+ * modified line
+ */
+static int callFromCLCCLine(char *line, RIL_Call *p_call)
+{
+    //+CLCC: 1,0,2,0,0,\"+18005551212\",145
+    //     index,isMT,state,mode,isMpty(,number,TOA)?
+
+    int err;
+    int state;
+    int mode;
+
+    err = at_tok_start(&line);
+    if (err < 0) goto error;
+
+    err = at_tok_nextint(&line, &(p_call->index));
+    if (err < 0) goto error;
+
+    err = at_tok_nextbool(&line, &(p_call->isMT));
+    if (err < 0) goto error;
+
+    err = at_tok_nextint(&line, &state);
+    if (err < 0) goto error;
+
+    err = clccStateToRILState(state, &(p_call->state));
+    if (err < 0) goto error;
+
+    err = at_tok_nextint(&line, &mode);
+    if (err < 0) goto error;
+
+    p_call->isVoice = (mode == 0);
+
+    err = at_tok_nextbool(&line, &(p_call->isMpty));
+    if (err < 0) goto error;
+
+    if (at_tok_hasmore(&line)) {
+        err = at_tok_nextstr(&line, &(p_call->number));
+
+        /* tolerate null here */
+        if (err < 0) return 0;
+
+        // Some lame implementations return strings
+        // like "NOT AVAILABLE" in the CLCC line
+        if (p_call->number != NULL
+                && 0 == strspn(p_call->number, "+0123456789")
+           ) {
+            p_call->number = NULL;
+        }
+
+        err = at_tok_nextint(&line, &p_call->toa);
+        if (err < 0) goto error;
+    }
+
+    return 0;
+
+error:
+    ALOGE("invalid CLCC line");
+    return -1;
+}
+
+static const struct timespec TIMEVAL_CALLSTATEPOLL = {0,500000};
+
+static void requestGetCurrentCalls(void *data, size_t datalen, RIL_Token t)
+{
+    int err,fd;
+    ATResponse *atResponse;
+    ATLine *p_cur;
+    int countCalls;
+    RIL_Call *p_calls;
+    RIL_Call **pp_calls;
+    int i;
+    char status[1];
+    int needRepoll = 0;
+    int countValidCalls=0;
+
+
+    if(getRadioState() != RADIO_STATE_SIM_READY){
+        /* Might be waiting for SIM PIN */
+        RIL_onRequestComplete(t, RIL_E_RADIO_NOT_AVAILABLE, NULL, 0);
+    }
+
+    err = at_send_command_multiline ("AT+CLCC", "+CLCC:", &atResponse);
+    if (err != AT_NOERROR) {
+        RIL_onRequestComplete(t, RIL_E_GENERIC_FAILURE, NULL, 0);
+        at_response_free(atResponse);
+        return;
+    }
+
+    /* count the calls */
+    for (countCalls = 0, p_cur = atResponse->p_intermediates
+            ; p_cur != NULL
+            ; p_cur = p_cur->p_next
+        ) {
+        countCalls++;
+    }
+
+    /* yes, there's an array of pointers and then an array of structures */
+    pp_calls = (RIL_Call **)alloca(countCalls * sizeof(RIL_Call *));
+    p_calls = (RIL_Call *)alloca(countCalls * sizeof(RIL_Call));
+    memset (p_calls, 0, countCalls * sizeof(RIL_Call));
+
+    /* init the pointer array */
+    for(i = 0; i < countCalls ; i++) {
+        pp_calls[i] = &(p_calls[i]);
+    }
+
+    for (countValidCalls = 0, p_cur = atResponse->p_intermediates
+            ; p_cur != NULL
+            ; p_cur = p_cur->p_next
+        ) {
+        err = callFromCLCCLine(p_cur->line, p_calls + countValidCalls);
+
+        if (err != 0) {
+            continue;
+        }
+
+        if (p_calls[countValidCalls].state != RIL_CALL_ACTIVE
+                && p_calls[countValidCalls].state != RIL_CALL_HOLDING
+           ) {
+            needRepoll = 1;
+        }
+        if(p_calls[countValidCalls].isVoice) // only count voice calls
+            countValidCalls++;
+    }
+
+    ALOGI("Calls=%d,Valid=%d", countCalls, countValidCalls);
+
+    /* If some voice calls are active, enable audio tunnel */
+    if (countValidCalls) {
+        startAudioTunnel();
+    } else {
+        stopAudioTunnel();
+    }
+
+    RIL_onRequestComplete(t, RIL_E_SUCCESS, pp_calls,
+            countValidCalls * sizeof (RIL_Call *));
+
+    at_response_free(atResponse);
+
+    if (needRepoll) {
+        enqueueRILEvent(sendCallStateChanged, NULL, &TIMEVAL_CALLSTATEPOLL);
+    }
+    return;
+
+error:
+    RIL_onRequestComplete(t, RIL_E_GENERIC_FAILURE, NULL, 0);
+    at_response_free(atResponse);
+}
+
+static void requestScreenState(void *data, size_t datalen, RIL_Token t)
+{
+    int err, screenState;
+
+    assert (datalen >= sizeof(int *));
+    screenState = ((int*)data)[0];
+
+    if (screenState == 1) {
+        /* Screen is on - be sure to enable all unsolicited notifications again */
+
+        /* Enable proactive network registration notifications */
+        err = at_send_command("AT+CREG=2");
+        if (err != AT_NOERROR) goto error;
+
+        /* Enable proactive network registration notifications */
+        err = at_send_command("AT+CGREG=2");
+        if (err != AT_NOERROR) goto error;
+
+        /* Enable unsolicited reports */
+        err = at_send_command("AT^CURC=1");
+        if (err != AT_NOERROR) goto error;
+
+        /* Enable GPRS reporting */
+        err = at_send_command("AT+CGEREP=1,0");
+        if (err != AT_NOERROR) goto error;
+
+
+
+    } else if (screenState == 0) {
+
+        /* Screen is off - disable all unsolicited notifications */
+        err = at_send_command("AT+CREG=0");
+        if (err != AT_NOERROR) goto error;
+        err = at_send_command("AT+CGREG=0");
+        if (err != AT_NOERROR) goto error;
+        err = at_send_command("AT^CURC=0");
+        if (err != AT_NOERROR) goto error;
+        err = at_send_command("AT+CGEREP=0,0");
+        if (err != AT_NOERROR) goto error;
+
+
+    } else {
+        /* Not a defined value - error */
+        goto error;
+    }
+
+    RIL_onRequestComplete(t, RIL_E_SUCCESS, NULL, 0);
+    return;
+
+error:
+    ALOGE("ERROR: requestScreenState failed");
+    RIL_onRequestComplete(t, RIL_E_GENERIC_FAILURE, NULL, 0);
+}
+
+
+/* wait for a propertyvalue change */
+static int wait_for_property(const char *name, const char *desired_value, int maxwait)
+{
+    char value[PROPERTY_VALUE_MAX] = {'\0'};
+    int maxnaps = maxwait / 1;
+
+    if (maxnaps < 1) {
+        maxnaps = 1;
+    }
+
+    while (maxnaps-- > 0) {
+        usleep(1000000);
+        if (property_get(name, value, NULL)) {
+            if (desired_value == NULL ||
+                    strcmp(value, desired_value) == 0) {
+                return 0;
+            }
+        }
+    }
+    return -1; /* failure */
+}
+
+static int killConn(const char * cid)
+{
+    int err;
+    int fd;
+    int i=0;
+
+    D("killConn");
+
+#if 0
+    while ((fd = open("/sys/class/net/ppp0/ifindex",O_RDONLY)) > 0)
+    {
+        if(i%5 == 0)
+            system("killall pppd");
+        if(i>25)
+            goto error;
+        i++;
+        close(fd);
+        sleep(2);
+    }
+#elif 0
+    // Requires root access...
+    property_set("ctl.stop", "pppd_gprs");
+    if (wait_for_property("init.svc.pppd_gprs", "stopped", 10) < 0) {
+        goto error;
+    }
+#endif
+
+    D("killall pppd finished");
+
+    err = at_send_command("AT+CGACT=0,%s", cid);
+    if (err != AT_NOERROR)
+        goto error;
+
+    at_send_command("ATH");
+    return 0;
+
+error:
+    return -1;
+}
+
+
+//static char userPassStatic[512] = "preload";
+
+static void requestSetupDefaultPDP(void *data, size_t datalen, RIL_Token t)
+{
+    const char *apn;
+    const char *user = NULL;
+    const char *pass = NULL;
+    char pppdcmd[512] = "/system/bin/pppd ";
+    int err;
+    int fd, pppstatus,i;
+    FILE *pppconfig;
+    size_t cur = 0;
+    ssize_t written, rlen;
+    char status[32] = {0};
+    char *buffer;
+    long buffSize, len;
+    int retry = 10;
+
+    int n = 1;
+    RIL_Data_Call_Response_v6 responses;
+    char ppp_dnses[(PROPERTY_VALUE_MAX * 2) + 3] = {'\0'};
+    char ppp_local_ip[PROPERTY_VALUE_MAX] = {'\0'};
+    char ppp_dns1[PROPERTY_VALUE_MAX] = {'\0'};
+    char ppp_dns2[PROPERTY_VALUE_MAX] = {'\0'};
+    char ppp_gw[PROPERTY_VALUE_MAX] = {'\0'};
+
+    apn = ((const char **)data)[2];
+    user = ((char **)data)[3];
+    if (user == NULL || strlen(user) < 2) {
+        user = "dummy";
+    }
+
+    pass = ((char **)data)[4];
+    if (pass == NULL || strlen(pass) < 2) {
+        pass = "dummy";
+    }
+
+    D("requesting data connection to APN '%s'", apn);
+
+    // Make sure there is no existing connection or pppd instance running
+    if (killConn("1") < 0) {
+        ALOGE("killConn Error!");
+        goto error;
+    }
+
+    /* Switch radio ON */
+    err = at_send_command("AT^RFSWITCH=1");
+    err = at_send_command("AT+CFUN=1");
+
+    /* Define the PDP context */
+    err = at_send_command("AT+CGDCONT=1,\"IP\",\"%s\",,0,0", apn);
+
+    /* Set required QoS params to default */
+    err = at_send_command("AT+CGQREQ=1");
+
+    /* Set minimum QoS params to default */
+    err = at_send_command("AT+CGQMIN=1");
+
+    /* packet-domain event reporting */
+    err = at_send_command("AT+CGEREP=1,0");
+
+    /* Hangup anything that's happening there now */
+    err = at_send_command("AT+CGACT=0,1");
+
+    /* Start data on PDP context 1 */
+    err = at_send_command("ATD*99***1#");
+//  if (err != AT_NOERROR) {
+//      goto error;
+//  }
+    sleep(2); //Wait for the modem to finish
+
+    // set up the pap/chap secrets file
+//  sprintf(userpass, "%s * %s", user, pass);
+
+    /* start the gprs pppd */
+#if 1
+    property_get("rild.ppp.tty", pppdcmd + strlen(pppdcmd), "/dev/ttyUSB0");
+    strcat(pppdcmd, " call gprs");
+    system(pppdcmd);
+#else
+    // Requires root access...
+    property_set("ctl.start", "ppp");
+    if (wait_for_property("init.svc.ppp", "running", 10) < 0) {
+        ALOGE("Timeout waiting init.svc.ppp - giving up!");
+        goto error;
+    }
+#endif
+
+    sleep(10); // Allow time for ip-up to complete
+
+    if (wait_for_property("net.ppp0.local-ip", NULL, 10) < 0) {
+        ALOGE("Timeout waiting net.ppp0.local-ip - giving up!");
+        goto error;
+    }
+
+    property_get("net.ppp0.local-ip", ppp_local_ip, NULL);
+    property_get("net.dns1", ppp_dns1, NULL);
+    property_get("net.dns2", ppp_dns2, NULL);
+    property_get("net.ppp0.gw", ppp_gw, NULL);
+    sprintf(ppp_dnses, "%s %s", ppp_dns1, ppp_dns2);
+
+    ALOGI("Got net.ppp0.local-ip: %s", ppp_local_ip);
+
+    responses.status = 0;
+    responses.suggestedRetryTime = -1;
+    responses.cid = 1;
+    responses.active = 2;
+    responses.type = (char*)"PPP";
+    responses.ifname = (char*)PPP_TTY_PATH;
+    responses.addresses = ppp_local_ip;
+    responses.dnses = ppp_dnses;
+    responses.gateways = ppp_gw;
+
+    RIL_onRequestComplete(t, RIL_E_SUCCESS, &responses, sizeof(RIL_Data_Call_Response_v6));
+    return;
+
+error:
+    ALOGE("Unable to setup PDP");
+    RIL_onRequestComplete(t, RIL_E_GENERIC_FAILURE, NULL, 0);
+
+}
+
+/* CHECK There are several error cases if PDP deactivation fails
+ * 24.008: 8, 25, 36, 38, 39, 112
+ */
+static void requestDeactivateDefaultPDP(void *data, size_t datalen, RIL_Token t)
+{
+    char * cid;
+
+    D("requestDeactivateDefaultPDP()");
+
+    cid = ((char **)data)[0];
+    if (killConn(cid) < 0)
+        goto error;
+
+    RIL_onRequestComplete(t, RIL_E_SUCCESS, NULL, 0);
+    return;
+
+error:
+    RIL_onRequestComplete(t, RIL_E_GENERIC_FAILURE, NULL, 0);
+}
+
+/**
+ * RIL_REQUEST_LAST_PDP_FAIL_CAUSE
+ *
+ * Requests the failure cause code for the most recently failed PDP
+ * context activate.
+ *
+ * See also: RIL_REQUEST_LAST_CALL_FAIL_CAUSE.
+ *
+ */
+
+/* Last pdp fail cause */
+static int s_lastPdpFailCause = PDP_FAIL_ERROR_UNSPECIFIED;
+static void requestLastPDPFailCause(RIL_Token t)
+{
+    RIL_onRequestComplete(t, RIL_E_SUCCESS, &s_lastPdpFailCause, sizeof(int));
+}
+
+static void requestOrSendPDPContextList(RIL_Token *t)
+{
+    ATResponse *atResponse = NULL;
+    ATLine *p_cur;
+    RIL_Data_Call_Response_v6 *responses = NULL;
+
+    int err;
+    int fd;
+    char *line, *out;
+    int i,n = 0;
+
+    err = at_send_command_multiline ("AT+CGACT?", "+CGACT:", &atResponse);
+    if (err != AT_NOERROR) {
+        if (t != NULL)
+            RIL_onRequestComplete(*t, RIL_E_GENERIC_FAILURE, NULL, 0);
+        else
+            RIL_onUnsolicitedResponse(RIL_UNSOL_DATA_CALL_LIST_CHANGED, NULL, 0);
+
+        at_response_free(atResponse);
+        return;
+    }
+
+    for (p_cur = atResponse->p_intermediates; p_cur != NULL;
+            p_cur = p_cur->p_next)
+        n++;
+    if (n == 0)
+        goto error;
+
+    responses = (RIL_Data_Call_Response_v6*)
+                    alloca(n * sizeof(RIL_Data_Call_Response_v6));
+
+    for (i = 0; i < n; i++) {
+        responses[i].status = -1;
+        responses[i].suggestedRetryTime = -1;
+        responses[i].cid = -1;
+        responses[i].active = -1;
+        responses[i].type = (char*)"";
+        responses[i].ifname = (char*)PPP_TTY_PATH;
+        responses[i].addresses = (char*)"";
+        responses[i].dnses = (char*)"";
+        responses[i].gateways = (char*)"";
+    }
+
+    RIL_Data_Call_Response_v6 *response = responses;
+    for (p_cur = atResponse->p_intermediates; p_cur != NULL;
+            p_cur = p_cur->p_next) {
+
+        char *line = p_cur->line;
+
+        err = at_tok_start(&line);
+        if (err < 0)
+            goto error;
+
+        err = at_tok_nextint(&line, &response->cid);
+        if (err < 0)
+            goto error;
+
+        err = at_tok_nextint(&line, &response->active);
+        if (err < 0)
+            goto error;
+
+        response++;
+    }
+
+    at_response_free(atResponse);
+
+    err = at_send_command_multiline("AT+CGDCONT?", "+CGDCONT:", &atResponse);
+    if (err != AT_NOERROR)
+        goto error;
+
+    for (p_cur = atResponse->p_intermediates; p_cur != NULL;
+            p_cur = p_cur->p_next) {
+
+        char *line = p_cur->line;
+        int cid;
+
+        err = at_tok_start(&line);
+        if (err < 0)
+            goto error;
+
+        err = at_tok_nextint(&line, &cid);
+        if (err < 0)
+            goto error;
+
+        for (i = 0; i < n; i++) {
+            if (responses[i].cid == cid)
+                break;
+        }
+
+        if (i >= n) {
+            /* details for a context we didn't hear about in the last request */
+            continue;
+        }
+
+        // Assume no error
+        responses[i].status = 0;
+
+        // type
+        err = at_tok_nextstr(&line, &out);
+        if (err < 0)
+            goto error;
+
+        responses[i].type = (char*)alloca(strlen(out) + 1);
+        strcpy(responses[i].type, out);
+
+        // APN ignored for v5
+        err = at_tok_nextstr(&line, &out);
+        if (err < 0)
+            goto error;
+
+        responses[i].ifname = (char*)PPP_TTY_PATH;
+
+        err = at_tok_nextstr(&line, &out);
+        if (err < 0)
+            goto error;
+
+        responses[i].addresses = (char*)alloca(strlen(out) + 1);
+        strcpy(responses[i].addresses, out);
+    }
+
+
+    at_response_free(atResponse);
+    atResponse = NULL;
+
+    if (t != NULL)
+        RIL_onRequestComplete(*t, RIL_E_SUCCESS, responses,
+                n * sizeof(RIL_Data_Call_Response_v6));
+    else
+        RIL_onUnsolicitedResponse(RIL_UNSOL_DATA_CALL_LIST_CHANGED, responses,
+                n * sizeof(RIL_Data_Call_Response_v6));
+
+    return;
+
+error:
+    if (t != NULL)
+        RIL_onRequestComplete(*t, RIL_E_GENERIC_FAILURE, NULL, 0);
+    else
+        RIL_onUnsolicitedResponse(RIL_UNSOL_DATA_CALL_LIST_CHANGED, NULL, 0);
+
+    at_response_free(atResponse);
+}
+
+/**
+ * RIL_REQUEST_PDP_CONTEXT_LIST
+ *
+ * Queries the status of PDP contexts, returning for each
+ * its CID, whether or not it is active, and its PDP type,
+ * APN, and PDP adddress.
+ */
+static void requestPDPContextList(RIL_Token t)
+{
+    requestOrSendPDPContextList(&t);
+}
+
+static void onDataCallListChanged(void *param)
+{
+    requestOrSendPDPContextList(NULL);
+}
+
+/*
+ * Configure preferred message storage
+ *  mem1 = SM, mem2 = SM
+ */
+static int setPreferredMessageStorage(void)
+{
+    ATResponse *atResponse = NULL;
+    char *tok = NULL;
+    int used1, total1;
+    int err;
+    int return_value;
+
+    err = at_send_command_singleline("AT+CPMS=\"SM\",\"SM\"","+CPMS: ", &atResponse);
+    if (err != AT_NOERROR) {
+        ALOGE("%s() Unable to set preferred message storage", __func__);
+        goto error;
+    }
+
+    /*
+     * Depending on the host boot time the indication that message storage
+     * on SIM is full (+CIEV: 10,1) may be sent before the RIL is started.
+     * The RIL will explicitly check status of SIM messages storage using
+     * +CPMS intermediate response and inform Android if storage is full.
+     * +CPMS: <used1>,<total1>,<used2>,<total2>,<used3>,<total3>
+     */
+    tok = atResponse->p_intermediates->line;
+
+    err = at_tok_start(&tok);
+    if (err < 0)
+        goto error;
+
+    err = at_tok_nextint(&tok, &used1);
+    if (err < 0)
+        goto error;
+
+    err = at_tok_nextint(&tok, &total1);
+    if (err < 0)
+        goto error;
+
+    if (used1 >= total1)
+        RIL_onUnsolicitedResponse(RIL_UNSOL_SIM_SMS_STORAGE_FULL,NULL, 0);
+
+    return_value = 0;
+
+    goto exit;
+
+error:
+    ALOGE("%s() Failed during AT+CPMS sending/handling!", __func__);
+    return_value = 1;
+
+exit:
+    at_response_free(atResponse);
+    return return_value;
+}
+
+static char s_outstanding_acknowledge = 0;
+
+#define OUTSTANDING_SMS    0
+#define OUTSTANDING_STATUS 1
+#define OUTSTANDING_CB     2
+
+#define MESSAGE_STORAGE_READY_TIMER 3
+
+#define BSM_LENGTH 88
+
+/* Check if ME is ready to set preferred message storage */
+static void checkMessageStorageReady(void *p)
+{
+    int err;
+    struct timespec trigger_time;
+    (void) p;
+
+    err = at_send_command_singleline("AT+CPMS?","+CPMS: ", NULL);
+    if (err == AT_NOERROR) {
+        if (setPreferredMessageStorage() == 0) {
+            ALOGI("Message storage is ready");
+            return;
+        }
+    }
+
+    ALOGE("%s() Message storage is not ready"
+            "A new attempt will be done in %d seconds",
+            __func__, MESSAGE_STORAGE_READY_TIMER);
+
+    trigger_time.tv_sec = MESSAGE_STORAGE_READY_TIMER;
+    trigger_time.tv_nsec = 0;
+
+    enqueueRILEvent(checkMessageStorageReady, NULL, &trigger_time);
+}
+
+struct held_pdu {
+    char type;
+    char *sms_pdu;
+    struct held_pdu *next;
+};
+
+static pthread_mutex_t s_held_pdus_mutex = PTHREAD_MUTEX_INITIALIZER;
+static struct held_pdu *s_held_pdus = NULL;
+
+static struct held_pdu *dequeue_held_pdu(void)
+{
+    struct held_pdu *hpdu = NULL;
+
+    if (s_held_pdus != NULL) {
+        hpdu = s_held_pdus;
+        s_held_pdus = hpdu->next;
+        hpdu->next = NULL;
+    }
+
+    return hpdu;
+}
+
+static void enqueue_held_pdu(char type, const char *sms_pdu)
+{
+    struct held_pdu *hpdu = (struct held_pdu *) malloc(sizeof(*hpdu));
+    if (hpdu == NULL) {
+        ALOGE("%s() failed to allocate memory!", __func__);
+        return;
+    }
+
+    memset(hpdu, 0, sizeof(*hpdu));
+    hpdu->type = type;
+    hpdu->sms_pdu = strdup(sms_pdu);
+    if (NULL == hpdu->sms_pdu) {
+        ALOGE("%s() failed to allocate memory!", __func__);
+        return;
+    }
+
+    if (s_held_pdus == NULL)
+       s_held_pdus = hpdu;
+    else {
+        struct held_pdu *p = s_held_pdus;
+        while (p->next != NULL)
+            p = p->next;
+
+        p->next = hpdu;
+    }
+}
+
+void isSimSmsStorageFull(void *p)
+{
+    ATResponse *atResponse = NULL;
+    char *tok = NULL;
+    char* storage_area = NULL;
+    int used1, total1;
+    int err;
+    (void) p;
+
+    err = at_send_command_singleline("AT+CPMS?", "+CPMS: ", &atResponse);
+    if (err != AT_NOERROR)
+        goto error;
+
+    tok = atResponse->p_intermediates->line;
+
+    err = at_tok_start(&tok);
+    if (err < 0)
+        goto error;
+
+    err = at_tok_nextstr(&tok, &storage_area);
+    if (err < 0)
+        goto error;
+
+    err = at_tok_nextint(&tok, &used1);
+    if (err < 0)
+        goto error;
+
+    err = at_tok_nextint(&tok, &total1);
+    if (err < 0)
+        goto error;
+
+    if (used1 >= total1)
+        RIL_onUnsolicitedResponse(RIL_UNSOL_SIM_SMS_STORAGE_FULL, NULL, 0);
+
+    goto exit;
+
+error:
+    ALOGE("%s() failed during AT+CPMS sending/handling!", __func__);
+exit:
+    at_response_free(atResponse);
+    return;
+}
+
+/**
+ * RIL_UNSOL_SIM_SMS_STORAGE_FULL
+ *
+ * SIM SMS storage area is full, cannot receive
+ * more messages until memory freed
+ */
+void onNewSmsIndication(void)
+{
+    enqueueRILEvent(isSimSmsStorageFull, NULL, NULL);
+}
+
+void onNewSms(const char *sms_pdu)
+{
+    pthread_mutex_lock(&s_held_pdus_mutex);
+
+    /* No RIL_UNSOL_RESPONSE_NEW_SMS or RIL_UNSOL_RESPONSE_NEW_SMS_STATUS_REPORT
+     * messages should be sent until a RIL_REQUEST_SMS_ACKNOWLEDGE has been received for
+     * previous new SMS.
+     */
+    if (s_outstanding_acknowledge) {
+        ALOGI("Waiting for ack for previous sms, enqueueing PDU");
+        enqueue_held_pdu(OUTSTANDING_SMS, sms_pdu);
+    } else {
+        s_outstanding_acknowledge = 1;
+        RIL_onUnsolicitedResponse(RIL_UNSOL_RESPONSE_NEW_SMS,
+                                  sms_pdu, strlen(sms_pdu));
+    }
+
+    pthread_mutex_unlock(&s_held_pdus_mutex);
+}
+
+void onNewStatusReport(const char *sms_pdu)
+{
+    char *response = NULL;
+    int err;
+
+    /* Baseband will not prepend SMSC addr, but Android expects it. */
+    err = asprintf(&response, "%s%s", "00", sms_pdu);
+    if (err == -1) {
+        D("%s() Error allocating memory!", __func__);
+        return;
+    }
+
+    pthread_mutex_lock(&s_held_pdus_mutex);
+
+    /* No RIL_UNSOL_RESPONSE_NEW_SMS or RIL_UNSOL_RESPONSE_NEW_SMS_STATUS_REPORT
+     * messages should be sent until a RIL_REQUEST_SMS_ACKNOWLEDGE has been received for
+     * previous new SMS.
+     */
+    if (s_outstanding_acknowledge) {
+        ALOGE("%s() Waiting for previous ack, enqueueing PDU..", __func__);
+        enqueue_held_pdu(OUTSTANDING_STATUS, response);
+    } else {
+        s_outstanding_acknowledge = 1;
+        RIL_onUnsolicitedResponse(RIL_UNSOL_RESPONSE_NEW_SMS_STATUS_REPORT,
+                                  response, strlen(response));
+    }
+    free(response);
+    pthread_mutex_unlock(&s_held_pdus_mutex);
+}
+
+void onNewBroadcastSms(const char *pdu)
+{
+    char *message = NULL;
+    D("%s() Length : %d", __func__, strlen(pdu));
+
+    if (strlen(pdu) != (2 * BSM_LENGTH)) {
+        ALOGE("%s() Broadcast Message length error! Discarding!", __func__);
+        goto error;
+    }
+    D("%s() PDU: %176s", __func__, pdu);
+
+    message = (char*) alloca(BSM_LENGTH);
+    if (!message) {
+        ALOGE("%s() error allocating memory for message! Discarding!", __func__);
+        goto error;
+    }
+
+    stringToBinary(pdu, 2*BSM_LENGTH, (unsigned char *)message);
+    D("%s() Message: %88s", __func__, message);
+
+    pthread_mutex_lock(&s_held_pdus_mutex);
+
+    /* No RIL_UNSOL_RESPONSE_NEW_SMS or RIL_UNSOL_RESPONSE_NEW_SMS_STATUS_REPORT
+     * or RIL_UNSOL_RESPONSE_NEW_CB
+     * messages should be sent until a RIL_REQUEST_SMS_ACKNOWLEDGE has been received for
+     * previous new SMS.
+     */
+    if (s_outstanding_acknowledge) {
+        ALOGE("%s() Waiting for previous ack, enqueueing PDU..", __func__);
+        enqueue_held_pdu(OUTSTANDING_CB, message);
+    } else {
+        s_outstanding_acknowledge = 1;
+        RIL_onUnsolicitedResponse(RIL_UNSOL_RESPONSE_NEW_BROADCAST_SMS,
+                              message, BSM_LENGTH);
+    }
+
+    pthread_mutex_unlock(&s_held_pdus_mutex);
+
+error:
+    return;
+}
+
+void onNewSmsOnSIM(const char *s)
+{
+    char *line;
+    char *mem;
+    char *tok;
+    int err = 0;
+    int index = -1;
+
+    tok = line = strdup(s);
+
+    err = at_tok_start(&tok);
+    if (err < 0)
+        goto error;
+
+    err = at_tok_nextstr(&tok, &mem);
+    if (err < 0)
+        goto error;
+
+    if (strncmp(mem, "SM", 2) != 0)
+        goto error;
+
+    err = at_tok_nextint(&tok, &index);
+    if (err < 0)
+        goto error;
+
+    RIL_onUnsolicitedResponse(RIL_UNSOL_RESPONSE_NEW_SMS_ON_SIM,
+                              &index, sizeof(int *));
+
+finally:
+    free(line);
+    return;
+
+error:
+    ALOGE("%s() Failed to parse +CMTI.", __func__);
+    goto finally;
+}
+
+
+/**
+ * RIL_REQUEST_SEND_SMS
+ *
+ * Sends an SMS message.
+*/
+static void requestSendSMS(void *data, size_t datalen, RIL_Token t)
+{
+    (void) datalen;
+    int err, aterr = 0;
+    const char *testSmsc;
+    char smsc[30];
+    const char *pdu;
+    char *line;
+    int tpLayerLength;
+    char *cmd1, *cmd2;
+    RIL_SMS_Response response;
+    RIL_Errno ret = RIL_E_SUCCESS;
+    ATResponse *atResponse = NULL;
+
+    testSmsc = ((const char **) data)[0];
+    pdu = ((const char **) data)[1];
+
+    tpLayerLength = strlen(pdu) / 2;
+
+    /* NULL for default SMSC. */
+    if (testSmsc == NULL) {
+        char* temp;
+        int tosca;
+        int plus = 0;
+        int length = 0;
+        int i,curChar = 0;
+
+        err = at_send_command_singleline("AT+CSCA?", "+CSCA:", &atResponse);
+        if (err != AT_NOERROR)
+            goto error;
+
+        line = atResponse->p_intermediates->line;
+
+        err = at_tok_start(&line);
+        if (err < 0) goto error;
+
+        err = at_tok_nextstr(&line, &temp);
+        if (err < 0) goto error;
+
+        err = at_tok_nextint(&line, &tosca);
+        if (err < 0) goto error;
+
+        if(temp[0]=='+')
+            plus = 1;
+
+        length = strlen(temp) - plus;
+        sprintf(smsc,"%.2x%.2x",(length + 1) / 2 + 1, tosca);
+
+        for (i = 0; curChar < length - 1; i+=2 ) {
+            smsc[5+i] = temp[plus+curChar++];
+            smsc[4+i] = temp[plus+curChar++];
+        }
+
+        if ( length % 2) { // One extra number
+            smsc[4+length] = temp[curChar];
+            smsc[3+length]='F';
+            smsc[5+length]='\0';
+        } else {
+            smsc[4+length] = '\0';
+        }
+        at_response_free(atResponse);
+    }
+    else
+        strcpy(smsc,testSmsc);
+
+    asprintf(&cmd1, "AT+CMGS=%d", tpLayerLength);
+    asprintf(&cmd2, "%s%s", smsc, pdu);
+    aterr = at_send_command_sms(cmd1, cmd2, "+CMGS:", &atResponse);
+    free(cmd1);
+    free(cmd2);
+
+    if (aterr != AT_NOERROR)
+        goto error;
+
+    memset(&response, 0, sizeof(response));
+
+    /* Set errorCode to -1 if unknown or not applicable
+     * See 3GPP 27.005, 3.2.5 for GSM/UMTS
+     */
+    response.errorCode = -1;
+
+    line = atResponse->p_intermediates->line;
+
+    err = at_tok_start(&line);
+    if (err < 0)
+        goto error;
+
+    err = at_tok_nextint(&line, &response.messageRef);
+    if (err < 0)
+        goto error;
+
+    /* No support for ackPDU. Do we need it? */
+    RIL_onRequestComplete(t, RIL_E_SUCCESS, &response, sizeof(response));
+
+finally:
+    at_response_free(atResponse);
+    return;
+
+error:
+    switch (at_get_cms_error(aterr)) {
+    case CMS_NO_NETWORK_SERVICE:
+    case CMS_NETWORK_TIMEOUT:
+        ret = RIL_E_SMS_SEND_FAIL_RETRY;
+        break;
+    default:
+        ret = RIL_E_GENERIC_FAILURE;
+        break;
+    }
+    RIL_onRequestComplete(t, ret, NULL, 0);
+    goto finally;
+}
+
+
+/**
+ * RIL_REQUEST_SEND_SMS_EXPECT_MORE
+ *
+ * Send an SMS message. Identical to RIL_REQUEST_SEND_SMS,
+ * except that more messages are expected to be sent soon. If possible,
+ * keep SMS relay protocol link open (eg TS 27.005 AT+CMMS command).
+*/
+static void requestSendSMSExpectMore(void *data, size_t datalen, RIL_Token t)
+{
+    /* Throw the command on the channel and ignore any errors, since we
+       need to send the SMS anyway and subsequent SMSes will be sent anyway. */
+    at_send_command("AT+CMMS=1");
+
+    requestSendSMS(data, datalen, t);
+}
+
+
+/**
+ * RIL_REQUEST_WRITE_SMS_TO_SIM
+ *
+ * Stores a SMS message to SIM memory.
+*/
+void requestWriteSmsToSim(void *data, size_t datalen, RIL_Token t)
+{
+    RIL_SMS_WriteArgs *args;
+    char *cmd;
+    char *pdu;
+    char *line;
+    int length;
+    int index;
+    int err;
+    ATResponse *atResponse = NULL;
+
+    (void) datalen;
+
+    args = (RIL_SMS_WriteArgs *) data;
+
+    length = strlen(args->pdu) / 2;
+
+    asprintf(&cmd, "AT+CMGW=%d,%d", length, args->status);
+    asprintf(&pdu, "%s%s", (args->smsc ? args->smsc : "00"), args->pdu);
+    err = at_send_command_sms(cmd, pdu, "+CMGW:", &atResponse);
+    free(cmd);
+    free(pdu);
+
+    if (err != AT_NOERROR)
+        goto error;
+
+    line = atResponse->p_intermediates->line;
+
+    err = at_tok_start(&line);
+    if (err < 0)
+        goto error;
+
+    err = at_tok_nextint(&line, &index);
+    if (err < 0)
+        goto error;
+
+    RIL_onRequestComplete(t, RIL_E_SUCCESS, &index, sizeof(int *));
+
+finally:
+    at_response_free(atResponse);
+    return;
+
+error:
+    RIL_onRequestComplete(t, RIL_E_GENERIC_FAILURE, NULL, 0);
+    goto finally;
+}
+
+/**
+ * RIL_REQUEST_DELETE_SMS_ON_SIM
+ *
+ * Deletes a SMS message from SIM memory.
+ */
+static void requestDeleteSmsOnSim(void *data, size_t datalen, RIL_Token t)
+{
+    int err;
+
+    err = at_send_command("AT+CMGD=%d", ((int *) data)[0]);
+    if (err != AT_NOERROR)
+        RIL_onRequestComplete(t, RIL_E_GENERIC_FAILURE, NULL, 0);
+    else
+        RIL_onRequestComplete(t, RIL_E_SUCCESS, NULL, 0);
+}
+
+/**
+ * RIL_REQUEST_GET_SMSC_ADDRESS
+ */
+static void requestGetSMSCAddress(RIL_Token t)
+{
+    ATResponse *atResponse = NULL;
+    int err;
+    char *line;
+    char *response;
+
+    err = at_send_command_singleline("AT+CSCA?", "+CSCA:", &atResponse);
+
+    if (err != AT_NOERROR)
+        goto error;
+
+    line = atResponse->p_intermediates->line;
+
+    err = at_tok_start(&line);
+    if (err < 0)
+        goto error;
+
+    err = at_tok_nextstr(&line, &response);
+    if (err < 0)
+        goto error;
+
+    RIL_onRequestComplete(t, RIL_E_SUCCESS, response, sizeof(char *));
+
+finally:
+    at_response_free(atResponse);
+    return;
+
+error:
+    RIL_onRequestComplete(t, RIL_E_GENERIC_FAILURE, NULL, 0);
+    goto finally;
+}
+
+
+
+/**
+ * RIL_REQUEST_SET_SMSC_ADDRESS
+ */
+static void requestSetSMSCAddress(void *data, size_t datalen, RIL_Token t)
+{
+    (void) datalen;
+    int err;
+    const char *smsc = (const char *)data;
+
+    err = at_send_command("AT+CSCA=\"%s\"", smsc);
+    if (err != AT_NOERROR)
+        RIL_onRequestComplete(t, RIL_E_GENERIC_FAILURE, NULL, 0);
+    else
+        RIL_onRequestComplete(t, RIL_E_SUCCESS, NULL, 0);
+}
+
+
+/**
+ * RIL_REQUEST_REPORT_SMS_MEMORY_STATUS
+ */
+static void requestSmsStorageFull(void *data, size_t datalen, RIL_Token t)
+{
+    int ack;
+
+    ack = ((int *) data)[0];
+
+    /* Android will call RIL_REQUEST_REPORT_SMS_MEMORY_STATUS in case of:
+     * 0. memory is full
+     * 1. memory was full and been cleaned up, inform modem memory is available now.
+     */
+    switch (ack) {
+    case 0:
+        /* Android will handle this, no need to inform modem. always return success. */
+        ALOGI("SMS storage full");
+        break;
+
+    case 1:
+        /* Since we are not using +CNMA command. It's fine to return without informing network */
+        ALOGI("Failed to inform network for Message Cleanup. Need cmd : ESMSMEMAVAIL");
+        break;
+
+    default:
+        ALOGE("%s() Invalid parameter", __func__);
+        RIL_onRequestComplete(t, RIL_E_GENERIC_FAILURE, NULL, 0);
+        return;
+    }
+
+    RIL_onRequestComplete(t, RIL_E_SUCCESS, NULL, 0);
+}
+
+
+/**
+ * RIL_REQUEST_SMS_ACKNOWLEDGE
+ *
+ * Acknowledge successful or failed receipt of SMS previously indicated
+ * via RIL_UNSOL_RESPONSE_NEW_SMS .
+*/
+static void requestSMSAcknowledge(void *data, size_t datalen, RIL_Token t)
+{
+    int ackSuccess;
+    int err;
+    struct held_pdu *hpdu;
+
+    ackSuccess = ((int *)data)[0];
+
+    if (ackSuccess == 1) {
+        err = at_send_command("AT+CNMA=1");
+    } else if (ackSuccess == 0)  {
+        err = at_send_command("AT+CNMA=2");
+    } else {
+        ALOGE("unsupported arg to RIL_REQUEST_SMS_ACKNOWLEDGE");
+        goto error;
+    }
+
+    RIL_onRequestComplete(t, RIL_E_SUCCESS, NULL, 0);
+
+    pthread_mutex_lock(&s_held_pdus_mutex);
+    hpdu = dequeue_held_pdu();
+
+    if (hpdu != NULL) {
+        ALOGE("%s() Outstanding requests in queue, dequeueing and sending.",
+         __func__);
+        int unsolResponse = 0;
+
+        if (hpdu->type == OUTSTANDING_SMS)
+            unsolResponse = RIL_UNSOL_RESPONSE_NEW_SMS;
+        else if (hpdu->type == OUTSTANDING_CB)
+            unsolResponse = RIL_UNSOL_RESPONSE_NEW_BROADCAST_SMS;
+        else
+            unsolResponse = RIL_UNSOL_RESPONSE_NEW_SMS_STATUS_REPORT;
+
+        RIL_onUnsolicitedResponse(unsolResponse, hpdu->sms_pdu,
+                                  strlen(hpdu->sms_pdu));
+
+        free(hpdu->sms_pdu);
+        free(hpdu);
+    } else
+        s_outstanding_acknowledge = 0;
+
+    pthread_mutex_unlock(&s_held_pdus_mutex);
+
+    return;
+
+error:
+    RIL_onRequestComplete(t, RIL_E_GENERIC_FAILURE, NULL, 0);
+
+}
+
+
+
+
+#define BROADCAST_MAX_RANGES_SUPPORTED 10
+
+/**
+ * RIL_REQUEST_GSM_GET_BROADCAST_SMS_CONFIG
+ */
+static void requestGSMGetBroadcastSMSConfig(RIL_Token t)
+{
+    ATResponse *atResponse = NULL;
+    int mode, err = 0;
+    unsigned int i, count = 0;
+    char *mids;
+    char *range;
+    char *trange;
+    char *tok = NULL;
+
+    RIL_GSM_BroadcastSmsConfigInfo *configInfo[BROADCAST_MAX_RANGES_SUPPORTED];
+
+    err = at_send_command_singleline("AT+CSCB?", "+CSCB:", &atResponse);
+
+    if (err != AT_NOERROR)
+        goto error;
+
+    tok = atResponse->p_intermediates->line;
+
+    err = at_tok_start(&tok);
+    if (err < 0)
+        goto error;
+
+    err = at_tok_nextint(&tok, &mode);
+    if (err < 0)
+        goto error;
+
+    /**
+     * Get the string that yields the service ids (mids). AT+CSCB <mids>
+     * parameter may contain a mix of single service ids (,%d,) and service id
+     * ranges (,%d-%d,).
+     */
+    err = at_tok_nextstr(&tok, &mids);
+    if (err < 0)
+        goto error;
+
+    while (at_tok_nextstr(&mids, &range) == 0) {
+        /**
+         * Replace any '-' sign with ',' sign to allow for at_tok_nextint
+         * for both fromServiceId and toServiceId below.
+         */
+        trange = range;
+        while ((NULL != trange) && ('\0' != *trange)) {
+            if ('-' == *trange)
+                *trange = ',';
+            trange++;
+        }
+        if (count < BROADCAST_MAX_RANGES_SUPPORTED) {
+            configInfo[count] = (RIL_GSM_BroadcastSmsConfigInfo*) calloc(1,
+                sizeof(RIL_GSM_BroadcastSmsConfigInfo));
+            if (NULL == configInfo[count])
+                goto error;
+
+            /* No support for "Not accepted mids", selected is always 1 */
+            configInfo[count]->selected = 1;
+
+            /* Fetch fromServiceId value */
+            err = at_tok_nextint(&range, &(configInfo[count]->fromServiceId));
+            if (err < 0)
+                goto error;
+            /* Try to fetch toServiceId value if it exist */
+            err = at_tok_nextint(&range, &(configInfo[count]->toServiceId));
+            if (err < 0)
+                configInfo[count]->toServiceId =
+                    configInfo[count]->fromServiceId;
+
+            count++;
+        } else {
+            ALOGW("%s() Max limit (%d) passed, can not send all ranges "
+                 "reported by modem.", __func__,
+                 BROADCAST_MAX_RANGES_SUPPORTED);
+            break;
+        }
+    }
+
+    RIL_onRequestComplete(t, RIL_E_SUCCESS, &configInfo,
+                          sizeof(RIL_GSM_BroadcastSmsConfigInfo *) * count);
+
+    goto exit;
+
+error:
+    RIL_onRequestComplete(t, RIL_E_GENERIC_FAILURE, NULL, 0);
+
+exit:
+    at_response_free(atResponse);
+    for (i = 0; i < count; i++)
+        free(configInfo[i]);
+}
+
+
+
+/**
+ * RIL_REQUEST_GSM_SET_BROADCAST_SMS_CONFIG
+ */
+static void requestGSMSetBroadcastSMSConfig(void *data, size_t datalen,
+                                     RIL_Token t)
+{
+    int err, count, i;
+    char *tmp, *mids = NULL;
+    RIL_GSM_BroadcastSmsConfigInfo **configInfoArray =
+        (RIL_GSM_BroadcastSmsConfigInfo **) data;
+    RIL_GSM_BroadcastSmsConfigInfo *configInfo = NULL;
+
+    count = datalen / sizeof(RIL_GSM_BroadcastSmsConfigInfo *);
+    ALOGI("Number of MID ranges in BROADCAST_SMS_CONFIG: %d", count);
+
+    for (i = 0; i < count; i++) {
+        configInfo = configInfoArray[i];
+        /* No support for "Not accepted mids" in AT */
+        if (configInfo->selected) {
+            tmp = mids;
+            asprintf(&mids, "%s%d-%d%s", (tmp ? tmp : ""),
+                configInfo->fromServiceId, configInfo->toServiceId,
+                (i == (count - 1) ? "" : ",")); /* Last one? Skip comma */
+            free(tmp);
+        }
+    }
+
+    if (mids == NULL) {
+        RIL_onRequestComplete(t, RIL_E_GENERIC_FAILURE, NULL, 0);
+        return;
+    }
+
+    err = at_send_command("AT+CSCB=0,\"%s\"", mids);
+    free(mids);
+
+    if (err != AT_NOERROR) {
+        RIL_onRequestComplete(t, RIL_E_GENERIC_FAILURE, NULL, 0);
+        return;
+    }
+
+    RIL_onRequestComplete(t, RIL_E_SUCCESS, NULL, 0);
+}
+
+
+/**
+ * RIL_REQUEST_GSM_SMS_BROADCAST_ACTIVATION
+ */
+static void requestGSMSMSBroadcastActivation(void *data, size_t datalen,
+                                      RIL_Token t)
+{
+    ATResponse *atResponse = NULL;
+    int mode, mt, bm, ds, bfr, skip;
+    int activation;
+    char *tok;
+    int err;
+
+    (void) datalen;
+
+    /* AT+CNMI=[<mode>[,<mt>[,<bm>[,<ds>[,<bfr>]]]]] */
+    err = at_send_command_singleline("AT+CNMI?", "+CNMI:", &atResponse);
+    if (err != AT_NOERROR)
+        goto error;
+
+    tok = atResponse->p_intermediates->line;
+
+    err = at_tok_start(&tok);
+    if (err < 0)
+        goto error;
+    err = at_tok_nextint(&tok, &mode);
+    if (err < 0)
+        goto error;
+    err = at_tok_nextint(&tok, &mt);
+    if (err < 0)
+        goto error;
+    err = at_tok_nextint(&tok, &skip);
+    if (err < 0)
+        goto error;
+    err = at_tok_nextint(&tok, &ds);
+    if (err < 0)
+        goto error;
+    err = at_tok_nextint(&tok, &bfr);
+    if (err < 0)
+        goto error;
+
+    /* 0 - Activate, 1 - Turn off */
+    activation = *((const int *)data);
+    if (activation == 0)
+        bm = 2;
+    else
+        bm = 0;
+
+    err = at_send_command("AT+CNMI=%d,%d,%d,%d,%d", mode, mt, bm, ds, bfr);
+
+    if (err != AT_NOERROR)
+        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;
+}
+
+/**
+ * Fetch information about UICC card type (SIM/USIM)
+ *
+ * \return UICC_Type: type of UICC card.
+ */
+static UICC_Type getUICCType(void)
+{
+    ATResponse *atResponse = NULL;
+    char* line;
+    static UICC_Type UiccType = UICC_TYPE_UNKNOWN;
+    int err,type;
+
+    if (getRadioState() == RADIO_STATE_OFF ||
+        getRadioState() == RADIO_STATE_UNAVAILABLE) {
+        return UICC_TYPE_UNKNOWN;
+    }
+
+    /* Only if card type is not known yet ... */
+    if (UiccType == UICC_TYPE_UNKNOWN) {
+
+        /* Get the card type */
+        err = at_send_command_singleline("AT^CARDMODE", "^CARDMODE:", &atResponse);
+        if (err != AT_NOERROR)
+            goto error;
+
+        line = atResponse->p_intermediates->line;
+
+        err = at_tok_start(&line);
+
+        if (err < 0)
+            goto error;
+
+        err = at_tok_nextint(&line, &type);
+        if (err < 0)
+            goto error;
+
+
+        UiccType =   (type == 0) ? UICC_TYPE_SIM :
+                    ((type == 1) ? UICC_TYPE_USIM :
+                    ((type == 2) ? UICC_TYPE_UIM : UICC_TYPE_UNKNOWN));
+
+    }
+
+    ALOGI("Detected card type :%d", UiccType);
+
+error:
+    at_response_free(atResponse);
+
+    return UiccType;
+}
+
+/**
+ * RIL_REQUEST_SIM_IO
+ *
+ * Request SIM I/O operation.
+ * This is similar to the TS 27.007 "restricted SIM" operation
+ * where it assumes all of the EF selection will be done by the
+ * callee.
+ */
+
+
+/* All files listed under ADF_USIM in 3GPP TS 31.102 */
+static const int ef_usim_files[] = {
+    0x6F05, 0x6F06, 0x6F07, 0x6F08, 0x6F09,
+    0x6F2C, 0x6F31, 0x6F32, 0x6F37, 0x6F38,
+    0x6F39, 0x6F3B, 0x6F3C, 0x6F3E, 0x6F3F,
+    0x6F40, 0x6F41, 0x6F42, 0x6F43, 0x6F45,
+    0x6F46, 0x6F47, 0x6F48, 0x6F49, 0x6F4B,
+    0x6F4C, 0x6F4D, 0x6F4E, 0x6F4F, 0x6F50,
+    0x6F55, 0x6F56, 0x6F57, 0x6F58, 0x6F5B,
+    0x6F5C, 0x6F60, 0x6F61, 0x6F62, 0x6F73,
+    0x6F78, 0x6F7B, 0x6F7E, 0x6F80, 0x6F81,
+    0x6F82, 0x6F83, 0x6FAD, 0x6FB1, 0x6FB2,
+    0x6FB3, 0x6FB4, 0x6FB5, 0x6FB6, 0x6FB7,
+    0x6FC3, 0x6FC4, 0x6FC5, 0x6FC6, 0x6FC7,
+    0x6FC8, 0x6FC9, 0x6FCA, 0x6FCB, 0x6FCC,
+    0x6FCD, 0x6FCE, 0x6FCF, 0x6FD0, 0x6FD1,
+    0x6FD2, 0x6FD3, 0x6FD4, 0x6FD5, 0x6FD6,
+    0x6FD7, 0x6FD8, 0x6FD9, 0x6FDA, 0x6FDB,
+};
+
+static const int ef_telecom_files[] = {
+    0x6F3A, 0x6F3D, 0x6F44, 0x6F4A, 0x6F54,
+};
+
+#define PATH_ADF_USIM_DIRECTORY      "3F007FFF"
+#define PATH_ADF_TELECOM_DIRECTORY   "3F007F10"
+
+/* RID: A000000087 = 3GPP, PIX: 1002 = 3GPP USIM */
+#define USIM_APPLICATION_ID          "A0000000871002"
+
+static int sendSimIOCmdICC(const RIL_SIM_IO_v6 *ioargs, ATResponse **atResponse, RIL_SIM_IO_Response *sr)
+{
+    int err;
+    char *fmt;
+    char *arg6;
+    char *arg7;
+    char *line;
+
+    /* FIXME Handle pin2. */
+    memset(sr, 0, sizeof(*sr));
+
+    arg6 = ioargs->data;
+    arg7 = ioargs->path;
+
+    if (arg7 && arg6) {
+        fmt = "AT+CRSM=%d,%d,%d,%d,%d,\"%s\",\"%s\"";
+    } else if (arg7) {
+        fmt = "AT+CRSM=%d,%d,%d,%d,%d,,\"%s\"";
+        arg6 = arg7;
+    } else if (arg6) {
+        fmt = "AT+CRSM=%d,%d,%d,%d,%d,\"%s\"";
+    } else {
+        fmt = "AT+CRSM=%d,%d,%d,%d,%d";
+    }
+
+    err = at_send_command_singleline(fmt, "+CRSM:", atResponse,ioargs->command,
+                 ioargs->fileid, ioargs->p1,
+                 ioargs->p2, ioargs->p3,
+                 arg6, arg7);
+
+    if (err != AT_NOERROR)
+        return err;
+
+    line = (*atResponse)->p_intermediates->line;
+
+    err = at_tok_start(&line);
+    if (err < 0)
+        goto finally;
+
+    err = at_tok_nextint(&line, &(sr->sw1));
+    if (err < 0)
+        goto finally;
+
+    err = at_tok_nextint(&line, &(sr->sw2));
+    if (err < 0)
+        goto finally;
+
+    if (at_tok_hasmore(&line)) {
+        err = at_tok_nextstr(&line, &(sr->simResponse));
+        if (err < 0)
+            goto finally;
+    }
+
+finally:
+    return err;
+}
+
+static int convertSimIoFcp(RIL_SIM_IO_Response *sr, char **cvt)
+{
+    int err;
+    /* size_t pos; */
+    size_t fcplen;
+    struct ts_51011_921_resp resp;
+    void *cvt_buf = NULL;
+
+    if (!sr->simResponse || !cvt) {
+        err = -EINVAL;
+        goto error;
+    }
+
+    fcplen = strlen(sr->simResponse);
+    if ((fcplen == 0) || (fcplen & 1)) {
+        err = -EINVAL;
+        goto error;
+    }
+
+    err = fcp_to_ts_51011(sr->simResponse, fcplen, &resp);
+    if (err < 0)
+        goto error;
+
+    cvt_buf = malloc(sizeof(resp) * 2 + 1);
+    if (!cvt_buf) {
+        err = -ENOMEM;
+        goto error;
+    }
+
+    err = binaryToString((unsigned char*)(&resp),
+                   sizeof(resp), cvt_buf);
+    if (err < 0)
+        goto error;
+
+    /* cvt_buf ownership is moved to the caller */
+    *cvt = cvt_buf;
+    cvt_buf = NULL;
+
+finally:
+    return err;
+
+error:
+    free(cvt_buf);
+    goto finally;
+}
+
+/**
+ * RIL_REQUEST_SIM_IO
+ *
+ * Request SIM I/O operation.
+ * This is similar to the TS 27.007 "restricted SIM" operation
+ * where it assumes all of the EF selection will be done by the
+ * callee.
+ */
+static void requestSIM_IO(void *data, size_t datalen, RIL_Token t)
+{
+    (void) datalen;
+    ATResponse *atResponse = NULL;
+    RIL_SIM_IO_Response sr;
+    int cvt_done = 0;
+    int err;
+    UICC_Type UiccType = getUICCType();
+
+    int pathReplaced = 0;
+    RIL_SIM_IO_v6 ioargsDup;
+
+    /*
+     * Android telephony framework does not support USIM cards properly,
+     * send GSM filepath where as active cardtype is USIM.
+     * Android RIL needs to change the file path of files listed under ADF-USIM
+     * if current active cardtype is USIM
+     */
+    memcpy(&ioargsDup, data, sizeof(RIL_SIM_IO_v6));
+    if (UICC_TYPE_USIM == UiccType) {
+        unsigned int i;
+        int err;
+        unsigned int count = sizeof(ef_usim_files) / sizeof(int);
+
+        for (i = 0; i < count; i++) {
+            if (ef_usim_files[i] == ioargsDup.fileid) {
+                err = asprintf(&ioargsDup.path, PATH_ADF_USIM_DIRECTORY);
+                if (err < 0)
+                    goto error;
+                pathReplaced = 1;
+                ALOGD("%s() Path replaced for USIM: %d", __func__, ioargsDup.fileid);
+                break;
+            }
+        }
+        if(!pathReplaced){
+            unsigned int count2 = sizeof(ef_telecom_files) / sizeof(int);
+            for (i = 0; i < count2; i++) {
+                if (ef_telecom_files[i] == ioargsDup.fileid) {
+                    err = asprintf(&ioargsDup.path, PATH_ADF_TELECOM_DIRECTORY);
+                    if (err < 0)
+                        goto error;
+                    pathReplaced = 1;
+                    ALOGD("%s() Path replaced for telecom: %d", __func__, ioargsDup.fileid);
+                    break;
+                }
+            }
+        }
+    }
+
+    memset(&sr, 0, sizeof(sr));
+
+    err = sendSimIOCmdICC(&ioargsDup, &atResponse, &sr);
+
+    if (err < 0)
+        goto error;
+
+    /*
+     * In case the command is GET_RESPONSE and cardtype is 3G SIM
+     * convert to 2G FCP
+     */
+    if (ioargsDup.command == 0xC0 && UiccType != UICC_TYPE_SIM) {
+        err = convertSimIoFcp(&sr, &sr.simResponse);
+        if (err < 0)
+            goto error;
+        cvt_done = 1; /* sr.simResponse needs to be freed */
+    }
+
+    RIL_onRequestComplete(t, RIL_E_SUCCESS, &sr, sizeof(sr));
+
+finally:
+    at_response_free(atResponse);
+    if (cvt_done)
+        free(sr.simResponse);
+
+    if (pathReplaced)
+        free(ioargsDup.path);
+    return;
+
+error:
+    RIL_onRequestComplete(t, RIL_E_GENERIC_FAILURE, NULL, 0);
+    goto finally;
+}
+
+
+/*
+ * The following list contains values for the structure "RIL_AppStatus" to be
+ * sent to Android on a given SIM state. It is indexed by the SIM_Status above.
+ */
+static const RIL_AppStatus app_status_array[] = {
+    /*
+     * RIL_AppType,  RIL_AppState,
+     * RIL_PersoSubstate,
+     * Aid pointer, App Label pointer, PIN1 replaced,
+     * RIL_PinState (PIN1),
+     * RIL_PinState (PIN2)
+     */
+    /* SIM_ABSENT = 0 */
+    {
+        RIL_APPTYPE_UNKNOWN, RIL_APPSTATE_UNKNOWN,
+        RIL_PERSOSUBSTATE_UNKNOWN,
+        NULL, NULL, 0,
+        RIL_PINSTATE_UNKNOWN,
+        RIL_PINSTATE_UNKNOWN
+    },
+    /* SIM_NOT_READY = 1 */
+    {
+        RIL_APPTYPE_SIM, RIL_APPSTATE_DETECTED,
+        RIL_PERSOSUBSTATE_UNKNOWN,
+        NULL, NULL, 0,
+        RIL_PINSTATE_UNKNOWN,
+        RIL_PINSTATE_UNKNOWN
+    },
+    /* SIM_READY = 2 */
+    {
+        RIL_APPTYPE_SIM, RIL_APPSTATE_READY,
+        RIL_PERSOSUBSTATE_READY,
+        NULL, NULL, 0,
+        RIL_PINSTATE_UNKNOWN,
+        RIL_PINSTATE_UNKNOWN
+    },
+    /* SIM_PIN = 3 */
+    {
+        RIL_APPTYPE_SIM, RIL_APPSTATE_PIN,
+        RIL_PERSOSUBSTATE_UNKNOWN,
+        NULL, NULL, 0,
+        RIL_PINSTATE_ENABLED_NOT_VERIFIED,
+        RIL_PINSTATE_UNKNOWN
+    },
+    /* SIM_PUK = 4 */
+    {
+        RIL_APPTYPE_SIM, RIL_APPSTATE_PUK,
+        RIL_PERSOSUBSTATE_UNKNOWN,
+        NULL, NULL, 0,
+        RIL_PINSTATE_ENABLED_BLOCKED,
+        RIL_PINSTATE_UNKNOWN
+    },
+    /* SIM_NETWORK_PERSO = 5 */
+    {
+        RIL_APPTYPE_SIM, RIL_APPSTATE_SUBSCRIPTION_PERSO,
+        RIL_PERSOSUBSTATE_SIM_NETWORK,
+        NULL, NULL, 0,
+        RIL_PINSTATE_ENABLED_NOT_VERIFIED,
+        RIL_PINSTATE_UNKNOWN
+    },
+    /* SIM_PIN2 = 6 */
+    {
+        RIL_APPTYPE_SIM, RIL_APPSTATE_READY,
+        RIL_PERSOSUBSTATE_UNKNOWN,
+        NULL, NULL, 0,
+        RIL_PINSTATE_UNKNOWN,
+        RIL_PINSTATE_ENABLED_NOT_VERIFIED
+    },
+    /* SIM_PUK2 = 7 */
+    {
+        RIL_APPTYPE_SIM, RIL_APPSTATE_READY,
+        RIL_PERSOSUBSTATE_UNKNOWN,
+        NULL, NULL, 0,
+        RIL_PINSTATE_UNKNOWN,
+        RIL_PINSTATE_ENABLED_BLOCKED
+    },
+    /* SIM_NETWORK_SUBSET_PERSO = 8 */
+    {
+        RIL_APPTYPE_SIM, RIL_APPSTATE_SUBSCRIPTION_PERSO,
+        RIL_PERSOSUBSTATE_SIM_NETWORK_SUBSET,
+        NULL, NULL, 0,
+        RIL_PINSTATE_ENABLED_NOT_VERIFIED,
+        RIL_PINSTATE_UNKNOWN
+    },
+    /* SIM_SERVICE_PROVIDER_PERSO = 9 */
+    {
+        RIL_APPTYPE_SIM, RIL_APPSTATE_SUBSCRIPTION_PERSO,
+        RIL_PERSOSUBSTATE_SIM_SERVICE_PROVIDER,
+        NULL, NULL, 0,
+        RIL_PINSTATE_ENABLED_NOT_VERIFIED,
+        RIL_PINSTATE_UNKNOWN
+    },
+    /* SIM_CORPORATE_PERSO = 10 */
+    {
+        RIL_APPTYPE_SIM, RIL_APPSTATE_SUBSCRIPTION_PERSO,
+        RIL_PERSOSUBSTATE_SIM_CORPORATE,
+        NULL, NULL, 0,
+        RIL_PINSTATE_ENABLED_NOT_VERIFIED,
+        RIL_PINSTATE_UNKNOWN
+    },
+    /* SIM_SIM_PERSO = 11 */
+    {
+        RIL_APPTYPE_SIM, RIL_APPSTATE_SUBSCRIPTION_PERSO,
+        RIL_PERSOSUBSTATE_SIM_SIM,
+        NULL, NULL, 0,
+        RIL_PINSTATE_ENABLED_NOT_VERIFIED,
+        RIL_PINSTATE_UNKNOWN
+    },
+    /* SIM_STERICSSON_LOCK = 12 */
+    {
+        RIL_APPTYPE_SIM, RIL_APPSTATE_SUBSCRIPTION_PERSO,
+        RIL_PERSOSUBSTATE_UNKNOWN,    /* API (ril.h) does not have this lock */
+        NULL, NULL, 0,
+        RIL_PINSTATE_ENABLED_NOT_VERIFIED,
+        RIL_PINSTATE_UNKNOWN
+    },
+    /* SIM_BLOCKED = 13 */
+    {
+        RIL_APPTYPE_SIM, RIL_APPSTATE_UNKNOWN,
+        RIL_PERSOSUBSTATE_UNKNOWN,
+        NULL, NULL, 0,
+        RIL_PINSTATE_ENABLED_BLOCKED,
+        RIL_PINSTATE_UNKNOWN
+    },
+    /* SIM_PERM_BLOCKED = 14 */
+    {
+        RIL_APPTYPE_SIM, RIL_APPSTATE_UNKNOWN,
+        RIL_PERSOSUBSTATE_UNKNOWN,
+        NULL, NULL, 0,
+        RIL_PINSTATE_ENABLED_PERM_BLOCKED,
+        RIL_PINSTATE_UNKNOWN
+    },
+    /* SIM_NETWORK_PERSO_PUK = 15 */
+    {
+        RIL_APPTYPE_SIM, RIL_APPSTATE_SUBSCRIPTION_PERSO,
+        RIL_PERSOSUBSTATE_SIM_NETWORK_PUK,
+        NULL, NULL, 0,
+        RIL_PINSTATE_ENABLED_NOT_VERIFIED,
+        RIL_PINSTATE_UNKNOWN
+    },
+    /* SIM_NETWORK_SUBSET_PERSO_PUK = 16 */
+    {
+        RIL_APPTYPE_SIM, RIL_APPSTATE_SUBSCRIPTION_PERSO,
+        RIL_PERSOSUBSTATE_SIM_NETWORK_SUBSET_PUK,
+        NULL, NULL, 0,
+        RIL_PINSTATE_ENABLED_NOT_VERIFIED,
+        RIL_PINSTATE_UNKNOWN
+    },
+    /* SIM_SERVICE_PROVIDER_PERSO_PUK = 17 */
+    {
+        RIL_APPTYPE_SIM, RIL_APPSTATE_SUBSCRIPTION_PERSO,
+        RIL_PERSOSUBSTATE_SIM_SERVICE_PROVIDER_PUK,
+        NULL, NULL, 0,
+        RIL_PINSTATE_ENABLED_NOT_VERIFIED,
+        RIL_PINSTATE_UNKNOWN
+    },
+    /* SIM_CORPORATE_PERSO_PUK = 18 */
+    {
+        RIL_APPTYPE_SIM, RIL_APPSTATE_SUBSCRIPTION_PERSO,
+        RIL_PERSOSUBSTATE_SIM_CORPORATE_PUK,
+        NULL, NULL, 0,
+        RIL_PINSTATE_ENABLED_NOT_VERIFIED,
+        RIL_PINSTATE_UNKNOWN
+    },
+    /* SIM_SIM_PERSO_PUK = 19 */
+    {
+        RIL_APPTYPE_SIM, RIL_APPSTATE_SUBSCRIPTION_PERSO,
+        RIL_PERSOSUBSTATE_SIM_SIM_PUK,
+        NULL, NULL, 0,
+        RIL_PINSTATE_ENABLED_NOT_VERIFIED,
+        RIL_PINSTATE_UNKNOWN
+    },
+    /* SIM_PUK2_PERM_BLOCKED = 20 */
+    {
+        RIL_APPTYPE_SIM, RIL_APPSTATE_UNKNOWN,
+        RIL_PERSOSUBSTATE_UNKNOWN,
+        NULL, NULL, 0,
+        RIL_PINSTATE_UNKNOWN,
+        RIL_PINSTATE_ENABLED_PERM_BLOCKED
+    }
+};
+
+
+/**
+ * Get the current card status.
+ *
+ * This must be freed using freeCardStatus.
+ * @return: On success returns RIL_E_SUCCESS.
+ */
+static int getCardStatus(RIL_CardStatus_v6 **pp_card_status)
+{
+    RIL_CardState card_state;
+    int num_apps;
+
+    SIM_Status sim_status = getSIMStatus();
+    if (sim_status == SIM_ABSENT) {
+        card_state = RIL_CARDSTATE_ABSENT;
+        num_apps = 0;
+    } else {
+        card_state = RIL_CARDSTATE_PRESENT;
+        num_apps = 1;
+    }
+
+    /* Allocate and initialize base card status. */
+    RIL_CardStatus_v6 *p_card_status = (RIL_CardStatus_v6 *) malloc(sizeof(RIL_CardStatus_v6));
+    p_card_status->card_state = card_state;
+    p_card_status->universal_pin_state = RIL_PINSTATE_UNKNOWN;
+    p_card_status->gsm_umts_subscription_app_index = RIL_CARD_MAX_APPS;
+    p_card_status->cdma_subscription_app_index = RIL_CARD_MAX_APPS;
+    p_card_status->ims_subscription_app_index = RIL_CARD_MAX_APPS;
+    p_card_status->num_applications = num_apps;
+
+    /* Initialize application status. */
+    int i;
+    for (i = 0; i < RIL_CARD_MAX_APPS; i++)
+        p_card_status->applications[i] = app_status_array[SIM_ABSENT];
+
+    /* Pickup the appropriate application status
+       that reflects sim_status for gsm. */
+    if (num_apps != 0) {
+        UICC_Type uicc_type = getUICCType();
+
+        /* Only support one app, gsm/wcdma. */
+        p_card_status->num_applications = 1;
+        p_card_status->gsm_umts_subscription_app_index = 0;
+
+        /* Get the correct app status. */
+        p_card_status->applications[0] = app_status_array[sim_status];
+        if (uicc_type == UICC_TYPE_SIM)
+            ALOGI("[Card type discovery]: Legacy SIM");
+        else { /* defaulting to USIM */
+            ALOGI("[Card type discovery]: USIM");
+            p_card_status->applications[0].app_type = RIL_APPTYPE_USIM;
+        }
+    }
+
+    *pp_card_status = p_card_status;
+    return RIL_E_SUCCESS;
+}
+
+/**
+ * Free the card status returned by getCardStatus.
+ */
+static void freeCardStatus(RIL_CardStatus_v6 *p_card_status) {
+    free(p_card_status);
+}
+
+/**
+ * RIL_REQUEST_GET_SIM_STATUS
+ *
+ * Requests status of the SIM interface and the SIM card.
+ *
+ * Valid errors:
+ *  Must never fail.
+ */
+static void requestGetSimStatus(RIL_Token t)
+{
+    RIL_CardStatus_v6* p_card_status = NULL;
+
+    if (getCardStatus(&p_card_status) != RIL_E_SUCCESS)
+        goto error;
+
+    RIL_onRequestComplete(t, RIL_E_SUCCESS, (char*)p_card_status, sizeof(*p_card_status));
+
+finally:
+    if (p_card_status != NULL)
+        freeCardStatus(p_card_status);
+    return;
+
+error:
+    RIL_onRequestComplete(t, RIL_E_GENERIC_FAILURE, NULL, 0);
+    goto finally;
+}
+
+
+
+/**
+ * RIL_REQUEST_CHANGE_SIM_PIN
+ * RIL_REQUEST_CHANGE_SIM_PIN2
+*/
+static void requestChangePassword(void *data, size_t datalen, RIL_Token t,
+                           const char *facility, int request)
+{
+    int err = 0;
+    char *oldPassword = NULL;
+    char *newPassword = NULL;
+    int num_retries = -1;
+
+    if (datalen != 3 * sizeof(char *) || strlen(facility) != 2)
+        goto error;
+
+
+    oldPassword = ((char **) data)[0];
+    newPassword = ((char **) data)[1];
+
+    err = at_send_command("AT+CPWD=\"%s\",\"%s\",\"%s\"", facility,
+                oldPassword, newPassword);
+    if (err != AT_NOERROR)
+        goto error;
+
+    num_retries = 3;
+    RIL_onRequestComplete(t, RIL_E_SUCCESS, &num_retries, sizeof(int *));
+
+    return;
+
+error:
+    if (at_get_cme_error(err) == CME_INCORRECT_PASSWORD) {
+        num_retries = 3;
+        RIL_onRequestComplete(t, RIL_E_PASSWORD_INCORRECT, &num_retries, sizeof(int *));
+    } else {
+        RIL_onRequestComplete(t, RIL_E_GENERIC_FAILURE, NULL, 0);
+    }
+}
+
+
+/**
+ * RIL_REQUEST_QUERY_FACILITY_LOCK
+ *
+ * Query the status of a facility lock state.
+ */
+static void requestQueryFacilityLock(void *data, size_t datalen, RIL_Token t)
+{
+    int err, response;
+    ATResponse *atResponse = NULL;
+    char *line = NULL;
+    char *facility_string = NULL;
+    char *facility_password = NULL;
+    char *facility_class = NULL;
+
+    (void) datalen;
+
+    if (datalen < 3 * sizeof(char **)) {
+        ALOGE("%s() bad data length!", __func__);
+        goto error;
+    }
+
+    facility_string = ((char **) data)[0];
+    facility_password = ((char **) data)[1];
+    facility_class = ((char **) data)[2];
+
+    err = at_send_command_singleline("AT+CLCK=\"%s\",2,%s,%s", "+CLCK:", &atResponse,
+            facility_string, facility_password, facility_class);
+    if (err != AT_NOERROR)
+        goto error;
+
+    line = atResponse->p_intermediates->line;
+
+    err = at_tok_start(&line);
+
+    if (err < 0)
+        goto error;
+
+    err = at_tok_nextint(&line, &response);
+
+    if (err < 0)
+        goto error;
+
+    RIL_onRequestComplete(t, RIL_E_SUCCESS, &response, sizeof(int));
+    at_response_free(atResponse);
+    return;
+
+error:
+    RIL_onRequestComplete(t, RIL_E_GENERIC_FAILURE, NULL, 0);
+    at_response_free(atResponse);
+}
+
+
+/**
+ * RIL_REQUEST_SET_FACILITY_LOCK
+ *
+ * Enable/disable one facility lock.
+ */
+static void requestSetFacilityLock(void *data, size_t datalen, RIL_Token t)
+{
+    int err;
+    char *facility_string = NULL;
+    int facility_mode = -1;
+    char *facility_mode_str = NULL;
+    char *facility_password = NULL;
+    char *facility_class = NULL;
+    int num_retries = -1;
+    RIL_Errno errorril = RIL_E_GENERIC_FAILURE;
+    (void) datalen;
+
+    if (datalen < 4 * sizeof(char **)) {
+        ALOGE("%s() bad data length!", __func__);
+        goto exit;
+    }
+
+    facility_string = ((char **) data)[0];
+    facility_mode_str = ((char **) data)[1];
+    facility_password = ((char **) data)[2];
+    facility_class = ((char **) data)[3];
+
+    if (*facility_mode_str != '0' && *facility_mode_str != '1') {
+        ALOGE("%s() bad facility mode!", __func__);
+        goto exit;
+    }
+
+    facility_mode = atoi(facility_mode_str);
+
+    /*
+     * Skip adding facility_password to AT command parameters if it is NULL,
+     * printing NULL with %s will give string "(null)".
+     */
+    err = at_send_command("AT+CLCK=\"%s\",%d,\"%s\",%s", facility_string,
+            facility_mode, facility_password, facility_class);
+
+    if (at_get_error_type(err) == AT_ERROR)
+        goto exit;
+    if (err != AT_NOERROR) {
+        switch (at_get_cme_error(err)) {
+        /* CME ERROR 11: "SIM PIN required" happens when PIN is wrong */
+        case CME_SIM_PIN_REQUIRED:
+            ALOGI("Wrong PIN");
+            errorril = RIL_E_PASSWORD_INCORRECT;
+            break;
+        /*
+         * CME ERROR 12: "SIM PUK required" happens when wrong PIN is used
+         * 3 times in a row
+         */
+        case CME_SIM_PUK_REQUIRED:
+            ALOGI("PIN locked, change PIN with PUK");
+            num_retries = 0;/* PUK required */
+            errorril = RIL_E_PASSWORD_INCORRECT;
+            break;
+        /* CME ERROR 16: "Incorrect password" happens when PIN is wrong */
+        case CME_INCORRECT_PASSWORD:
+            ALOGI("Incorrect password, Facility: %s", facility_string);
+            errorril = RIL_E_PASSWORD_INCORRECT;
+            break;
+        /* CME ERROR 17: "SIM PIN2 required" happens when PIN2 is wrong */
+        case CME_SIM_PIN2_REQUIRED:
+            ALOGI("Wrong PIN2");
+            errorril = RIL_E_PASSWORD_INCORRECT;
+            break;
+        /*
+         * CME ERROR 18: "SIM PUK2 required" happens when wrong PIN2 is used
+         * 3 times in a row
+         */
+        case CME_SIM_PUK2_REQUIRED:
+            ALOGI("PIN2 locked, change PIN2 with PUK2");
+            num_retries = 0;/* PUK2 required */
+            errorril = RIL_E_SIM_PUK2;
+            break;
+        default: /* some other error */
+            num_retries = -1;
+            break;
+        }
+        goto finally;
+    }
+
+    errorril = RIL_E_SUCCESS;
+
+finally:
+    if (strncmp(facility_string, "SC", 2) == 0)
+        num_retries = 1;
+    else if  (strncmp(facility_string, "FD", 2) == 0)
+        num_retries = 1;
+exit:
+    RIL_onRequestComplete(t, errorril, &num_retries,  sizeof(int *));
+}
+
+
+
+/**
+ * Enter SIM PIN, might be PIN, PIN2, PUK, PUK2, etc.
+ *
+ * Data can hold pointers to one or two strings, depending on what we
+ * want to enter. (PUK requires new PIN, etc.).
+ *
+ * FIXME: Do we need to return remaining tries left on error as well?
+ *        Also applies to the rest of the requests that got the retries
+ *        in later commits to ril.h.
+ */
+static void requestEnterSimPin(void *data, size_t datalen, RIL_Token t, int request)
+{
+    int err = 0;
+    int cme_err;
+    const char **strings = (const char **) data;
+    int num_retries = -1;
+
+    if (datalen == sizeof(char *)) {
+        err = at_send_command("AT+CPIN=\"%s\"", strings[0]);
+    } else if (datalen == 2 * sizeof(char *)) {
+        if (!strings[1]){
+            err = at_send_command("AT+CPIN=\"%s\"", strings[0]);
+        } else {
+            err = at_send_command("AT+CPIN=\"%s\",\"%s\"", strings[0], strings[1]);
+        }
+    } else if (datalen == 3 * sizeof(char *)) {
+            err = at_send_command("AT+CPIN=\"%s\",\"%s\"", strings[0], strings[1]);
+    } else {
+        goto error;
+    }
+
+    cme_err = at_get_cme_error(err);
+
+    if (cme_err != CME_ERROR_NON_CME && err != AT_NOERROR) {
+        switch (cme_err) {
+        case CME_SIM_PIN_REQUIRED:
+        case CME_SIM_PUK_REQUIRED:
+        case CME_INCORRECT_PASSWORD:
+        case CME_SIM_PIN2_REQUIRED:
+        case CME_SIM_PUK2_REQUIRED:
+            num_retries = 1;
+            RIL_onRequestComplete(t, RIL_E_PASSWORD_INCORRECT, &num_retries, sizeof(int *));
+            break;
+        default:
+            goto error;
+        }
+    } else {
+        /*
+         * Got OK, return success and wait for *EPEV to trigger poll
+         * of SIM state.
+         */
+
+        num_retries = 1;
+        RIL_onRequestComplete(t, RIL_E_SUCCESS, &num_retries, sizeof(int *));
+    }
+    return;
+error:
+    RIL_onRequestComplete(t, RIL_E_GENERIC_FAILURE, NULL, 0);
+}
+
+
+/**
+ * RIL_REQUEST_QUERY_NETWORK_SELECTION_MODE
+ *
+ * Query current network selectin mode.
+ */
+static void requestQueryNetworkSelectionMode(RIL_Token t)
+{
+    int err;
+    ATResponse *atResponse = NULL;
+    int response = 0;
+    char *line;
+
+    err = at_send_command_singleline("AT+COPS?", "+COPS:", &atResponse);
+
+    if (err != AT_NOERROR)
+        goto error;
+
+    line = atResponse->p_intermediates->line;
+
+    err = at_tok_start(&line);
+
+    if (err < 0)
+        goto error;
+
+    err = at_tok_nextint(&line, &response);
+
+    if (err < 0)
+        goto error;
+
+    /*
+     * Android accepts 0(automatic) and 1(manual).
+     * Modem may return mode 4(Manual/automatic).
+     * Convert it to 1(Manual) as android expects.
+     */
+    if (response == 4)
+        response = 1;
+
+    RIL_onRequestComplete(t, RIL_E_SUCCESS, &response, sizeof(int));
+
+finally:
+    at_response_free(atResponse);
+    return;
+
+error:
+    ALOGE("%s() Must never return error when radio is on", __func__);
+    RIL_onRequestComplete(t, RIL_E_GENERIC_FAILURE, NULL, 0);
+    goto finally;
+}
+
+
+struct operatorPollParams {
+    RIL_Token t;
+    int loopcount;
+};
+
+
+#define REPOLL_OPERATOR_SELECTED 30     /* 30 * 2 = 1M = ok? */
+static const struct timespec TIMEVAL_OPERATOR_SELECT_POLL = { 2, 0 };
+
+/*
+ * s_registrationDeniedReason is used to keep track of registration deny
+ * reason for which is called by pollOperatorSelected from
+ * RIL_REQUEST_SET_NETWORK_SELECTION_AUTOMATIC, so that in case
+ * of invalid SIM/ME, Android will not continuously poll for operator.
+ *
+ */
+static Reg_Deny_DetailReason s_registrationDeniedReason = DEFAULT_VALUE;
+
+
+/**
+ * Poll +COPS? and return a success, or if the loop counter reaches
+ * REPOLL_OPERATOR_SELECTED, return generic failure.
+ */
+static void pollOperatorSelected(void *params)
+{
+    int err = 0;
+    int response = 0;
+    char *line = NULL;
+    ATResponse *atResponse = NULL;
+    struct operatorPollParams *poll_params;
+    RIL_Token t;
+
+    assert(params != NULL);
+
+    poll_params = (struct operatorPollParams *) params;
+    t = poll_params->t;
+
+    if (poll_params->loopcount >= REPOLL_OPERATOR_SELECTED)
+        goto error;
+
+    err = at_send_command_singleline("AT+COPS?", "+COPS:", &atResponse);
+    if (err != AT_NOERROR)
+        goto error;
+
+    line = atResponse->p_intermediates->line;
+
+    err = at_tok_start(&line);
+    if (err < 0)
+        goto error;
+
+    err = at_tok_nextint(&line, &response);
+    if (err < 0)
+        goto error;
+
+    /* If we don't get more than the COPS: {0-4} we are not registered.
+       Loop and try again. */
+    if (!at_tok_hasmore(&line)) {
+        switch (s_registrationDeniedReason) {
+        case IMSI_UNKNOWN_IN_HLR: /* fall through */
+        case ILLEGAL_ME:
+            RIL_onRequestComplete(t, RIL_E_ILLEGAL_SIM_OR_ME, NULL, 0);
+            free(poll_params);
+            break;
+        default:
+            poll_params->loopcount++;
+            enqueueRILEvent(pollOperatorSelected,
+                            poll_params, &TIMEVAL_OPERATOR_SELECT_POLL);
+        }
+    } else {
+        /* We got operator, throw a success! */
+        RIL_onRequestComplete(t, RIL_E_SUCCESS, NULL, 0);
+        free(poll_params);
+    }
+
+    at_response_free(atResponse);
+    return;
+
+error:
+    RIL_onRequestComplete(t, RIL_E_GENERIC_FAILURE, NULL, 0);
+    free(poll_params);
+    at_response_free(atResponse);
+    return;
+}
+
+/**
+ * RIL_REQUEST_SET_NETWORK_SELECTION_AUTOMATIC
+ *
+ * Specify that the network should be selected automatically.
+*/
+static void requestSetNetworkSelectionAutomatic(RIL_Token t)
+{
+    int err = 0;
+    ATResponse *atResponse = NULL;
+    int mode = 0;
+    int skip;
+    char *line;
+    char *netOperator = NULL;
+    struct operatorPollParams *poll_params = NULL;
+
+    poll_params = (struct operatorPollParams*)
+                    malloc(sizeof(struct operatorPollParams));
+    if (NULL == poll_params)
+        goto error;
+
+    /* First check if we are already scanning or in manual mode */
+    err = at_send_command_singleline("AT+COPS=3,2;+COPS?", "+COPS:", &atResponse);
+    if (err != AT_NOERROR)
+        goto error;
+
+    line = atResponse->p_intermediates->line;
+
+    err = at_tok_start(&line);
+    if (err < 0)
+        goto error;
+
+    /* Read network selection mode */
+    err = at_tok_nextint(&line, &mode);
+    if (err < 0)
+        goto error;
+
+    /* If we're unregistered, we may just get
+       a "+COPS: 0" response. */
+    if (!at_tok_hasmore(&line)) {
+        if (mode == 1) {
+            D("%s() Changing manual to automatic network mode", __func__);
+            goto do_auto;
+        } else
+            goto check_reg;
+    }
+
+    err = at_tok_nextint(&line, &skip);
+    if (err < 0)
+        goto error;
+
+    /* A "+COPS: 0, n" response is also possible. */
+    if (!at_tok_hasmore(&line)) {
+        if (mode == 1) {
+            D("%s() Changing manual to automatic network mode", __func__);
+            goto do_auto;
+        } else
+            goto check_reg;
+    }
+
+    /* Read numeric operator */
+    err = at_tok_nextstr(&line, &netOperator);
+    if (err < 0)
+        goto error;
+
+    /* If operator is found then do a new scan,
+       else let it continue the already pending scan */
+    if (netOperator && strlen(netOperator) == 0) {
+        if (mode == 1) {
+            D("%s() Changing manual to automatic network mode", __func__);
+            goto do_auto;
+        } else
+            goto check_reg;
+    }
+
+    /* Operator found */
+    if (mode == 1) {
+        D("%s() Changing manual to automatic network mode", __func__);
+        goto do_auto;
+    } else {
+        D("%s() Already in automatic mode with known operator, trigger a new network scan",
+        __func__);
+        goto do_auto;
+    }
+
+    /* Check if module is scanning,
+       if not then trigger a rescan */
+check_reg:
+    at_response_free(atResponse);
+    atResponse = NULL;
+
+    /* Check CS domain first */
+    err = at_send_command_singleline("AT+CREG?", "+CREG:", &atResponse);
+    if (err != AT_NOERROR)
+        goto error;
+
+    line = atResponse->p_intermediates->line;
+
+    err = at_tok_start(&line);
+    if (err < 0)
+        goto error;
+
+    /* Read registration unsolicited mode */
+    err = at_tok_nextint(&line, &mode);
+    if (err < 0)
+        goto error;
+
+    /* Read registration status */
+    err = at_tok_nextint(&line, &mode);
+    if (err < 0)
+        goto error;
+
+    /* If scanning has stopped, then perform a new scan */
+    if (mode == 0) {
+        D("%s() Already in automatic mode, but not currently scanning on CS,"
+         "trigger a new network scan", __func__);
+        goto do_auto;
+    }
+
+    /* Now check PS domain */
+    at_response_free(atResponse);
+    atResponse = NULL;
+    err = at_send_command_singleline("AT+CGREG?", "+CGREG:", &atResponse);
+    if (err != AT_NOERROR)
+        goto error;
+
+    line = atResponse->p_intermediates->line;
+
+    err = at_tok_start(&line);
+    if (err < 0)
+        goto error;
+
+    /* Read registration unsolicited mode */
+    err = at_tok_nextint(&line, &mode);
+    if (err < 0)
+        goto error;
+
+    /* Read registration status */
+    err = at_tok_nextint(&line, &mode);
+    if (err < 0)
+        goto error;
+
+    /* If scanning has stopped, then perform a new scan */
+    if (mode == 0) {
+        D("%s() Already in automatic mode, but not currently scanning on PS,"
+         "trigger a new network scan", __func__);
+        goto do_auto;
+    } else {
+        D("%s() Already in automatic mode and scanning", __func__);
+        goto finish_scan;
+    }
+
+do_auto:
+    at_response_free(atResponse);
+    atResponse = NULL;
+
+    /* This command does two things, one it sets automatic mode,
+       two it starts a new network scan! */
+    err = at_send_command("AT+COPS=0");
+    if (err != AT_NOERROR)
+        goto error;
+
+finish_scan:
+
+    at_response_free(atResponse);
+    atResponse = NULL;
+
+    poll_params->loopcount = 0;
+    poll_params->t = t;
+
+    enqueueRILEvent(pollOperatorSelected,
+                    poll_params, &TIMEVAL_OPERATOR_SELECT_POLL);
+
+    return;
+
+error:
+    free(poll_params);
+    at_response_free(atResponse);
+    RIL_onRequestComplete(t, RIL_E_GENERIC_FAILURE, NULL, 0);
+    return;
+}
+
+
+/**
+ * RIL_REQUEST_SET_NETWORK_SELECTION_MANUAL
+ *
+ * Manually select a specified network.
+ *
+ * The radio baseband/RIL implementation is expected to fall back to
+ * automatic selection mode if the manually selected network should go
+ * out of range in the future.
+ */
+void requestSetNetworkSelectionManual(void *data, size_t datalen,
+                                      RIL_Token t)
+{
+    /*
+     * AT+COPS=[<mode>[,<format>[,<oper>[,<AcT>]]]]
+     *    <mode>   = 4 = Manual (<oper> field shall be present and AcT optionally) with fallback to automatic if manual fails.
+     *    <format> = 2 = Numeric <oper>, the number has structure:
+     *                   (country code digit 3)(country code digit 2)(country code digit 1)
+     *                   (network code digit 2)(network code digit 1)
+     */
+
+    (void) datalen;
+    int err = 0;
+    const char *mccMnc = (const char *) data;
+
+    /* Check inparameter. */
+    if (mccMnc == NULL)
+        goto error;
+
+    /* Build and send command. */
+    err = at_send_command("AT+COPS=1,2,\"%s\"", mccMnc);
+    if (err != AT_NOERROR) {
+        err = at_send_command("AT+COPS=0");
+        if(err != AT_NOERROR)
+            goto error;
+    }
+
+    RIL_onRequestComplete(t, RIL_E_SUCCESS, NULL, 0);
+    return;
+
+error:
+    RIL_onRequestComplete(t, RIL_E_GENERIC_FAILURE, NULL, 0);
+}
+
+
+
+/**
+ * RIL_REQUEST_QUERY_AVAILABLE_NETWORKS
+ *
+ * Scans for available networks.
+*/
+static void requestQueryAvailableNetworks(RIL_Token t)
+{
+    #define QUERY_NW_NUM_PARAMS 4
+
+    /*
+     * AT+COPS=?
+     *   +COPS: [list of supported (<stat>,long alphanumeric <oper>
+     *           ,short alphanumeric <oper>,numeric <oper>[,<AcT>])s]
+     *          [,,(list of supported <mode>s),(list of supported <format>s)]
+     *
+     *   <stat>
+     *     0 = unknown
+     *     1 = available
+     *     2 = current
+     *     3 = forbidden
+     */
+    int err = 0;
+    ATResponse *atResponse = NULL;
+    const char *statusTable[] =
+        { "unknown", "available", "current", "forbidden" };
+    char **responseArray = NULL;
+    char *p;
+    int n = 0;
+    int i = 0;
+
+    err = at_send_command_multiline("AT+COPS=?", "+COPS:", &atResponse);
+    if (err != AT_NOERROR || atResponse->p_intermediates == NULL) {
+        ALOGE("Failed to get operator responses: err:%d, intermediates: %p",err,atResponse->p_intermediates);
+        goto error;
+    }
+
+    p = atResponse->p_intermediates->line;
+    ALOGD("Got line '%s'",p);
+
+    /* count number of '('. */
+    err = at_tok_charcounter(p, '(', &n);
+    if (err < 0) goto error;
+
+    /* We don't want to process the list of supported modes and the list of supported
+       commands, so just skip them */
+    if (n < 2) n = 0;
+    else n -= 2;
+
+    /* Allocate array of strings, blocks of 4 strings. */
+    responseArray = (char **)alloca(n * QUERY_NW_NUM_PARAMS * sizeof(char *));
+
+    /* Loop and collect response information into the response array. */
+    for (i = 0; i < n; i++) {
+        int status = 0;
+        char *line = NULL;
+        char *s = NULL;
+        char *longAlphaNumeric = NULL;
+        char *shortAlphaNumeric = NULL;
+        char *numeric = NULL;
+        char *remaining = NULL;
+
+        s = line = getFirstElementValue(p, "(", ")", &remaining);
+        p = remaining;
+
+        if (line == NULL) {
+            ALOGE("%s() Null pointer while parsing COPS response."
+             "This should not happen.", __func__);
+            break;
+        }
+
+        ALOGD("%d operator: '%s'",i,line);
+
+        /* <stat> */
+        err = at_tok_nextint(&line, &status);
+        if (err < 0) {
+error2:
+            free(s);
+            goto error;
+        }
+
+        /* long alphanumeric <oper> */
+        err = at_tok_nextstr(&line, &longAlphaNumeric);
+        if (err < 0)
+            goto error2;
+
+        /* short alphanumeric <oper> */
+        err = at_tok_nextstr(&line, &shortAlphaNumeric);
+        if (err < 0)
+            goto error2;
+
+        /* numeric <oper> */
+        err = at_tok_nextstr(&line, &numeric);
+        if (err < 0)
+            goto error2;
+
+        responseArray[i * QUERY_NW_NUM_PARAMS + 0] = (char*) alloca(strlen(longAlphaNumeric) + 1);
+        strcpy(responseArray[i * QUERY_NW_NUM_PARAMS + 0], longAlphaNumeric);
+
+        responseArray[i * QUERY_NW_NUM_PARAMS + 1] = (char*) alloca(strlen(shortAlphaNumeric) + 1);
+        strcpy(responseArray[i * QUERY_NW_NUM_PARAMS + 1], shortAlphaNumeric);
+
+        responseArray[i * QUERY_NW_NUM_PARAMS + 2] = (char*) alloca(strlen(numeric) + 1);
+        strcpy(responseArray[i * QUERY_NW_NUM_PARAMS + 2], numeric);
+
+        free(s);
+
+        /*
+         * Check if modem returned an empty string, and fill it with MNC/MMC
+         * if that's the case.
+         */
+        if (responseArray[i * QUERY_NW_NUM_PARAMS + 0] && strlen(responseArray[i * QUERY_NW_NUM_PARAMS + 0]) == 0) {
+            responseArray[i * QUERY_NW_NUM_PARAMS + 0] = (char*) alloca(strlen(responseArray[i * QUERY_NW_NUM_PARAMS + 2]) + 1);
+            strcpy(responseArray[i * QUERY_NW_NUM_PARAMS + 0], responseArray[i * QUERY_NW_NUM_PARAMS + 2]);
+        }
+
+        if (responseArray[i * QUERY_NW_NUM_PARAMS + 1] && strlen(responseArray[i * QUERY_NW_NUM_PARAMS + 1]) == 0) {
+            responseArray[i * QUERY_NW_NUM_PARAMS + 1] = (char*) alloca(strlen(responseArray[i * QUERY_NW_NUM_PARAMS + 2]) + 1);
+            strcpy(responseArray[i * QUERY_NW_NUM_PARAMS + 1], responseArray[i * QUERY_NW_NUM_PARAMS + 2]);
+        }
+
+        /* Add status */
+        responseArray[i * QUERY_NW_NUM_PARAMS + 3] = (char*) alloca(strlen(statusTable[status])+1);
+        strcpy(responseArray[i * QUERY_NW_NUM_PARAMS + 3],statusTable[status]);
+    }
+
+    RIL_onRequestComplete(t, RIL_E_SUCCESS, responseArray,
+                          i * QUERY_NW_NUM_PARAMS * sizeof(char *));
+
+finally:
+    at_response_free(atResponse);
+    return;
+
+error:
+    RIL_onRequestComplete(t, RIL_E_GENERIC_FAILURE, NULL, 0);
+    goto finally;
+}
+
+
+/**
+ * RIL_REQUEST_SET_PREFERRED_NETWORK_TYPE
+ *
+ * Requests to set the preferred network type for searching and registering
+ * (CS/PS domain, RAT, and operation mode).
+ */
+static void requestSetPreferredNetworkType(void *data, size_t datalen, RIL_Token t)
+{
+    int rat,err;
+    const char* cmd;
+
+    assert (datalen >= sizeof(int *));
+    rat = ((int *)data)[0];
+
+    /* Huawei specific
+        AT^SYSCFG=2,1,3FFFFFFF,2,4 for GPRS/EDGE Preferred
+        AT^SYSCFG=2,2,3FFFFFFF,2,4 for 3G Preferred
+        AT^SYSCFG=13,1,3FFFFFFF,2,4 for GPRS/EDGE Only
+        AT^SYSCFG=14,2,3FFFFFFF,2,4 for 3G Only
+
+        The third parameter, 0x3FFFFFFF tells the card to use all bands.
+        A value of 0x400380 here means GSM900/1800/WCDMA2100 only and a
+        value of 0x200000 here means GSM1900 only.
+
+    */
+
+    switch (rat) {
+        case PREF_NET_TYPE_GSM_ONLY:  /* GSM only */
+            cmd = "AT^SYSCFG=13,1,3FFFFFFF,2,4"; /* for GPRS/EDGE Only */
+            break;
+        case PREF_NET_TYPE_GSM_WCDMA: /* WCDMA only */
+            cmd = "AT^SYSCFG=14,2,3FFFFFFF,2,4"; /* for 3G Only */
+            break;
+        case PREF_NET_TYPE_GSM_WCDMA_AUTO:
+        default:    /* Dual Mode - WCDMA preferred*/
+            cmd = "AT^SYSCFG=2,2,3FFFFFFF,2,4";  /* for 3G Preferred */
+            break;
+    }
+
+    /* Set mode */
+    err = at_send_command(cmd);
+    if (err != AT_NOERROR)
+        goto error;
+
+
+    /* Trigger autoregister */
+    err = at_send_command("AT+COPS=0");
+    if (err != AT_NOERROR)
+        goto error;
+
+    RIL_onRequestComplete(t, RIL_E_SUCCESS, NULL, sizeof(int));
+    return;
+
+error:
+    ALOGE("ERROR: requestSetPreferredNetworkType() failed");
+    RIL_onRequestComplete(t, RIL_E_GENERIC_FAILURE, NULL, 0);
+}
+
+
+/**
+ * RIL_REQUEST_GET_PREFERRED_NETWORK_TYPE
+ *
+ * Query the preferred network type (CS/PS domain, RAT, and operation mode)
+ * for searching and registering.
+ */
+static void requestGetPreferredNetworkType(RIL_Token t)
+{
+    /*
+    AT^SYSCFG=2,1,3FFFFFFF,1,2 for GPRS/EDGE Preferred
+    AT^SYSCFG=2,2,3FFFFFFF,1,2 for 3G Preferred
+    AT^SYSCFG=13,1,3FFFFFFF,1,2 for GPRS/EDGE Only
+    AT^SYSCFG=14,2,3FFFFFFF,1,2 for 3G Only
+    */
+
+    ATResponse *atResponse = NULL;
+    int err;
+    char *line;
+    int ret1,ret2;
+    int response = PREF_NET_TYPE_GSM_WCDMA_AUTO;
+
+    err = at_send_command_singleline("AT^SYSCFG?", "^SYSCFG:", &atResponse);
+
+    if (err != AT_NOERROR) {
+        // assume radio is off
+        goto error;
+    }
+
+    line = atResponse->p_intermediates->line;
+
+    err = at_tok_start(&line);
+    if (err < 0) goto error;
+
+    err = at_tok_nextint(&line, &ret1);
+    if (err < 0) goto error;
+
+    err = at_tok_nextint(&line, &ret2);
+    if (err < 0) goto error;
+
+    /* Based on reported syscfg */
+    if (ret1 == 13) {
+        response = PREF_NET_TYPE_GSM_ONLY;  /* GSM only */
+    } else if (ret1 == 14) {
+        response = PREF_NET_TYPE_GSM_WCDMA; /* WCDMA only */
+    } else {
+        response = PREF_NET_TYPE_GSM_WCDMA_AUTO; /* for 3G Preferred */
+    }
+
+    D("requestGetPreferredNetworkType() mode:%d", response);
+    RIL_onRequestComplete(t, RIL_E_SUCCESS, &response, sizeof(int));
+    at_response_free(atResponse);
+    return;
+
+error:
+    ALOGE("ERROR: requestGetPreferredNetworkType() failed - modem does not support command");
+    RIL_onRequestComplete(t, RIL_E_GENERIC_FAILURE, NULL, 0);
+    at_response_free(atResponse);
+}
+
+/* +CGREG AcT values */
+enum CREG_AcT {
+    CGREG_ACT_GSM               = 0,
+    CGREG_ACT_GSM_COMPACT       = 1, /* Not Supported */
+    CGREG_ACT_UTRAN             = 2,
+    CGREG_ACT_GSM_EGPRS         = 3,
+    CGREG_ACT_UTRAN_HSDPA       = 4,
+    CGREG_ACT_UTRAN_HSUPA       = 5,
+    CGREG_ACT_UTRAN_HSUPA_HSDPA = 6
+};
+
+/* +CGREG stat values */
+enum CREG_stat {
+    CGREG_STAT_NOT_REG            = 0,
+    CGREG_STAT_REG_HOME_NET       = 1,
+    CGREG_STAT_NOT_REG_SEARCHING  = 2,
+    CGREG_STAT_REG_DENIED         = 3,
+    CGREG_STAT_UKNOWN             = 4,
+    CGREG_STAT_ROAMING            = 5
+};
+
+
+/**
+ * RIL_REQUEST_VOICE_REGISTRATION_STATE
+ *
+ * Request current registration state.
+ */
+static void requestRegistrationState(RIL_Token t)
+{
+    int err = 0;
+    const char resp_size = 15;
+    int response[15];
+    char *responseStr[15] = {0};
+    ATResponse *cgreg_resp = NULL;
+    char *line;
+    int commas = 0;
+    int skip, cs_status = 0;
+    int i;
+
+    /* Setting default values in case values are not returned by AT command */
+    for (i = 0; i < resp_size; i++)
+        responseStr[i] = NULL;
+
+    memset(response, 0, sizeof(response));
+
+    err = at_send_command_singleline("AT+CREG?", "+CREG:", &cgreg_resp);
+    if (err != AT_NOERROR)
+        goto error;
+
+    line = cgreg_resp->p_intermediates->line;
+
+    err = at_tok_start(&line);
+    if (err < 0)
+        goto error;
+
+    /*
+     * The solicited version of the CREG response is
+     * +CREG: n, stat, [lac, cid]
+     * and the unsolicited version is
+     * +CREG: stat, [lac, cid]
+     * The <n> parameter is basically "is unsolicited creg on?"
+     * which it should always be.
+     *
+     * Now we should normally get the solicited version here,
+     * but the unsolicited version could have snuck in
+     * so we have to handle both.
+     *
+     * Also since the LAC and CID are only reported when registered,
+     * we can have 1, 2, 3, or 4 arguments here.
+     *
+     * finally, a +CGREG: answer may have a fifth value that corresponds
+     * to the network type, as in;
+     *
+     *   +CGREG: n, stat [,lac, cid [,networkType]]
+     */
+
+    /* Count number of commas */
+    err = at_tok_charcounter(line, ',', &commas);
+
+    if (err < 0)
+        goto error;
+
+    switch (commas) {
+    case 0:                    /* +CREG: <stat> */
+        err = at_tok_nextint(&line, &response[0]);
+        if (err < 0)
+            goto error;
+
+        response[1] = -1;
+        response[2] = -1;
+        break;
+
+    case 1:                    /* +CREG: <n>, <stat> */
+        err = at_tok_nextint(&line, &skip);
+        if (err < 0)
+            goto error;
+
+        err = at_tok_nextint(&line, &response[0]);
+        if (err < 0)
+            goto error;
+
+        response[1] = -1;
+        response[2] = -1;
+        if (err < 0)
+            goto error;
+        break;
+    case 2:                    /* +CREG: <stat>, <lac>, <cid> */
+        err = at_tok_nextint(&line, &response[0]);
+        if (err < 0)
+            goto error;
+
+        err = at_tok_nexthexint(&line, &response[1]);
+        if (err < 0)
+            goto error;
+
+        err = at_tok_nexthexint(&line, &response[2]);
+        if (err < 0)
+            goto error;
+        break;
+    case 3:                    /* +CREG: <n>, <stat>, <lac>, <cid> */
+    case 4:                    /* +CREG: <n>, <stat>, <lac>, <cid>, <?> */
+        err = at_tok_nextint(&line, &skip);
+        if (err < 0)
+            goto error;
+
+        err = at_tok_nextint(&line, &response[0]);
+        if (err < 0)
+            goto error;
+
+        err = at_tok_nexthexint(&line, &response[1]);
+        if (err < 0)
+            goto error;
+
+        err = at_tok_nexthexint(&line, &response[2]);
+        if (err < 0)
+            goto error;
+        break;
+    default:
+        goto error;
+    }
+
+
+    asprintf(&responseStr[0], "%d", response[0]);
+
+    if (response[1] > 0)
+        asprintf(&responseStr[1], "%04x", response[1]);
+    if (response[2] > 0)
+        asprintf(&responseStr[2], "%08x", response[2]);
+
+    if (response[0] == CGREG_STAT_REG_HOME_NET ||
+        response[0] == CGREG_STAT_ROAMING)
+        responseStr[3] = (0);
+
+    RIL_onRequestComplete(t, RIL_E_SUCCESS, responseStr,
+                          resp_size * sizeof(char *));
+
+finally:
+
+    for (i = 0; i < resp_size; i++)
+        free(responseStr[i]);
+
+    at_response_free(cgreg_resp);
+    return;
+
+error:
+    ALOGE("%s() Must never return an error when radio is on", __func__);
+    RIL_onRequestComplete(t, RIL_E_GENERIC_FAILURE, NULL, 0);
+    goto finally;
+}
+
+
+/**
+ * RIL_REQUEST_DATA_REGISTRATION_STATE
+ *
+ * Request current GPRS registration state.
+ */
+static void requestGprsRegistrationState(RIL_Token t)
+{
+    int err = 0;
+    const char resp_size = 6;
+    int response[resp_size];
+    char *responseStr[resp_size];
+    ATResponse *atResponse = NULL;
+    char *line, *p;
+    int commas = 0;
+    int skip, tmp;
+    int count = 3;
+
+    memset(responseStr, 0, sizeof(responseStr));
+    memset(response, 0, sizeof(response));
+    response[1] = -1;
+    response[2] = -1;
+
+    err = at_send_command_singleline("AT+CGREG?", "+CGREG: ", &atResponse);
+    if (err != AT_NOERROR)
+        goto error;
+
+    line = atResponse->p_intermediates->line;
+    err = at_tok_start(&line);
+    if (err < 0)
+        goto error;
+    /*
+     * The solicited version of the +CGREG response is
+     * +CGREG: n, stat, [lac, cid [,<AcT>]]
+     * and the unsolicited version is
+     * +CGREG: stat, [lac, cid [,<AcT>]]
+     * The <n> parameter is basically "is unsolicited creg on?"
+     * which it should always be.
+     *
+     * Now we should normally get the solicited version here,
+     * but the unsolicited version could have snuck in
+     * so we have to handle both.
+     *
+     * Also since the LAC, CID and AcT are only reported when registered,
+     * we can have 1, 2, 3, 4 or 5 arguments here.
+     */
+    /* Count number of commas */
+    p = line;
+    err = at_tok_charcounter(line, ',', &commas);
+    if (err < 0) {
+        ALOGE("%s() at_tok_charcounter failed", __func__);
+        goto error;
+    }
+
+    switch (commas) {
+    case 0:                    /* +CGREG: <stat> */
+        err = at_tok_nextint(&line, &response[0]);
+        if (err < 0) goto error;
+        break;
+
+    case 1:                    /* +CGREG: <n>, <stat> */
+        err = at_tok_nextint(&line, &skip);
+        if (err < 0) goto error;
+        err = at_tok_nextint(&line, &response[0]);
+        if (err < 0) goto error;
+        break;
+
+    case 2:                    /* +CGREG: <stat>, <lac>, <cid> */
+        err = at_tok_nextint(&line, &response[0]);
+        if (err < 0) goto error;
+        err = at_tok_nexthexint(&line, &response[1]);
+        if (err < 0) goto error;
+        err = at_tok_nexthexint(&line, &response[2]);
+        if (err < 0) goto error;
+        break;
+
+    case 3:                    /* +CGREG: <n>, <stat>, <lac>, <cid> */
+                               /* +CGREG: <stat>, <lac>, <cid>, <AcT> */
+        err = at_tok_nextint(&line, &tmp);
+        if (err < 0) goto error;
+
+        /* We need to check if the second parameter is <lac> */
+        if (*(line) == '"') {
+            response[0] = tmp; /* <stat> */
+            err = at_tok_nexthexint(&line, &response[1]); /* <lac> */
+            if (err < 0) goto error;
+            err = at_tok_nexthexint(&line, &response[2]); /* <cid> */
+            if (err < 0) goto error;
+            err = at_tok_nextint(&line, &response[3]); /* <AcT> */
+            if (err < 0) goto error;
+            count = 4;
+        } else {
+            err = at_tok_nextint(&line, &response[0]); /* <stat> */
+            if (err < 0) goto error;
+            err = at_tok_nexthexint(&line, &response[1]); /* <lac> */
+            if (err < 0) goto error;
+            err = at_tok_nexthexint(&line, &response[2]); /* <cid> */
+            if (err < 0) goto error;
+        }
+        break;
+
+    case 4:                    /* +CGREG: <n>, <stat>, <lac>, <cid>, <AcT> */
+        err = at_tok_nextint(&line, &skip); /* <n> */
+        if (err < 0) goto error;
+        err = at_tok_nextint(&line, &response[0]); /* <stat> */
+        if (err < 0) goto error;
+        err = at_tok_nexthexint(&line, &response[1]); /* <lac> */
+        if (err < 0) goto error;
+        err = at_tok_nexthexint(&line, &response[2]); /* <cid> */
+        if (err < 0) goto error;
+        err = at_tok_nextint(&line, &response[3]); /* <AcT> */
+        if (err < 0) goto error;
+        count = 4;
+        break;
+
+    default:
+        ALOGE("%s() Invalid input", __func__);
+        goto error;
+    }
+
+    /* Converting to stringlist for Android */
+
+    asprintf(&responseStr[0], "%d", response[0]); /* state */
+
+    if (response[1] >= 0)
+        asprintf(&responseStr[1], "%04x", response[1]); /* LAC */
+    else
+        responseStr[1] = NULL;
+
+    if (response[2] >= 0)
+        asprintf(&responseStr[2], "%08x", response[2]); /* CID */
+    else
+        responseStr[2] = NULL;
+
+    if (response[0] == CGREG_STAT_REG_HOME_NET ||
+        response[0] == CGREG_STAT_ROAMING)
+        asprintf(&responseStr[3], "%d",response[3]);
+    else
+        responseStr[3] = NULL;
+
+    responseStr[5] = (char*) "1";
+
+    RIL_onRequestComplete(t, RIL_E_SUCCESS, responseStr, resp_size * sizeof(char *));
+
+finally:
+
+    if (responseStr[0])
+        free(responseStr[0]);
+    if (responseStr[1])
+        free(responseStr[1]);
+    if (responseStr[2])
+        free(responseStr[2]);
+    if (responseStr[3])
+        free(responseStr[3]);
+
+    at_response_free(atResponse);
+    return;
+
+error:
+    ALOGE("%s Must never return an error when radio is on", __func__);
+    RIL_onRequestComplete(t, RIL_E_GENERIC_FAILURE, NULL, 0);
+    goto finally;
+}
+
+
+/**
+ * RIL_REQUEST_OEM_HOOK_RAW
+ *
+ * This request reserved for OEM-specific uses. It passes raw byte arrays
+ * back and forth.
+*/
+static void requestOEMHookRaw(void *data, size_t datalen, RIL_Token t)
+{
+    RIL_onRequestComplete(t, RIL_E_SUCCESS, data, datalen);
+    return;
+}
+
+
+/**
+ * RIL_REQUEST_OEM_HOOK_STRINGS
+ *
+ * This request reserved for OEM-specific uses. It passes strings
+ * back and forth.
+ */
+static void requestOEMHookStrings(void * data, size_t datalen, RIL_Token t)
+{
+    int i;
+    const char **cur;
+    ATResponse *atResponse = NULL;
+    ATLine *atline;
+    int linecount;
+    int err;
+
+    D("%s() got OEM_HOOK_STRINGS: %8p %lu", __func__, data, (long) datalen);
+
+    for (i = (datalen / sizeof(char *)), cur = (const char **) data;
+         i > 0; cur++, i--) {
+        D("%s(): String: %s", __func__, *cur);
+    }
+
+    /* Only take the first string in the array for now */
+    cur = (const char **) data;
+    err = at_send_command_raw(*cur, &atResponse);
+
+    if ((err != AT_NOERROR && at_get_error_type(err) == AT_ERROR)
+            || atResponse == NULL || atResponse->finalResponse == NULL)
+        goto error;
+
+    /* Count number of lines including prefix, intermediate and final response */
+    linecount = 0;
+    atline = atResponse->p_intermediates;
+    while (atline != NULL && atline->line != NULL) {
+        linecount++;
+        atline = atline->p_next;
+    }
+    linecount++; /* for finalResponse */
+
+    /* Create RIL response */
+    if (linecount > 0) {
+        cur = (const char **) alloca(linecount * sizeof (char *));
+        if (cur != NULL) {
+            linecount = 0;
+            atline = atResponse->p_intermediates;
+            while (atline != NULL && atline->line != NULL) {
+                cur[linecount++] = atline->line;
+                atline = atline->p_next;
+            }
+            cur[linecount++] = atResponse->finalResponse;
+        } else
+            goto error;
+    }
+
+    RIL_onRequestComplete(t, RIL_E_SUCCESS, cur, linecount * sizeof(char *));
+    at_response_free(atResponse);
+    return;
+
+error:
+    RIL_onRequestComplete(t, RIL_E_GENERIC_FAILURE, NULL, 0);
+    at_response_free(atResponse);
+}
+
+
+static void unsolicitedNitzTime(const char * s)
+{
+    int err;
+    char * response = NULL;
+    char * line = NULL;
+    char * p = NULL;
+    char * tz = NULL; /* Timezone */
+    static char sNITZtime[64] = {0};
+
+    line = strdup(s);
+
+    /* Higher layers expect a NITZ string in this format:
+     *  08/10/28,19:08:37-20,1 (yy/mm/dd,hh:mm:ss(+/-)tz,dst)
+     */
+
+    if (strStartsWith(s,"+CTZV:")){
+
+        /* Get Time and Timezone data and store in static variable.
+         * Wait until DST is received to send response to upper layers
+         */
+        at_tok_start(&line);
+
+        err = at_tok_nextstr(&line, &tz);
+        if (err < 0) goto error;
+
+        err = at_tok_nextstr(&line, &response);
+        if (err < 0) goto error;
+
+        strcpy(sNITZtime,response);
+        strcat(sNITZtime,tz);
+
+        free(line);
+        return;
+
+    } else if (strStartsWith(s,"+CTZDST:")){
+
+        /* We got DST, now assemble the response and send to upper layers */
+        at_tok_start(&line);
+
+        err = at_tok_nextstr(&line, &tz);
+        if (err < 0) goto error;
+
+        asprintf(&response, "%s,%s", sNITZtime, tz);
+        RIL_onUnsolicitedResponse(RIL_UNSOL_NITZ_TIME_RECEIVED, response, strlen(response));
+        free(response);
+
+        free(line);
+        return;
+
+    }
+
+error:
+    ALOGE("Invalid NITZ line %s", s);
+}
+
+static void unsolicitedRSSI(const char * s)
+{
+    int err;
+    int rssi;
+    RIL_SignalStrength_v6 signalStrength;
+    char * line = NULL;
+
+    line = strdup(s);
+
+    err = at_tok_start(&line);
+    if (err < 0) goto error;
+
+    err = at_tok_nextint(&line, &rssi);
+    if (err < 0) goto error;
+
+    signalStrength.GW_SignalStrength.signalStrength = rssi;
+    signalStrength.GW_SignalStrength.bitErrorRate = 99;
+    signalStrength.CDMA_SignalStrength.dbm = 0;
+    signalStrength.CDMA_SignalStrength.ecio = 0;
+    signalStrength.EVDO_SignalStrength.dbm = 0;
+    signalStrength.EVDO_SignalStrength.ecio = 0;
+    signalStrength.EVDO_SignalStrength.signalNoiseRatio = 0;
+    signalStrength.LTE_SignalStrength.signalStrength = 0;
+    signalStrength.LTE_SignalStrength.rsrp = 0;
+    signalStrength.LTE_SignalStrength.rsrq = 0;
+    signalStrength.LTE_SignalStrength.rssnr = 0;
+    signalStrength.LTE_SignalStrength.cqi = 0;
+
+    ALOGI("Signal Strength %d", rssi);
+
+    RIL_onUnsolicitedResponse(RIL_UNSOL_SIGNAL_STRENGTH, &signalStrength, sizeof(signalStrength));
+    free(line);
+    return;
+
+error:
+    /* The notification was for a battery event - do not send a msg to upper layers */
+    ALOGI("Error getting Signal Strength");
+    free(line);
+    return;
+}
+
+static void unsolicitedMode(const char * s)
+{
+    int err;
+    int mode1;
+    int mode2;
+    char * line = NULL;
+
+    /*
+    ^MODE:3,2 indicates GPRS
+    ^MODE:3,3 indicates EDGE
+    ^MODE:5,4 indicates 3G
+    ^MODE:5,5 indicates HSDPA
+    */
+
+    line = strdup(s);
+
+    err = at_tok_start(&line);
+    if (err < 0) goto error;
+
+    err = at_tok_nextint(&line, &mode1);
+    if (err < 0) goto error;
+
+    err = at_tok_nextint(&line, &mode2);
+    if (err < 0) goto error;
+
+    free(line);
+    return;
+
+error:
+    ALOGI("Error getting mode");
+    free(line);
+    return;
+}
+
+
+/**
+ * RIL_REQUEST_SIGNAL_STRENGTH
+ *
+ * Requests current signal strength and bit error rate.
+ *
+ * Must succeed if radio is on.
+ */
+static void requestSignalStrength(RIL_Token t)
+{
+    RIL_SignalStrength_v6 signalStrength;
+    ATResponse *atResponse = NULL;
+    int err;
+    char *line;
+    int ber;
+    int rssi;
+
+    memset(&signalStrength, 0, sizeof(RIL_SignalStrength_v6));
+
+    signalStrength.LTE_SignalStrength.signalStrength = 0x7FFFFFFF;
+    signalStrength.LTE_SignalStrength.rsrp = 0x7FFFFFFF;
+    signalStrength.LTE_SignalStrength.rsrq = 0x7FFFFFFF;
+    signalStrength.LTE_SignalStrength.rssnr = 0x7FFFFFFF;
+    signalStrength.LTE_SignalStrength.cqi = 0x7FFFFFFF;
+
+    err = at_send_command_singleline("AT+CSQ", "+CSQ:", &atResponse);
+    if (err != AT_NOERROR)
+        goto error;
+
+    line = atResponse->p_intermediates->line;
+
+    err = at_tok_start(&line);
+    if (err < 0) goto error;
+
+    err = at_tok_nextint(&line,&rssi);
+    if (err < 0) goto error;
+
+    signalStrength.GW_SignalStrength.signalStrength = rssi;
+
+    err = at_tok_nextint(&line, &ber);
+    if (err < 0)
+        goto error;
+
+    signalStrength.GW_SignalStrength.bitErrorRate = ber;
+
+    signalStrength.CDMA_SignalStrength.dbm = 0;
+    signalStrength.CDMA_SignalStrength.ecio = 0;
+    signalStrength.EVDO_SignalStrength.dbm = 0;
+    signalStrength.EVDO_SignalStrength.ecio = 0;
+    signalStrength.EVDO_SignalStrength.signalNoiseRatio = 0;
+    signalStrength.LTE_SignalStrength.signalStrength = 0;
+    signalStrength.LTE_SignalStrength.rsrp = 0;
+    signalStrength.LTE_SignalStrength.rsrq = 0;
+    signalStrength.LTE_SignalStrength.rssnr = 0;
+    signalStrength.LTE_SignalStrength.cqi = 0;
+
+    ALOGI("SignalStrength %d BER: %d", rssi, ber);
+
+    RIL_onRequestComplete(t, RIL_E_SUCCESS, &signalStrength,
+                          sizeof(RIL_SignalStrength_v6));
+
+    at_response_free(atResponse);
+    atResponse = NULL;
+    return;
+
+error:
+    ALOGE("%s() Must never return an error when radio is on", __func__);
+    RIL_onRequestComplete(t, RIL_E_GENERIC_FAILURE, NULL, 0);
+    at_response_free(atResponse);
+    return;
+}
+
+
+
+/**
+ * RIL_REQUEST_OPERATOR
+ *
+ * Request current operator ONS or EONS.
+ */
+static void requestOperator(RIL_Token t)
+{
+    int err;
+    int i;
+    int skip;
+    ATLine *cursor;
+    static const int num_resp_lines = 3;
+    char *response[num_resp_lines];
+    ATResponse *atResponse = NULL;
+
+    memset(response, 0, sizeof(response));
+
+    err = at_send_command_multiline
+        ("AT+COPS=3,0;+COPS?;+COPS=3,1;+COPS?;+COPS=3,2;+COPS?", "+COPS:",
+         &atResponse);
+
+    if (err != AT_NOERROR)
+        goto error;
+
+    /* We expect 3 lines here:
+     * +COPS: 0,0,"T - Mobile"
+     * +COPS: 0,1,"TMO"
+     * +COPS: 0,2,"310170"
+     */
+    for (i = 0, cursor = atResponse->p_intermediates;
+         cursor != NULL && i < num_resp_lines;
+         cursor = cursor->p_next, i++) {
+        char *line = cursor->line;
+
+        err = at_tok_start(&line);
+
+        if (err < 0)
+            goto error;
+
+        err = at_tok_nextint(&line, &skip);
+
+        if (err < 0)
+            goto error;
+
+        /* If we're unregistered, we may just get
+           a "+COPS: 0" response. */
+        if (!at_tok_hasmore(&line)) {
+            response[i] = NULL;
+            continue;
+        }
+
+        err = at_tok_nextint(&line, &skip);
+
+        if (err < 0)
+            goto error;
+
+        /* A "+COPS: 0, n" response is also possible. */
+        if (!at_tok_hasmore(&line)) {
+            response[i] = NULL;
+            continue;
+        }
+
+        err = at_tok_nextstr(&line, &(response[i]));
+
+        if (err < 0)
+            goto error;
+    }
+
+    if (i != num_resp_lines)
+        goto error;
+
+    /*
+     * Check if modem returned an empty string, and fill it with MNC/MMC
+     * if that's the case.
+     */
+    if (response[2] && response[0] && strlen(response[0]) == 0) {
+        response[0] = (char*) alloca(strlen(response[2]) + 1);
+        strcpy(response[0], response[2]);
+    }
+
+    if (response[2] && response[1] && strlen(response[1]) == 0) {
+        response[1] = (char*) alloca(strlen(response[2]) + 1);
+        strcpy(response[1], response[2]);
+    }
+
+    RIL_onRequestComplete(t, RIL_E_SUCCESS, response, sizeof(response));
+
+finally:
+    at_response_free(atResponse);
+    return;
+
+error:
+    RIL_onRequestComplete(t, RIL_E_GENERIC_FAILURE, NULL, 0);
+    goto finally;
+}
+
+
+/**
+ * RIL_REQUEST_RADIO_POWER
+ *
+ * Toggle radio on and off (for "airplane" mode).
+*/
+static void requestRadioPower(void *data, size_t datalen, RIL_Token t)
+{
+    int onOff;
+    int err;
+    int restricted;
+
+    if (datalen < sizeof(int *)) {
+        ALOGE("%s() bad data length!", __func__);
+        goto error;
+    }
+
+    onOff = ((int *)data)[0];
+
+    if (onOff == 0 && getRadioState() != RADIO_STATE_OFF) {
+        char value[PROPERTY_VALUE_MAX];
+
+        /* Switch RF OFF */
+        err = at_send_command("AT^RFSWITCH=0");
+        if (err != AT_NOERROR)
+            goto error;
+
+        if (property_get("sys.shutdown.requested", value, NULL)) {
+            setRadioState(RADIO_STATE_UNAVAILABLE);
+        } else
+            setRadioState(RADIO_STATE_OFF);
+
+    } else if (onOff > 0 && getRadioState() == RADIO_STATE_OFF) {
+
+        /* Switch RF ON */
+        err = at_send_command("AT^RFSWITCH=1");
+        if (err != AT_NOERROR)
+            goto error;
+
+        if (err != AT_NOERROR) {
+            // Some stacks return an error when there is no SIM,
+            // but they really turn the RF portion on
+            // So, if we get an error, let's check to see if it
+            // turned on anyway
+
+            if (isRadioOn() != 1) {
+                goto error;
+            }
+        }
+        setRadioState(RADIO_STATE_SIM_NOT_READY);
+    }
+
+    RIL_onRequestComplete(t, RIL_E_SUCCESS, NULL, 0);
+
+    restricted = RIL_RESTRICTED_STATE_NONE;
+    RIL_onUnsolicitedResponse(RIL_UNSOL_RESTRICTED_STATE_CHANGED,
+                              &restricted, sizeof(int *));
+
+    return;
+
+error:
+    RIL_onRequestComplete(t, RIL_E_GENERIC_FAILURE, NULL, 0);
+}
+
+
+/**
+ * RIL_REQUEST_GET_IMSI
+*/
+static void requestGetIMSI(RIL_Token t)
+{
+    ATResponse *atResponse = NULL;
+    int err;
+
+    err = at_send_command_numeric("AT+CIMI", &atResponse);
+
+    if (err != AT_NOERROR)
+        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);
+}
+
+
+
+/**
+ * RIL_REQUEST_GET_IMEISV
+ *
+ * Get the device IMEISV, which should be two decimal digits.
+*/
+static void requestGetIMEISV(RIL_Token t)
+{
+    RIL_onRequestComplete(t, RIL_E_SUCCESS, (void*)"01", sizeof(char *));
+}
+
+
+
+/* RIL_REQUEST_DEVICE_IDENTITY
+ *
+ * Request the device ESN / MEID / IMEI / IMEISV.
+ *
+ */
+static void requestDeviceIdentity(RIL_Token t)
+{
+    ATResponse *atResponse = NULL;
+    char* response[4];
+    int err;
+
+    /* IMEI */
+    err = at_send_command_numeric("AT+CGSN", &atResponse);
+
+    if (err != AT_NOERROR)
+        goto error;
+
+    response[0] = atResponse->p_intermediates->line;
+
+    /* IMEISVN */
+    response[1] = (char*) "01";
+
+    /* CDMA not supported */
+    response[2] = (char*) "";
+    response[3] = (char*) "";
+
+    RIL_onRequestComplete(t, RIL_E_SUCCESS, &response, sizeof(response));
+
+    at_response_free(atResponse);
+    return;
+
+error:
+    RIL_onRequestComplete(t, RIL_E_GENERIC_FAILURE, NULL, 0);
+    at_response_free(atResponse);
+}
+
+
+
+/**
+ * RIL_REQUEST_BASEBAND_VERSION
+ *
+ * Return string value indicating baseband version, eg
+ * response from AT+CGMR.
+*/
+static void requestBasebandVersion(RIL_Token t)
+{
+    int err;
+    ATResponse *atResponse = NULL;
+    char *line;
+
+    err = at_send_command_singleline("AT+CGMR", "\0", &atResponse);
+
+    if (err != AT_NOERROR) {
+        ALOGE("%s() Error reading Base Band Version", __func__);
+        RIL_onRequestComplete(t, RIL_E_GENERIC_FAILURE, NULL, 0);
+        return;
+    }
+
+    line = atResponse->p_intermediates->line;
+
+    RIL_onRequestComplete(t, RIL_E_SUCCESS, line, sizeof(char *));
+
+    at_response_free(atResponse);
+}
+
+
+/**
+ * RIL_REQUEST_STK_SEND_TERMINAL_RESPONSE
+ *
+ * Requests to send a terminal response to SIM for a received
+ * proactive command.
+ */
+static void requestStkSendTerminalResponse(void * data, size_t datalen, RIL_Token t)
+{
+    (void)datalen;
+    const char *stkResponse = (const char *) data;
+    RIL_onRequestComplete(t, RIL_E_SUCCESS, NULL, 0);
+}
+
+
+/**
+ * RIL_REQUEST_STK_SEND_ENVELOPE_COMMAND
+ *
+ * Requests to send a SAT/USAT envelope command to SIM.
+ * The SAT/USAT envelope command refers to 3GPP TS 11.14 and 3GPP TS 31.111.
+ */
+static void requestStkSendEnvelopeCommand(void * data, size_t datalen, RIL_Token t)
+{
+    const char *ec = (const char *) data;
+    (void)datalen;
+    RIL_onRequestComplete(t, RIL_E_SUCCESS, (void*)"ok", sizeof(char *));
+}
+
+
+/**
+ * RIL_REQUEST_STK_GET_PROFILE
+ *
+ * Requests the profile of SIM tool kit.
+ * The profile indicates the SAT/USAT features supported by ME.
+ * The SAT/USAT features refer to 3GPP TS 11.14 and 3GPP TS 31.111.
+ */
+static void requestStkGetProfile(RIL_Token t)
+{
+    RIL_onRequestComplete(t, RIL_E_SUCCESS, (void*)"default", sizeof(char *));
+}
+
+
+/**
+ * RIL_REQUEST_STK_SET_PROFILE
+ *
+ * Download the STK terminal profile as part of SIM initialization
+ * procedure.
+ */
+static void requestStkSetProfile(void * data, size_t datalen, RIL_Token t)
+{
+    int err = 0;
+    int length = 0;
+    char *profile = NULL;
+
+    profile = (char *)data;
+    length = strlen(profile);
+
+    // Accept everyrhing
+    RIL_onRequestComplete(t, RIL_E_SUCCESS, NULL, 0);
+}
+
+
+/**
+ * RIL_REQUEST_REPORT_STK_SERVICE_IS_RUNNING
+ *
+ * Turn on STK unsol commands.
+ */
+void requestReportStkServiceIsRunning(RIL_Token t)
+{
+    RIL_onRequestComplete(t, RIL_E_SUCCESS, NULL, 0);
+}
+
+
+static void requestDial(void *data, size_t datalen, RIL_Token t)
+{
+    RIL_Dial *p_dial;
+    const char *clir;
+    int ret;
+
+    p_dial = (RIL_Dial *)data;
+
+    switch (p_dial->clir) {
+        case 1: clir = "I"; break;  /*invocation*/
+        case 2: clir = "i"; break;  /*suppression*/
+        default:
+        case 0: clir = ""; break;   /*subscription default*/
+    }
+
+    /* Originate call */
+    ret = at_send_command("ATD%s%s;", p_dial->address, clir);
+
+    /* success or failure is ignored by the upper layer here.
+       it will call GET_CURRENT_CALLS and determine success that way */
+    RIL_onRequestComplete(t, RIL_E_SUCCESS, NULL, 0);
+}
+
+
+static void requestHangup(void *data, size_t datalen, RIL_Token t)
+{
+    int *p_line;
+
+    int ret;
+
+    p_line = (int *)data;
+
+    // 3GPP 22.030 6.5.5
+    // "Releases a specific active call X"
+    ret = at_send_command("AT+CHLD=1%d", p_line[0]);
+
+    /* success or failure is ignored by the upper layer here.
+       it will call GET_CURRENT_CALLS and determine success that way */
+    RIL_onRequestComplete(t, RIL_E_SUCCESS, NULL, 0);
+}
+
+static void requestHangupWaitingOrBackground(RIL_Token t)
+{
+    // 3GPP 22.030 6.5.5
+    // "Releases all held calls or sets User Determined User Busy
+    //  (UDUB) for a waiting call."
+    at_send_command("AT+CHLD=0");
+
+    /* success or failure is ignored by the upper layer here.
+       it will call GET_CURRENT_CALLS and determine success that way */
+    RIL_onRequestComplete(t, RIL_E_SUCCESS, NULL, 0);
+}
+
+
+static void requestHangupForegroundResumeBackground(RIL_Token t)
+{
+    // 3GPP 22.030 6.5.5
+    // "Releases all active calls (if any exist) and accepts
+    //  the other (held or waiting) call."
+    at_send_command("AT+CHLD=1");
+
+    /* success or failure is ignored by the upper layer here.
+       it will call GET_CURRENT_CALLS and determine success that way */
+    RIL_onRequestComplete(t, RIL_E_SUCCESS, NULL, 0);
+}
+
+
+static void requestSwitchWaitingOrHoldingAndActive(RIL_Token t)
+{
+    // 3GPP 22.030 6.5.5
+    // "Places all active calls (if any exist) on hold and accepts
+    //  the other (held or waiting) call."
+    at_send_command("AT+CHLD=2");
+
+    /* success or failure is ignored by the upper layer here.
+       it will call GET_CURRENT_CALLS and determine success that way */
+    RIL_onRequestComplete(t, RIL_E_SUCCESS, NULL, 0);
+}
+
+
+static void requestAnswer(RIL_Token t)
+{
+    at_send_command("ATA");
+
+    /* success or failure is ignored by the upper layer here.
+       it will call GET_CURRENT_CALLS and determine success that way */
+    RIL_onRequestComplete(t, RIL_E_SUCCESS, NULL, 0);
+}
+
+
+static void requestConference(RIL_Token t)
+{
+    // 3GPP 22.030 6.5.5
+    // "Adds a held call to the conversation"
+    at_send_command("AT+CHLD=3");
+
+    /* success or failure is ignored by the upper layer here.
+       it will call GET_CURRENT_CALLS and determine success that way */
+    RIL_onRequestComplete(t, RIL_E_SUCCESS, NULL, 0);
+}
+
+
+static void requestUDUB(RIL_Token t)
+{
+    /* user determined user busy */
+    /* sometimes used: ATH */
+    at_send_command("ATH");
+
+    /* success or failure is ignored by the upper layer here.
+       it will call GET_CURRENT_CALLS and determine success that way */
+    RIL_onRequestComplete(t, RIL_E_SUCCESS, NULL, 0);
+
+}
+
+
+static void requestSeparateConnection(void * data, size_t datalen, RIL_Token t)
+{
+    int party = ((int*)data)[0];
+
+    // Make sure that party is in a valid range.
+    // (Note: The Telephony middle layer imposes a range of 1 to 7.
+    // It's sufficient for us to just make sure it's single digit.)
+    if (party > 0 && party < 10) {
+        at_send_command("AT+CHLD=2%d", party);
+        RIL_onRequestComplete(t, RIL_E_SUCCESS, NULL, 0);
+    } else {
+        RIL_onRequestComplete(t, RIL_E_GENERIC_FAILURE, NULL, 0);
+    }
+}
+
+
+static void requestSetMute(void *data, size_t datalen, RIL_Token t)
+{
+    int err;
+    assert (datalen >= sizeof(int *));
+
+    /* mute */
+    err = at_send_command("AT+CMUT=%d", ((int*)data)[0]);
+    if (err != AT_NOERROR)
+        goto error;
+
+    RIL_onRequestComplete(t, RIL_E_SUCCESS, NULL, 0);
+    return;
+
+error:
+    ALOGE("ERROR: requestSetMute failed");
+    RIL_onRequestComplete(t, RIL_E_GENERIC_FAILURE, NULL, 0);
+
+}
+
+
+static void requestGetMute(RIL_Token t)
+{
+    int err;
+    ATResponse *atResponse = NULL;
+    int response[1];
+    char *line;
+
+    err = at_send_command_singleline("AT+CMUT?", "+CMUT:", &atResponse);
+
+    if (err != AT_NOERROR)
+        goto error;
+
+    line = atResponse->p_intermediates->line;
+
+    err = at_tok_start(&line);
+    if (err < 0) goto error;
+
+    err = at_tok_nextint(&line, &response[0]);
+    if (err < 0) goto error;
+
+    RIL_onRequestComplete(t, RIL_E_SUCCESS, response, sizeof(char*));
+    at_response_free(atResponse);
+
+    return;
+
+error:
+    ALOGE("ERROR: requestGetMute failed");
+    RIL_onRequestComplete(t, RIL_E_GENERIC_FAILURE, NULL, 0);
+    at_response_free(atResponse);
+}
+
+
+static void requestDTMF(void * data, size_t datalen, RIL_Token t)
+{
+    int err = 0;
+    char c = ((char *)data)[0];
+
+    err = at_send_command("AT+VTS=%c", (int)c);
+    if (err != AT_NOERROR) {
+        RIL_onRequestComplete(t, RIL_E_GENERIC_FAILURE, NULL, 0);
+    } else {
+        RIL_onRequestComplete(t, RIL_E_SUCCESS, NULL, 0);
+    }
+
+}
+
+
+static void requestDtmfStop(RIL_Token t)
+{
+    int err;
+
+    /* Send a command to cancel the DTMF tone*/
+    err = at_send_command("AT");
+    if (err != AT_NOERROR)
+        goto error;
+
+    RIL_onRequestComplete(t, RIL_E_SUCCESS, NULL, 0);
+    return;
+
+error:
+    ALOGE("ERROR: requestDtmfStop failed");
+    RIL_onRequestComplete(t, RIL_E_GENERIC_FAILURE, NULL, 0);
+
+}
+
+
+static void requestDtmfStart(void *data, size_t datalen, RIL_Token t)
+{
+    int err;
+    char c;
+
+    assert (datalen >= sizeof(char *));
+
+    c = ((char *)data)[0];
+
+    /* Start DTMF generation fro 250ms */
+    err = at_send_command("AT^DTMF=0,%c", (int)c);
+    if (err != AT_NOERROR)
+        goto error;
+
+    RIL_onRequestComplete(t, RIL_E_SUCCESS, NULL, 0);
+    return;
+
+error:
+    ALOGE("ERROR: requestDtmfStart failed");
+    RIL_onRequestComplete(t, RIL_E_GENERIC_FAILURE, NULL, 0);
+
+}
+
+static void unsolicitedUSSD(const char *s)
+{
+    char *line, *linestart;
+    int typeCode, count, err, len;
+    char *message;
+    char *outputmessage;
+    char *responseStr[2] = {0,0};
+
+    D("unsolicitedUSSD %s", s);
+
+    linestart=line=strdup(s);
+    err = at_tok_start(&line);
+    if (err < 0) goto error;
+
+    err = at_tok_nextint(&line, &typeCode);
+    if (err < 0) goto error;
+
+    if (at_tok_hasmore(&line)) {
+        err = at_tok_nextstr(&line, &message);
+        if(err < 0) goto error;
+
+        outputmessage = (char*) malloc(strlen(message)*2+1);
+        gsm_hex_to_bytes((cbytes_t)message,strlen(message),(bytes_t)outputmessage);
+
+        responseStr[1] = (char*) malloc(strlen(outputmessage)*2+1);
+
+        len = utf8_from_gsm8((cbytes_t)outputmessage,strlen(outputmessage),(bytes_t)responseStr[1]);
+
+        responseStr[1][strlen(message)/2]='\0';
+        free(outputmessage);
+
+        count = 2;
+    } else {
+        responseStr[1]=NULL;
+        count = 1;
+    }
+    free(linestart);
+    asprintf(&responseStr[0], "%d", typeCode);
+
+    RIL_onUnsolicitedResponse (RIL_UNSOL_ON_USSD, responseStr, count*sizeof(char*));
+    if (responseStr[0])
+        free(responseStr[0]);
+    if (responseStr[1])
+        free(responseStr[1]);
+
+    return;
+
+error:
+    ALOGE("unexpectedUSSD error");
+    free(linestart);
+}
+
+
+static void requestCancelUSSD(RIL_Token t)
+{
+    int err = 0;
+    ATResponse *atResponse = NULL;
+
+    err = at_send_command_numeric("AT+CUSD=2", &atResponse);
+
+    if (err != AT_NOERROR) {
+        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;
+}
+
+
+static void requestQueryCallWaiting(void *data, size_t datalen, RIL_Token t)
+{
+    ATResponse *atResponse = NULL;
+    int err;
+    char *line;
+    int aclass;
+    int response[2];
+
+    aclass = ((int *)data)[0];
+
+    err = at_send_command_singleline("AT+CCWA=1,2,%d", "+CCWA:", &atResponse, aclass);
+    if (err != AT_NOERROR)
+        goto error;
+
+    line = atResponse->p_intermediates->line;
+
+    err = at_tok_start(&line);
+    if (err < 0) goto error;
+
+    err = at_tok_nextint(&line, &(response[0]));
+    if (err < 0) goto error;
+
+    if (at_tok_hasmore(&line)) {
+        err = at_tok_nextint(&line, &(response[1]));
+        if (err < 0) goto error;
+    }
+
+    RIL_onRequestComplete(t, RIL_E_SUCCESS, response, sizeof(response));
+    at_response_free(atResponse);
+    return;
+
+error:
+    RIL_onRequestComplete(t, RIL_E_GENERIC_FAILURE, NULL, 0);
+    at_response_free(atResponse);
+}
+
+static void requestSetCallWaiting(void *data, size_t datalen, RIL_Token t)
+{
+    ATResponse *atResponse = NULL;
+    int err;
+    int enabled, aclass;
+
+    if((datalen<2)||(data==NULL)) goto error;
+
+    enabled = ((int *)data)[0];
+    aclass = ((int *)data)[1];
+
+    err = at_send_command("AT+CCWA=0,%d,%d",enabled,aclass);
+    if (err != AT_NOERROR)
+        goto error;
+
+    RIL_onRequestComplete(t, RIL_E_SUCCESS, NULL, 0);
+    at_response_free(atResponse);
+    return;
+
+error:
+    RIL_onRequestComplete(t, RIL_E_GENERIC_FAILURE, NULL, 0);
+    at_response_free(atResponse);
+}
+
+
+static void requestQueryCallForwardStatus(RIL_Token t)
+{
+    int err = 0;
+    int i = 0;
+    int n = 0;
+    int tmp = 0;
+    ATResponse *atResponse = NULL;
+    ATLine *p_cur;
+    RIL_CallForwardInfo **responses = NULL;
+
+    err = at_send_command_multiline("AT+CCFC=0,2", "+CCFC:", &atResponse);
+
+    if (err != AT_NOERROR)
+        goto error;
+
+    for (p_cur = atResponse->p_intermediates; p_cur != NULL; p_cur = p_cur->p_next)
+        n++;
+
+    responses = (RIL_CallForwardInfo **) alloca(n * sizeof(RIL_CallForwardInfo *));
+
+    for (i = 0; i < n; i++) {
+        responses[i] = (RIL_CallForwardInfo*)alloca(sizeof(RIL_CallForwardInfo));
+        responses[i]->status = 0;
+        responses[i]->reason = 0;
+        responses[i]->serviceClass = 0;
+        responses[i]->toa = 0;
+        responses[i]->number = (char*)"";
+        responses[i]->timeSeconds = 0;
+    }
+
+    for (i = 0,p_cur = atResponse->p_intermediates; p_cur != NULL; p_cur = p_cur->p_next, i++) {
+        char *line = p_cur->line;
+
+        err = at_tok_start(&line);
+        if (err < 0) goto error;
+
+        err = at_tok_nextint(&line, &(responses[i]->status));
+        if (err < 0) goto error;
+
+        err = at_tok_nextint(&line, &(responses[i]->serviceClass));
+        if (err < 0) goto error;
+
+        if(!at_tok_hasmore(&line)) continue;
+
+        err = at_tok_nextstr(&line, &(responses[i]->number));
+        if (err < 0) goto error;
+
+        if(!at_tok_hasmore(&line)) continue;
+
+        err = at_tok_nextint(&line, &(responses[i]->toa));
+        if (err < 0) goto error;
+
+        if(!at_tok_hasmore(&line)) continue;
+        at_tok_nextint(&line,&tmp);
+
+        if(!at_tok_hasmore(&line)) continue;
+        at_tok_nextint(&line,&tmp);
+
+        if(!at_tok_hasmore(&line)) continue;
+        err = at_tok_nextint(&line, &(responses[i]->timeSeconds));
+        if (err < 0) goto error;
+
+    }
+
+    RIL_onRequestComplete(t, RIL_E_SUCCESS, responses, sizeof(RIL_CallForwardInfo **));
+    at_response_free(atResponse);
+
+    return;
+
+error:
+    at_response_free(atResponse);
+    RIL_onRequestComplete(t, RIL_E_GENERIC_FAILURE, NULL, 0);
+}
+
+
+static void requestSetCallForward(void *data, RIL_Token t)
+{
+    int err = 0;
+    RIL_CallForwardInfo *info = NULL;
+
+    info = ((RIL_CallForwardInfo *) data);
+
+    if(data == NULL)
+        goto error;
+
+    err = at_send_command("AT+CCFC = %d, %d, \"%s\"",
+            info->reason, info->status,
+            info->number);
+    if (err != AT_NOERROR)
+        goto error;
+
+    RIL_onRequestComplete(t, RIL_E_SUCCESS, NULL, 0);
+    return;
+
+error:
+    RIL_onRequestComplete(t, RIL_E_GENERIC_FAILURE, NULL, 0);
+}
+
+
+static void requestGetCLIR(void *data, size_t datalen, RIL_Token t)
+{
+    /* Even though data and datalen must be NULL and 0 respectively this
+     * condition is not verified
+     */
+    ATResponse *atResponse = NULL;
+    int response[2];
+    char *line = NULL;
+    int err = 0;
+
+    err = at_send_command_singleline("AT+CLIR?", "+CLIR:", &atResponse);
+    if (err != AT_NOERROR)
+        goto error;
+
+
+    line = atResponse->p_intermediates->line;
+    err = at_tok_start(&line);
+    if (err < 0) goto error;
+
+    err = at_tok_nextint(&line, &(response[0]));
+    if (err < 0) goto error;
+
+    err = at_tok_nextint(&line, &(response[1]));
+    if (err < 0) goto error;
+
+    RIL_onRequestComplete(t, RIL_E_SUCCESS, response, sizeof(response));
+    at_response_free(atResponse);
+    return;
+
+error:
+    RIL_onRequestComplete(t, RIL_E_GENERIC_FAILURE, NULL, 0);
+    at_response_free(atResponse);
+}
+
+
+static void requestSetCLIR(void *data, size_t datalen, RIL_Token t)
+{
+    int err = 0;
+    err = at_send_command("AT+CLIR=%d", ((int *)data)[0]);
+    if (err < 0)
+        RIL_onRequestComplete(t, RIL_E_PASSWORD_INCORRECT, NULL, 0);
+    else
+        RIL_onRequestComplete(t, RIL_E_SUCCESS, NULL, 0);
+}
+
+
+static void requestSendUSSD(void *data, size_t datalen, RIL_Token t)
+{
+    int err = 0;
+    int len;
+    cbytes_t ussdRequest;
+    bytes_t temp;
+    char *newUSSDRequest;
+    ussdRequest = (cbytes_t)(data);
+
+    temp = (bytes_t) malloc(strlen((char *)ussdRequest)*sizeof(char)+1);
+    len = utf8_to_gsm8(ussdRequest,strlen((char *)ussdRequest),temp);
+    newUSSDRequest = (char*) malloc(2*len*sizeof(char)+1);
+    gsm_hex_from_bytes(newUSSDRequest,temp, len);
+    newUSSDRequest[2*len]='\0';
+    free(temp);
+
+    err = at_send_command("AT+CUSD=1,\"%s\",15", newUSSDRequest);
+    free(newUSSDRequest);
+    if (err != AT_NOERROR)
+        goto error;
+
+    RIL_onRequestComplete(t, RIL_E_SUCCESS, NULL, 0);
+    return;
+
+error:
+    RIL_onRequestComplete(t, RIL_E_GENERIC_FAILURE, NULL, 0);
+}
+
+
+static void requestChangeBarringPassword(void *data, size_t datalen, RIL_Token t)
+{
+    int err = 0;
+    char *string = NULL;
+    char *old_password = NULL;
+    char *new_password = NULL;
+
+    assert (datalen >=  (3 * sizeof(char **)));
+
+    string     = ((char **)data)[0];
+    old_password = ((char **)data)[1];
+    new_password = ((char **)data)[2];
+
+    err = at_send_command("AT+CPWD=\"%s\",\"%s\",\"%s\"", string, old_password, new_password);
+    if (err != AT_NOERROR)
+        goto error;
+
+    RIL_onRequestComplete(t, RIL_E_SUCCESS, NULL, 0);
+    return;
+
+error:
+    RIL_onRequestComplete(t, RIL_E_GENERIC_FAILURE, NULL, 0);
+}
+
+
+static void requestQueryCLIP(RIL_Token t)
+{
+    ATResponse *atResponse = NULL;
+    int err;
+    char *line;
+    int response;
+
+    err = at_send_command_singleline("AT+CLIP?","+CLIP:",&atResponse);
+    if (err != AT_NOERROR)
+        goto error;
+
+    line = atResponse->p_intermediates->line;
+    err = at_tok_start(&line);
+    if (err < 0) goto error;
+
+    /* The first number is discarded */
+    err = at_tok_nextint(&line, &response);
+    if (err < 0) goto error;
+
+    err = at_tok_nextint(&line, &response);
+    if (err < 0) goto error;
+
+    RIL_onRequestComplete(t, RIL_E_SUCCESS, &response, sizeof(int));
+    at_response_free(atResponse);
+    return;
+
+error:
+    at_response_free(atResponse);
+    RIL_onRequestComplete(t, RIL_E_GENERIC_FAILURE, NULL, 0);
+}
+
+static void requestResetRadio(RIL_Token t)
+{
+    int err = 0;
+
+    /* Reset MS */
+    err = at_send_command("AT+CFUN=6");
+    if (err != AT_NOERROR)
+        goto error;
+
+    /* Go online */
+    err = at_send_command("AT^RFSWITCH=1");
+    err = at_send_command("AT+CFUN=1");
+    if (err != AT_NOERROR)
+        goto error;
+
+    RIL_onRequestComplete(t, RIL_E_SUCCESS, NULL, 0);
+    return;
+
+error:
+    RIL_onRequestComplete(t, RIL_E_GENERIC_FAILURE, NULL, 0);
+    return;
+}
+
+static void requestSetSuppSVCNotification(void *data, size_t datalen, RIL_Token t)
+{
+    int err = 0;
+    int enabled = 0;
+    enabled = ((int *)data)[0];
+
+    err = at_send_command("AT+CSSN=%d,%d", enabled, enabled);
+    if (err != AT_NOERROR)
+        RIL_onRequestComplete(t, RIL_E_GENERIC_FAILURE, NULL, 0);
+    else
+        RIL_onRequestComplete(t, RIL_E_SUCCESS, NULL, 0);
+}
+
+
+static void requestExplicitCallTransfer(RIL_Token t)
+{
+    int err = 0;
+    err = at_send_command("AT+CHLD=4");
+    if (err != AT_NOERROR)
+        RIL_onRequestComplete(t, RIL_E_GENERIC_FAILURE, NULL, 0);
+    else
+        RIL_onRequestComplete(t, RIL_E_SUCCESS, NULL, 0);
+}
+
+
+static void requestSetLocationUpdates(void *data, size_t datalen, RIL_Token t)
+{
+    int err = 0;
+    int updates = 0;
+    ATResponse *atResponse = NULL;
+
+    updates = ((int *)data)[0] == 1? 2 : 1;
+
+    err = at_send_command_singleline("AT+CREG=%d","+CLIP:",&atResponse, updates);
+    if (err != AT_NOERROR)
+        goto error;
+
+    at_response_free(atResponse);
+
+    //Always return success for CDMA (for now)
+    RIL_onRequestComplete(t, RIL_E_SUCCESS, NULL, 0);
+    return;
+
+error:
+    at_response_free(atResponse);
+    RIL_onRequestComplete(t, RIL_E_GENERIC_FAILURE, NULL, 0);
+}
+
+static void requestLastFailCause(RIL_Token t)
+{
+    ATResponse *atResponse = NULL;
+    int err = 0;
+    int response = 0;
+    char *tmp = NULL;
+    char *line = NULL;
+
+    err = at_send_command_singleline("AT+CEER", "+CEER:", &atResponse);
+    if(err != AT_NOERROR)
+        goto error;
+
+    line = atResponse->p_intermediates->line;
+    err = at_tok_start(&line);
+    if(err < 0) goto error;
+
+    err = at_tok_nextstr(&line, &tmp);
+    if(err < 0) goto error;
+
+    err = at_tok_nextint(&line, &response);
+    if(err < 0) goto error;
+
+    RIL_onRequestComplete(t, RIL_E_SUCCESS, &response, sizeof(int));
+    at_response_free(atResponse);
+    return;
+
+error:
+    at_response_free(atResponse);
+    RIL_onRequestComplete(t, RIL_E_GENERIC_FAILURE, NULL, 0);
+
+}
+
+/*
+ * "data" is int *
+ * ((int *)data)[0] is == 0 from RUIM/SIM (default)
+ * ((int *)data)[0] is == 1 from NV
+ */
+static void requestCdmaSubscription(void * data, size_t datalen, RIL_Token t)
+{
+
+    /*
+    * "data" is int *
+    * ((int *)data)[0] is == 0 from RUIM/SIM (default)
+    * ((int *)data)[0] is == 1 from NV
+    */
+
+    RIL_onRequestComplete(t, RIL_E_SUCCESS, NULL, 0);
+}
+
+
+/*
+ * RIL_REQUEST_GET_NEIGHBORING_CELL_IDS
+ */
+static void requestNeighboringCellIds(void * data, size_t datalen, RIL_Token t)
+{
+    int err;
+    ATResponse *atResponse = NULL;
+    char *line, *p;
+    int commas;
+    int skip;
+    int lac = 0,cid = 0;
+    int count = 3;
+    int ber;
+    int rssi;
+
+    RIL_NeighboringCell **pp_cellIds;
+    RIL_NeighboringCell *p_cellIds;
+
+    pp_cellIds = (RIL_NeighboringCell **)alloca(sizeof(RIL_NeighboringCell *));
+    p_cellIds = (RIL_NeighboringCell *)alloca(sizeof(RIL_NeighboringCell));
+    pp_cellIds[0]=p_cellIds;
+
+    err = at_send_command_singleline("AT+CREG?", "+CREG:", &atResponse);
+    if (err != AT_NOERROR)
+        goto error;
+
+    line = atResponse->p_intermediates->line;
+
+    err = at_tok_start(&line);
+    if (err < 0) goto error;
+
+    /* Ok you have to be careful here
+     * The solicited version of the CREG response is
+     * +CREG: n, stat, [lac, cid]
+     * and the unsolicited version is
+     * +CREG: stat, [lac, cid]
+     * The <n> parameter is basically "is unsolicited creg on?"
+     * which it should always be
+     *
+     * Now we should normally get the solicited version here,
+     * but the unsolicited version could have snuck in
+     * so we have to handle both
+     *
+     * Also since the LAC and CID are only reported when registered,
+     * we can have 1, 2, 3, or 4 arguments here
+     *
+     * finally, a +CGREG: answer may have a fifth value that corresponds
+     * to the network type, as in;
+     *
+     *   +CGREG: n, stat [,lac, cid [,networkType]]
+     */
+
+    /* count number of commas */
+    commas = 0;
+    for (p = line ; *p != '\0' ;p++) {
+        if (*p == ',') commas++;
+    }
+    switch (commas) {
+        case 0: /* +CREG: <stat> */
+        case 1: /* +CREG: <n>, <stat> */
+            goto error;
+
+        case 2: /* +CREG: <stat>, <lac>, <cid> */
+            err = at_tok_nextint(&line, &skip);
+            if (err < 0) goto error;
+            err = at_tok_nexthexint(&line, &lac);
+            if (err < 0) goto error;
+            err = at_tok_nexthexint(&line, &cid);
+            if (err < 0) goto error;
+            break;
+        case 3: /* +CREG: <n>, <stat>, <lac>, <cid> */
+            err = at_tok_nextint(&line, &skip);
+            if (err < 0) goto error;
+            err = at_tok_nextint(&line, &skip);
+            if (err < 0) goto error;
+            err = at_tok_nexthexint(&line, &lac);
+            if (err < 0) goto error;
+            err = at_tok_nexthexint(&line, &cid);
+            if (err < 0) goto error;
+            break;
+        case 4: /* +CGREG: <n>, <stat>, <lac>, <cid>, <networkType> */
+            err = at_tok_nextint(&line, &skip);
+            if (err < 0) goto error;
+            err = at_tok_nextint(&line, &skip);
+            if (err < 0) goto error;
+            err = at_tok_nexthexint(&line, &lac);
+            if (err < 0) goto error;
+            err = at_tok_nexthexint(&line, &cid);
+            if (err < 0) goto error;
+            err = at_tok_nexthexint(&line, &skip);
+            if (err < 0) goto error;
+            count = 4;
+            break;
+        default:
+            goto error;
+    }
+
+
+    at_response_free(atResponse);
+
+    err = at_send_command_singleline("AT+CSQ", "+CSQ:", &atResponse);
+    if (err != AT_NOERROR)
+        goto error;
+
+    line = atResponse->p_intermediates->line;
+
+    err = at_tok_start(&line);
+    if (err < 0) goto error;
+
+    err = at_tok_nextint(&line,&rssi);
+    if (err < 0) goto error;
+
+    err = at_tok_nextint(&line, &ber);
+    if (err < 0)
+        goto error;
+
+    /* Dump the current cell information */
+    p_cellIds[0].cid = (char*) alloca(16);
+    sprintf(p_cellIds[0].cid, "%04x%04x", lac & 0xFFFF, cid & 0xFFFF );
+    p_cellIds[0].rssi = rssi;
+
+    RIL_onRequestComplete(t, RIL_E_SUCCESS, pp_cellIds, sizeof(pp_cellIds));
+    at_response_free(atResponse);
+    return;
+
+error:
+    RIL_onRequestComplete(t, RIL_E_GENERIC_FAILURE, NULL, 0);
+    at_response_free(atResponse);
+}
+
+static pthread_t s_tid_queueRunner;
+
+
+/** Do post- SIM ready initialization. */
+static void onSIMReady(void *p)
+{
+    int err = 0;
+    (void) p;
+
+    /* Check if ME is ready to set preferred message storage */
+    checkMessageStorageReady(NULL);
+
+    /* Select message service */
+    at_send_command("AT+CSMS=0");
+
+    /*
+     * Always send SMS messages directly to the TE
+     *
+     * mode = 1 // discard when link is reserved (link should never be
+     *             reserved)
+     * mt = 2   // most messages routed to TE
+     * bm = 2   // new cell BM's routed to TE
+     * ds = 1   // Status reports routed to TE
+     * bfr = 1  // flush buffer
+     */
+    at_send_command("AT+CNMI=1,2,2,1,1");
+
+    /* Select cell broadcast messages: Do not accept messages */
+    at_send_command("AT+CSCB=1");
+
+    /* Enable automatic timezone update */
+    at_send_command("AT+CTZU=1");
+
+    /* Enable timezone change reporting */
+    at_send_command("AT+CTZR=1");
+
+    /* Subscribe to network registration events.
+     *  n = 2 - Enable network registration and location information
+     *          unsolicited result code +CREG: <stat>[,<lac>,<ci>]
+     */
+    err = at_send_command("AT+CREG=2");
+    if (err != AT_NOERROR) {
+        /* Some handsets -- in tethered mode -- don't support CREG=2. */
+        at_send_command("AT+CREG=1");
+    }
+
+    /* Configure Short Message (SMS) Format
+     *  mode = 0 - PDU mode.
+     */
+    at_send_command("AT+CMGF=0");
+
+    /* Enable unsolicited RSSI reporting */
+    at_send_command("AT^CURC=1");
+
+    /* Enable GPRS reporting */
+    at_send_command("AT+CGEREP=1,0");
+
+
+    /* Select SMS type */
+    at_send_command_singleline("AT+CSMS=1", "+CSMS:", NULL);
+}
+
+
+
+static void requestNotSupported(RIL_Token t, int request)
+{
+    D("Request %d is unsupported", request);
+    RIL_onRequestComplete(t, RIL_E_REQUEST_NOT_SUPPORTED, NULL, 0);
+    return;
+}
+
+
+/*** Callback methods from the RIL library to us ***/
+
+static void processRequest (int request, void *data, size_t datalen, RIL_Token t)
+{
+    D("%s() %s", __func__, requestToString(request));
+
+    /*
+     * These commands won't accept RADIO_NOT_AVAILABLE, so we just return
+     * GENERIC_FAILURE if we're not in SIM_STATE_READY.
+     */
+    RIL_RadioState radio_state = getRadioState();
+D("%s() %s", __func__, radioStateToString(radio_state));
+
+    if (radio_state != RADIO_STATE_SIM_READY &&
+            (request == RIL_REQUEST_WRITE_SMS_TO_SIM ||
+             request == RIL_REQUEST_DELETE_SMS_ON_SIM)) {
+        RIL_onRequestComplete(t, RIL_E_GENERIC_FAILURE, NULL, 0);
+        return;
+    }
+
+    /* Ignore all requests except RIL_REQUEST_GET_SIM_STATUS
+     * when RADIO_STATE_UNAVAILABLE.
+     */
+    if (radio_state == RADIO_STATE_UNAVAILABLE
+            && request != RIL_REQUEST_GET_SIM_STATUS) {
+        RIL_onRequestComplete(t, RIL_E_RADIO_NOT_AVAILABLE, NULL, 0);
+        return;
+    }
+
+    /* Ignore all non-power requests when RADIO_STATE_OFF
+     * (except RIL_REQUEST_GET_SIM_STATUS and a few more).
+     */
+    if ((radio_state == RADIO_STATE_OFF || radio_state == RADIO_STATE_SIM_NOT_READY) &&
+           !(request == RIL_REQUEST_RADIO_POWER ||
+             request == RIL_REQUEST_GET_SIM_STATUS ||
+             request == RIL_REQUEST_STK_GET_PROFILE ||
+             request == RIL_REQUEST_STK_SET_PROFILE ||
+             request == RIL_REQUEST_REPORT_STK_SERVICE_IS_RUNNING ||
+             request == RIL_REQUEST_GET_IMEISV ||
+             request == RIL_REQUEST_GET_IMEI ||
+             request == RIL_REQUEST_BASEBAND_VERSION ||
+             request == RIL_REQUEST_SCREEN_STATE)) {
+        RIL_onRequestComplete(t, RIL_E_RADIO_NOT_AVAILABLE, NULL, 0);
+        return;
+    }
+
+    /* Don't allow radio operations when sim is absent or locked! */
+    if (radio_state == RADIO_STATE_SIM_LOCKED_OR_ABSENT &&
+           !(request == RIL_REQUEST_ENTER_SIM_PIN ||
+             request == RIL_REQUEST_ENTER_SIM_PUK ||
+             request == RIL_REQUEST_ENTER_SIM_PIN2 ||
+             request == RIL_REQUEST_ENTER_SIM_PUK2 ||
+             request == RIL_REQUEST_ENTER_NETWORK_DEPERSONALIZATION ||
+             request == RIL_REQUEST_GET_SIM_STATUS ||
+             request == RIL_REQUEST_RADIO_POWER ||
+             request == RIL_REQUEST_GET_IMEISV ||
+             request == RIL_REQUEST_GET_IMEI ||
+             request == RIL_REQUEST_BASEBAND_VERSION ||
+             request == RIL_REQUEST_GET_CURRENT_CALLS)) {
+        RIL_onRequestComplete(t, RIL_E_GENERIC_FAILURE, NULL, 0);
+        return;
+    }
+
+
+    switch (request) {
+        case RIL_REQUEST_GET_CURRENT_CALLS:
+            if (radio_state == RADIO_STATE_SIM_LOCKED_OR_ABSENT)
+                RIL_onRequestComplete(t, RIL_E_RADIO_NOT_AVAILABLE, NULL, 0);
+            else
+                requestGetCurrentCalls(data, datalen, t);
+            break;
+
+        case RIL_REQUEST_SCREEN_STATE:
+            requestScreenState(data, datalen, t);
+            /* Trigger a rehash of network values, just to be sure. */
+            if (((int *)data)[0] == 1)
+                RIL_onUnsolicitedResponse(
+                                   RIL_UNSOL_RESPONSE_VOICE_NETWORK_STATE_CHANGED,
+                                   NULL, 0);
+            break;
+
+        /* Data Call Requests */
+        case RIL_REQUEST_SETUP_DATA_CALL:
+            requestSetupDefaultPDP(data, datalen, t);
+            break;
+        case RIL_REQUEST_DEACTIVATE_DATA_CALL:
+            requestDeactivateDefaultPDP(data, datalen, t);
+            break;
+        case RIL_REQUEST_LAST_DATA_CALL_FAIL_CAUSE:
+            requestLastPDPFailCause(t);
+            break;
+        case RIL_REQUEST_DATA_CALL_LIST:
+            requestPDPContextList(t);
+            break;
+
+        /* SMS Requests */
+        case RIL_REQUEST_SEND_SMS:
+            requestSendSMS(data, datalen, t);
+            break;
+        case RIL_REQUEST_SEND_SMS_EXPECT_MORE:
+            requestSendSMSExpectMore(data, datalen, t);
+            break;
+        case RIL_REQUEST_WRITE_SMS_TO_SIM:
+            requestWriteSmsToSim(data, datalen, t);
+            break;
+        case RIL_REQUEST_DELETE_SMS_ON_SIM:
+            requestDeleteSmsOnSim(data, datalen, t);
+            break;
+        case RIL_REQUEST_GET_SMSC_ADDRESS:
+            requestGetSMSCAddress(t);
+            break;
+        case RIL_REQUEST_SET_SMSC_ADDRESS:
+            requestSetSMSCAddress(data, datalen, t);
+            break;
+        case RIL_REQUEST_REPORT_SMS_MEMORY_STATUS:
+            requestSmsStorageFull(data, datalen, t);
+            break;
+        case RIL_REQUEST_SMS_ACKNOWLEDGE:
+            requestSMSAcknowledge(data, datalen, t);
+            break;
+        case RIL_REQUEST_GSM_GET_BROADCAST_SMS_CONFIG:
+            requestGSMGetBroadcastSMSConfig(t);
+            break;
+        case RIL_REQUEST_GSM_SET_BROADCAST_SMS_CONFIG:
+            requestGSMSetBroadcastSMSConfig(data, datalen, t);
+            break;
+        case RIL_REQUEST_GSM_SMS_BROADCAST_ACTIVATION:
+            requestGSMSMSBroadcastActivation(data, datalen, t);
+            break;
+
+        /* SIM Handling Requests */
+        case RIL_REQUEST_SIM_IO:
+            requestSIM_IO(data, datalen, t);
+            break;
+        case RIL_REQUEST_GET_SIM_STATUS:
+            requestGetSimStatus(t);
+            break;
+        case RIL_REQUEST_ENTER_SIM_PIN:
+        case RIL_REQUEST_ENTER_SIM_PUK:
+        case RIL_REQUEST_ENTER_SIM_PIN2:
+        case RIL_REQUEST_ENTER_SIM_PUK2:
+            requestEnterSimPin(data, datalen, t, request);
+            break;
+        case RIL_REQUEST_CHANGE_SIM_PIN:
+            requestChangePassword(data, datalen, t, "SC", request);
+            break;
+        case RIL_REQUEST_CHANGE_SIM_PIN2:
+            requestChangePassword(data, datalen, t, "P2", request);
+            break;
+        case RIL_REQUEST_QUERY_FACILITY_LOCK:
+            requestQueryFacilityLock(data, datalen, t);
+            break;
+        case RIL_REQUEST_SET_FACILITY_LOCK:
+            requestSetFacilityLock(data, datalen, t);
+            break;
+
+        /* Network Requests */
+        case RIL_REQUEST_ENTER_NETWORK_DEPERSONALIZATION:
+            requestEnterSimPin(data, datalen, t, request);
+            break;
+        case RIL_REQUEST_QUERY_NETWORK_SELECTION_MODE:
+            requestQueryNetworkSelectionMode(t);
+            break;
+        case RIL_REQUEST_SET_NETWORK_SELECTION_AUTOMATIC:
+            requestSetNetworkSelectionAutomatic(t);
+            break;
+        case RIL_REQUEST_SET_NETWORK_SELECTION_MANUAL:
+            requestSetNetworkSelectionManual(data, datalen, t);
+            break;
+        case RIL_REQUEST_QUERY_AVAILABLE_NETWORKS:
+            requestQueryAvailableNetworks(t);
+            break;
+        case RIL_REQUEST_SET_PREFERRED_NETWORK_TYPE:
+            requestSetPreferredNetworkType(data, datalen, t);
+            break;
+        case RIL_REQUEST_GET_PREFERRED_NETWORK_TYPE:
+            requestGetPreferredNetworkType(t);
+            break;
+        case RIL_REQUEST_VOICE_REGISTRATION_STATE:
+            requestRegistrationState(t);
+            break;
+        case RIL_REQUEST_DATA_REGISTRATION_STATE:
+            requestGprsRegistrationState(t);
+            break;
+
+        /* OEM */
+        case RIL_REQUEST_OEM_HOOK_RAW:
+            // echo back data
+            requestOEMHookRaw(data, datalen, t);
+            break;
+        case RIL_REQUEST_OEM_HOOK_STRINGS:
+            requestOEMHookStrings(data, datalen, t);
+            break;
+
+        /* Misc */
+        case RIL_REQUEST_SIGNAL_STRENGTH:
+            requestSignalStrength(t);
+            break;
+        case RIL_REQUEST_OPERATOR:
+            requestOperator(t);
+            break;
+        case RIL_REQUEST_RADIO_POWER:
+            requestRadioPower(data, datalen, t);
+            break;
+        case RIL_REQUEST_GET_IMSI:
+            requestGetIMSI(t);
+            break;
+        case RIL_REQUEST_GET_IMEI:
+            requestGetIMEISV(t);
+            break;
+        case RIL_REQUEST_GET_IMEISV:
+            requestGetIMEISV(t);
+            break;
+        case RIL_REQUEST_DEVICE_IDENTITY:
+            requestDeviceIdentity(t);
+            break;
+        case RIL_REQUEST_BASEBAND_VERSION:
+            requestBasebandVersion(t);
+            break;
+
+        /* SIM Application Toolkit */
+        case RIL_REQUEST_STK_SEND_TERMINAL_RESPONSE:
+            requestStkSendTerminalResponse(data, datalen, t);
+            break;
+        case RIL_REQUEST_STK_SEND_ENVELOPE_COMMAND:
+            requestStkSendEnvelopeCommand(data, datalen, t);
+            break;
+        case RIL_REQUEST_STK_GET_PROFILE:
+            requestStkGetProfile(t);
+            break;
+        case RIL_REQUEST_STK_SET_PROFILE:
+            requestStkSetProfile(data, datalen, t);
+            break;
+        case RIL_REQUEST_REPORT_STK_SERVICE_IS_RUNNING:
+            requestReportStkServiceIsRunning(t);
+            break;
+
+        /* Misc ops */
+        case RIL_REQUEST_DIAL:
+            requestDial(data, datalen, t);
+            break;
+        case RIL_REQUEST_HANGUP:
+            requestHangup(data, datalen, t);
+            break;
+        case RIL_REQUEST_HANGUP_WAITING_OR_BACKGROUND:
+            requestHangupWaitingOrBackground(t);
+            break;
+        case RIL_REQUEST_HANGUP_FOREGROUND_RESUME_BACKGROUND:
+            requestHangupForegroundResumeBackground(t);
+            break;
+        case RIL_REQUEST_SWITCH_WAITING_OR_HOLDING_AND_ACTIVE:
+            requestSwitchWaitingOrHoldingAndActive(t);
+            break;
+        case RIL_REQUEST_ANSWER:
+            requestAnswer(t);
+            break;
+        case RIL_REQUEST_CONFERENCE:
+            requestConference(t);
+            break;
+        case RIL_REQUEST_UDUB:
+            requestUDUB(t);
+            break;
+        case RIL_REQUEST_SEPARATE_CONNECTION:
+            requestSeparateConnection(data, datalen, t);
+            break;
+        case RIL_REQUEST_SET_MUTE:
+            requestSetMute(data, datalen, t);
+            break;
+        case RIL_REQUEST_GET_MUTE:
+            requestGetMute(t);
+            break;
+        case RIL_REQUEST_DTMF:
+            requestDTMF(data, datalen, t);
+            break;
+        case RIL_REQUEST_DTMF_STOP:
+            requestDtmfStop(t);
+            break;
+        case RIL_REQUEST_DTMF_START:
+            requestDtmfStart(data, datalen, t);
+            break;
+        case RIL_REQUEST_CANCEL_USSD:
+            requestCancelUSSD(t);
+            break;
+        case RIL_REQUEST_QUERY_CALL_WAITING:
+            requestQueryCallWaiting(data, datalen, t);
+            break;
+        case RIL_REQUEST_SET_CALL_WAITING:
+            requestSetCallWaiting(data, datalen, t);
+            break;
+        case RIL_REQUEST_QUERY_CALL_FORWARD_STATUS:
+            requestQueryCallForwardStatus(t);
+            break;
+        case RIL_REQUEST_SET_CALL_FORWARD:
+            requestSetCallForward(data, t);
+            break;
+        case RIL_REQUEST_GET_CLIR:
+            requestGetCLIR(data, datalen, t);
+            break;
+        case RIL_REQUEST_SET_CLIR:
+            requestSetCLIR(data, datalen, t);
+            break;
+        case RIL_REQUEST_SEND_USSD:
+            requestSendUSSD(data, datalen, t);
+            break;
+        case RIL_REQUEST_CHANGE_BARRING_PASSWORD:
+            requestChangeBarringPassword(data, datalen, t);
+            break;
+        case RIL_REQUEST_QUERY_CLIP:
+            requestQueryCLIP(t);
+            break;
+        case RIL_REQUEST_RESET_RADIO:
+            requestResetRadio(t);
+            break;
+        case RIL_REQUEST_SET_SUPP_SVC_NOTIFICATION:
+            requestSetSuppSVCNotification(data, datalen, t);
+            break;
+        case RIL_REQUEST_EXPLICIT_CALL_TRANSFER:
+            requestExplicitCallTransfer(t);
+            break;
+        case RIL_REQUEST_SET_LOCATION_UPDATES:
+            requestSetLocationUpdates(data, datalen, t);
+            break;
+        case RIL_REQUEST_LAST_CALL_FAIL_CAUSE:
+            requestLastFailCause(t);
+            break;
+        case 503: //GET_SIM_TYPES
+            RIL_onRequestComplete(t, RIL_E_SUCCESS, 0, sizeof(int));
+            break;
+        case 504: //GET_PB_ENTRIES_LENGTH
+            {
+                int response[6];
+                response[0]=1;
+                response[1]=0;
+                response[2]=0;
+                response[3]=0;
+                response[4]=0;
+                response[5]=0;
+                RIL_onRequestComplete(t, RIL_E_SUCCESS, response, sizeof(response));
+            }
+            break;
+
+        case 525: //GET_SUBSCRIBER_NUMBER
+            break;
+
+        case RIL_REQUEST_CDMA_SET_SUBSCRIPTION_SOURCE:
+            requestCdmaSubscription(data, datalen, t);
+            break;
+
+        case RIL_REQUEST_GET_NEIGHBORING_CELL_IDS:
+            requestNeighboringCellIds(data, datalen, t);
+            break;
+
+        default:
+            ALOGW("FIXME: Unsupported request logged: %s",
+                 requestToString(request));
+            requestNotSupported(t, request);
+            break;
+    }
+}
+
+/**
+ * Call from RIL to us to make a RIL_REQUEST
+ *
+ * Must be completed with a call to RIL_onRequestComplete()
+ *
+ * RIL_onRequestComplete() may be called from any thread, before or after
+ * this function returns.
+ *
+ * Will always be called from the same thread, so returning here implies
+ * that the radio is ready to process another command (whether or not
+ * the previous command has completed).
+ */
+
+/**
+ * Call from RIL to us to make a RIL_REQUEST.
+ *
+ * Must be completed with a call to RIL_onRequestComplete().
+ */
+static void onRequest(int request, void *data, size_t datalen, RIL_Token t)
+{
+    RILRequest *r;
+    RequestQueue *q = &s_requestQueue;
+    int err;
+
+    r = (RILRequest *) malloc(sizeof(RILRequest));
+    memset(r, 0, sizeof(RILRequest));
+
+    /* Formulate a RILRequest and put it in the queue. */
+    r->request = request;
+    r->data = dupRequestData(request, data, datalen);
+    r->datalen = datalen;
+    r->token = t;
+
+    if ((err = pthread_mutex_lock(&q->queueMutex)) != 0)
+        ALOGE("%s() failed to take queue mutex: %s!", __func__, strerror(err));
+
+    /* Queue empty, just throw r on top. */
+    if (q->requestList == NULL) {
+        q->requestList = r;
+    } else {
+        RILRequest *l = q->requestList;
+        while (l->next != NULL)
+            l = l->next;
+
+        l->next = r;
+    }
+
+    if ((err = pthread_cond_broadcast(&q->cond)) != 0)
+        ALOGE("%s() failed to broadcast queue update: %s!",
+            __func__, strerror(err));
+
+    if ((err = pthread_mutex_unlock(&q->queueMutex)) != 0)
+        ALOGE("%s() failed to release queue mutex: %s!",
+            __func__, strerror(err));
+}
+
+/**
+ * Call from RIL to us to find out whether a specific request code
+ * is supported by this implementation.
+ *
+ * Return 1 for "supported" and 0 for "unsupported".
+ *
+ * Currently just stubbed with the default value of one. This is currently
+ * not used by android, and therefore not implemented here. We return
+ * RIL_E_REQUEST_NOT_SUPPORTED when we encounter unsupported requests.
+ */
+static int onSupports(int requestCode)
+{
+    (void) requestCode;
+    ALOGI("onSupports() called!");
+
+    return 1;
+}
+
+/**
+ * onCancel() is currently stubbed, because android doesn't use it and
+ * our implementation will depend on how a cancellation is handled in
+ * the upper layers.
+ */
+static void onCancel(RIL_Token t)
+{
+    (void) t;
+    ALOGI("onCancel() called!");
+}
+
+static const char * getVersion(void)
+{
+    return RIL_VERSION_STRING;
+}
+
+static void sendTime(void *p)
+{
+    time_t t;
+    struct tm tm;
+    char str[20];
+    char tz[6];
+    int num[4];
+    int tzi;
+    int i;
+    (void) p;
+
+    tzset();
+    t = time(NULL);
+
+    if (!(localtime_r(&t, &tm)))
+        return;
+    if (!(strftime(tz, 12, "%z", &tm)))
+        return;
+
+    for (i = 0; i < 4; i++)
+        num[i] = tz[i+1] - '0';
+
+    /* convert timezone hours to timezone quarters of hours */
+    tzi = (num[0] * 10 + num[1]) * 4 + (num[2] * 10 + num[3]) / 15;
+    strftime(str, 20, "%y/%m/%d,%T", &tm);
+    at_send_command("AT+CCLK=\"%s%c%02d\"", str, tz[0], tzi);
+}
+
+
+static char initializeCommon(void)
+{
+    int err = 0;
+    unsigned int i;
+    static const char* const initcmd[] = {
+
+        /* Reset the MS*/
+        "AT+CFUN=6",
+
+        /*  atchannel is tolerant of echo but it must */
+        /*  reset and have verbose result codes */
+        "ATZV1",
+
+        /*  echo off */
+        "ATE0",
+
+        /*  No auto-answer */
+        "ATS0=0",
+
+        /*  No auto-answer */
+        "AT%AUTOANSWER=0",
+
+        /*  send results */
+        "ATQ0",
+
+        /*  check for busy, don't check for dialone */
+        "ATX3",
+
+        /*  set DCD depending on service */
+        "AT&C1",
+
+        /*  set DTR according to service */
+        "AT&D1",
+
+        /*  Extended errors without textual decriptions */
+        "AT+CMEE=1",
+
+        /*  detailed rings, unknown */
+        "AT+CRC=1;+CR=1",
+
+        /*  caller id = yes */
+        "AT+CLIP=1",
+
+        /*  don't hide outgoing callerID */
+        "AT+CLIR=0",
+
+        /*  Call Waiting notifications */
+        "AT+CCWA=1",
+
+        /*  No connected line identification */
+        "AT+COLP=0",
+
+        /*  USSD unsolicited */
+        "AT+CUSD=1",
+
+        /*  SMS PDU mode */
+        "AT+CMGF=0",
+
+        //"AT+GTKC=2",
+
+        /*  +CSSU unsolicited supp service notifications */
+        "AT+CSSN=0,1",
+
+        /*  HEX character set */
+        //"AT+CSCS=\"HEX\"",
+        "AT+CSCS=\"IRA\"",
+
+        /*  Modem mode */
+        "AT+FCLASS=0",
+
+        "AT+CNMI=1,2,2,2,0",
+        //"AT+CPPP=1",
+
+
+        "AT"
+    };
+
+    if (at_handshake() < 0) {
+        LOG_FATAL("Handshake failed!");
+        return 1;
+    }
+
+
+    for (i=0; i < sizeof(initcmd) / sizeof(initcmd[0]); i++) {
+        err = at_send_command(initcmd[i]);
+        if (err != AT_NOERROR) {
+            ALOGE("Failed sending command '%s'",initcmd[i]);
+        }
+    }
+
+    /* Send the current time of the OS to the module */
+    sendTime(NULL);
+
+    return 0;
+}
+
+/**
+ * Initialize everything
+ */
+static char initializeChannel(void)
+{
+    int err = 0;
+    unsigned int i;
+    static const char* const initcmd[] = {
+
+        /* Configure Packet Domain Network Registration Status events
+         *    2 = Enable network registration and location information
+         *        unsolicited result code
+         */
+
+        /*  Alternating voice/data off */
+        "AT+CMOD=0",
+
+        /*  Not muted */
+        "AT+CMUT=0",
+
+        /*  Network registration events */
+        "AT+CREG=2",
+
+        /*  GPRS registration events */
+        "AT+CGREG=2",
+
+        "AT+CGEQREQ=1,4,0,0,0,0,2,0,\"0E0\",\"0E0\",3,0,0",
+
+        /* Enable unsolicited reports */
+        "AT^CURC=1",
+
+        /* Enable GPRS reporting */
+        "AT+CGEREP=1,0",
+
+        /* for 3G Preferred */
+        "AT^SYSCFG=2,2,3FFFFFFF,1,2",
+
+        /* Disable RF */
+        "AT^RFSWITCH=0"
+    };
+
+    D("%s()", __func__);
+
+    setRadioState(RADIO_STATE_OFF);
+
+    for (i=0; i < sizeof(initcmd) / sizeof(initcmd[0]); i++) {
+        err = at_send_command(initcmd[i]);
+        if (err != AT_NOERROR) {
+            ALOGE("Failed sending command '%s'",initcmd[i]);
+        }
+    }
+
+    /* Assume radio is off on error. */
+    if (isRadioOn() > 0)
+        setRadioState(RADIO_STATE_SIM_NOT_READY);
+
+    return 0;
+}
+
+/**
+ * Called by atchannel when an unsolicited line appears
+ * This is called on atchannel's reader thread. AT commands may
+ * not be issued here
+ */
+static void onUnsolicited (const char *s, const char *sms_pdu)
+{
+    /* Ignore unsolicited responses until we're initialized.
+       This is OK because the RIL library will poll for initial state. */
+    if (getRadioState() == RADIO_STATE_UNAVAILABLE)
+        return;
+
+    if (strStartsWith(s, "%CTZV:")
+            || strStartsWith(s,"+CTZV:")
+            || strStartsWith(s,"+CTZDST:")
+            || strStartsWith(s,"+HTCCTZV:")) {
+        unsolicitedNitzTime(s);
+    } else if (strStartsWith(s,"+CRING:")
+            || strStartsWith(s,"RING")
+            || strStartsWith(s,"NO CARRIER")
+            || strStartsWith(s,"+CCWA")
+          ) {
+        RIL_onUnsolicitedResponse (
+                RIL_UNSOL_RESPONSE_CALL_STATE_CHANGED,
+                NULL, 0);
+        enqueueRILEvent(onDataCallListChanged, NULL, NULL);
+    } else if (strStartsWith(s,"^RSSI:") ||
+               strStartsWith(s,"%RSSI:")) {
+        unsolicitedRSSI(s);
+    } else if (strStartsWith(s,"^MODE:")) {
+        unsolicitedMode(s);
+    } else if (strStartsWith(s,"+CREG:")
+            || strStartsWith(s,"+CGREG:")) {
+        RIL_onUnsolicitedResponse (
+                RIL_UNSOL_RESPONSE_VOICE_NETWORK_STATE_CHANGED,
+                NULL, 0);
+        enqueueRILEvent(onDataCallListChanged, NULL, NULL);
+    } else if (strStartsWith(s, "+CMT:")) {
+        onNewSms(sms_pdu);
+    } else if (strStartsWith(s, "+CBM:")) {
+        onNewBroadcastSms(sms_pdu);
+    } else if (strStartsWith(s, "+CMTI:")) {
+        onNewSmsOnSIM(s);
+    } else if (strStartsWith(s, "+CDS:")) {
+        onNewStatusReport(sms_pdu);
+    } else if (strStartsWith(s, "+CGEV:")) {
+        /* Really, we can ignore NW CLASS and ME CLASS events here,
+         * but right now we don't since extranous
+         * RIL_UNSOL_DATA_CALL_LIST_CHANGED calls are tolerated
+         */
+        /* can't issue AT commands here -- call on main thread */
+        enqueueRILEvent(onDataCallListChanged, NULL, NULL);
+    } else if (strStartsWith(s, "+CUSD:")) {
+        unsolicitedUSSD(s);
+    } else if (strStartsWith(s, "+CIEV: 7") ||
+               strStartsWith(s, "Received SMS:")) {
+        onNewSmsIndication();
+    }
+}
+
+static void signalCloseQueues(void)
+{
+    unsigned int i;
+    for (i = 0; i < (sizeof(s_requestQueues) / sizeof(RequestQueue *)); i++) {
+        int err;
+        RequestQueue *q = s_requestQueues[i];
+        if ((err = pthread_mutex_lock(&q->queueMutex)) != 0)
+            ALOGE("%s() failed to take queue mutex: %s",
+                __func__, strerror(err));
+
+        q->closed = 1;
+        if ((err = pthread_cond_signal(&q->cond)) != 0)
+            ALOGE("%s() failed to broadcast queue update: %s",
+                __func__, strerror(err));
+
+        if ((err = pthread_mutex_unlock(&q->queueMutex)) != 0)
+            ALOGE("%s() failed to take queue mutex: %s", __func__,
+                 strerror(err));
+    }
+}
+
+
+/* Called on command or reader thread */
+static void onATReaderClosed()
+{
+    ALOGI("AT channel closed");
+
+    setRadioState (RADIO_STATE_UNAVAILABLE);
+    signalCloseQueues();
+    at_close();
+}
+
+/* Called on command thread */
+static void onATTimeout()
+{
+    ALOGI("AT channel timeout; restarting..");
+    /* Last resort, throw escape on the line, close the channel
+       and hope for the best. */
+    at_send_escape();
+
+    setRadioState(RADIO_STATE_UNAVAILABLE);
+    signalCloseQueues();
+
+    /* TODO We may cause a radio reset here. */
+}
+
+static void usage(char *s)
+{
+#ifdef RIL_SHLIB
+    fprintf(stderr, "htcgeneric-ril requires: -p <tcp port> or -d /dev/tty_device\n");
+#else
+    fprintf(stderr, "usage: %s [-p <tcp port>] [-d /dev/tty_device] [-v /dev/tty_device]\n", s);
+    exit(-1);
+#endif
+}
+
+struct queueArgs {
+    int port;
+    char * loophost;
+    const char *device_path;
+};
+
+static int safe_read(int fd, char *buf, int count)
+{
+    int n;
+    int i = 0;
+
+    while (i < count) {
+        n = read(fd, buf + i, count - i);
+        if (n > 0)
+            i += n;
+        else if (!(n < 0 && errno == EINTR))
+            return -1;
+    }
+
+    return count;
+}
+
+static void *queueRunner(void *param)
+{
+    int fd = -1;
+    int ret = 0;
+    int n;
+    fd_set input;
+    struct timespec timeout;
+    int max_fd = -1;
+    char start[MAX_BUF];
+    struct queueArgs *queueArgs = (struct queueArgs *) param;
+    struct RequestQueue *q = NULL;
+
+    ALOGI("%s() starting!", __func__);
+
+    for (;;) {
+        fd = -1;
+        max_fd = -1;
+        while (fd < 0) {
+            if (queueArgs->port > 0) {
+                if (queueArgs->loophost)
+                    fd = socket_network_client(queueArgs->loophost, queueArgs->port, SOCK_STREAM);
+                else
+                    fd = socket_loopback_client(queueArgs->port, SOCK_STREAM);
+            } else if (queueArgs->device_path != NULL) {
+
+                fd = open (queueArgs->device_path, O_RDWR);
+                if ( fd >= 0 && !memcmp( queueArgs->device_path, "/dev/ttyUSB", 11 ) ) {
+
+                    /* disable echo on serial ports */
+                    struct termios  ios;
+                    tcgetattr( fd, &ios );
+                    ios.c_lflag = 0;  /* disable ECHO, ICANON, etc... */
+                    tcsetattr( fd, TCSANOW, &ios );
+                }
+            }
+
+            if (fd < 0) {
+                ALOGE("%s() Failed to open AT channel %s (%s), retrying in %d.",
+                    __func__, queueArgs->device_path,
+                    strerror(errno), TIMEOUT_SEARCH_FOR_TTY);
+                sleep(TIMEOUT_SEARCH_FOR_TTY);
+                /* Never returns. */
+            }
+        }
+
+        ret = at_open(fd, onUnsolicited);
+
+        if (ret < 0) {
+            ALOGE("%s() AT error %d on at_open", __func__, ret);
+            at_close();
+            continue;
+        }
+
+        at_set_on_reader_closed(onATReaderClosed);
+        at_set_on_timeout(onATTimeout);
+
+        q = &s_requestQueue;
+
+        if (initializeCommon()) {
+            ALOGE("%s() Failed to initialize channel!", __func__);
+            at_close();
+            continue;
+        }
+
+        q->closed = 0;
+        if (initializeChannel()) {
+            ALOGE("%s() Failed to initialize channel!", __func__);
+            at_close();
+            continue;
+        }
+
+        at_make_default_channel();
+
+        ALOGE("%s() Looping the requestQueue!", __func__);
+        for (;;) {
+            RILRequest *r;
+            RILEvent *e;
+            struct timespec ts;
+            int err;
+
+            memset(&ts, 0, sizeof(ts));
+
+        if ((err = pthread_mutex_lock(&q->queueMutex)) != 0)
+            ALOGE("%s() failed to take queue mutex: %s!",
+                __func__, strerror(err));
+
+            if (q->closed != 0) {
+                ALOGW("%s() AT Channel error, attempting to recover..", __func__);
+                if ((err = pthread_mutex_unlock(&q->queueMutex)) != 0)
+                    ALOGE("Failed to release queue mutex: %s!", strerror(err));
+                break;
+            }
+
+            while (q->closed == 0 && q->requestList == NULL &&
+                q->eventList == NULL) {
+                if ((err = pthread_cond_wait(&q->cond, &q->queueMutex)) != 0)
+                    ALOGE("%s() failed broadcast queue cond: %s!",
+                        __func__, strerror(err));
+            }
+
+            /* eventList is prioritized, smallest abstime first. */
+            if (q->closed == 0 && q->requestList == NULL && q->eventList) {
+                int err = 0;
+                err = pthread_cond_timedwait(&q->cond, &q->queueMutex, &q->eventList->abstime);
+                if (err && err != ETIMEDOUT)
+                    ALOGE("%s() timedwait returned unexpected error: %s",
+                __func__, strerror(err));
+            }
+
+            if (q->closed != 0) {
+                if ((err = pthread_mutex_unlock(&q->queueMutex)) != 0)
+                    ALOGE("%s(): Failed to release queue mutex: %s!",
+                        __func__, strerror(err));
+                continue; /* Catch the closed bit at the top of the loop. */
+            }
+
+            e = NULL;
+            r = NULL;
+
+            clock_gettime(CLOCK_MONOTONIC, &ts);
+
+            if (q->eventList != NULL &&
+                timespec_cmp(q->eventList->abstime, ts, < )) {
+                e = q->eventList;
+                q->eventList = e->next;
+            }
+
+            if (q->requestList != NULL) {
+                r = q->requestList;
+                q->requestList = r->next;
+            }
+
+            if ((err = pthread_mutex_unlock(&q->queueMutex)) != 0)
+                ALOGE("%s(): Failed to release queue mutex: %s!",
+                    __func__, strerror(err));
+
+            if (e) {
+                e->eventCallback(e->param);
+                free(e);
+            }
+
+            if (r) {
+                processRequest(r->request, r->data, r->datalen, r->token);
+                freeRequestData(r->request, r->data, r->datalen);
+                free(r);
+            }
+        }
+
+        at_close();
+        ALOGE("%s() Re-opening after close", __func__);
+    }
+    return NULL;
+}
+
+#ifdef RIL_SHLIB
+const RIL_RadioFunctions *RIL_Init(const struct RIL_Env *env, int argc,
+                                   char **argv)
+{
+    int opt;
+    int port = -1;
+    char *loophost = NULL;
+    const char *device_path = NULL;
+    struct queueArgs *queueArgs;
+    pthread_attr_t attr;
+
+    s_rilenv = env;
+
+    /* By default, use USB1 as audio channel */
+    strcpy(sAudioDevice,"/dev/ttyUSB1");
+
+    D("%s() entering...", __func__);
+
+    while (-1 != (opt = getopt(argc, argv, "z:p:d:v:"))) {
+        switch (opt) {
+            case 'z':
+                loophost = optarg;
+                D("%s() Using loopback host %s..", __func__, loophost);
+                break;
+
+            case 'p':
+                port = atoi(optarg);
+                if (port == 0) {
+                    usage(argv[0]);
+                    return NULL;
+                }
+                D("%s() Opening loopback port %d", __func__, port);
+                break;
+
+            case 'd':
+                device_path = optarg;
+                D("%s() Opening tty device %s", __func__, device_path);
+                break;
+
+            case 'v':
+                strcpy(sAudioDevice,optarg);
+                D("%s() Opening voice tty device %s", __func__, sAudioDevice);
+                break;
+
+            default:
+                usage(argv[0]);
+                return NULL;
+        }
+    }
+
+    if (port < 0 && device_path == NULL) {
+        usage(argv[0]);
+        return NULL;
+    }
+
+    queueArgs = (struct queueArgs*) malloc(sizeof(struct queueArgs));
+    memset(queueArgs, 0, sizeof(struct queueArgs));
+
+    queueArgs->device_path = device_path;
+    queueArgs->port = port;
+    queueArgs->loophost = loophost;
+
+    pthread_attr_init(&attr);
+    pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
+
+    pthread_create(&s_tid_queueRunner, &attr, queueRunner, queueArgs);
+
+    return &s_callbacks;
+}
+
+#else /* RIL_SHLIB */
+int main (int argc, char **argv)
+{
+    int opt;
+    int port = -1;
+    char *loophost = NULL;
+    const char *device_path = NULL;
+    struct queueArgs *queueArgs;
+
+    /* By default, use USB1 as audio channel */
+    strcpy(sAudioDevice,"/dev/ttyUSB1");
+
+    D("%s() entering...", __func__);
+
+    while (-1 != (opt = getopt(argc, argv, "z:p:d:v:"))) {
+        switch (opt) {
+            case 'z':
+                loophost = optarg;
+                D("%s() Using loopback host %s..", __func__, loophost);
+                break;
+
+            case 'p':
+                port = atoi(optarg);
+                if (port == 0) {
+                    usage(argv[0]);
+                    return 0;
+                }
+                D("%s() Opening loopback port %d", __func__, port);
+                break;
+
+            case 'd':
+                device_path = optarg;
+                D("%s() Opening tty device %s", __func__, device_path);
+                break;
+
+            case 'v':
+                strcpy(sAudioDevice,optarg);
+                D("%s() Opening voice tty device %s", __func__, sAudioDevice);
+                break;
+
+            default:
+                usage(argv[0]);
+                return 0;
+        }
+    }
+
+    if (port < 0 && device_path == NULL) {
+        usage(argv[0]);
+        return 0;
+    }
+
+    queueArgs = (struct queueArgs*) malloc(sizeof(struct queueArgs));
+    memset(queueArgs, 0, sizeof(struct queueArgs));
+
+    queueArgs->device_path = device_path;
+    queueArgs->port = port;
+    queueArgs->loophost = loophost;
+
+    RIL_register(&s_callbacks);
+
+    queueRunner(queueArgs);
+
+    return 0;
+}
+#endif
diff --git a/huaweigeneric-ril/misc.c b/huaweigeneric-ril/misc.c
new file mode 100644 (file)
index 0000000..6a67223
--- /dev/null
@@ -0,0 +1,169 @@
+/*
+**
+** 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.
+*/
+
+#include <string.h>
+#include <errno.h>
+
+#include "misc.h"
+
+/** Returns 1 if line starts with prefix, 0 if it does not. */
+int strStartsWith(const char *line, const char *prefix)
+{
+    for (; *line != '\0' && *prefix != '\0'; line++, prefix++)
+        if (*line != *prefix)
+            return 0;
+
+    return *prefix == '\0';
+}
+
+/**
+  * Very simple function that extract and returns whats between ElementBeginTag
+  * and ElementEndTag.
+  *
+  * Optional ppszRemainingDocument that can return a pointer to the remaining
+  * of the document to be "scanned". This can be used if subsequent
+  * scanning/searching is desired.
+  *
+  * This function is used to extract the parameters from the XML result
+  * returned by U3xx during a PDP Context setup, and used to parse the
+  * tuples of operators returned from AT+COPS.
+  *
+  *  const char* document        - Document to be scanned
+  *  const char* elementBeginTag - Begin tag to scan for, return whats
+  *                                between begin/end tags
+  *  const char* elementEndTag   - End tag to scan for, return whats
+  *                                between begin/end tags
+  *  char** remainingDocumen t   - Can return the a pointer to the remaining
+  *                                of pszDocument, this parameter is optional
+  *
+  *  return char* containing whats between begin/end tags, allocated on the
+  *               heap, need to free this.
+  *               return NULL if nothing is found.
+  */
+char *getFirstElementValue(const char* document,
+                                  const char* elementBeginTag,
+                                  const char* elementEndTag,
+                                  char** remainingDocument)
+{
+    char* value = NULL;
+    char* start = NULL;
+    char* end = NULL;
+
+    if (document != NULL && elementBeginTag != NULL && elementEndTag != NULL) {
+        start = strstr(document, elementBeginTag);
+        if (start != NULL) {
+            end = strstr(start, elementEndTag);
+            if (end != NULL) {
+                int n = strlen(elementBeginTag);
+                int m = end - (start + n);
+                value = (char*) malloc((m + 1) * sizeof(char));
+                strncpy(value, (start + n), m);
+                value[m] = (char) 0;
+
+                /* Optional, return a pointer to the remaining document,
+                   to be used when document contains many tags with same name. */
+                if (remainingDocument != NULL)
+                    *remainingDocument = end + strlen(elementEndTag);
+        }
+    }
+    }
+    return value;
+}
+
+char char2nib(char c)
+{
+    if (c >= 0x30 && c <= 0x39)
+        return c - 0x30;
+
+    if (c >= 0x41 && c <= 0x46)
+        return c - 0x41 + 0xA;
+
+    if (c >= 0x61 && c <= 0x66)
+        return c - 0x61 + 0xA;
+
+    return 0;
+}
+
+int stringToBinary(/*in*/ const char *string,
+                   /*in*/ size_t len,
+                   /*out*/ unsigned char *binary)
+{
+    int pos;
+    const char *it;
+    const char *end = &string[len];
+
+    if (end < string)
+        return -EINVAL;
+
+    if (len & 1)
+        return -EINVAL;
+
+    for (pos = 0, it = string; it != end; ++pos, it += 2) {
+        binary[pos] = char2nib(it[0]) << 4 | char2nib(it[1]);
+    }
+    return 0;
+}
+
+int binaryToString(/*in*/ const unsigned char *binary,
+                   /*in*/ size_t len,
+                   /*out*/ char *string)
+{
+    int pos;
+    const unsigned char *it;
+    const unsigned char *end = &binary[len];
+    static const char nibbles[] =
+        {'0', '1', '2', '3', '4', '5', '6', '7',
+         '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'};
+
+    if (end < binary)
+        return -EINVAL;
+
+    for (pos = 0, it = binary; it != end; ++it, pos += 2) {
+        string[pos + 0] = nibbles[*it >> 4];
+        string[pos + 1] = nibbles[*it & 0x0f];
+    }
+    string[pos] = 0;
+    return 0;
+}
+
+int parseTlv(/*in*/ const char *stream,
+             /*in*/ const char *end,
+             /*out*/ struct tlv *tlv)
+{
+#define TLV_STREAM_GET(stream, end, p)  \
+    do {                                \
+        if (stream + 1 >= end)          \
+            goto underflow;             \
+        p = ((unsigned)char2nib(stream[0]) << 4)  \
+          | ((unsigned)char2nib(stream[1]) << 0); \
+        stream += 2;                    \
+    } while (0)
+
+    size_t size;
+
+    TLV_STREAM_GET(stream, end, tlv->tag);
+    TLV_STREAM_GET(stream, end, size);
+    if (stream + size * 2 > end)
+        goto underflow;
+    tlv->data = &stream[0];
+    tlv->end  = &stream[size * 2];
+    return 0;
+
+underflow:
+    return -EINVAL;
+#undef TLV_STREAM_GET
+}
diff --git a/huaweigeneric-ril/misc.h b/huaweigeneric-ril/misc.h
new file mode 100644 (file)
index 0000000..f0a3da4
--- /dev/null
@@ -0,0 +1,53 @@
+/* //device/system/reference-ril/misc.h
+**
+** 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.
+*/
+
+#ifndef _MISC_H
+#define _MISC_H 1
+
+struct tlv {
+    unsigned    tag;
+    const char *data;
+    const char *end;
+};
+
+/** Returns 1 if line starts with prefix, 0 if it does not. */
+int strStartsWith(const char *line, const char *prefix);
+
+char *getFirstElementValue(const char* document,
+                           const char* elementBeginTag,
+                           const char* elementEndTag,
+                           char** remainingDocument);
+
+char char2nib(char c);
+
+int stringToBinary(/*in*/ const char *string,
+                   /*in*/ size_t len,
+                   /*out*/ unsigned char *binary);
+
+int binaryToString(/*in*/ const unsigned char *binary,
+                   /*in*/ size_t len,
+                   /*out*/ char *string);
+
+int parseTlv(/*in*/ const char *stream,
+             /*in*/ const char *end,
+             /*out*/ struct tlv *tlv);
+#define TLV_DATA(tlv, pos) (((unsigned)char2nib(tlv.data[(pos) * 2 + 0]) << 4) | \
+                            ((unsigned)char2nib(tlv.data[(pos) * 2 + 1]) << 0))
+
+#define NUM_ELEMS(x) (sizeof(x) / sizeof(x[0]))
+
+#endif
diff --git a/huaweigeneric-ril/requestdatahandler.c b/huaweigeneric-ril/requestdatahandler.c
new file mode 100644 (file)
index 0000000..4bd9263
--- /dev/null
@@ -0,0 +1,344 @@
+/*
+**
+** Copyright (C) ST-Ericsson AB 2008-2009
+** 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 the Android ril daemon and reference RIL by
+** The Android Open Source Project.
+**
+** Heavily modified for ST-Ericsson U300 modems.
+** Author: Christian Bejram <christian.bejram@stericsson.com>
+*/
+
+#include <stdlib.h>
+#include <telephony/ril.h>
+#include <assert.h>
+
+#define LOG_TAG "RIL"
+#include <utils/Log.h>
+
+/* Handler functions. The names are because we cheat by including
+ * ril_commands.h from rild. In here we generate local allocations
+ * of the data representations, as well as free:ing them after
+ * they've been handled.
+ *
+ * This design might not be ideal, but considering the alternatives,
+ * it's good enough.
+ */
+static void *dummyDispatch(void *data, size_t datalen);
+
+#define dispatchCdmaSms dummyDispatch
+#define dispatchCdmaSmsAck dummyDispatch
+#define dispatchCdmaBrSmsCnf dummyDispatch
+#define dispatchRilCdmaSmsWriteArgs dummyDispatch
+#define dispatchCdmaSubscriptionSource dummyDispatch
+#define dispatchVoiceRadioTech dummyDispatch
+
+static void *dispatchCallForward(void *data, size_t datalen);
+static void *dispatchDial(void *data, size_t datalen);
+static void *dispatchSIM_IO(void *data, size_t datalen);
+static void *dispatchSmsWrite(void *data, size_t datalen);
+static void *dispatchString(void *data, size_t datalen);
+static void *dispatchStrings(void *data, size_t datalen);
+static void *dispatchRaw(void *data, size_t datalen);
+static void *dispatchVoid(void *data, size_t datalen);
+static void *dispatchGsmBrSmsCnf(void *data, size_t datalen);
+
+#define dispatchInts dispatchRaw
+
+static void dummyResponse(void);
+
+#define responseCallForwards dummyResponse
+#define responseCallList dummyResponse
+#define responseCellList dummyResponse
+#define responseContexts dummyResponse
+#define responseInts dummyResponse
+#define responseRaw dummyResponse
+#define responseSIM_IO dummyResponse
+#define responseSMS dummyResponse
+#define responseString dummyResponse
+#define responseStrings dummyResponse
+#define responseVoid dummyResponse
+
+#define responseSimStatus dummyResponse
+#define responseRilSignalStrength dummyResponse
+#define responseDataCallList dummyResponse
+#define responseGsmBrSmsCnf dummyResponse
+#define responseCdmaBrSmsCnf dummyResponse
+#define responseCellInfoList dummyResponse
+#define responseSetupDataCall responseStrings
+
+#define dispatchDataCall dispatchStrings
+#define dispatchSetInitialAttachApn dispatchVoid
+#define dispatchImsSms dispatchVoid
+
+/*
+should be looked into how dispatchDataCall and others really should be handled,
+not just use dispatchStrings but it seems to work. This feature
+was added in android 3.0, might be just be a nicer way handling
+things seperatly. This has no impact on older versions and should
+work as it is on both (hence we can't really remove code from
+dispatchStrings if it should be in distpatchDataCall).
+
+static void *dispatchDataCall(void *data, size_t datalen){
+...
+} */
+
+typedef struct CommandInfo {
+    int requestId;
+    void *(*dispatchFunction) (void *data, size_t datalen);
+    void (*responseFunction) (void);
+} CommandInfo;
+
+/* RILD made me do it! */
+static CommandInfo s_commandInfo[] = {
+#include <ril_commands.h>
+};
+
+static void *dummyDispatch(void *data, size_t datalen)
+{
+    (void) data; (void) datalen;
+    return 0;
+}
+
+static void dummyResponse(void)
+{
+    return;
+}
+
+/**
+ * dupRequestData will copy the data pointed to by *data, returning a pointer
+ * to a freshly allocated representation of the data.
+ */
+void *dupRequestData(int requestId, void *data, size_t datalen)
+{
+    CommandInfo *ci = &s_commandInfo[requestId];
+
+    return ci->dispatchFunction(data, datalen);
+}
+
+static void *dispatchCallForward(void *data, size_t datalen)
+{
+    RIL_CallForwardInfo *ret = dispatchRaw(data, datalen);
+
+    if (ret->number)
+        ret->number = strdup(ret->number);
+
+    return ret;
+}
+
+static void *dispatchDial(void *data, size_t datalen)
+{
+    RIL_Dial *ret = dispatchRaw(data, datalen);
+
+    if (ret->address)
+        ret->address = strdup(ret->address);
+
+    return ret;
+}
+
+static void *dispatchSIM_IO(void *data, size_t datalen)
+{
+    RIL_SIM_IO_v6 *ret = dispatchRaw(data, datalen);
+
+    if (ret->path)
+        ret->path = strdup(ret->path);
+    if (ret->data)
+        ret->data = strdup(ret->data);
+    if (ret->pin2)
+        ret->pin2 = strdup(ret->pin2);
+    if (ret->aidPtr)
+        ret->aidPtr = strdup(ret->aidPtr);
+
+    return ret;
+}
+
+static void *dispatchSmsWrite(void *data, size_t datalen)
+{
+    RIL_SMS_WriteArgs *ret = dispatchRaw(data, datalen);
+
+    if (ret->pdu)
+        ret->pdu = strdup(ret->pdu);
+
+    if (ret->smsc)
+        ret->smsc = strdup(ret->smsc);
+
+    return ret;
+}
+
+static void *dispatchString(void *data, size_t datalen)
+{
+    (void) data; (void) datalen;
+    assert(datalen == sizeof(char *));
+
+    if (data)
+        return strdup((char *) data);
+
+    return NULL;
+}
+
+static void *dispatchStrings(void *data, size_t datalen)
+{
+    char **a = (char **)data;
+    char **ret;
+    int strCount = datalen / sizeof(char *);
+    int i;
+
+    assert((datalen % sizeof(char *)) == 0);
+
+    ret = malloc(strCount * sizeof(char *));
+    memset(ret, 0, sizeof(char *) * strCount);
+
+    for (i = 0; i < strCount; i++) {
+        if (a[i])
+            ret[i] = strdup(a[i]);
+    }
+
+    return (void *) ret;
+}
+
+static void *dispatchGsmBrSmsCnf(void *data, size_t datalen)
+{
+    RIL_GSM_BroadcastSmsConfigInfo **a =
+        (RIL_GSM_BroadcastSmsConfigInfo **) data;
+    int count;
+    void **ret;
+    int i;
+
+    count = datalen / sizeof(RIL_GSM_BroadcastSmsConfigInfo *);
+
+    ret = malloc(count * sizeof(RIL_GSM_BroadcastSmsConfigInfo *));
+    memset(ret, 0, sizeof(*ret));
+
+    for (i = 0; i < count; i++) {
+        if (a[i])
+            ret[i] = dispatchRaw(a[i], sizeof(RIL_GSM_BroadcastSmsConfigInfo));
+    }
+
+    return ret;
+}
+
+static void *dispatchRaw(void *data, size_t datalen)
+{
+    void *ret = malloc(datalen);
+    memcpy(ret, data, datalen);
+
+    return (void *) ret;
+}
+
+static void *dispatchVoid(void *data, size_t datalen)
+{
+    (void) data; (void) datalen;
+    return NULL;
+}
+
+static void freeDial(void *data)
+{
+    RIL_Dial *d = data;
+
+    if (d->address)
+        free(d->address);
+
+    free(d);
+}
+
+static void freeStrings(void *data, size_t datalen)
+{
+    int count = datalen / sizeof(char *);
+    int i;
+
+    for (i = 0; i < count; i++) {
+        if (((char **) data)[i])
+            free(((char **) data)[i]);
+    }
+
+    free(data);
+}
+
+static void freeGsmBrSmsCnf(void *data, size_t datalen)
+{
+    int count = datalen / sizeof(RIL_GSM_BroadcastSmsConfigInfo);
+    int i;
+
+    for (i = 0; i < count; i++) {
+        if (((RIL_GSM_BroadcastSmsConfigInfo **) data)[i])
+            free(((RIL_GSM_BroadcastSmsConfigInfo **) data)[i]);
+    }
+
+    free(data);
+}
+
+static void freeSIM_IO(void *data)
+{
+    RIL_SIM_IO_v6 *sio = data;
+
+    if (sio->path)
+        free(sio->path);
+    if (sio->data)
+        free(sio->data);
+    if (sio->pin2)
+        free(sio->pin2);
+    if (sio->aidPtr)
+        free(sio->aidPtr);
+
+    free(sio);
+}
+
+static void freeSmsWrite(void *data)
+{
+    RIL_SMS_WriteArgs *args = data;
+
+    if (args->pdu)
+        free(args->pdu);
+
+    if (args->smsc)
+        free(args->smsc);
+
+    free(args);
+}
+
+static void freeCallForward(void *data)
+{
+    RIL_CallForwardInfo *cff = data;
+
+    if (cff->number)
+        free(cff->number);
+
+    free(cff);
+}
+
+void freeRequestData(int requestId, void *data, size_t datalen)
+{
+    CommandInfo *ci = &s_commandInfo[requestId];
+
+    if (ci->dispatchFunction == dispatchInts ||
+        ci->dispatchFunction == dispatchRaw ||
+        ci->dispatchFunction == dispatchString) {
+        free(data);
+    } else if (ci->dispatchFunction == dispatchStrings) {
+        freeStrings(data, datalen);
+    } else if (ci->dispatchFunction == dispatchSIM_IO) {
+        freeSIM_IO(data);
+    } else if (ci->dispatchFunction == dispatchDial) {
+        freeDial(data);
+    } else if (ci->dispatchFunction == dispatchVoid) {
+    } else if (ci->dispatchFunction == dispatchCallForward) {
+        freeCallForward(data);
+    } else if (ci->dispatchFunction == dispatchSmsWrite) {
+        freeSmsWrite(data);
+    } else if (ci->dispatchFunction == dispatchGsmBrSmsCnf) {
+        freeGsmBrSmsCnf(data, datalen);
+    }
+}
diff --git a/huaweigeneric-ril/requestdatahandler.h b/huaweigeneric-ril/requestdatahandler.h
new file mode 100644 (file)
index 0000000..54893cb
--- /dev/null
@@ -0,0 +1,25 @@
+/*
+**
+** Copyright (C) ST-Ericsson AB 2009
+**
+** 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.
+**
+** Author: Christian Bejram <christian.bejram@stericsson.com>
+*/
+#ifndef _REQUESTDATAHANDLER_H
+#define _REQUESTDATAHANDLER_H 1
+
+void *dupRequestData(int requestId, void *data, size_t datalen);
+void freeRequestData(int requestId, void *data, size_t datalen);
+
+#endif
diff --git a/huaweigeneric-ril/sms.c b/huaweigeneric-ril/sms.c
new file mode 100644 (file)
index 0000000..aebe05a
--- /dev/null
@@ -0,0 +1,285 @@
+//
+// Convert CDMA SMS to GSM and vice versa.
+// By Martin Johnson <M.J.Johnson@massey.ac.nz>
+// GPL
+//
+#include <stdio.h>
+#include <string.h>
+#include "sms_gsm.h"
+
+#ifndef nodroid
+#define LOG_TAG "SMS_RIL"
+#include <utils/Log.h>
+#else
+#define ALOGD printf
+#define ALOGE printf
+#define ALOGI printf
+#endif
+
+int hex2int(char c)
+{
+    if (c>'9') return c-'A'+10;
+    return c-'0';
+}
+
+int getbit(char *s,int b)
+{
+    int byte=b/4;
+    int bit=b%4;
+
+    int data=hex2int(s[byte]);
+    if (data&(1<<(3-bit))) return 1;
+        else return 0;
+}
+
+const char hextable[17]="0123456789ABCDEF";
+
+void setbit(char *s,int b, int val)
+{
+    int byte=b/4;
+    int bit=b%4;
+
+    s[byte]=hextable[hex2int(s[byte]) | (val<<(3-bit))] ;
+}
+
+int getbits(char *s,int startbit,int nbits)
+{
+    int val=0;
+    int i;
+
+    for (i=0;i<nbits;i++)
+        val = val | (getbit(s,startbit+i)<<(nbits-i-1));
+    return val;
+}
+
+void setbits(char *s,int startbit,int nbits,int val)
+{
+    int i;
+
+    for (i=0;i<nbits;i++)
+        setbit(s,startbit+i,(val>>(nbits-i-1))&1);
+}
+
+const char decode_table[17]=".1234567890*#...";
+
+void decode_number(char *msg, int length, char *no)
+{
+
+    int ndigits=getbits(msg,2,8);
+    int j,digit;
+
+    for (j=0;j<ndigits;j++)
+        *no++=decode_table[getbits(msg,10+j*4,4)];
+    *no=0;
+}
+
+int encode_digit(int d)
+{
+    int i;
+    for (i=0;i<16;i++)
+        if (decode_table[i]==d)
+            return i;
+    return 0;
+}
+
+int encode_number(char *msg, char *no)
+{
+    unsigned int i;
+    int digit;
+
+    setbits(msg, 0, 2, 0);
+    setbits(msg, 2, 8, strlen(no));
+    for (i=0;i<strlen(no);i++)
+        setbits(msg,10+i*4, 4, encode_digit(no[i]));
+    return (10+i*4+7)/8;
+}
+
+void get_code_and_length(char *msg, int *code, int *length)
+{
+    *code=hex2int(msg[0])*16+hex2int(msg[1]);
+    *length=hex2int(msg[2])*16+hex2int(msg[3]);
+}
+
+void decode_bearer_data(char *msg, int length, char *message, int *is_vm)
+{
+    int i=0,j;
+    int code,sublength;
+
+    while (i<length) {
+        get_code_and_length(msg+i*2,&code,&sublength);
+        if (code==1) {
+            int encoding=getbits(msg+i*2+4,0,5);
+            int nchars=getbits(msg+i*2+4,5,8);
+            if (encoding==2 || encoding==3) {
+                for (j=0;j<nchars;j++)
+                    *message++=getbits(msg+i*2+4,13+7*j,7);
+            } else if (encoding==8 || encoding==0) {
+                for (j=0;j<nchars;j++)
+                    *message++=getbits(msg+i*2+4,13+8*j,8);
+            } else if (encoding==4) {
+                for (j=0;j<nchars;j++)
+                    *message++=getbits(msg+i*2+6,13+16*j,8);
+            } else {
+                strcpy(message,"bad SMS encoding");
+                ALOGE("Bad encoding: %d",encoding);
+                message+=16;
+            }
+            *message=0;
+        } else if (code == 11 && sublength == 1) {
+            int msgs;
+            if (is_vm) {
+                *is_vm = 1;
+                msgs = hex2int(msg[i*2+4])+16*hex2int(msg[i*2+5]);
+                if (msgs)
+                    *is_vm |= 0x10;
+            }
+        }
+        i+=sublength+2;
+    }
+}
+
+int encode_bearer_data(char *msg, char *data)
+{
+    int msgid=0;
+    unsigned int i;
+        int b;
+    char *start=msg;
+
+    for (i=0;i<strlen(data);i++)
+        msgid+=data[i];
+
+    setbits(msg,0,8,0); // message id
+    setbits(msg,8,8,3); // 3 bytes
+    setbits(msg,16,4,2); // 2 means send
+    setbits(msg,20,16,msgid); // use message sum for id
+    msg+=10;
+    setbits(msg,0,8,01); // user data
+    setbits(msg,16,5,02); // set encoding
+    setbits(msg,21,8,strlen(data)); // length
+    b=29;
+    for (i=0;i<strlen(data);i++) {
+        setbits(msg,b,7,data[i]);
+        b=b+7;
+    }
+    setbits(msg,8,8,(b+7)/8-2);
+    msg=msg+2*((b+7)/8);
+    setbits(msg,0,24,0x80100);
+    setbits(msg,24,24,0x0D0100);
+    msg=msg+12;
+    return (msg-start)/2;
+}
+
+void decode_cdma_sms(char *pdu, char *from, char *message, int *is_vm)
+{
+    unsigned int i=1;
+    int code,length;
+    strcpy(from,"000000"); // in case something fails
+    strcpy(message,"UNKNOWN");
+
+    if (is_vm)
+        *is_vm = 0;
+
+    while (i*2<strlen(pdu)) {
+        get_code_and_length(pdu+i*2,&code,&length);
+        if (code==2) // from
+            decode_number(pdu+i*2+4,length,from);
+        if (code==8) // bearer_data
+            decode_bearer_data(pdu+i*2+4,length,message,is_vm);
+        i+=length+2;
+    }
+}
+
+void encode_cdma_sms(char *pdu, char *to, char *message)
+{
+    int length;
+
+    ALOGE_IF(strlen(message)>160, "Error: Message String too long");
+    memset(pdu, '0', 512);
+    setbits(pdu,0,16,0);
+    setbits(pdu,16,24,0x021002);
+    pdu=pdu+10;
+    setbits(pdu,0,8,0x04);
+    length=encode_number(pdu+4, to);
+    setbits(pdu,8,8,length);
+    pdu=pdu+length*2+4;
+    setbits(pdu,0,24,0x060100);
+    pdu=pdu+6;
+    setbits(pdu,0,8,0x08);
+    length=encode_bearer_data(pdu+4, message);
+    ALOGE_IF(length>255, "Error: Message Hex too long");
+    setbits(pdu,8,8,length);
+    pdu=pdu+length*2+4;
+    *pdu=0;
+}
+
+char **cdma_to_gsmpdu(char *msg)
+{
+    char from[256];
+    char message[256];
+    static char hexpdu[1024];
+    static char *hexpdus[16];
+    int i=0;
+    int is_vm=0;
+    decode_cdma_sms(msg,from,message,&is_vm);
+    //  if(strlen(message)>=160) message[159]=0;
+    ALOGD("CDMA Message:%s From:%s\n",message,from);
+    SmsAddressRec smsaddr;
+    SmsTimeStampRec smstime;
+    if (is_vm) {
+        /* voicemail notifications must have a 4 byte address */
+        if (is_vm & 0x10) {
+            /* set message waiting indicator */
+            strcpy(from, "1100");
+        } else {
+            /* clear message waiting indicator */
+            strcpy(from, "0100");
+        }
+    }
+    sms_address_from_str(&smsaddr,from,strlen(from));
+    if (is_vm) {
+        /* voicemail notifications have a clear bottom nibble in toa
+         * and an alphanumeric address type */
+        smsaddr.toa = 0xd0;
+    }
+    sms_timestamp_now(&smstime);
+    SmsPDU *pdu=smspdu_create_deliver_utf8((const unsigned char *)message,strlen(message),&smsaddr,&smstime);
+    //hexpdu=malloc(512);
+    char *s=hexpdu;
+    while (*pdu) {
+        smspdu_to_hex(*pdu, s,512);
+        hexpdus[i]=s;
+        s=s+strlen(s)+2;
+        smspdu_free(*pdu);
+        i++;
+        pdu++;
+    }
+    hexpdus[i]=0;
+    return hexpdus;
+}
+
+char *gsm_to_cdmapdu(char *msg)
+{
+    char to[256];
+    char message[256];
+    static char hexpdu[512];
+    SmsAddressRec smsaddr;
+    sms_address_from_str(&smsaddr,"000000",6);
+
+    SmsPDU pdu=smspdu_create_from_hex( msg, strlen(msg) );
+    if (smspdu_get_receiver_address(pdu,&smsaddr)<0) {
+        ALOGE("Error: no receiver address");
+        smspdu_get_sender_address(pdu,&smsaddr);
+    }
+    sms_address_to_str(&smsaddr,to,256);
+    if (to[0]=='+') { // convert + to 00 otherwise international sms doesn't work
+        memmove(to+1,to,255);
+        to[0]='0';
+        to[1]='0';
+    }
+    int length=smspdu_get_text_message(pdu, message, 256);
+    message[length]=0;
+    smspdu_free(pdu);
+    ALOGD("GSM Message:%s To:%s\n",message,to);
+    encode_cdma_sms(hexpdu,to,message);
+    return hexpdu;
+}
diff --git a/huaweigeneric-ril/sms_gsm.c b/huaweigeneric-ril/sms_gsm.c
new file mode 100644 (file)
index 0000000..76bf6fb
--- /dev/null
@@ -0,0 +1,1099 @@
+#include "sms_gsm.h"
+#include "gsm.h"
+#include <memory.h>
+#include <stdlib.h>
+//#include <assert.h>
+
+/* maximum number of data bytes in a SMS data message */
+#define  MAX_USER_DATA_BYTES   140
+
+/* maximum number of 7-bit septets in a SMS text message */
+#define  MAX_USER_DATA_SEPTETS  160
+
+/* size of the user data header in bytes */
+#define  USER_DATA_HEADER_SIZE   6
+
+/** MESSAGE TEXT
+ **/
+int
+sms_utf8_from_message_str( const char*  str, int  strlen, unsigned char*  utf8, int  utf8len )
+{
+    const char*     p       = str;
+    const char*     end     = p + strlen;
+    int             count   = 0;
+    int             escaped = 0;
+
+    while (p < end) {
+        int  c = p[0];
+
+        /* read the value from the string */
+        p += 1;
+        if (c >= 128) {
+            if ((c & 0xe0) == 0xc0)
+                c &= 0x1f;
+            else if ((c & 0xf0) == 0xe0)
+                c &= 0x0f;
+            else
+                c &= 0x07;
+            p++;
+            while (p < end && (p[0] & 0xc0) == 0x80) {
+                c = (c << 6) | (p[0] & 0x3f);
+                p++;
+            }
+        }
+        if (escaped) {
+            switch (c) {
+                case '\\':
+                    break;
+                case 'n':  /* \n is line feed */
+                    c = 10;
+                    break;
+
+                case 'x':  /* \xNN, where NN is a 2-digit hexadecimal value */
+                    if (p+2 > end)
+                        return -1;
+                    c = gsm_hex2_to_byte( p );
+                    if (c < 0)
+                        return -1;
+                    break;
+
+                case 'u':  /* \uNNNN where NNNN is a 4-digiti hexadecimal value */
+                    if (p + 4 > end)
+                        return -1;
+                    c = gsm_hex4_to_short( p );
+                    if (c < 0)
+                        return -1;
+                    break;
+
+                default:  /* invalid escape, return -1 */
+                    return -1;
+            }
+            escaped = 0;
+        } else if (c == '\\') {
+            escaped = 1;
+            continue;
+        }
+
+        /* now, try to write it to the destination */
+        if (c < 128) {
+            if (count < utf8len)
+                utf8[count] = (byte_t) c;
+            count += 1;
+        } else if (c < 0x800) {
+            if (count < utf8len)
+                utf8[count]   = (byte_t)(0xc0 | ((c >> 6) & 0x1f));
+            if (count+1 < utf8len)
+                utf8[count+1] = (byte_t)(0x80 | (c & 0x3f));
+            count += 2;
+        } else {
+            if (count < utf8len)
+                utf8[count]   = (byte_t)(0xc0 | ((c >> 12) & 0xf));
+            if (count+1 < utf8len)
+                utf8[count+1] = (byte_t)(0x80 | ((c >> 6) & 0x3f));
+            if (count+2 < utf8len)
+                utf8[count+2] = (byte_t)(0x80 | (c & 0x3f));
+            count += 3;
+        }
+    }
+
+    if (escaped)   /* bad final escape */
+        return -1;
+
+    return count;
+}
+
+/** TIMESTAMPS
+ **/
+void
+sms_timestamp_now( SmsTimeStamp  stamp )
+{
+    time_t     now_time = time(NULL);
+    struct tm  gm       = *(gmtime(&now_time));
+    struct tm  local    = *(localtime(&now_time));
+    int        tzdiff   = 0;
+
+    stamp->data[0] = gsm_int_to_bcdi( local.tm_year % 100 );
+    stamp->data[1] = gsm_int_to_bcdi( local.tm_mon+1 );
+    stamp->data[2] = gsm_int_to_bcdi( local.tm_mday );
+    stamp->data[3] = gsm_int_to_bcdi( local.tm_hour );
+    stamp->data[4] = gsm_int_to_bcdi( local.tm_min );
+    stamp->data[5] = gsm_int_to_bcdi( local.tm_sec );
+
+    tzdiff = (local.tm_hour*4 + local.tm_min/15) - (gm.tm_hour*4 + gm.tm_min/15);
+    if (local.tm_yday > gm.tm_yday)
+        tzdiff += 24*4;
+    else if (local.tm_yday < gm.tm_yday)
+        tzdiff -= 24*4;
+
+    stamp->data[6] = gsm_int_to_bcdi( tzdiff >= 0 ? tzdiff : -tzdiff );
+    if (tzdiff < 0)
+        stamp->data[6] |= 0x08;
+}
+
+int
+sms_timestamp_to_tm( SmsTimeStamp  stamp, struct tm*  tm )
+{
+    int  tzdiff;
+
+    tm->tm_year = gsm_int_from_bcdi( stamp->data[0] );
+    if (tm->tm_year < 50)
+        tm->tm_year += 100;
+    tm->tm_mon  = gsm_int_from_bcdi( stamp->data[1] ) -1;
+    tm->tm_mday = gsm_int_from_bcdi( stamp->data[2] );
+    tm->tm_hour = gsm_int_from_bcdi( stamp->data[3] );
+    tm->tm_min  = gsm_int_from_bcdi( stamp->data[4] );
+    tm->tm_sec  = gsm_int_from_bcdi( stamp->data[5] );
+
+    tm->tm_isdst = -1;
+
+    tzdiff = gsm_int_from_bcdi( stamp->data[6] & 0xf7 );
+    if (stamp->data[6] & 0x8)
+        tzdiff = -tzdiff;
+
+    return tzdiff;
+}
+
+static void
+gsm_rope_add_timestamp( GsmRope  rope, const SmsTimeStampRec*  ts )
+{
+    gsm_rope_add( rope, ts->data, 7 );
+}
+
+
+/** SMS ADDRESSES
+ **/
+int
+sms_address_to_str( SmsAddress  address, char*  str, int  strlen )
+{
+    bytes_t      data = address->data;
+    if(address->toa == 0x91)
+        *str++='+';
+    int i;
+    char c;
+    for (i=0;i<address->len;i++) {
+        c=data[i/2];
+        if(i&1) c=c>>4;
+        *str++='0'+(c&15);
+    }
+    *str=0;
+    return 1;
+}
+
+
+int
+sms_address_from_str( SmsAddress  address, const char*  src, int  srclen )
+{
+    const char*  end   = src + srclen;
+    int          shift = 0, len = 0;
+    bytes_t      data = address->data;
+
+    address->len = 0;
+    address->toa = 0x81;
+
+    if (src >= end)
+        return -1;
+
+    if (src[0] == '+') {
+        address->toa = 0x91;
+        if (++src == end)
+            goto Fail;
+    }
+
+    memset( address->data, 0, sizeof(address->data) );
+
+    shift = 0;
+
+    while (src < end) {
+        int  c = *src++ - '0';
+
+        if ( (unsigned)c >= 10 ||
+              data >= address->data + sizeof(address->data) )
+            goto Fail;
+
+        data[0] |= c << shift;
+        len   += 1;
+        shift += 4;
+        if (shift == 8) {
+            shift = 0;
+            data += 1;
+        }
+    }
+    if (shift != 0)
+        data[0] |= 0xf0;
+
+    address->len = len;
+    return 0;
+
+Fail:
+    return -1;
+}
+
+int
+sms_address_from_bytes( SmsAddress  address, const unsigned char*  buf, int  buflen )
+{
+    int   len = sizeof(address->data), num_digits;
+
+    if (buflen < 2)
+        return -1;
+
+    address->len = num_digits = buf[0];
+    address->toa = buf[1];
+
+    len = (num_digits+1)/2;
+    if ( len > sizeof(address->data) )
+        return -1;
+
+    memcpy( address->data, buf+2, len );
+    return 0;
+}
+
+int
+sms_address_to_bytes( SmsAddress  address, unsigned char*  buf, int  bufsize )
+{
+    int  len = (address->len + 1)/2 + 2;
+
+    if (buf == NULL)
+        bufsize = 0;
+
+    if (bufsize < 1) goto Exit;
+    buf[0] = address->len;
+
+    if (bufsize < 2) goto Exit;
+    buf[1] = address->toa;
+
+    buf     += 2;
+    bufsize -= 2;
+    if (bufsize > len-2)
+        bufsize = len - 2;
+
+    memcpy( buf, address->data, bufsize );
+Exit:
+    return len;
+}
+
+int
+sms_address_from_hex  ( SmsAddress  address, const char*  hex, int  hexlen )
+{
+    const char*  hexend = hex + hexlen;
+    int          nn, len, num_digits;
+
+    if (hexlen < 4)
+        return -1;
+
+    address->len = num_digits = gsm_hex2_to_byte( hex );
+    address->toa = gsm_hex2_to_byte( hex+2 );
+    hex += 4;
+
+    len = (num_digits + 1)/2;
+    if (hex + len*2 > hexend)
+        return -1;
+
+    for ( nn = 0; nn < len; nn++ )
+        address->data[nn] = gsm_hex2_to_byte( hex + nn*2 );
+
+    return 0;
+}
+
+int
+sms_address_to_hex    ( SmsAddress  address, char*   hex, int  hexlen )
+{
+    int  len = (address->len + 1)/2 + 2;
+    int  nn;
+
+    if (hex == NULL)
+        hexlen = 0;
+
+    if (hexlen < 2) goto Exit;
+    gsm_hex_from_byte( hex, address->len );
+    if (hexlen < 4) goto Exit;
+    gsm_hex_from_byte( hex+2, address->toa );
+    hex    += 4;
+    hexlen -= 4;
+    if ( hexlen > 2*(len - 2) )
+        hexlen = (len - 2)/2;
+
+    for ( nn = 0; nn < hexlen; nn += 2 )
+        gsm_hex_from_byte( hex+nn, address->data[nn/2] );
+
+Exit:
+    return len*2;
+}
+
+static void
+gsm_rope_add_address( GsmRope  rope, const SmsAddressRec*  addr )
+{
+    gsm_rope_add_c( rope, addr->len );
+    gsm_rope_add_c( rope, addr->toa );
+    gsm_rope_add( rope, addr->data, (addr->len+1)/2 );
+    if (addr->len & 1) {
+        if (!rope->error && rope->data != NULL)
+            rope->data[ rope->pos-1 ] |= 0xf0;
+    }
+}
+
+
+/** SMS PARSER
+ **/
+static int
+sms_get_byte( cbytes_t  *pcur, cbytes_t  end )
+{
+    cbytes_t  cur    = *pcur;
+    int       result = -1;
+
+    if (cur < end) {
+        result = cur[0];
+        *pcur  = cur + 1;
+    }
+    return result;
+}
+
+/* parse a service center address, returns -1 in case of error */
+static int
+sms_get_sc_address( cbytes_t   *pcur,
+                    cbytes_t    end,
+                    SmsAddress  address )
+{
+    cbytes_t  cur    = *pcur;
+    int       result = -1;
+
+    if (cur < end) {
+        int  len = cur[0];
+        int  dlen, adjust = 0;
+
+        cur += 1;
+
+        if (len == 0) {   /* empty address */
+            address->len = 0;
+            address->toa = 0x00;
+            result       = 0;
+            goto Exit;
+        }
+
+        if (cur + len > end) {
+            goto Exit;
+        }
+
+        address->toa = *cur++;
+        len         -= 1;
+        result       = 0;
+
+        for (dlen = 0; dlen < len; dlen+=1) {
+            int  c = cur[dlen];
+            int  v;
+
+            adjust = 0;
+            if (dlen >= sizeof(address->data)) {
+                result = -1;
+                break;
+            }
+
+            v = (c & 0xf);
+            if (v >= 0xe)
+                break;
+
+            adjust              = 1;
+            address->data[dlen] = (byte_t) c;
+
+            v = (c >> 4) & 0xf;
+            if (v >= 0xe) {
+                break;
+            }
+        }
+        address->len = 2*dlen + adjust;
+    }
+Exit:
+    if (!result)
+        *pcur = cur;
+
+    return result;
+}
+
+static int
+sms_skip_sc_address( cbytes_t   *pcur,
+                     cbytes_t    end )
+{
+    cbytes_t  cur    = *pcur;
+    int       result = -1;
+    int       len;
+
+    if (cur >= end)
+        goto Exit;
+
+    len  = cur[0];
+    cur += 1 + len;
+    if (cur > end)
+        goto Exit;
+
+    *pcur  = cur;
+    result = 0;
+Exit:
+    return result;
+}
+
+/* parse a sender/receiver address, returns -1 in case of error */
+static int
+sms_get_address( cbytes_t   *pcur,
+                 cbytes_t    end,
+                 SmsAddress  address )
+{
+    cbytes_t  cur    = *pcur;
+    int       result = -1;
+    int       len, dlen;
+
+    if (cur >= end)
+        goto Exit;
+
+    dlen = *cur++;
+
+    if (dlen == 0) {
+        address->len = 0;
+        address->toa = 0;
+        result       = 0;
+        goto Exit;
+    }
+
+    if (cur + 1 + (dlen+1)/2 > end)
+        goto Exit;
+
+    address->len = dlen;
+    address->toa = *cur++;
+
+    len = (dlen + 1)/2;
+    if (len > sizeof(address->data))
+        goto Exit;
+
+    memcpy( address->data, cur, len );
+    cur   += len;
+    result = 0;
+
+Exit:
+    if (!result)
+        *pcur = cur;
+
+    return result;
+}
+
+static int
+sms_skip_address( cbytes_t   *pcur,
+                  cbytes_t    end  )
+{
+    cbytes_t  cur    = *pcur;
+    int       result = -1;
+    int       dlen;
+
+    if (cur + 2 > end)
+        goto Exit;
+
+    dlen = cur[0];
+    cur += 2 + (dlen + 1)/2;
+    if (cur > end)
+        goto Exit;
+
+    result = 0;
+Exit:
+    return result;
+}
+
+/* parse a service center timestamp */
+static int
+sms_get_timestamp( cbytes_t     *pcur,
+                   cbytes_t      end,
+                   SmsTimeStamp  ts )
+{
+    cbytes_t  cur = *pcur;
+
+    if (cur + 7 > end)
+        return -1;
+
+    memcpy( ts->data, cur, 7 );
+    *pcur = cur + 7;
+    return 0;
+}
+
+static int
+sms_skip_timestamp( cbytes_t  *pcur,
+                    cbytes_t   end )
+{
+    cbytes_t  cur = *pcur;
+
+    if (cur + 7 > end)
+        return -1;
+
+    *pcur = cur + 7;
+    return 0;
+}
+
+
+/** SMS PDU
+ **/
+
+typedef struct SmsPDURec {
+    bytes_t  base;
+    bytes_t  end;
+    bytes_t  tpdu;
+} SmsPDURec;
+
+void
+smspdu_free( SmsPDU  pdu )
+{
+    if (pdu) {
+        free( pdu->base );
+        pdu->base = NULL;
+        pdu->end  = NULL;
+        pdu->tpdu = NULL;
+    }
+}
+
+SmsPduType
+smspdu_get_type( SmsPDU  pdu )
+{
+    cbytes_t  data    = pdu->tpdu;
+    cbytes_t  end     = pdu->end;
+    int       mtiByte = sms_get_byte(&data, end);
+
+    switch (mtiByte & 3) {
+        case 0:  return SMS_PDU_DELIVER;
+        case 1:  return SMS_PDU_SUBMIT;
+        case 2:  return SMS_PDU_STATUS_REPORT;
+        default: return SMS_PDU_INVALID;
+    }
+}
+
+int
+smspdu_get_sender_address( SmsPDU  pdu, SmsAddress  address )
+{
+    cbytes_t  data    = pdu->tpdu;
+    cbytes_t  end     = pdu->end;
+    int       mtiByte = sms_get_byte(&data, end);
+
+    switch (mtiByte & 3) {
+        case 0: /* SMS_PDU_DELIVER; */
+            return sms_get_sc_address( &data, end, address );
+
+        default: return -1;
+    }
+}
+
+int
+smspdu_get_sc_timestamp( SmsPDU  pdu, SmsTimeStamp  ts )
+{
+    cbytes_t  data    = pdu->tpdu;
+    cbytes_t  end     = pdu->end;
+    int       mtiByte = sms_get_byte( &data, end );
+
+    switch (mtiByte & 3) {
+        case 0:  /* SMS_PDU_DELIVER */
+            {
+                SmsAddressRec  address;
+
+                if ( sms_get_sc_address( &data, end, &address ) < 0 )
+                    return -1;
+
+                data += 2;  /* skip protocol identifer + coding scheme */
+
+                return sms_get_timestamp( &data, end, ts );
+            }
+
+        default: return -1;
+    }
+}
+
+int
+smspdu_get_receiver_address( SmsPDU  pdu, SmsAddress  address )
+{
+    cbytes_t  data    = pdu->tpdu;
+    cbytes_t  end     = pdu->end;
+    int       mtiByte = sms_get_byte( &data, end );
+
+    switch (mtiByte & 3) {
+        case 1:  /* SMS_PDU_SUBMIT */
+            {
+                data += 1;  /* skip message reference */
+                return sms_get_address( &data, end, address );
+            }
+
+        default: return -1;
+    }
+}
+
+typedef enum {
+    SMS_CODING_SCHEME_UNKNOWN = 0,
+    SMS_CODING_SCHEME_GSM7,
+    SMS_CODING_SCHEME_UCS2
+
+} SmsCodingScheme;
+
+/* see TS 23.038 Section 5 for details */
+static SmsCodingScheme
+sms_get_coding_scheme( cbytes_t  *pcur,
+                       cbytes_t   end )
+{
+    cbytes_t  cur = *pcur;
+    int       dataCoding;
+
+    if (cur >= end)
+        return SMS_CODING_SCHEME_UNKNOWN;
+
+    dataCoding = *cur++;
+    *pcur      = cur;
+
+    switch (dataCoding >> 4) {
+        case 0x00:
+        case 0x02:
+        case 0x03:
+            return SMS_CODING_SCHEME_GSM7;
+
+        case 0x01:
+            if (dataCoding == 0x10) return SMS_CODING_SCHEME_GSM7;
+            if (dataCoding == 0x11) return SMS_CODING_SCHEME_UCS2;
+            break;
+
+        case 0x04: case 0x05: case 0x06: case 0x07:
+            if (dataCoding & 0x20)           return SMS_CODING_SCHEME_UNKNOWN; /* compressed 7-bits */
+            if (((dataCoding >> 2) & 3) == 0) return SMS_CODING_SCHEME_GSM7;
+            if (((dataCoding >> 2) & 3) == 2) return SMS_CODING_SCHEME_UCS2;
+            break;
+
+        case 0xF:
+            if (!(dataCoding & 4)) return SMS_CODING_SCHEME_GSM7;
+            break;
+    }
+    return SMS_CODING_SCHEME_UNKNOWN;
+}
+
+
+/* see TS 23.040 section 9.2.3.24 for details */
+static int
+sms_get_text_utf8( cbytes_t        *pcur,
+                   cbytes_t         end,
+                   int              hasUDH,
+                   SmsCodingScheme  coding,
+                   GsmRope          rope )
+{
+    cbytes_t  cur    = *pcur;
+    int       result = -1;
+    int       len;
+
+#ifdef nodroid
+    printf("sms_get_text_utf8 %d %d\n",hasUDH,coding);
+#endif
+    if (cur >= end)
+        goto Exit;
+
+    len = *cur++;
+
+    /* skip user data header if any */
+    if ( hasUDH )
+    {
+        int  hlen;
+
+        if (cur >= end)
+            goto Exit;
+
+        hlen = *cur++;
+        if (cur + hlen > end)
+            goto Exit;
+
+        cur += hlen;
+
+        if (coding == SMS_CODING_SCHEME_GSM7)
+            len -= (hlen*2-2);
+        else
+            len -= hlen+1;
+
+        if (len < 0)
+            goto Exit;
+    }
+
+    /* switch the user data header if any */
+    if (coding == SMS_CODING_SCHEME_GSM7) {
+        int  count = utf8_from_gsm7( cur, 0, len, NULL );
+
+        if (rope != NULL)
+        {
+            bytes_t  dst = gsm_rope_reserve( rope, count );
+        if(hasUDH && dst)
+        *dst++=(*cur++)>>1;
+            if (dst != NULL)
+                utf8_from_gsm7( cur, 0, len, dst );
+        }
+        cur += (len+1)/2;
+    } else if (coding == SMS_CODING_SCHEME_UCS2) {
+        int  count = ucs2_to_utf8( cur, len/2, NULL );
+
+        if (rope != NULL)
+        {
+            bytes_t  dst = gsm_rope_reserve( rope, count );
+            if (dst != NULL)
+                ucs2_to_utf8( cur, len/2, dst );
+        }
+        cur += len;
+    }
+    result = 0;
+
+Exit:
+    if (!result)
+        *pcur = cur;
+
+    return result;
+}
+
+/* get the message embedded in a SMS PDU as a utf8 byte array, returns the length of the message in bytes */
+/* or -1 in case of error */
+int
+smspdu_get_text_message( SmsPDU  pdu, unsigned char*  utf8, int  utf8len )
+{
+    cbytes_t  data    = pdu->tpdu;
+    cbytes_t  end     = pdu->end;
+    int       mtiByte = sms_get_byte( &data, end );
+
+    switch (mtiByte & 3) {
+        case 0:  /* SMS_PDU_DELIVER */
+            {
+                SmsAddressRec    address;
+                SmsTimeStampRec  timestamp;
+                SmsCodingScheme  coding;
+                GsmRopeRec       rope[1];
+                int              result;
+
+                if ( sms_get_sc_address( &data, end, &address ) < 0 )
+                    goto Fail;
+
+                data  += 1;  /* skip protocol identifier */
+                coding = sms_get_coding_scheme( &data, end );
+                if (coding == SMS_CODING_SCHEME_UNKNOWN)
+                    goto Fail;
+
+                if ( sms_get_timestamp( &data, end, &timestamp ) < 0 )
+                    goto Fail;
+
+                gsm_rope_init_alloc( rope, 0 );
+                if ( sms_get_text_utf8( &data, end, (mtiByte & 0x40), coding, rope ) < 0 )
+                    goto Fail;
+
+                result = rope->pos;
+                if (utf8len > result)
+                    utf8len = result;
+
+                if (utf8len > 0)
+                    memcpy( utf8, rope->data, utf8len );
+
+                gsm_rope_done( rope );
+                return result;
+            }
+
+        case 1:  /* SMS_PDU_SUBMIT */
+            {
+                SmsAddressRec    address;
+                SmsCodingScheme  coding;
+                GsmRopeRec       rope[1];
+                int              result;
+
+                data += 1;  /* message reference */
+
+                if ( sms_get_address( &data, end, &address ) < 0 )
+                    goto Fail;
+
+                data  += 1;  /* skip protocol identifier */
+                coding = sms_get_coding_scheme( &data, end );
+                if (coding == SMS_CODING_SCHEME_UNKNOWN)
+                    goto Fail;
+
+                gsm_rope_init_alloc( rope, 0 );
+                if ( sms_get_text_utf8( &data, end, (mtiByte & 0x40), coding, rope ) < 0 ) {
+                    gsm_rope_done( rope );
+                    goto Fail;
+                }
+
+                result = rope->pos;
+                if (utf8len > result)
+                    utf8len = result;
+
+                if (utf8len > 0)
+                    memcpy( utf8, rope->data, utf8len );
+
+                gsm_rope_done( rope );
+                return result;
+            }
+    }
+Fail:
+    return -1;
+}
+
+
+static void
+gsm_rope_add_sms_user_header( GsmRope  rope,
+                              int      ref_number,
+                              int      pdu_count,
+                              int      pdu_index )
+{
+    gsm_rope_add_c( rope, 0x05 );     /* total header length == 5 bytes */
+    gsm_rope_add_c( rope, 0x00 );     /* element id: concatenated message reference number */
+    gsm_rope_add_c( rope, 0x03 );     /* element len: 3 bytes */
+    gsm_rope_add_c( rope, (byte_t)ref_number );  /* reference number */
+    gsm_rope_add_c( rope, (byte_t)pdu_count );     /* max pdu index */
+    gsm_rope_add_c( rope, (byte_t)pdu_index+1 );   /* current pdu index */
+}
+
+/* write a SMS-DELIVER PDU into a rope */
+static void
+gsm_rope_add_sms_deliver_pdu( GsmRope                 rope,
+                              cbytes_t                utf8,
+                              int                     utf8len,
+                              int                     use_gsm7,
+                              const SmsAddressRec*    sender_address,
+                              const SmsTimeStampRec*  timestamp,
+                              int                     ref_num,
+                              int                     pdu_count,
+                              int                     pdu_index)
+{
+    int  count;
+    int  coding;
+    int  mtiByte  = 0x20;  /* message type - SMS DELIVER */
+
+    if (pdu_count > 1)
+        mtiByte |= 0x40;  /* user data header indicator */
+
+    gsm_rope_add_c( rope, 0 );        /* no SC Address */
+    gsm_rope_add_c( rope, mtiByte );     /* message type - SMS-DELIVER */
+    gsm_rope_add_address( rope, sender_address );
+    gsm_rope_add_c( rope, 0 );        /* protocol identifier */
+
+    /* data coding scheme - GSM 7 bits / no class - or - 16-bit UCS2 / class 1 */
+    coding = (use_gsm7 ? 0x00 : 0x09);
+
+    gsm_rope_add_c( rope, coding );               /* data coding scheme       */
+    gsm_rope_add_timestamp( rope, timestamp );    /* service center timestamp */
+
+    if (use_gsm7) {
+        bytes_t  dst;
+        int    count = utf8_to_gsm7( utf8, utf8len, NULL, 0 );
+        int    pad   = 0;
+
+        //assert( count <= MAX_USER_DATA_SEPTETS - USER_DATA_HEADER_SIZE );
+
+        if (pdu_count > 1) {
+            int  headerBits    = 6*8;  /* 6 is size of header in bytes */
+            int  headerSeptets = headerBits / 7;
+            if (headerBits % 7 > 0)
+                headerSeptets += 1;
+
+            pad = headerSeptets*7 - headerBits;
+
+            gsm_rope_add_c( rope, count + headerSeptets );
+            gsm_rope_add_sms_user_header(rope, ref_num, pdu_count, pdu_index);
+        } else {
+            gsm_rope_add_c( rope, count );
+        }
+
+        count = (count*7+pad+7)/8;  /* convert to byte count */
+
+        dst = gsm_rope_reserve( rope, count );
+        if (dst != NULL) {
+            utf8_to_gsm7( utf8, utf8len, dst, pad );
+        }
+    } else {
+        bytes_t  dst;
+        int      count = utf8_to_ucs2( utf8, utf8len, NULL );
+
+        //assert( count*2 <= MAX_USER_DATA_BYTES - USER_DATA_HEADER_SIZE );
+
+        if (pdu_count > 1) {
+            gsm_rope_add_c( rope, count*2 + 6 );
+            gsm_rope_add_sms_user_header( rope, ref_num, pdu_count, pdu_index );
+        } else {
+            gsm_rope_add_c( rope, count*2 );
+        }
+
+        gsm_rope_add_c( rope, count*2 );
+        dst = gsm_rope_reserve( rope, count*2 );
+        if (dst != NULL) {
+            utf8_to_ucs2( utf8, utf8len, dst );
+        }
+    }
+}
+
+
+static SmsPDU
+smspdu_create_deliver( cbytes_t               utf8,
+                       int                    utf8len,
+                       int                    use_gsm7,
+                       const SmsAddressRec*   sender_address,
+                       const SmsTimeStampRec* timestamp,
+                       int                    ref_num,
+                       int                    pdu_count,
+                       int                    pdu_index )
+{
+    SmsPDU      p;
+    GsmRopeRec  rope[1];
+    int         size;
+
+    p = calloc( sizeof(*p), 1 );
+    if (!p) goto Exit;
+
+    gsm_rope_init( rope );
+    gsm_rope_add_sms_deliver_pdu( rope, utf8, utf8len, use_gsm7,
+                                 sender_address, timestamp,
+                                 ref_num, pdu_count, pdu_index);
+    if (rope->error)
+        goto Fail;
+
+    gsm_rope_init_alloc( rope, rope->pos );
+
+    gsm_rope_add_sms_deliver_pdu( rope, utf8, utf8len, use_gsm7,
+                                 sender_address, timestamp,
+                                 ref_num, pdu_count, pdu_index );
+
+    p->base = gsm_rope_done_acquire( rope, &size );
+    if (p->base == NULL)
+        goto Fail;
+
+    p->end  = p->base + size;
+    p->tpdu = p->base + 1;
+Exit:
+    return p;
+
+Fail:
+    free(p);
+    return NULL;
+}
+
+
+void
+smspdu_free_list( SmsPDU*  pdus )
+{
+    if (pdus) {
+        int  nn;
+        for (nn = 0; pdus[nn] != NULL; nn++)
+            smspdu_free( pdus[nn] );
+
+        free( pdus );
+    }
+}
+
+
+
+SmsPDU*
+smspdu_create_deliver_utf8( const unsigned char*   utf8,
+                            int                    utf8len,
+                            const SmsAddressRec*   sender_address,
+                            const SmsTimeStampRec* timestamp )
+{
+    SmsTimeStampRec  ts0;
+    int              use_gsm7;
+    int              count, block;
+    int              num_pdus = 0;
+    int              leftover = 0;
+    SmsPDU*          list = NULL;
+
+    static unsigned char  ref_num = 0;
+
+    if (timestamp == NULL) {
+        sms_timestamp_now( &ts0 );
+        timestamp = &ts0;
+    }
+
+    /* can we encode the message with the GSM 7-bit alphabet ? */
+    use_gsm7 = utf8_check_gsm7( utf8, utf8len );
+
+    /* count the number of SMS PDUs we'll need */
+    block = MAX_USER_DATA_SEPTETS - USER_DATA_HEADER_SIZE;
+
+    if (use_gsm7) {
+        count = utf8_to_gsm7( utf8, utf8len, NULL, 0 );
+    } else {
+        count = utf8_to_ucs2( utf8, utf8len, NULL );
+        block = MAX_USER_DATA_BYTES - USER_DATA_HEADER_SIZE;
+    }
+
+    num_pdus = count / block;
+    leftover = count - num_pdus*block;
+    if (leftover > 0)
+        num_pdus += 1;
+
+    list = calloc( sizeof(SmsPDU*), count + 1 );
+    if (list == NULL)
+        return NULL;
+
+    /* now create each SMS PDU */
+    {
+        cbytes_t   src     = utf8;
+        cbytes_t   src_end = utf8 + utf8len;
+        int        nn;
+
+        for (nn = 0; nn < num_pdus; nn++) {
+            int       skip = block;
+            cbytes_t  src_next;
+
+            if (leftover > 0 && nn == num_pdus-1)
+                skip = leftover;
+
+            src_next = utf8_skip( src, src_end, skip );
+            list[nn] = smspdu_create_deliver( src, src_next - src, use_gsm7, sender_address, timestamp,
+                                              ref_num, num_pdus, nn );
+            if (list[nn] == NULL)
+                goto Fail;
+
+            src = src_next;
+        }
+    }
+
+    ref_num++;
+
+Exit:
+    return list;
+
+Fail:
+    smspdu_free_list(list);
+    return NULL;
+}
+
+
+SmsPDU
+smspdu_create_from_hex( const char*  hex, int  hexlen )
+{
+    SmsPDU    p;
+    cbytes_t  data;
+
+    p = calloc( sizeof(*p), 1 );
+    if (!p) goto Exit;
+
+    p->base = malloc( (hexlen+1)/2 );
+    if (p->base == NULL) {
+        free(p);
+        p = NULL;
+        goto Exit;
+    }
+
+    gsm_hex_to_bytes( hex, hexlen, p->base );
+    p->end = p->base + (hexlen+1)/2;
+
+    data = p->base;
+    if ( sms_skip_sc_address( &data, p->end ) < 0 )
+        goto Fail;
+
+    p->tpdu = (bytes_t) data;
+
+Exit:
+    return p;
+
+Fail:
+    free(p->base);
+    free(p);
+    return NULL;
+}
+
+int
+smspdu_to_hex( SmsPDU  pdu, char*  hex, int  hexlen )
+{
+    int  result = (pdu->end - pdu->base)*2;
+    int  nn;
+
+    if (hexlen > result)
+        hexlen = result;
+
+    for (nn = 0; nn*2 < hexlen; nn++) {
+        gsm_hex_from_byte( &hex[nn*2], pdu->base[nn] );
+    }
+    return result;
+}
diff --git a/huaweigeneric-ril/sms_gsm.h b/huaweigeneric-ril/sms_gsm.h
new file mode 100644 (file)
index 0000000..e521803
--- /dev/null
@@ -0,0 +1,85 @@
+#ifndef _android_sms_h
+#define _android_sms_h
+
+#include <time.h>
+
+/** MESSAGE TEXT
+ **/
+/* convert a quoted message text into a utf8 string. Note: you can use 'str' as the destination buffer
+ * with the current implementation. always return the number of utf8 bytes corresponding to the original
+ * message string, even if utf8 is NULL and utf8len is 0
+ */
+extern int  sms_utf8_from_message_str( const char*  str, int  strlen, unsigned char*  utf8, int  utf8len );
+
+/** TIMESTAMPS
+ **/
+
+/* An SMS timestamp structure */
+typedef struct {
+    unsigned char  data[7];
+} SmsTimeStampRec, *SmsTimeStamp;
+
+extern void  sms_timestamp_now( SmsTimeStamp  stamp );
+extern int   sms_timestamp_to_tm( SmsTimeStamp  stamp, struct tm*  tm );
+
+/** SMS ADDRESSES
+ **/
+
+#define  SMS_ADDRESS_MAX_SIZE  16
+
+typedef struct {
+    unsigned char  len;
+    unsigned char  toa;
+    unsigned char  data[ SMS_ADDRESS_MAX_SIZE ];
+} SmsAddressRec, *SmsAddress;
+
+extern int  sms_address_from_str( SmsAddress  address, const char*  src, int  srclen );
+extern int  sms_address_to_str( SmsAddress  address, char*  src, int  srclen );
+
+extern int  sms_address_from_bytes( SmsAddress  address, const unsigned char*  buf, int  buflen );
+extern int  sms_address_to_bytes  ( SmsAddress  address, unsigned char*  buf, int  bufsize );
+extern int  sms_address_from_hex  ( SmsAddress  address, const char*  hex, int  hexlen );
+extern int  sms_address_to_hex    ( SmsAddress  address, char*   hex, int  hexsize );
+
+/** SMS PROTOCOL DATA UNITS
+ **/
+
+typedef struct SmsPDURec*   SmsPDU;
+
+extern SmsPDU*  smspdu_create_deliver_utf8( const unsigned char*   utf8,
+                                            int                    utf8len,
+                                            const SmsAddressRec*   sender_address,
+                                            const SmsTimeStampRec* timestamp );
+
+extern void     smspdu_free_list( SmsPDU*  pdus );
+
+extern SmsPDU   smspdu_create_from_hex( const char*  hex, int  hexlen );
+
+extern int      smspdu_to_hex( SmsPDU  pdu, char*  hex, int  hexsize );
+
+/* free a given SMS PDU */
+extern void     smspdu_free( SmsPDU  pdu );
+
+typedef enum {
+    SMS_PDU_INVALID = 0,
+    SMS_PDU_DELIVER,
+    SMS_PDU_SUBMIT,
+    SMS_PDU_STATUS_REPORT
+} SmsPduType;
+
+extern SmsPduType    smspdu_get_type( SmsPDU  pdu );
+
+/* retrieve the sender address of a SMS-DELIVER pdu, returns -1 otherwise */
+extern int  smspdu_get_sender_address( SmsPDU  pdu, SmsAddress  address );
+
+/* retrieve the service center timestamp of a SMS-DELIVER pdu, return -1 otherwise */
+extern int  smspdu_get_sc_timestamp( SmsPDU  pdu, SmsTimeStamp  timestamp );
+
+/* retrieve the receiver address of a SMS-SUBMIT pdu, return -1 otherwise */
+extern int  smspdu_get_receiver_address( SmsPDU  pdu, SmsAddress  address );
+
+/* get the message embedded in a SMS PDU as a utf8 byte array, returns the length of the message in bytes */
+/* or -1 in case of error */
+extern int  smspdu_get_text_message( SmsPDU  pdu, unsigned char*  utf8, int  utf8len );
+
+#endif /* _android_sms_h */
index 1957939..5073f53 100644 (file)
@@ -87,7 +87,7 @@ namespace android {
 #define PRINTBUF_SIZE 8096
 
 // Enable RILC log
-#define RILC_LOG 0
+#define RILC_LOG 1
 
 #if RILC_LOG
     #define startRequest           sprintf(printBuf, "(")
@@ -298,6 +298,7 @@ strdupReadString(Parcel &p) {
 }
 
 static void writeStringToParcel(Parcel &p, const char *s) {
+if (!s) { RLOGE("writeStringToParcel s is null"); }
     char16_t *s16;
     size_t s16_len;
     s16 = strdup8to16(s, &s16_len);
@@ -1037,7 +1038,6 @@ dispatchImsGsmSms(Parcel &p, RequestInfo *pRI, uint8_t retry, int32_t messageRef
     rism.messageRef = messageRef;
 
     startRequest;
-    appendPrintBuf("%sformat=%d,", printBuf, rism.format);
     if (countStrings == 0) {
         // just some non-null pointer
         pStrings = (char **)alloca(sizeof(char *));
@@ -1482,7 +1482,7 @@ static void dispatchSetInitialAttachApn(Parcel &p, RequestInfo *pRI)
 
     startRequest;
     appendPrintBuf("%sapn=%s, protocol=%s, auth_type=%d, username=%s, password=%s",
-            printBuf, pf.apn, pf.protocol, pf.auth_type, pf.username, pf.password);
+            printBuf, pf.apn, pf.protocol, pf.authtype, pf.username, pf.password);
     closeRequest;
     printRequest(pRI->token, pRI->pCI->requestNumber);