--- /dev/null
+CMAKE_MINIMUM_REQUIRED(VERSION 2.6)
+PROJECT(atdongle-plugin C)
+
+#INCLUDE(FindPkgConfig)
+SET(LIBNAME atdongle-plugin)
+SET(PREFIX ${CMAKE_INSTALL_PREFIX})
+SET(EXEC_PREFIX "\${prefix}")
+SET(LIBDIR ${LIB_INSTALL_DIR})
+SET(INCLUDEDIR "\${prefix}/include")
+
+# Set required packages
+INCLUDE(FindPkgConfig)
+pkg_check_modules(pkgs REQUIRED glib-2.0 tcore dlog key-manager)
+
+FOREACH(flag ${pkgs_CFLAGS})
+ SET(EXTRA_CFLAGS "${EXTRA_CFLAGS} ${flag}")
+ENDFOREACH(flag)
+
+INCLUDE_DIRECTORIES(${CMAKE_SOURCE_DIR}/include
+ )
+
+SET(ADDITIONAL_CFLAGS "-Werror -Wall -Wcast-qual -Wno-array-bounds -Wno-empty-body -Wno-ignored-qualifiers -Wshadow -Wwrite-strings -Wswitch-default -Wno-unused-but-set-parameter -Wno-unused-but-set-variable -Wmaybe-uninitialized -Wuninitialized")
+SET(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${EXTRA_CFLAGS} -Wextra -Wno-unused-parameter -Wno-missing-field-initializers -Wdeclaration-after-statement -Wmissing-declarations -Wredundant-decls -Wcast-align")
+SET(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${ADDITIONAL_CFLAGS}")
+
+ADD_DEFINITIONS("-DFEATURE_TLOG_DEBUG")
+ADD_DEFINITIONS ("-DTIZEN_DEBUG_ENABLE")
+ADD_DEFINITIONS("-DTCORE_LOG_TAG=\"ATDONGLE\"")
+
+MESSAGE(${CMAKE_C_FLAGS})
+MESSAGE(${CMAKE_EXE_LINKER_FLAGS})
+
+SET(SRCS
+ src/desc-atdongle.c
+ src/atd_modem.c
+ src/atd_network.c
+ src/atd_sim.c
+ src/atd_ps.c
+)
+
+# library build
+ADD_LIBRARY(${LIBNAME} SHARED ${SRCS})
+TARGET_LINK_LIBRARIES(${LIBNAME} ${pkgs_LDFLAGS})
+SET_TARGET_PROPERTIES(${LIBNAME} PROPERTIES PREFIX "" OUTPUT_NAME ${LIBNAME})
+
+
+# install
+INSTALL(TARGETS ${LIBNAME}
+ LIBRARY DESTINATION ${LIB_INSTALL_DIR}/telephony/plugins/modems)
+
+INSTALL(FILES ${CMAKE_SOURCE_DIR}/LICENSE DESTINATION /usr/share/license RENAME tel-plugin-atdongle)
+
--- /dev/null
+Copyright (c) 2000 - 2014 Samsung Electronics Co., Ltd. All rights reserved.
+
+ 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
+
+ APPENDIX: How to apply the Apache License to your work.
+
+ To apply the Apache License to your work, attach the following
+ boilerplate notice, with the fields enclosed by brackets "[]"
+ replaced with your own identifying information. (Don't include
+ the brackets!) The text should be enclosed in the appropriate
+ comment syntax for the file format. We also recommend that a
+ file or class name and description of purpose be included on the
+ same "printed page" as the copyright notice for easier
+ identification within third-party archives.
+
+ Copyright [yyyy] [name of copyright owner]
+
+ 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.
+
--- /dev/null
+/*
+ * tel-plugin-atdongle
+ *
+ * Copyright (c) 2014 Samsung Electronics Co., Ltd. All rights reserved.
+ *
+ * Contact:
+ *
+ * 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 __ATD_MODEM_H__
+#define __ATD_MODEM_H__
+
+gboolean atd_modem_init(TcorePlugin *p, CoreObject *co_modem);
+void atd_modem_exit(TcorePlugin *p, CoreObject *co_modem);
+
+gboolean modem_power_on(TcorePlugin *plugin);
+
+#endif
--- /dev/null
+/*
+ * tel-plugin-atdongle
+ *
+ * Copyright (c) 2014 Samsung Electronics Co., Ltd. All rights reserved.
+ *
+ * Contact:
+ *
+ * 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 __ATD_NETWORK_H__
+#define __ATD_NETWORK_H__
+
+gboolean atd_network_init(TcorePlugin *p, CoreObject *co_network);
+void atd_network_exit(TcorePlugin *p, CoreObject *co_network);
+
+#endif
--- /dev/null
+/*
+ * tel-plugin-atdongle
+ *
+ * Copyright (c) 2014 Samsung Electronics Co., Ltd. All rights reserved.
+ *
+ * Contact:
+ *
+ * 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 __ATD_PS_H__
+#define __ATD_PS_H__
+
+gboolean atd_ps_init(TcorePlugin *p, CoreObject *co_ps);
+void atd_ps_exit(TcorePlugin *p, CoreObject *co_ps);
+
+#endif /*__ATD_PS_H__*/
--- /dev/null
+/**
+ * tel-plugin-atdongle
+ *
+ * Copyright (c) 2014 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ * Contact:
+ *
+ * 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 __ATD_SIM_H__
+#define __ATD_SIM_H__
+
+gboolean atd_sim_init(TcorePlugin *p, CoreObject *co_sim);
+void atd_sim_exit(TcorePlugin *p, CoreObject *co_sim);
+
+gboolean get_sim_status(TcorePlugin *plugin);
+
+#endif
--- /dev/null
+Name: tel-plugin-atdongle
+Summary: AT dongle plugin for telephony
+Version: 0.1.35
+Release: 1
+Group: Development/Libraries
+License: Apache-2.0
+Source0: tel-plugin-atdongle-%{version}.tar.gz
+Requires(post): /sbin/ldconfig
+Requires(postun):/sbin/ldconfig
+BuildRequires: cmake
+BuildRequires: pkgconfig(glib-2.0)
+BuildRequires: pkgconfig(tcore)
+BuildRequires: pkgconfig(dlog)
+BuildRequires: pkgconfig(vconf)
+BuildRequires: pkgconfig(key-manager)
+
+# This package would be built only TV
+%if "%{?tizen_profile_name}" != "tv"
+ExcludeArch: %{arm} %ix86 x86_64
+%endif
+
+%if "%{?_repository}" == "emulator"
+ExcludeArch: %{arm} %ix86 x86_64
+%endif
+
+%description
+AT dongle plugin for telephony
+
+%prep
+%setup -q
+
+%build
+%cmake .
+make %{?jobs:-j%jobs}
+
+%post
+/sbin/ldconfig
+
+%postun -p /sbin/ldconfig
+
+%install
+rm -rf %{buildroot}
+%make_install
+mkdir -p %{buildroot}/usr/share/license
+cp LICENSE %{buildroot}/usr/share/license/%{name}
+
+%files
+%manifest tel-plugin-atdongle.manifest
+%defattr(-,root,root,-)
+%{_libdir}/telephony/plugins/modems/*
+/usr/share/license/%{name}
--- /dev/null
+/*
+ * tel-plugin-atdongle
+ *
+ * Copyright (c) 2015 Samsung Electronics Co., Ltd. All rights reserved.
+ *
+ * Contact:
+ *
+ * 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 <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <ctype.h>
+#include <glib.h>
+
+#include <tcore.h>
+#include <hal.h>
+#include <core_object.h>
+#include <plugin.h>
+#include <user_request.h>
+#include <queue.h>
+#include <co_modem.h>
+#include <server.h>
+#include <at.h>
+
+#include "atd_modem.h"
+#include "atd_sim.h"
+
+#define IMEI_LEN_MAX 16
+#define DONGLE_NAME_LEN_MAX 32
+
+typedef struct {
+ gboolean device_info;
+ gboolean imei_valid;
+ char imei[IMEI_LEN_MAX+1];
+
+ gboolean modem_power_on;
+ gboolean device_info_valid;
+ char vendor_name[DONGLE_NAME_LEN_MAX+1];
+ char device_name[DONGLE_NAME_LEN_MAX+1];
+} PrivateObject;
+
+static void on_response_get_device_info(TcorePending *p,
+ int data_len, const void *data, void *user_data)
+{
+ const TcoreATResponse *resp = data;
+ UserRequest *ur = tcore_pending_ref_user_request(p);
+ struct tresp_modem_get_device_info res = {0,};
+ CoreObject *co = tcore_pending_ref_core_object(p);
+ PrivateObject *po = tcore_object_ref_user_data(co);
+
+
+ dbg("Entry");
+
+ if (resp && resp->success) {
+ GSList *lines;
+ const char *line;
+ int line_index = 0;
+
+ dbg("RESPONSE OK");
+ dbg("resp->lines[%p]", resp->lines);
+ res.result = TCORE_RETURN_SUCCESS;
+ /* Make device_info TRUE, no need to fetch device info again */
+ po->device_info = TRUE;
+
+ lines = resp->lines;
+ while (lines) {
+ line = (const char *)lines->data;
+ line_index++;
+
+ /* AT+CGMI;+CGMM;+CGSN
+ * <CR><LF><manufacturer><CR><LF>
+ * <CR><LF><model><CR><LF>
+ * <CR><LF><imei><CR><LF>
+ * <CR><LF>OK<CR><LF>
+ */
+ if (line_index == 2) {
+ /* Manufacturer */
+ snprintf(po->vendor_name, DONGLE_NAME_LEN_MAX+1, "%s", line);
+ dbg("Vendor name: [%s]", po->vendor_name);
+
+ po->device_info_valid = TRUE;
+ } else if (line_index == 3) {
+ /* Model */
+ snprintf(po->device_name, DONGLE_NAME_LEN_MAX+1, "%s", line);
+ dbg("Device name: [%s]", po->device_name);
+
+ po->device_info_valid = TRUE;
+ } else if (line_index == 4) {
+ /* IMEI */
+ snprintf(po->imei, IMEI_LEN_MAX+1, "%s", line);
+ dbg("IMEI: [%s]", po->imei);
+
+ po->imei_valid = TRUE;
+ } else {
+ dbg("Neglecting line - [%s]", line);
+ }
+
+ /* Move to next line */
+ lines = lines->next;
+ }
+ } else {
+ err("RESPONSE NOK");
+ res.result = TCORE_RETURN_FAILURE;
+ }
+
+ if (ur) {
+ /* External request */
+ dbg("Send TRESP_MODEM_GET_DEVICE_INFO Response");
+ if (res.result == TCORE_RETURN_SUCCESS) {
+ g_strlcpy(res.device_name, po->device_name,
+ strlen(po->device_name) + 1);
+ g_strlcpy(res.vendor_name, po->vendor_name,
+ strlen(po->vendor_name) + 1);
+ }
+
+ /* Send Response */
+ tcore_user_request_send_response(ur,
+ TRESP_MODEM_GET_DEVICE_INFO,
+ sizeof(struct tresp_modem_get_device_info), &res);
+ }
+}
+
+static TReturn __get_device_info(CoreObject *co, UserRequest *ur)
+{
+ TReturn ret = TCORE_RETURN_FAILURE;
+
+ dbg("Entry");
+
+ /* Get device information */
+ ret = tcore_prepare_and_send_at_request(co,
+ "AT+CGMI;+CGMM;+CGSN", NULL,
+ TCORE_AT_MULTILINE,
+ ur,
+ on_response_get_device_info, NULL,
+ NULL, NULL,
+ 0, NULL, NULL);
+ dbg("ret: [0x%x]", ret);
+
+ return ret;
+}
+
+static enum tcore_hook_return on_hook_modem_plugin_completed(Server *s,
+ CoreObject *source,
+ enum tcore_notification_command command,
+ unsigned int data_len, void *data, void *user_data)
+{
+ CoreObject *co_modem = (CoreObject *)user_data;
+ gboolean dongle_added = TRUE;
+ PrivateObject *po = NULL;
+
+ dbg("Enter");
+ if (co_modem == NULL) {
+ err("CoreObject is NULL");
+ return TCORE_HOOK_RETURN_CONTINUE;
+ }
+ po = tcore_object_ref_user_data(co_modem);
+
+ /*
+ * Send Notification
+ * - Dongle status added
+ */
+ tcore_server_send_notification(s, co_modem,
+ TNOTI_MODEM_DONGLE_STATUS,
+ sizeof(gboolean), &dongle_added);
+
+ /*
+ * Send Notification
+ * - Modem added
+ */
+ tcore_server_send_notification(s, co_modem,
+ TNOTI_MODEM_ADDED,
+ 0, NULL);
+
+ if (po && po->modem_power_on) {
+ struct tnoti_modem_power modem_power = {0, };
+
+ /* Send Notification to TAPI - MODEM_POWER */
+ modem_power.state = MODEM_STATE_ONLINE;
+ dbg("Sending notification - Modem Power state: [ONLINE]");
+
+ tcore_server_send_notification(s, co_modem,
+ TNOTI_MODEM_POWER,
+ sizeof(modem_power), &modem_power);
+
+ /*
+ * Send Notification
+ * - Dongle login success
+ * : some dongles which use their own API require authorization.
+ * Provide same response with them.
+ * it is always true for AT command dongle.
+ */
+ tcore_server_send_notification(s, co_modem,
+ TNOTI_MODEM_DONGLE_LOGIN,
+ sizeof(gboolean), &dongle_added);
+ } else {
+ err("PO [%p] is NULL or modem is not powered on !", po);
+ }
+
+ return TCORE_HOOK_RETURN_CONTINUE;
+}
+
+static enum tcore_hook_return on_hook_sim_init(Server *s, CoreObject *source,
+ enum tcore_notification_command command,
+ unsigned int data_len, void *data, void *user_data)
+{
+ CoreObject *co_modem = (CoreObject *)user_data;
+ const struct tnoti_sim_status *sim = data;
+ PrivateObject *po;
+
+ dbg("Enter");
+
+ if (co_modem == NULL) {
+ err("Invalid core object !");
+ return TCORE_HOOK_RETURN_CONTINUE;
+ }
+
+ if (!sim) {
+ err("Invalid data");
+ return TCORE_HOOK_RETURN_CONTINUE;
+ }
+
+ /* Fetch device info only once*/
+ po = tcore_object_ref_user_data(co_modem);
+ if (po && po->device_info == FALSE) {
+ dbg("Fetch device information");
+ __get_device_info(co_modem, NULL);
+ }
+
+ dbg("Exit");
+ return TCORE_HOOK_RETURN_CONTINUE;
+}
+
+static void on_response_get_imei(TcorePending *p,
+ int data_len, const void *data,
+ void *user_data)
+{
+ const TcoreATResponse *resp = data;
+ struct tresp_modem_get_imei res = {0,};
+ UserRequest *ur = NULL;
+ GSList *tokens = NULL;
+ const char *line;
+ res.result = TCORE_RETURN_FAILURE;
+
+ if (resp && resp->success) {
+ dbg("RESPONSE OK");
+ if (resp->lines) {
+ line = (const char *) resp->lines->data;
+ tokens = tcore_at_tok_new(line);
+ if (g_slist_length(tokens) != 1) {
+ err("invalid message");
+ goto OUT;
+ }
+
+ /* Copy IMEI */
+ g_strlcpy(res.imei, g_slist_nth_data(tokens, 0),
+ IMEI_LEN_MAX + 1);
+ res.result = TCORE_RETURN_SUCCESS;
+ goto OUT;
+ }
+ }
+
+ err("RESPONSE NOK");
+ if (resp && resp->lines) {
+ int response = 0;
+ const char *check = NULL;
+ line = (const char *) resp->lines->data;
+ tokens = tcore_at_tok_new(line);
+ if (g_slist_length(tokens) == 1) {
+ check = g_slist_nth_data(tokens, 0);
+ if (check) {
+ response = atoi(check);
+ /* TODO: CMEE error mapping is required. */
+ err("Response: [%d]", response);
+ } else {
+ err("Unexpected response: [%s]", line);
+ }
+ }
+ }
+OUT:
+ ur = tcore_pending_ref_user_request(p);
+ tcore_user_request_send_response(ur,
+ TRESP_MODEM_GET_IMEI,
+ sizeof(struct tresp_modem_get_imei), &res);
+
+ /* Free resources */
+ tcore_at_tok_free(tokens);
+}
+
+static void on_response_modem_power_on(TcorePending *p,
+ int data_len, const void *data,
+ void *user_data)
+{
+ CoreObject *co = NULL;
+ TcorePlugin *plugin = NULL;
+ gboolean ret;
+ const TcoreATResponse *resp = data;
+
+ if (resp && resp->success) {
+ dbg("RESPONSE OK");
+
+ /* Get SIM status */
+ co = tcore_pending_ref_core_object(p);
+ plugin = tcore_object_ref_plugin(co);
+ ret = get_sim_status(plugin);
+ dbg("ret[%d]", ret);
+ } else {
+ err("RESPONSE NOK");
+ }
+}
+
+static void on_response_power_off(TcorePending *p,
+ int data_len, const void *data,
+ void *user_data)
+{
+ const TcoreATResponse *resp = data;
+ struct tresp_modem_power_off pwr_resp;
+ UserRequest *ur = NULL;
+
+ if (resp && resp->success) {
+ CoreObject *co = NULL;
+ TcoreHal *h = NULL;
+
+ dbg("RESPONSE OK");
+
+ pwr_resp.result = TCORE_RETURN_SUCCESS;
+
+ /* Set HAL state to 'FALSE' */
+ co = tcore_pending_ref_core_object(p);
+ h = tcore_object_get_hal(co);
+ tcore_hal_set_power_state(h, FALSE);
+
+ } else {
+ err("RESPONSE NOK");
+ pwr_resp.result = TCORE_RETURN_FAILURE;
+ }
+
+ ur = tcore_pending_ref_user_request(p);
+ tcore_user_request_send_response(ur,
+ TRESP_MODEM_POWER_OFF,
+ sizeof(struct tresp_modem_power_off), &pwr_resp);
+}
+
+/**< Modem Power ON */
+gboolean modem_power_on(TcorePlugin *plugin)
+{
+ PrivateObject *po = NULL;
+ CoreObject *co_modem = NULL;
+ TReturn ret = TCORE_RETURN_FAILURE;
+ gboolean result = FALSE;
+
+ co_modem = tcore_plugin_ref_core_object(plugin, CORE_OBJECT_TYPE_MODEM);
+ if (co_modem == NULL) {
+ err("Modem Core object is NULL");
+ return result;
+ }
+ po = tcore_object_ref_user_data(co_modem);
+
+ /* Radio ON */
+ ret = tcore_prepare_and_send_at_request(co_modem,
+ "AT+CFUN=1", NULL,
+ TCORE_AT_NO_RESULT,
+ NULL,
+ on_response_modem_power_on, NULL,
+ NULL, NULL,
+ 0, NULL, NULL);
+ if (ret == TCORE_RETURN_SUCCESS) {
+ /* Set Modem Power State to 'ON' */
+ tcore_modem_set_powered(co_modem, TRUE);
+
+ /* To send notification to TAPI - MODEM_POWER */
+ if (po)
+ po->modem_power_on = TRUE;
+
+ result = TRUE;
+ } else {
+ err("ret: [0x%x]", ret);
+ }
+
+ return result;
+}
+
+/**< Modem Power OFF */
+static TReturn atd_modem_power_off(CoreObject *co, UserRequest *ur)
+{
+ TReturn ret = TCORE_RETURN_FAILURE;
+
+ dbg("Entry");
+
+ ret = tcore_prepare_and_send_at_request(co,
+ "AT+CFUN=0", NULL,
+ TCORE_AT_NO_RESULT,
+ ur,
+ on_response_power_off, NULL,
+ NULL, NULL,
+ 0, NULL, NULL);
+ dbg("ret: [0x%x]", ret);
+
+ return ret;
+}
+
+/**< Get IMEI */
+static TReturn atd_modem_get_imei(CoreObject *co, UserRequest *ur)
+{
+ PrivateObject *po = tcore_object_ref_user_data(co);
+ TReturn ret = TCORE_RETURN_FAILURE;
+
+ dbg("Entry");
+
+ if (po->imei_valid == TRUE) {
+ struct tresp_modem_get_imei res = {0,};
+
+ g_strlcpy(res.imei, po->imei, IMEI_LEN_MAX + 1);
+ res.result = TCORE_RETURN_SUCCESS;
+ dbg("IMEI information - Available!!! IMEI [%s]", po->imei);
+
+ /* Send Response */
+ ret = tcore_user_request_send_response(ur,
+ TRESP_MODEM_GET_IMEI,
+ sizeof(struct tresp_modem_get_imei), &res);
+ } else {
+ dbg("IMEI information - Not Available, fetching from Modem");
+
+ ret = tcore_prepare_and_send_at_request(co,
+ "AT+CGSN", NULL,
+ TCORE_AT_NUMERIC,
+ ur,
+ on_response_get_imei, NULL,
+ NULL, NULL,
+ 0, NULL, NULL);
+ }
+
+ dbg("ret: [0x%x]", ret);
+ return ret;
+}
+
+/**< Get Device information */
+static TReturn atd_modem_get_device_info(CoreObject *co, UserRequest *ur)
+{
+ PrivateObject *po = tcore_object_ref_user_data(co);
+ TReturn ret = TCORE_RETURN_FAILURE;
+ dbg("Entry");
+
+ if (po->device_info_valid == TRUE) {
+ struct tresp_modem_get_device_info res = {0,};
+
+ dbg("Device information - Available");
+
+ res.result = TCORE_RETURN_SUCCESS;
+ g_strlcpy(res.device_name, po->device_name,
+ strlen(po->device_name) + 1);
+ g_strlcpy(res.vendor_name, po->vendor_name,
+ strlen(po->vendor_name) + 1);
+ dbg("Vendor name: [%s] Device name: [%s]",
+ po->vendor_name, po->device_name);
+
+ /* Send Response */
+ ret = tcore_user_request_send_response(ur,
+ TRESP_MODEM_GET_DEVICE_INFO,
+ sizeof(struct tresp_modem_get_device_info), &res);
+ } else {
+ dbg("Device information - Not Available, fetching from Modem");
+
+ ret = __get_device_info(co, ur);
+ dbg("ret: [0x%x]", ret);
+ }
+
+ return ret;
+}
+
+/**< Modem Operations */
+static struct tcore_modem_operations modem_ops = {
+ .power_on = NULL,
+ .power_off = atd_modem_power_off,
+ .power_reset = NULL,
+ .power_low = NULL,
+ .set_flight_mode = NULL,
+ .get_imei = atd_modem_get_imei,
+ .get_version = NULL,
+ .get_sn = NULL,
+ .dun_pin_ctrl = NULL,
+ .get_flight_mode = NULL,
+ .get_device_info = atd_modem_get_device_info,
+};
+
+/**< Modem init function */
+gboolean atd_modem_init(TcorePlugin *p, CoreObject *co_modem)
+{
+ Server *s = tcore_plugin_ref_server(tcore_object_ref_plugin(co_modem));
+ PrivateObject *po;
+
+ dbg("Enter");
+
+ /* Set operations */
+ tcore_modem_set_ops(co_modem, &modem_ops, TCORE_OPS_TYPE_CP);
+
+ po = g_malloc0(sizeof(PrivateObject));
+ tcore_object_link_user_data(co_modem, po);
+
+ /* Wait for sim init */
+ tcore_server_add_notification_hook(s,
+ TNOTI_SIM_STATUS,
+ on_hook_sim_init, co_modem);
+
+ /*
+ * Wait for adding modem plugin completion
+ * - to Send dongle/modem 'added' notification
+ */
+ tcore_server_add_notification_hook(s,
+ TNOTI_SERVER_ADDED_MODEM_PLUGIN_COMPLETED,
+ on_hook_modem_plugin_completed, co_modem);
+
+ return TRUE;
+}
+
+/**< Modem exit function */
+void atd_modem_exit(TcorePlugin *p, CoreObject *co_modem)
+{
+ Server *s = tcore_plugin_ref_server(tcore_object_ref_plugin(co_modem));
+ PrivateObject *po;
+ gboolean dongle_added = FALSE;
+
+ dbg("Enter");
+
+ /*
+ * Send Notification
+ * - Dongle logout :
+ * : some dongles which use their own API require authorization.
+ * Provide same response with them.
+ * reset its value to false.
+ * - Dongle removed notification
+ */
+ tcore_server_remove_notification_hook(s, on_hook_sim_init);
+ tcore_server_remove_notification_hook(s, on_hook_modem_plugin_completed);
+
+ tcore_server_send_notification(s, co_modem,
+ TNOTI_MODEM_DONGLE_LOGIN,
+ sizeof(gboolean), &dongle_added);
+ tcore_server_send_notification(s, co_modem,
+ TNOTI_MODEM_DONGLE_STATUS,
+ sizeof(gboolean), &dongle_added);
+
+ /* Unset 'ops' */
+ tcore_modem_set_ops(co_modem, NULL, TCORE_OPS_TYPE_CP);
+
+ po = tcore_object_ref_user_data(co_modem);
+ g_free(po);
+
+ dbg("Exit");
+}
--- /dev/null
+/*
+ * tel-plugin-atdongle
+ *
+ * Copyright (c) 2015 Samsung Electronics Co., Ltd. All rights reserved.
+ *
+ * Contact:
+ *
+ * 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 <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <glib.h>
+
+#include <tcore.h>
+#include <hal.h>
+#include <core_object.h>
+#include <plugin.h>
+#include <user_request.h>
+#include <queue.h>
+#include <co_network.h>
+#include <co_ps.h>
+#include <server.h>
+#include <storage.h>
+#include <util.h>
+#include <at.h>
+
+#include "atd_network.h"
+
+#define RSSI_POLLING_TIMEOUT 30
+
+#define AT_COPS_ACT_GSM 0 /* GSM */
+#define AT_COPS_ACT_GSM_COMPACT 1 /* GSM Compact */
+#define AT_COPS_ACT_UTRAN 2 /* UTRAN */
+#define AT_COPS_ACT_GSM_EGPRS 3 /* GSM w/EGPRS */
+#define AT_COPS_ACT_UTRAN_HSDPA 4 /* UTRAN w/HSDPA */
+#define AT_COPS_ACT_UTRAN_HSUPA 5 /* UTRAN w/HSUPA */
+#define AT_COPS_ACT_UTRAN_HSDPA_HSUPA 6 /* UTRAN w/HSDPA and HSUPA */
+#define AT_COPS_ACT_E_UTRAN 7 /* E-UTRAN */
+#define AT_COPS_ACT_VALUE_MAX 8
+
+struct network_reg_status_cb_data {
+ struct tnoti_network_registration_status regist_status;
+};
+
+typedef struct {
+ gboolean subscribe_creg_event;
+ gboolean subscribe_cgreg_event;
+ guint rssi_src_id;
+} PrivateObject;
+
+static TReturn get_serving_network(CoreObject *co, UserRequest *ur);
+static void __handle_ps_network_status(CoreObject *co, int stat);
+static void __handle_cs_network_status(CoreObject *co, int stat);
+
+static int __convert_rssi_bar(int rssi)
+{
+ int rssi_dBm = 0, rssi_bar = 0;
+ int table[5] = {-113, -106, -100, -90, -80}; /* (0~5 level) */
+
+ /* <rssi>
+ * 0 : -113 dBm or less
+ * 1 : -111 dBm
+ * 2~30 : -109 ~ -53 dBm
+ * 31 : -51 dBm or greater
+ * 99 : not known or not detectable
+ */
+ rssi_dBm = -113 + rssi*2;
+
+ if (rssi_dBm > table[4])
+ rssi_bar = 5; /* antenna bar : 5 */
+ else if (rssi_dBm > table[3])
+ rssi_bar = 4; /* antenna bar : 4 */
+ else if (rssi_dBm > table[2])
+ rssi_bar = 3; /* antenna bar : 3 */
+ else if (rssi_dBm > table[1])
+ rssi_bar = 2; /* antenna bar : 2 */
+ else if (rssi_dBm > table[0])
+ rssi_bar = 1; /* antenna bar : 1 */
+ else
+ rssi_bar = 0; /* antenna bar : 0 */
+
+ dbg("rssi[%d, %d(dBm)] -> rssi_bar[%d]", rssi, rssi_dBm, rssi_bar);
+
+ return rssi_bar;
+}
+
+static unsigned int lookup_tbl_access_technology[] = {
+ [AT_COPS_ACT_GSM] = NETWORK_ACT_GSM,
+ [AT_COPS_ACT_GSM_COMPACT] = NETWORK_ACT_GSM,
+ [AT_COPS_ACT_UTRAN] = NETWORK_ACT_UTRAN,
+ [AT_COPS_ACT_GSM_EGPRS] = NETWORK_ACT_EGPRS,
+ [AT_COPS_ACT_UTRAN_HSDPA] = NETWORK_ACT_UTRAN,
+ [AT_COPS_ACT_UTRAN_HSUPA] = NETWORK_ACT_UTRAN,
+ [AT_COPS_ACT_UTRAN_HSDPA_HSUPA] = NETWORK_ACT_UTRAN,
+ [AT_COPS_ACT_E_UTRAN] = NETWORK_ACT_GSM_UTRAN,
+};
+
+static enum telephony_network_service_domain_status
+ __mapping_network_status(int stat)
+{
+ switch (stat) {
+ case 0:
+ case 4:
+ return NETWORK_SERVICE_DOMAIN_STATUS_NO;
+
+ case 1:
+ return NETWORK_SERVICE_DOMAIN_STATUS_FULL;
+
+ case 2:
+ return NETWORK_SERVICE_DOMAIN_STATUS_SEARCH;
+
+ case 3:
+ return NETWORK_SERVICE_DOMAIN_STATUS_EMERGENCY;
+
+ case 5:
+ return NETWORK_SERVICE_DOMAIN_STATUS_FULL;
+
+ default:
+ return NETWORK_SERVICE_DOMAIN_STATUS_NO;
+ }
+}
+
+static enum telephony_network_service_type
+ _get_service_type(enum telephony_network_service_type prev_type,
+ int act, int cs_status, int ps_status)
+{
+ enum telephony_network_service_type ret;
+ dbg("Enter");
+
+ ret = prev_type;
+
+ switch (act) {
+ case NETWORK_ACT_UNKNOWN:
+ ret = NETWORK_SERVICE_TYPE_UNKNOWN;
+ break;
+ case NETWORK_ACT_GSM:
+ ret = NETWORK_SERVICE_TYPE_2G;
+ break;
+ case NETWORK_ACT_UMTS:
+ ret = NETWORK_SERVICE_TYPE_3G;
+ break;
+ case NETWORK_ACT_LTE:
+ ret = NETWORK_SERVICE_TYPE_LTE;
+ break;
+ default:
+ break;
+ }
+
+ if (cs_status == NETWORK_SERVICE_DOMAIN_STATUS_NO &&
+ ps_status == NETWORK_SERVICE_DOMAIN_STATUS_NO) {
+ ret = NETWORK_SERVICE_TYPE_NO_SERVICE;
+ } else if (cs_status == NETWORK_SERVICE_DOMAIN_STATUS_SEARCH ||
+ ps_status == NETWORK_SERVICE_DOMAIN_STATUS_SEARCH) {
+ if (cs_status == NETWORK_SERVICE_DOMAIN_STATUS_FULL ||
+ ps_status == NETWORK_SERVICE_DOMAIN_STATUS_FULL) {
+ /* no change */
+ } else {
+ ret = NETWORK_SERVICE_TYPE_SEARCH;
+ }
+ } else if (cs_status == NETWORK_SERVICE_DOMAIN_STATUS_EMERGENCY ||
+ ps_status == NETWORK_SERVICE_DOMAIN_STATUS_EMERGENCY) {
+ if (cs_status == NETWORK_SERVICE_DOMAIN_STATUS_FULL ||
+ ps_status == NETWORK_SERVICE_DOMAIN_STATUS_FULL) {
+ /* no change */
+ } else {
+ ret = NETWORK_SERVICE_TYPE_EMERGENCY;
+ }
+ }
+ dbg("service type[%d]", ret);
+
+ return ret;
+}
+
+static void __ps_set(TcorePlugin *p, int status)
+{
+ CoreObject *co_ps;
+
+ co_ps = tcore_plugin_ref_core_object(p, CORE_OBJECT_TYPE_PS);
+ if (co_ps == NULL) {
+ err("No PS Core Object on plugin");
+ return;
+ }
+
+ if (status == NETWORK_SERVICE_DOMAIN_STATUS_FULL)
+ tcore_ps_set_online(co_ps, TRUE);
+ else
+ tcore_ps_set_online(co_ps, FALSE);
+}
+
+static void __on_response_get_act_information(TcorePending *p, int data_len,
+ const void *data, void *user_data)
+{
+ const TcoreATResponse *resp = data;
+ CoreObject *co;
+ GSList *tokens = NULL;
+ const char *line;
+ int AcT = 0;
+ int value = 0;
+ char *pResp = NULL;
+ enum telephony_network_service_type service_type;
+ struct network_reg_status_cb_data *reg_status = user_data;
+ TcorePlugin *plugin = NULL;
+ TReturn ret = TCORE_RETURN_FAILURE;
+ struct tnoti_ps_protocol_status noti = {0,};
+
+ /* +COPS: <mode>[,<format>,<oper>[,<rat>]] */
+ if (!reg_status) {
+ err("invalid cb data");
+ return;
+ }
+
+ co = tcore_pending_ref_core_object(p);
+ if (resp->success <= 0) {
+ err("RESPONSE NOK");
+ goto OUT;
+ }
+
+ dbg("RESPONSE OK");
+ if (g_slist_length(resp->lines) < 1) {
+ err("invalid message format");
+ goto OUT;
+ }
+
+ line = (const char *)resp->lines->data;
+ tokens = tcore_at_tok_new(line);
+
+ /* mode */
+ if ((pResp = tcore_at_tok_nth(tokens, 0)))
+ dbg("mode : %s", pResp);
+
+ /*
+ * Skip <format>, <oper>
+ * Parse rat (act)
+ */
+ if ((pResp = tcore_at_tok_nth(tokens, 3))) {
+ dbg("AcT : %s", pResp);
+ if (!pResp) {
+ err("ACT is invalid");
+ goto OUT;
+ }
+ value = atoi(pResp);
+ if (value < AT_COPS_ACT_VALUE_MAX) {
+ AcT = lookup_tbl_access_technology[value];
+ tcore_network_set_access_technology(co, AcT);
+ } else {
+ err("ACT is invalid or missing");
+ goto OUT;
+ }
+ }
+
+ tcore_network_get_service_type(co, &service_type);
+ dbg("prev_service_type = 0x%x", service_type);
+
+ service_type = _get_service_type(service_type, AcT,
+ reg_status->regist_status.cs_domain_status,
+ reg_status->regist_status.ps_domain_status);
+
+ dbg("new_service_type = 0x%x", service_type);
+ tcore_network_set_service_type(co, service_type);
+ reg_status->regist_status.service_type = service_type;
+
+ plugin = tcore_object_ref_plugin(co);
+ tcore_server_send_notification(tcore_plugin_ref_server(plugin),
+ co, TNOTI_NETWORK_REGISTRATION_STATUS,
+ sizeof(struct tnoti_network_registration_status),
+ &(reg_status->regist_status));
+
+ /* Get PLMN ID needed to application */
+ if ((NETWORK_SERVICE_DOMAIN_STATUS_FULL
+ == reg_status->regist_status.cs_domain_status) ||
+ NETWORK_SERVICE_DOMAIN_STATUS_FULL
+ == reg_status->regist_status.ps_domain_status) {
+ get_serving_network(co, NULL);
+ }
+
+ /* Update PS Protocal status */
+ switch (value) {
+ case AT_COPS_ACT_GSM: /*Fall Through*/
+ case AT_COPS_ACT_GSM_COMPACT: /*Fall Through*/
+ case AT_COPS_ACT_UTRAN: /*Fall Through*/
+ case AT_COPS_ACT_GSM_EGPRS: /*Fall Through*/
+ case AT_COPS_ACT_E_UTRAN:
+ {
+ dbg("Not required for Protocol Status Notification");
+ goto OUT;
+ }
+ case AT_COPS_ACT_UTRAN_HSDPA:
+ {
+ dbg("HSDPA");
+ noti.status = TELEPHONY_HSDPA_ON;
+ break;
+ }
+ case AT_COPS_ACT_UTRAN_HSUPA:
+ {
+ dbg("HSUPA");
+ noti.status = TELEPHONY_HSUPA_ON;
+ break;
+ }
+ case AT_COPS_ACT_UTRAN_HSDPA_HSUPA:
+ {
+ dbg("HSPA");
+ noti.status = TELEPHONY_HSPA_ON;
+ break;
+ }
+ default:
+ {
+ dbg("Ignore");
+ goto OUT;
+ }
+ }
+
+ ret = tcore_server_send_notification(tcore_plugin_ref_server(plugin),
+ co, TNOTI_PS_PROTOCOL_STATUS,
+ sizeof(struct tnoti_ps_protocol_status), ¬i);
+
+ dbg("ret: [0x%x]", ret);
+
+OUT:
+ /* Free resources */
+ tcore_at_tok_free(tokens);
+ g_free(reg_status);
+ return;
+}
+
+static TReturn __get_act_information(CoreObject *co, void *user_data)
+{
+ dbg("ENTER!!");
+
+ if (!co)
+ return TCORE_RETURN_EINVAL;
+
+ /* Send Request to modem */
+ tcore_prepare_and_send_at_request(co,
+ "AT+COPS?", "+COPS",
+ TCORE_AT_SINGLELINE,
+ NULL,
+ __on_response_get_act_information, user_data,
+ NULL, NULL,
+ 0, NULL, NULL);
+
+ return TCORE_RETURN_SUCCESS;
+}
+
+static guint __get_gslist_length(const GSList *list)
+{
+ guint length = 0;
+ const GSList *iter = NULL;
+
+ for (iter = list; iter; iter = iter->next)
+ length++;
+
+ return length;
+}
+
+static gboolean on_event_ps_network_regist(CoreObject *co,
+ const void *event_info, void *user_data)
+{
+ TcorePlugin *plugin = NULL;
+ Server *server = NULL;
+
+ int stat = 0;
+ unsigned int lac = 0, ci = 0xffff;
+ GSList *tokens = NULL;
+ guint token_count = 0;
+ char *pResp = NULL;
+ char *line = NULL;
+ const GSList *lines = NULL;
+
+ struct tnoti_network_location_cellinfo net_lac_cell_info = {0};
+
+ dbg("+CGREG NOTI RECEIVED");
+ lines = (const GSList *) event_info;
+ if (1 != __get_gslist_length(lines)) {
+ dbg("unsolicited msg but multiple line");
+ goto OUT;
+ }
+
+ line = (char *)(lines->data);
+ if (line == NULL) {
+ err("Invalid message");
+ goto OUT;
+ }
+
+ tokens = tcore_at_tok_new(line);
+ token_count = g_slist_length(tokens);
+ dbg("CGREG token count: %d", token_count);
+ if (token_count == 1) {
+ /* Handle CGREG notification */
+ pResp = g_slist_nth_data(tokens, 0);
+ } else if (token_count == 3) {
+ /* Handle lac */
+ pResp = g_slist_nth_data(tokens, 1);
+ g_strstrip(g_strdelimit(pResp, "\"", ' '));
+ lac = strtol(pResp, NULL, 16);
+ dbg("CGREG token: lac [%x]", lac);
+
+ /* handle ci */
+ pResp = g_slist_nth_data(tokens, 2);
+ g_strstrip(g_strdelimit(pResp, "\"", ' '));
+ ci = strtol(pResp, NULL, 16);
+ dbg("CGREG token: ci [%x]", ci);
+
+ /* Handle CGREG notification */
+ pResp = g_slist_nth_data(tokens, 0);
+ } else {
+ err("Invalid message");
+ goto OUT;
+ }
+
+ if (!pResp) {
+ err("No <stat> in +CGREG");
+ goto OUT;
+ }
+
+ stat = atoi(pResp);
+ __handle_ps_network_status(co, stat);
+
+ tcore_network_set_lac(co, lac);
+ tcore_network_set_cell_id(co, ci);
+
+ net_lac_cell_info.lac = lac;
+ net_lac_cell_info.cell_id = ci;
+
+ plugin = tcore_object_ref_plugin(co);
+ server = tcore_plugin_ref_server(plugin);
+ tcore_server_send_notification(server, co, TNOTI_NETWORK_LOCATION_CELLINFO,
+ sizeof(struct tnoti_network_location_cellinfo), &net_lac_cell_info);
+OUT:
+ tcore_at_tok_free(tokens);
+ return TRUE;
+}
+
+static gboolean on_event_cs_network_regist(CoreObject *co,
+ const void *event_info, void *user_data)
+{
+ const GSList *lines = NULL;
+ char *line = NULL;
+ int stat = 0;
+ GSList *tokens = NULL;
+ guint token_count = 0;
+ char *pResp = NULL;
+
+ dbg("+CREG NOTI RECEIVED");
+
+ lines = (const GSList *) event_info;
+ if (1 != __get_gslist_length(lines)) {
+ err("unsolicited msg but multiple line");
+ goto OUT;
+ }
+
+ line = (char *) (lines->data);
+ if (line == NULL) {
+ err("Invalid message");
+ goto OUT;
+ }
+
+ /*
+ * +CREG::<n>,<stat>[,<lac>,<ci>]
+ *
+ * <n>:
+ * 0 - Disable proactive reporting of "CREG"
+ * 1 - Enable proactive reporting of "+CREG: <stat>"
+ * 2 - Enable proactive reporting of "+CREG: <stat>[,<lac>,<ci>]"..
+ * <stat>:
+ * 0 - Not registered.
+ * The MS is not searching the new operators to be registered.
+ * 1 - Local network is registered
+ * 2 - Not registered.
+ * But the MS is searching the new operators to be registered.
+ * 3 - Registration rejected
+ * 4 - Unknown reasons
+ * 5 - Roaming network is registered
+ * <lac>:
+ * Position code information, composed of four characters and expressed
+ * in hexadecimal. (Example: \9300C3\94= \93195\94 in decimal)
+ * <ci>:
+ * Cell information,
+ * composed of four characters and expressed in hexadecimal.
+ * (Extended Information:
+ * according 3GPP Rel7, four characters are requested,
+ * but if before Rel7, for example the currently network is Rel6 mostly,
+ * only the last two characters is valid, the other characters is invalid
+ * and should be ignored. For example, if the <CI> return 3B3DE1C,
+ * only DE1C is valid and could be used as DE1C is the last two characters.)
+ */
+ tokens = tcore_at_tok_new(line);
+ token_count = g_slist_length(tokens);
+ dbg("CREG token count: %d", token_count);
+ if (token_count == 1 || token_count == 3) {
+ /* Handle CREG notification */
+ pResp = g_slist_nth_data(tokens, 0);
+ } else {
+ err("Invalid message");
+ goto OUT;
+ }
+
+ if (!pResp) {
+ err("No <stat> in +CREG");
+ goto OUT;
+ }
+
+ stat = atoi(pResp);
+ __handle_cs_network_status(co, stat);
+OUT:
+ tcore_at_tok_free(tokens);
+ return TRUE;
+}
+
+static void __on_response_rssi_info(TcorePending *p, int data_len,
+ const void *data, void *user_data)
+{
+ const TcoreATResponse *resp = data;
+ CoreObject *co = NULL;
+ const char *line;
+ GSList *tokens = NULL;
+
+ co = tcore_pending_ref_core_object(p);
+ if (!co) {
+ err("Failed to get CoreObject");
+ return;
+ }
+
+ dbg("RSSI Info Response");
+ if (resp && resp->success) {
+ char *rssi_token = NULL;
+
+ dbg("RESPONSE OK");
+
+ line = (const char *) resp->lines->data;
+ tokens = tcore_at_tok_new(line);
+ /* +CSQ: <rssi>,<ber> */
+ if (g_slist_length(tokens) != 2) {
+ err("invalid response");
+ goto OUT;
+ }
+
+ rssi_token = (char *) g_slist_nth_data(tokens, 0);
+ if (rssi_token && strlen(rssi_token)) {
+ TcorePlugin *plugin = NULL;
+ struct tnoti_network_icon_info net_icon_info = {0,};
+ int rssi_value = 0;
+
+ net_icon_info.type = NETWORK_ICON_INFO_RSSI;
+ rssi_value = atoi(rssi_token);
+ if (rssi_value == 99) {
+ warn("Unknown or unmeasurable");
+ goto OUT;
+ }
+
+ net_icon_info.rssi = __convert_rssi_bar(rssi_value);
+
+ /* Send notification */
+ plugin = tcore_object_ref_plugin(co);
+ tcore_server_send_notification(tcore_plugin_ref_server(plugin),
+ co, TNOTI_NETWORK_ICON_INFO,
+ sizeof(struct tnoti_network_icon_info),
+ &net_icon_info);
+ }
+ } else {
+ err("RESPONSE NOK");
+ }
+OUT:
+ tcore_at_tok_free(tokens);
+}
+
+static void on_response_ps_network_status(TcorePending *p, int data_len,
+ const void *data, void *user_data)
+{
+ const TcoreATResponse *resp = data;
+ CoreObject *co = NULL;
+ GSList *tokens = NULL;
+ const char *line;
+ int stat = 0;
+ guint token_count = 0;
+ char *pResp = NULL;
+
+ dbg("Enter");
+
+ co = tcore_pending_ref_core_object(p);
+ if (!co) {
+ err("Failed to get CoreObject");
+ return;
+ }
+
+ if (resp && resp->success) {
+ dbg("RESPONSE OK");
+ if (1 != g_slist_length(resp->lines)) {
+ dbg("unsolicited msg but multiple line");
+ goto OUT;
+ }
+
+ line = (const char *) resp->lines->data;
+ tokens = tcore_at_tok_new(line);
+ token_count = g_slist_length(tokens);
+ dbg("CGREG token count: %d", token_count);
+
+ if (token_count == 2 || token_count == 4) {
+ /* Handle CGREG query response */
+ pResp = g_slist_nth_data(tokens, 1);
+ } else {
+ err("Invalid message");
+ goto OUT;
+ }
+
+ if (!pResp) {
+ err("No <stat> in +CGREG");
+ goto OUT;
+ }
+
+ stat = atoi(pResp);
+ __handle_ps_network_status(co, stat);
+ } else {
+ err("RESPONSE NOK");
+ }
+OUT:
+ tcore_at_tok_free(tokens);
+}
+
+static void on_response_cs_network_status(TcorePending *p, int data_len,
+ const void *data, void *user_data)
+{
+ const TcoreATResponse *resp = data;
+ CoreObject *co = NULL;
+ GSList *tokens = NULL;
+ const char *line;
+ int stat = 0;
+ guint token_count = 0;
+ char *pResp = NULL;
+
+ dbg("Enter");
+
+ co = tcore_pending_ref_core_object(p);
+ if (!co) {
+ err("Failed to get CoreObject");
+ return;
+ }
+
+ if (resp && resp->success) {
+ dbg("RESPONSE OK");
+ if (1 != g_slist_length(resp->lines)) {
+ dbg("unsolicited msg but multiple line");
+ goto OUT;
+ }
+
+ line = (const char *) resp->lines->data;
+ tokens = tcore_at_tok_new(line);
+ token_count = g_slist_length(tokens);
+ dbg("CREG token count: %d", token_count);
+
+ if (token_count == 2 || token_count == 4) {
+ /* Handle CREG query response */
+ pResp = g_slist_nth_data(tokens, 1);
+ } else {
+ err("Invalid message");
+ goto OUT;
+ }
+
+ if (!pResp) {
+ err("No <stat> in +CREG");
+ goto OUT;
+ }
+
+ stat = atoi(pResp);
+ __handle_cs_network_status(co, stat);
+ } else {
+ err("RESPONSE NOK");
+ }
+OUT:
+ tcore_at_tok_free(tokens);
+}
+
+static void __handle_ps_network_status(CoreObject *co, int stat)
+{
+ enum telephony_network_service_domain_status cs_status;
+ enum telephony_network_service_domain_status ps_status, prev_status;
+ struct network_reg_status_cb_data *reg_status = NULL;
+
+ if (!co) {
+ err("CoreObject is NULL");
+ return;
+ }
+
+ dbg("stat[%d]", stat);
+ if (stat < 0 || stat > 5) {
+ err("Invalid <stat> value");
+ return;
+ }
+
+ if (stat == 5)
+ tcore_network_set_roaming_state(co, TRUE);
+ else
+ tcore_network_set_roaming_state(co, FALSE);
+
+ ps_status = __mapping_network_status(stat);
+
+ tcore_network_get_service_status(co,
+ TCORE_NETWORK_SERVICE_DOMAIN_TYPE_PACKET,
+ &prev_status);
+ dbg(" previous ps_status [%d], current ps_status [%d]",
+ prev_status, ps_status);
+
+ if (ps_status != prev_status) {
+
+ tcore_network_get_service_status(co,
+ TCORE_NETWORK_SERVICE_DOMAIN_TYPE_CIRCUIT,
+ &cs_status);
+
+ /* Callback data */
+ reg_status = g_malloc0(sizeof(struct network_reg_status_cb_data));
+ reg_status->regist_status.cs_domain_status = cs_status;
+ reg_status->regist_status.ps_domain_status = ps_status;
+ reg_status->regist_status.roaming_status
+ = tcore_network_get_roaming_state(co);
+
+ /* Set PS online state */
+ __ps_set(tcore_object_ref_plugin(co), ps_status);
+
+ /* Set PS status */
+ tcore_network_set_service_status(co,
+ TCORE_NETWORK_SERVICE_DOMAIN_TYPE_PACKET,
+ ps_status);
+
+ /* Get AcT */
+ __get_act_information(co, reg_status);
+ }
+}
+
+static void __handle_cs_network_status(CoreObject *co, int stat)
+{
+ enum telephony_network_service_domain_status cs_status, prev_status;
+ enum telephony_network_service_domain_status ps_status;
+ struct network_reg_status_cb_data *reg_status = NULL;
+
+ if (!co) {
+ err("CoreObject is NULL");
+ return;
+ }
+
+ dbg("stat[%d]", stat);
+ if (stat < 0 || stat > 5) {
+ err("Invalid <stat> value");
+ return;
+ }
+
+ if (stat == 5)
+ tcore_network_set_roaming_state(co, TRUE);
+ else
+ tcore_network_set_roaming_state(co, FALSE);
+
+ cs_status = __mapping_network_status(stat);
+
+ tcore_network_get_service_status(co,
+ TCORE_NETWORK_SERVICE_DOMAIN_TYPE_CIRCUIT,
+ &prev_status);
+ dbg("previous cs_status[%d], current cs_status [%d] ",
+ prev_status, cs_status);
+
+ if (cs_status != prev_status) {
+
+ if (cs_status == NETWORK_SERVICE_DOMAIN_STATUS_FULL) {
+ /* Get Previous PS status */
+ tcore_network_get_service_status(co,
+ TCORE_NETWORK_SERVICE_DOMAIN_TYPE_PACKET,
+ &ps_status);
+ } else {
+ /* When CS is detached or detaching state,
+ * PS will also be in same state */
+ ps_status = NETWORK_SERVICE_DOMAIN_STATUS_NO;
+ }
+ dbg("ps_status [%d]", ps_status);
+ /* Callback data */
+ reg_status = g_malloc0(sizeof(struct network_reg_status_cb_data));
+ reg_status->regist_status.cs_domain_status = cs_status;
+ reg_status->regist_status.ps_domain_status = ps_status;
+ reg_status->regist_status.roaming_status
+ = tcore_network_get_roaming_state(co);
+
+ /* Set current CS status*/
+ tcore_network_set_service_status(co,
+ TCORE_NETWORK_SERVICE_DOMAIN_TYPE_CIRCUIT,
+ cs_status);
+
+ /* Get AcT */
+ __get_act_information(co, reg_status);
+ }
+}
+
+static void __send_request(CoreObject *co, const gchar *at_cmd,
+ TcorePendingResponseCallback resp_cb, void *resp_cb_data)
+{
+ tcore_prepare_and_send_at_request(co,
+ at_cmd, NULL,
+ TCORE_AT_NO_RESULT,
+ NULL,
+ resp_cb, resp_cb_data,
+ NULL, NULL,
+ 0, NULL, NULL);
+}
+
+static gboolean __get_rssi_signal_cb(gpointer data)
+{
+ TReturn ret;
+ CoreObject *co_network = (CoreObject*)data;
+
+ ret = tcore_prepare_and_send_at_request(co_network,
+ "AT+CSQ", "+CSQ",
+ TCORE_AT_SINGLELINE,
+ NULL,
+ __on_response_rssi_info, NULL,
+ NULL, NULL,
+ 0, NULL, NULL);
+ dbg("ret: [0x%x]", ret);
+
+ /* Request rssi signal strength periodically */
+ /* Return FALSE to stop cycle */
+ return TRUE;
+}
+
+static void on_response_network_subscribe_events(TcorePending *p,
+ int data_len, const void *data,
+ void *user_data)
+{
+ CoreObject *co;
+ PrivateObject *po;
+ const TcoreATResponse *at_resp = data;
+ co = tcore_pending_ref_core_object(p);
+
+ if (at_resp && at_resp->success) {
+ dbg("[Last] Subscription for '%s' - [OK]", (char *)user_data);
+ po = tcore_object_ref_user_data(co);
+ if (po) {
+ if (g_strcmp0((char *)user_data, "AT+CGREG=2") == 0)
+ po->subscribe_cgreg_event = TRUE;
+ else if (g_strcmp0((char *)user_data, "AT+CREG=1") == 0)
+ po->subscribe_creg_event = TRUE;
+
+ dbg("subscribe_creg_event[%d], subscribe_cgreg_event[%d]",
+ po->subscribe_creg_event, po->subscribe_cgreg_event);
+ }
+
+ } else {
+ err("[Last] Subscription for '%s' - [NOK]", (char *)user_data);
+ }
+
+ /* Free resource */
+ g_free(user_data);
+}
+
+static void on_response_network_registration(TcorePending *p, int data_len,
+ const void *data, void *user_data)
+{
+ const TcoreATResponse *resp = data;
+
+ if (resp && resp->success > 0)
+ dbg("Response OK");
+ else
+ err("Response NOK");
+}
+
+static enum tcore_hook_return on_hook_sim_init(Server *s, CoreObject *source,
+ enum tcore_notification_command command,
+ unsigned int data_len, void *data, void *user_data)
+{
+ TcorePlugin *plugin = (TcorePlugin *)user_data;
+ CoreObject *co_network;
+ PrivateObject *po;
+ const struct tnoti_sim_status *sim = data;
+
+ dbg("Enter");
+
+ if (plugin == NULL) {
+ err("Invalid plugin !");
+ return TCORE_HOOK_RETURN_CONTINUE;
+ }
+
+ /*
+ * Before SIM card successfully init,
+ * +CGREG / +CREG returns error
+ * [+CME ERROR: SIM failure]
+ * If SIM locked, it returns error
+ * [+CME ERROR: SIM PIN required]
+ */
+ /* URC Subscriptions */
+ /* Whether sim is locked or not, it should be subscribed. */
+ /* when it is subscribed successfully, it doesn't need anymore */
+ co_network = tcore_plugin_ref_core_object(plugin, CORE_OBJECT_TYPE_NETWORK);
+ po = tcore_object_ref_user_data(co_network);
+ if (po) {
+ if (po->subscribe_cgreg_event == FALSE) {
+ __send_request(co_network, "AT+CGREG=2",
+ on_response_network_subscribe_events,
+ g_strdup("AT+CGREG=2"));
+ }
+
+ if (po->subscribe_creg_event == FALSE) {
+ __send_request(co_network, "AT+CREG=1",
+ on_response_network_subscribe_events,
+ g_strdup("AT+CREG=1"));
+ }
+ }
+
+ tcore_prepare_and_send_at_request(co_network,
+ "AT+CREG?", "+CREG:",
+ TCORE_AT_SINGLELINE,
+ NULL,
+ on_response_cs_network_status, NULL,
+ NULL, NULL,
+ 0, NULL, NULL);
+
+ tcore_prepare_and_send_at_request(co_network,
+ "AT+CGREG?", "+CGREG:",
+ TCORE_AT_SINGLELINE,
+ NULL,
+ on_response_ps_network_status, NULL,
+ NULL, NULL,
+ 0, NULL, NULL);
+
+ if (sim->sim_status == SIM_STATUS_INIT_COMPLETED) {
+ /* Sending AT+COPS */
+ tcore_prepare_and_send_at_request(co_network,
+ "AT+COPS=0", NULL,
+ TCORE_AT_NO_RESULT,
+ NULL,
+ on_response_network_registration, NULL,
+ NULL, NULL,
+ 0, NULL, NULL);
+
+ /* Fetch RSSI */
+ po->rssi_src_id = g_timeout_add_seconds(RSSI_POLLING_TIMEOUT,
+ __get_rssi_signal_cb, co_network);
+ }
+
+ dbg("Exit");
+ return TCORE_HOOK_RETURN_CONTINUE;
+}
+
+static void on_response_get_serving_network(TcorePending *p, int data_len,
+ const void *data, void *user_data)
+{
+ const TcoreATResponse *resp = data;
+ UserRequest *ur;
+ struct tresp_network_get_serving_network serv_nw_resp = {0};
+ char *long_plmn_name = NULL;
+ char *short_plmn_name = NULL;
+ char *plmn_id = NULL;
+ CoreObject *co;
+ GSList *tokens = NULL;
+ const char *line;
+ int network_mode = -1;
+ int plmn_format = -1;
+ int value = -1;
+ struct tnoti_network_identity noti;
+ char *pResp = NULL;
+ int nol, count = 0;
+
+ co = tcore_pending_ref_core_object(p);
+
+ if (resp->success <= 0) {
+ err("RESPONSE NOK");
+
+ ur = tcore_pending_ref_user_request(p);
+ if (ur) {
+ serv_nw_resp.result = TCORE_RETURN_FAILURE;
+ tcore_user_request_send_response(ur,
+ TRESP_NETWORK_GET_SERVING_NETWORK,
+ sizeof(struct tresp_network_get_serving_network),
+ &serv_nw_resp);
+ }
+
+ return;
+ }
+
+ dbg("RESPONSE OK");
+ nol = g_slist_length(resp->lines);
+ dbg("nun of lines : %d", nol);
+
+ for (count = 0; count < nol; count++) {
+ /* parse each line */
+ line = g_slist_nth_data(resp->lines, count);
+ tokens = tcore_at_tok_new(line);
+ dbg("line %d start---------------", count);
+ /* mode */
+ if ((pResp = tcore_at_tok_nth(tokens, 0))) {
+ dbg("mode : %s", pResp);
+ if (pResp)
+ network_mode = atoi(pResp);
+ }
+
+ /* format (optional) */
+ if ((pResp = tcore_at_tok_nth(tokens, 1))) {
+ dbg("format : %s", pResp);
+ if (pResp && strlen(pResp) > 0)
+ plmn_format = atoi(pResp);
+ }
+
+ /* plmn */
+ switch (plmn_format) {
+ case 0:
+ if ((pResp = tcore_at_tok_nth(tokens, 2))) {
+ dbg("long PLMN : %s", pResp);
+ if (pResp && strlen(pResp) > 0) {
+ long_plmn_name =
+ tcore_at_tok_extract((const char *)pResp);
+
+ /* set network name into po */
+ tcore_network_set_network_name(co,
+ TCORE_NETWORK_NAME_TYPE_FULL, long_plmn_name);
+ }
+ }
+ break;
+ case 1:
+ if ((pResp = tcore_at_tok_nth(tokens, 2))) {
+ dbg("short PLMN : %s", pResp);
+ if (pResp && strlen(pResp) > 0) {
+ short_plmn_name =
+ tcore_at_tok_extract((const char *)pResp);
+
+ /* set network name into po */
+ tcore_network_set_network_name(co,
+ TCORE_NETWORK_NAME_TYPE_SHORT,
+ short_plmn_name);
+ }
+ }
+ break;
+ case 2:
+ if ((pResp = tcore_at_tok_nth(tokens, 2))) {
+ dbg("numeric : %s", pResp);
+ if (pResp && strlen(pResp) > 0) {
+ plmn_id =
+ tcore_at_tok_extract((const char *)pResp);
+
+ /* set plmn id into po */
+ tcore_network_set_plmn(co, plmn_id);
+ }
+ }
+ break;
+ default:
+ err("invalid plmn format");
+ break;
+ }
+
+ /* rat (act) */
+ if ((pResp = tcore_at_tok_nth(tokens, 3))) {
+ dbg("AcT : %s", pResp);
+ if (!pResp) {
+ err("ACT is invalid");
+ } else {
+ value = atoi(pResp);
+ if (value < AT_COPS_ACT_VALUE_MAX) {
+ int AcT;
+ AcT = lookup_tbl_access_technology[value];
+ tcore_network_set_access_technology(co, AcT);
+ } else {
+ err("AcT is invalid or missing");
+ }
+ }
+ }
+ /* Free resources */
+ tcore_at_tok_free(tokens);
+ }
+
+ if (plmn_id) {
+ int plmn_len = strlen(plmn_id);
+ if (plmn_len > NETWORK_MAX_PLMN_LEN)
+ plmn_len = NETWORK_MAX_PLMN_LEN;
+ memcpy(serv_nw_resp.plmn, plmn_id, plmn_len);
+ }
+
+ tcore_network_get_access_technology(co, &(serv_nw_resp.act));
+ tcore_network_get_lac(co, &(serv_nw_resp.gsm.lac));
+
+ ur = tcore_pending_ref_user_request(p);
+ if (ur) {
+ serv_nw_resp.result = TCORE_RETURN_SUCCESS;
+ tcore_user_request_send_response(ur, TRESP_NETWORK_GET_SERVING_NETWORK,
+ sizeof(struct tresp_network_get_serving_network), &serv_nw_resp);
+ } else {
+ TcorePlugin *plugin = NULL;
+ /* Network change noti */
+ struct tnoti_network_change network_change;
+
+ memset(&network_change, 0, sizeof(struct tnoti_network_change));
+ if (plmn_id) {
+ int plmn_len = strlen(plmn_id);
+ if (plmn_len > NETWORK_MAX_PLMN_LEN)
+ plmn_len = NETWORK_MAX_PLMN_LEN;
+ memcpy(network_change.plmn, plmn_id, plmn_len);
+ }
+
+ tcore_network_get_access_technology(co, &(network_change.act));
+ tcore_network_get_lac(co, &(network_change.gsm.lac));
+
+ plugin = tcore_pending_ref_plugin(p);
+ tcore_server_send_notification(tcore_plugin_ref_server(plugin),
+ tcore_pending_ref_core_object(p),
+ TNOTI_NETWORK_CHANGE,
+ sizeof(struct tnoti_network_change),
+ &network_change);
+
+ dbg("network_change.plmn : %s", network_change.plmn);
+ dbg("network_change.act : %d", network_change.act);
+ dbg("network_change.gsm.lac : %d", network_change.gsm.lac);
+
+ if ((2 != network_mode) && (3 != network_mode)) {
+ /* Network identity noti */
+ memset(¬i, 0x0, sizeof(struct tnoti_network_identity));
+ if (long_plmn_name)
+ memcpy(noti.full_name, long_plmn_name,
+ MIN(32, strlen(long_plmn_name)));
+ if (short_plmn_name)
+ memcpy(noti.short_name, short_plmn_name,
+ MIN(16, strlen(short_plmn_name)));
+ if (plmn_id) {
+ /* plmn_id length is necessarily <= 6 */
+ int plmn_len = strlen(plmn_id);
+ if (plmn_len > NETWORK_MAX_PLMN_LEN)
+ plmn_len = NETWORK_MAX_PLMN_LEN;
+ memcpy(noti.plmn, plmn_id, plmn_len);
+ }
+
+ plugin = tcore_object_ref_plugin(co);
+ tcore_server_send_notification(tcore_plugin_ref_server(plugin),
+ co, TNOTI_NETWORK_IDENTITY,
+ sizeof(struct tnoti_network_identity),
+ ¬i);
+
+ dbg("noti.short_name : %s", noti.short_name);
+ dbg("noti.full_name : %s", noti.full_name);
+ dbg("noti.plmn : %s", noti.plmn);
+ }
+ }
+
+ g_free(long_plmn_name);
+ g_free(short_plmn_name);
+ g_free(plmn_id);
+ return;
+}
+
+static TReturn get_serving_network(CoreObject *co, UserRequest *ur)
+{
+ dbg("ENTER!!");
+
+ if (!co)
+ return TCORE_RETURN_EINVAL;
+
+ /* Send Request to modem */
+ tcore_prepare_and_send_at_request(co,
+ "AT+COPS=3,2;+COPS?;+COPS=3,0;+COPS?", "+COPS",
+ TCORE_AT_MULTILINE,
+ ur,
+ on_response_get_serving_network, NULL,
+ NULL, NULL,
+ 0, NULL, NULL);
+
+ return TCORE_RETURN_SUCCESS;
+}
+
+static TReturn get_default_subscription(CoreObject *co, UserRequest *ur)
+{
+ struct tresp_network_get_default_subs resp_data = {0, };
+ TReturn ret = TCORE_RETURN_SUCCESS;
+
+ dbg("Enter");
+
+ /* Only supports SIM 1 */
+ resp_data.default_subs = NETWORK_DEFAULT_SUBS_SIM1;
+ resp_data.result = TCORE_RETURN_SUCCESS;
+
+ /* Send Response */
+ ret = tcore_user_request_send_response(ur,
+ TRESP_NETWORK_GET_DEFAULT_SUBSCRIPTION,
+ sizeof(struct tresp_network_get_default_subs), &resp_data);
+
+ dbg("ret: [0x%x]", ret);
+ return ret;
+}
+
+static TReturn get_default_data_subscription(CoreObject *co, UserRequest *ur)
+{
+ struct tresp_network_get_default_data_subs resp = {0,};
+ TReturn ret = TCORE_RETURN_SUCCESS;
+
+ dbg("Enter");
+
+ /* Only supports SIM 1 */
+ resp.result = TCORE_RETURN_SUCCESS;
+ resp.default_subs = NETWORK_DEFAULT_DATA_SUBS_SIM1;
+
+ /* Send Response */
+ ret = tcore_user_request_send_response(ur,
+ TRESP_NETWORK_GET_DEFAULT_DATA_SUBSCRIPTION,
+ sizeof(struct tresp_network_get_default_data_subs), &resp);
+
+ dbg("ret: [0x%x]", ret);
+ return ret;
+}
+
+static struct tcore_network_operations network_ops = {
+ .search = NULL,
+ .set_plmn_selection_mode = NULL,
+ .get_plmn_selection_mode = NULL,
+ .set_service_domain = NULL,
+ .get_service_domain = NULL,
+ .set_band = NULL,
+ .get_band = NULL,
+ .set_preferred_plmn = NULL,
+ .get_preferred_plmn = NULL,
+ .set_order = NULL,
+ .get_order = NULL,
+ .set_power_on_attach = NULL,
+ .get_power_on_attach = NULL,
+ .set_cancel_manual_search = NULL,
+ .get_serving_network = get_serving_network,
+ .set_mode = NULL,
+ .get_mode = NULL,
+ .set_neighboring_cell_info = NULL,
+ .get_neighboring_cell_info = NULL,
+ .set_default_data_subscription = NULL,
+ .get_default_data_subscription = get_default_data_subscription,
+ .set_default_subscription = NULL,
+ .get_default_subscription = get_default_subscription,
+ .set_emergency_callback_mode = NULL,
+ .set_roaming_preference = NULL,
+ .get_roaming_preference = NULL,
+ .get_subscription_info = NULL,
+ .search_ecc_rat = NULL,
+};
+
+gboolean atd_network_init(TcorePlugin *p, CoreObject *co_network)
+{
+ PrivateObject *po = NULL;
+ dbg("Enter");
+
+ po = g_malloc0(sizeof(PrivateObject));
+ tcore_object_link_user_data(co_network, po);
+
+ /* Set operations */
+ tcore_network_set_ops(co_network, &network_ops, TCORE_OPS_TYPE_CP);
+
+ /* Add Callbacks */
+ tcore_object_add_callback(co_network, "+CREG",
+ on_event_cs_network_regist, NULL);
+ tcore_object_add_callback(co_network, "+CGREG",
+ on_event_ps_network_regist, NULL);
+
+ /* Wait for sim init */
+ tcore_server_add_notification_hook(tcore_plugin_ref_server(p),
+ TNOTI_SIM_STATUS, on_hook_sim_init, p);
+
+ /* Set CS and PS service status to invalid */
+ tcore_network_set_service_status(co_network,
+ TCORE_NETWORK_SERVICE_DOMAIN_TYPE_CIRCUIT,
+ -1);
+ tcore_network_set_service_status(co_network,
+ TCORE_NETWORK_SERVICE_DOMAIN_TYPE_PACKET,
+ -1);
+
+ dbg("Exit");
+ return TRUE;
+}
+
+void atd_network_exit(TcorePlugin *p, CoreObject *co_network)
+{
+ Server *s;
+ PrivateObject *po;
+
+ dbg("Enter");
+
+ s = tcore_plugin_ref_server(p);
+ tcore_server_remove_notification_hook(s, on_hook_sim_init);
+
+ tcore_object_del_callback(co_network,
+ "+CREG", on_event_cs_network_regist);
+
+ tcore_object_del_callback(co_network,
+ "+CGREG", on_event_ps_network_regist);
+
+ po = tcore_object_ref_user_data(co_network);
+ if (po && po->rssi_src_id)
+ g_source_remove(po->rssi_src_id);
+
+ g_free(po);
+
+ /* Unset 'ops' */
+ tcore_network_set_ops(co_network, NULL, TCORE_OPS_TYPE_CP);
+
+ dbg("Exit");
+}
--- /dev/null
+/*
+ * tel-plugin-atdongle
+ *
+ * Copyright (c) 2015 Samsung Electronics Co., Ltd. All rights reserved.
+ *
+ * Contact:
+ *
+ * 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 <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/ioctl.h>
+#include <netinet/in.h>
+#include <net/if.h>
+#include <unistd.h>
+#include <arpa/inet.h>
+
+#include <glib.h>
+#include <fcntl.h>
+#include <sys/ioctl.h>
+#include <sys/stat.h>
+
+#include <tcore.h>
+#include <hal.h>
+#include <core_object.h>
+#include <plugin.h>
+#include <queue.h>
+#include <co_ps.h>
+#include <co_context.h>
+#include <storage.h>
+#include <server.h>
+#include <at.h>
+#include <util.h>
+#include <type/ps.h>
+#include <vconf/vconf.h>
+
+#include "atd_ps.h"
+
+/**< DNS related */
+#define PPP_RESOLV_CONF_FILE "/opt/etc/ppp/resolv.conf"
+#define DNS_ADDR_MAX 2
+#define DNS_BUF_LEN 100
+
+enum ps_data_call_status {
+ PS_DATA_CALL_CTX_DEFINED,
+ PS_DATA_CALL_CONNECTED,
+ PS_DATA_CALL_NOT_CONNECTED = 3
+};
+
+static gboolean __get_pdp_address(CoreObject *co_ps, CoreObject *ps_context,
+ const char *netif_name);
+
+static void __notify_context_status_changed(CoreObject *co_ps,
+ CoreObject *ps_context, enum ps_data_call_status state)
+{
+ struct tnoti_ps_call_status data_resp = {0, };
+ TcorePlugin *plugin = NULL;
+ Server *server = NULL;
+ Storage *strg = NULL;
+ gboolean result = FALSE;
+ dbg("Enter");
+
+ plugin = tcore_object_ref_plugin(co_ps);
+ server = tcore_plugin_ref_server(plugin);
+ strg = (Storage *)tcore_server_find_storage(server, "vconf");
+
+ data_resp.context_id = tcore_context_get_id(ps_context);
+ data_resp.state = state;
+ dbg("Sending Call Status Notification " \
+ "- Context ID: [%d], Context State: [%d]",
+ data_resp.context_id, state);
+
+ /* Update packet service state */
+ if (strg) {
+ dbg("Updating packet service state");
+ if (state == PS_DATA_CALL_CONNECTED) {
+ result = tcore_storage_set_int(strg,
+ STORAGE_KEY_PACKET_SERVICE_STATE,
+ VCONFKEY_DNET_NORMAL_CONNECTED);
+ } else {
+ result = tcore_storage_set_int(strg,
+ STORAGE_KEY_PACKET_SERVICE_STATE,
+ VCONFKEY_DNET_OFF);
+ }
+
+ if (!result)
+ err("Failed to update PS state vconf key!");
+ } else {
+ err("Failed to get Storage !");
+ }
+
+ /* Send CALL Status Notification */
+ tcore_server_send_notification(server, co_ps, TNOTI_PS_CALL_STATUS,
+ sizeof(data_resp), &data_resp);
+}
+
+static void __on_setup_pdp(CoreObject *co_ps, int result,
+ const char *netif_name, void *user_data)
+{
+ CoreObject *ps_context = user_data;
+ if (result < 0) {
+ /* Deactivate PDP context */
+ err("setup pdp failed");
+ goto OUT;
+ }
+ dbg("Device name: [%s]", netif_name);
+
+ /* Get IP address, netmask and DNS IP address */
+ if (__get_pdp_address(co_ps, ps_context, netif_name)) {
+ dbg("Get PDP address is success");
+ return;
+ }
+
+OUT:
+ /* Deactivate PDP context */
+ tcore_ps_deactivate_context(co_ps, ps_context, NULL);
+ __notify_context_status_changed(co_ps, ps_context,
+ PS_DATA_CALL_NOT_CONNECTED);
+}
+
+static void __on_deactivate_pdp(CoreObject *co_ps, int result,
+ const char *netif_name, void *user_data)
+{
+ CoreObject *ps_context = user_data;
+ dbg("Device name: [%s]", netif_name);
+
+ /* Send status notification */
+ __notify_context_status_changed(co_ps, ps_context,
+ PS_DATA_CALL_NOT_CONNECTED);
+}
+
+static void __convert_ipv4_atoi(unsigned char *ip4, const char *str)
+{
+ char *token = NULL;
+ char *temp = NULL;
+ char *ptr = NULL;
+ int local_index = 0;
+
+ temp = g_strdup(str);
+ if (temp == NULL) {
+ err("Failed to duplicate string.");
+ return;
+ }
+
+ token = strtok_r(temp, ".", &ptr);
+ while (token != NULL) {
+ ip4[local_index++] = atoi(token);
+ msg(" [%d]", ip4[local_index-1]);
+ token = strtok_r(NULL, ".", &ptr);
+ }
+ g_free(temp);
+}
+
+static gboolean __get_pdp_address(CoreObject *co_ps, CoreObject *ps_context,
+ const char *netif_name)
+{
+ struct ifreq ifr;
+ int fd;
+ FILE *fp = NULL;
+ char buff[DNS_BUF_LEN] = "";
+ char *tmp = NULL;
+ struct in_addr in;
+ int len = 0;
+ int i = 0;
+ char *ipaddr = NULL;
+ char *net_mask = NULL;
+ struct tnoti_ps_pdp_ipconfiguration data_call_conf = {0, };
+ TcorePlugin *plugin = NULL;
+
+ dbg("Enter");
+
+ /* Socket to fetch IP address */
+ /* Type of address to retrieve - IPv4 IP address */
+ fd = socket(AF_INET, SOCK_DGRAM, 0);
+ if (fd < 0) {
+ err("fd is negative value");
+ return FALSE;
+ }
+ /* Copy the interface name in the ifreq structure */
+ ifr.ifr_addr.sa_family = AF_INET;
+ strncpy(ifr.ifr_name, netif_name, IFNAMSIZ-1);
+
+ /* IOCTL call - Fetch IP address */
+ if (ioctl(fd, SIOCGIFADDR, &ifr) < 0) {
+ dbg("Unable to fetch IP address");
+ close(fd);
+ return FALSE;
+ }
+ /* IPv4 uses sockaddr_in,
+ * But socket library uses general structure - sockaddr */
+ ipaddr = inet_ntoa(((struct sockaddr_in *)((void *)&ifr.ifr_addr))->sin_addr);
+ dbg("IP address - interface (%s): [%s]", netif_name, ipaddr);
+ __convert_ipv4_atoi(data_call_conf.ip_address, ipaddr);
+
+ /* IOCTL call - Fetch IP netmask */
+ ioctl(fd, SIOCGIFNETMASK, &ifr);
+ net_mask = inet_ntoa(((struct sockaddr_in *)((void *)&ifr.ifr_addr))->sin_addr);
+ dbg("Netmask: [%s]", net_mask);
+ __convert_ipv4_atoi(data_call_conf.subnet_mask, net_mask);
+
+ close(fd);
+
+ /* Provide gateway address same as IP address */
+ memcpy(data_call_conf.gateway, data_call_conf.ip_address, 4);
+
+ dbg("Gateway address in hex");
+ tcore_util_hex_dump(" ", 4, data_call_conf.gateway);
+
+ /* Get DNS address */
+ /* Use dynamically configured DNS server address */
+ if ((fp = fopen(PPP_RESOLV_CONF_FILE, "r")) == NULL) {
+ err("Failed to open resolve.conf file.. setting DNS to 'default'");
+ /* Set Primary & Secondary DNS to default*/
+ __convert_ipv4_atoi(data_call_conf.primary_dns, "8.8.8.8");
+ __convert_ipv4_atoi(data_call_conf.secondary_dns, "8.8.4.4");
+ } else {
+
+ /* Fetch DNS info from /opt/etc/ppp/resolv.conf file */
+ while (fgets(buff, DNS_BUF_LEN, fp) != NULL) {
+ if ((tmp = strstr(buff, "nameserver")) == NULL) {
+ memset(buff, 0, DNS_BUF_LEN);
+ continue;
+ } else {
+ tmp = tmp + sizeof("nameserver");
+ while (*tmp == ' ')
+ tmp++;
+ }
+
+ /* remove trailing '\n' */
+ len = strlen(buff);
+ buff[len - 1] = '\0';
+ if (!inet_aton(tmp, &in)) {
+ err("Configured with invalid dns address[%s]\n", tmp);
+ fclose(fp);
+ return FALSE;
+ }
+
+ if (i < DNS_ADDR_MAX) {
+ dbg("[%d] - DNS server address is [%s]\n", i+1, inet_ntoa(in));
+ i++;
+ } else /* If configured with more DNS server address just break? */
+ break;
+
+ if (i == 1) {
+ /* Set primary DNS */
+ __convert_ipv4_atoi(data_call_conf.primary_dns,
+ inet_ntoa(in));
+ } else {
+ /* Set Secondary DNS */
+ __convert_ipv4_atoi(data_call_conf.secondary_dns,
+ inet_ntoa(in));
+ }
+
+ memset(buff, 0, DNS_BUF_LEN);
+ }
+ fclose(fp);
+ }
+
+ data_call_conf.context_id = (int)tcore_context_get_id(ps_context);
+ g_strlcpy(data_call_conf.devname, netif_name,
+ sizeof(data_call_conf.devname) + 1);
+
+ dbg("data_call_conf.context_id[%d], data_call_conf.devname[%s]",
+ data_call_conf.context_id,
+ data_call_conf.devname);
+
+ /* Send IP configuaration notification*/
+ plugin = tcore_object_ref_plugin(co_ps);
+ tcore_server_send_notification(tcore_plugin_ref_server(plugin),
+ co_ps,
+ TNOTI_PS_PDP_IPCONFIGURATION,
+ sizeof(struct tnoti_ps_pdp_ipconfiguration),
+ &data_call_conf);
+
+ __notify_context_status_changed(co_ps, ps_context, PS_DATA_CALL_CONNECTED);
+ return TRUE;
+}
+
+static void on_response_define_pdp_context(TcorePending *p, int data_len,
+ const void *data, void *user_data)
+{
+ const TcoreATResponse *resp = data;
+ CoreObject *ps_context = (CoreObject *)user_data;
+ CoreObject *co_ps = tcore_pending_ref_core_object(p);
+ enum ps_data_call_status status;
+ dbg("Enter");
+
+ if (resp && resp->success) {
+ dbg("Response OK");
+
+ status = PS_DATA_CALL_CTX_DEFINED;
+ } else {
+ err("Response NOK");
+ status = PS_DATA_CALL_NOT_CONNECTED;
+ }
+
+ __notify_context_status_changed(co_ps, ps_context, status);
+}
+
+static TReturn atd_define_pdp_context(CoreObject *co_ps, CoreObject *ps_context,
+ void *user_data)
+{
+ char *apn = NULL;
+ char *cmd_str = NULL;
+ char *pdp_type_str = NULL;
+ unsigned int cid;
+ enum co_context_type pdp_type;
+ gboolean d_comp = 1;
+ gboolean h_comp = 1;
+ TReturn ret = TCORE_RETURN_FAILURE;
+ dbg("Enter");
+
+ pdp_type = tcore_context_get_type(ps_context);
+ switch (pdp_type) {
+ case CONTEXT_TYPE_IPV4V6:
+ case CONTEXT_TYPE_IP: {
+ dbg("CONTEXT_TYPE_IP");
+ pdp_type_str = g_strdup_printf("IP");
+ }
+ break;
+
+ case CONTEXT_TYPE_PPP: {
+ dbg("CONTEXT_TYPE_PPP");
+ pdp_type_str = g_strdup_printf("PPP");
+ }
+ break;
+
+ case CONTEXT_TYPE_IPV6: {
+ dbg("CONTEXT_TYPE_IPV6");
+ pdp_type_str = g_strdup_printf("IPV6");
+ }
+ break;
+
+ default: {
+ /* PDP Type not supported supported */
+ err("Unsupported PDP type: %d returning", pdp_type);
+ return TCORE_RETURN_FAILURE;
+ }
+ }
+
+ cid = tcore_context_get_id(ps_context);
+ apn = tcore_context_get_apn(ps_context);
+
+ if (tcore_context_get_data_compression(ps_context) == CONTEXT_D_COMP_OFF)
+ d_comp = 0;
+
+ if (tcore_context_get_header_compression(ps_context) == CONTEXT_H_COMP_OFF)
+ h_comp = 0;
+
+ dbg("Define PDP context for CID[%d]", cid);
+ dbg(" pdp_type : [%s]", pdp_type_str);
+ dbg(" apn : [%s]", apn);
+ dbg(" comp : [%d, %d]", d_comp, h_comp);
+
+ cmd_str = g_strdup_printf("AT+CGDCONT=%d,\"%s\",\"%s\",,%d,%d",
+ cid, pdp_type_str, apn, d_comp, h_comp);
+
+ ret = tcore_prepare_and_send_at_request(co_ps,
+ cmd_str, NULL,
+ TCORE_AT_NO_RESULT,
+ NULL,
+ on_response_define_pdp_context, ps_context,
+ NULL, NULL,
+ 0, NULL, NULL);
+ dbg("ret: [0x%x]", ret);
+
+ g_free(cmd_str);
+ g_free(pdp_type_str);
+ g_free(apn);
+
+ return ret;
+}
+
+static TReturn atd_activate_pdp_context(CoreObject *co_ps, CoreObject *ps_context,
+ void *user_data)
+{
+ TReturn ret = TCORE_RETURN_FAILURE;
+ TcoreHal *hal = tcore_object_get_hal(co_ps);
+ unsigned int cid = tcore_context_get_id(ps_context);
+
+ dbg("Setting UP network for CID: [%d] context: [%p]", cid, ps_context);
+
+ /* Mount network interface */
+ ret = tcore_hal_setup_netif(hal, co_ps,
+ __on_setup_pdp, ps_context,
+ cid, TRUE);
+ if (ret != TCORE_RETURN_SUCCESS)
+ err("Setup network interface failed- ret: [0x%x]", ret);
+
+ return ret;
+}
+
+static TReturn atd_deactivate_pdp_context(CoreObject *co_ps,
+ CoreObject *ps_context, void *user_data)
+{
+ TcoreHal *hal = tcore_object_get_hal(co_ps);
+ unsigned int cid = 0;
+ TReturn ret = TCORE_RETURN_FAILURE;
+ dbg("Enter");
+
+ /* Getting Context ID from Core Object */
+ cid = tcore_context_get_id(ps_context);
+
+ /* Unmount network interface */
+ ret = tcore_hal_setup_netif(hal, co_ps,
+ __on_deactivate_pdp, ps_context, cid, FALSE);
+ if (ret != TCORE_RETURN_SUCCESS) {
+ err("Deactivate network interface failed. ret: [0x%x]", ret);
+ __notify_context_status_changed(co_ps, ps_context,
+ PS_DATA_CALL_NOT_CONNECTED);
+ }
+
+ return ret;
+}
+
+/**< PS operations */
+static struct tcore_ps_operations ps_ops = {
+ .define_context = atd_define_pdp_context,
+ .activate_context = atd_activate_pdp_context,
+ .deactivate_context = atd_deactivate_pdp_context,
+ .send_dormant_request = NULL,
+};
+
+/**< PS init function */
+gboolean atd_ps_init(TcorePlugin *p, CoreObject *co_ps)
+{
+ dbg("Enter");
+
+ /* Set operations */
+ tcore_ps_set_ops(co_ps, &ps_ops, TCORE_OPS_TYPE_CP);
+
+ return TRUE;
+}
+
+/**< PS exit function */
+void atd_ps_exit(TcorePlugin *p, CoreObject *co_ps)
+{
+ dbg("Enter");
+
+ /* Unset 'ops' */
+ tcore_ps_set_ops(co_ps, NULL, TCORE_OPS_TYPE_CP);
+}
--- /dev/null
+/*
+ * tel-plugin-atdongle
+ *
+ * Copyright (c) 2015 Samsung Electronics Co., Ltd. All rights reserved.
+ *
+ * Contact:
+ *
+ * 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 <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <glib.h>
+#include <unistd.h>
+
+#include <tcore.h>
+#include <hal.h>
+#include <core_object.h>
+#include <plugin.h>
+#include <queue.h>
+#include <co_sim.h>
+#include <user_request.h>
+#include <server.h>
+#include <storage.h>
+#include <at.h>
+#include <ckmc/ckmc-manager.h>
+
+#include "atd_sim.h"
+
+#define SIM_PIN_INCORRRECT_PASSWORD 16
+#define MAX_SIM_RETRY_COUNT 240
+
+#define SIM_STORE_KEY "telephony_sim_imsi1"
+
+typedef enum {
+ SEC_PIN1_VERIFY,
+ SEC_PIN2_VERIFY,
+ SEC_PUK1_VERIFY,
+ SEC_PUK2_VERIFY,
+ SEC_SIM_VERIFY,
+ SEC_ADM_VERIFY,
+ SEC_PIN1_STATUS,
+ SEC_PIN2_STATUS,
+ SEC_SIM_STATUS,
+ SEC_LOCK_INFO,
+ SEC_SIM_UNKNOWN = 0xff
+} atd_sim_sec_op_e;
+
+struct atd_sim_property {
+ atd_sim_sec_op_e current_sec_op; /**< current index to read */
+ int sim_status_counter; /* sim status counter*/
+ guint sim_init_retry_src_id; /* for sim init callback function */
+ TcorePlugin *plugin;
+};
+
+static void __sim_status_update(CoreObject *co_sim,
+ enum tel_sim_status sim_status);
+static void on_response_get_lock_info(TcorePending *p,
+ int data_len, const void *data, void *user_data);
+
+static enum tcore_response_command __find_resp_command(UserRequest *ur)
+{
+ enum tcore_request_command command;
+
+ command = tcore_user_request_get_command(ur);
+ switch (command) {
+ case TREQ_SIM_VERIFY_PINS:
+ return TRESP_SIM_VERIFY_PINS;
+
+ case TREQ_SIM_VERIFY_PUKS:
+ return TRESP_SIM_VERIFY_PUKS;
+
+ case TREQ_SIM_CHANGE_PINS:
+ return TRESP_SIM_CHANGE_PINS;
+
+ case TREQ_SIM_GET_FACILITY_STATUS:
+ return TRESP_SIM_GET_FACILITY_STATUS;
+
+ case TREQ_SIM_DISABLE_FACILITY:
+ return TRESP_SIM_DISABLE_FACILITY;
+
+ case TREQ_SIM_ENABLE_FACILITY:
+ return TRESP_SIM_ENABLE_FACILITY;
+
+ case TREQ_SIM_GET_LOCK_INFO:
+ return TRESP_SIM_GET_LOCK_INFO;
+
+ case TREQ_SIM_TRANSMIT_APDU:
+ return TRESP_SIM_TRANSMIT_APDU;
+
+ case TREQ_SIM_GET_ATR:
+ return TRESP_SIM_GET_ATR;
+
+ case TREQ_SIM_GET_ECC:
+ return TRESP_SIM_GET_ECC;
+
+ case TREQ_SIM_GET_LANGUAGE:
+ return TRESP_SIM_GET_LANGUAGE;
+
+ case TREQ_SIM_SET_LANGUAGE:
+ return TRESP_SIM_SET_LANGUAGE;
+
+ case TREQ_SIM_GET_ICCID:
+ return TRESP_SIM_GET_ICCID;
+
+ case TREQ_SIM_GET_MAILBOX:
+ return TRESP_SIM_GET_MAILBOX;
+
+ case TREQ_SIM_GET_CALLFORWARDING:
+ return TRESP_SIM_GET_CALLFORWARDING;
+
+ case TREQ_SIM_SET_CALLFORWARDING:
+ return TRESP_SIM_SET_CALLFORWARDING;
+
+ case TREQ_SIM_GET_MESSAGEWAITING:
+ return TRESP_SIM_GET_MESSAGEWAITING;
+
+ case TREQ_SIM_GET_CPHS_INFO:
+ return TRESP_SIM_GET_CPHS_INFO;
+
+ case TREQ_SIM_GET_MSISDN:
+ return TRESP_SIM_GET_MSISDN;
+
+ case TREQ_SIM_GET_SPN:
+ return TRESP_SIM_GET_SPN;
+
+ case TREQ_SIM_GET_SPDI:
+ return TRESP_SIM_GET_SPDI;
+
+ case TREQ_SIM_GET_OPL:
+ return TRESP_SIM_GET_OPL;
+
+ case TREQ_SIM_GET_PNN:
+ return TRESP_SIM_GET_PNN;
+
+ case TREQ_SIM_GET_CPHS_NETNAME:
+ return TRESP_SIM_GET_CPHS_NETNAME;
+
+ case TREQ_SIM_GET_OPLMNWACT:
+ return TRESP_SIM_GET_OPLMNWACT;
+
+ case TREQ_SIM_REQ_AUTHENTICATION:
+ return TRESP_SIM_REQ_AUTHENTICATION;
+
+ default:
+ break;
+ }
+
+ return TRESP_UNKNOWN;
+}
+
+static void __sim_status_update(CoreObject *co_sim,
+ enum tel_sim_status sim_status)
+{
+ if (sim_status != tcore_sim_get_status(co_sim)) {
+ TcorePlugin *plugin = tcore_object_ref_plugin(co_sim);
+ struct tnoti_sim_status noti_data = {0, };
+ dbg("Change in SIM State - Old State: [0x%02x] New State: [0x%02x]",
+ tcore_sim_get_status(co_sim), sim_status);
+
+ /* Update SIM Status */
+ tcore_sim_set_status(co_sim, sim_status);
+ noti_data.sim_status = sim_status;
+
+ /* Send notification */
+ tcore_server_send_notification(tcore_plugin_ref_server(plugin),
+ co_sim, TNOTI_SIM_STATUS,
+ sizeof(noti_data), ¬i_data);
+ }
+}
+
+static char *_add_shared_owner_prefix(const char *name)
+{
+ size_t alias_len = strlen(name) + strlen(ckmc_owner_id_system) + strlen(ckmc_owner_id_separator);
+ char *ckm_alias = (char *)malloc(alias_len + 1);
+ if (!ckm_alias) {
+ err("Failed to allocate memory");
+ return NULL;
+ }
+ memset(ckm_alias, 0, alias_len);
+ strncat(ckm_alias, ckmc_owner_id_system, strlen(ckmc_owner_id_system));
+ strncat(ckm_alias, ckmc_owner_id_separator, strlen(ckmc_owner_id_separator));
+ strncat(ckm_alias, name, strlen(name));
+
+ return ckm_alias;
+}
+
+static gboolean __sim_check_identity(CoreObject *co_sim,
+ struct tel_sim_imsi *imsi)
+{
+ gboolean is_changed = TRUE;
+ /* IMSI is 15 digit, but for distingushing between plmn and msin,
+ * define as 16 bytes. */
+ char new_imsi[16 + 1];
+ char *imsi_buf = NULL;
+ int ret_val = 0;
+ int ret = 0;
+
+ char *alias = NULL;
+ char *passwd = NULL;
+ ckmc_raw_buffer_s *ckmc_buffer;
+
+ dbg("Entry");
+ if (NULL == imsi) {
+ err("imsi is NULL");
+ return FALSE;
+ }
+
+ alias = _add_shared_owner_prefix(SIM_STORE_KEY);
+ if (alias == NULL) {
+ err("Failed to allocate alias name.");
+ return FALSE;
+ }
+
+ memset(new_imsi, 0x5F, 16);
+ memcpy(new_imsi, imsi->plmn, strlen(imsi->plmn));
+ memcpy(&new_imsi[6], imsi->msin, strlen(imsi->msin));
+ new_imsi[6 + strlen(imsi->msin)] = '\0';
+
+ ret_val = ckmc_get_data(alias, passwd, &ckmc_buffer);
+ if (ret_val == CKMC_ERROR_DB_ALIAS_UNKNOWN)
+ is_changed = TRUE;
+ else if (ret_val != CKMC_ERROR_NONE)
+ warn("ckmc_get_data failed. ret_val=[%d]", ret_val);
+ else
+ imsi_buf = (char*)ckmc_buffer->data;
+
+ if (ret_val == CKMC_ERROR_NONE && imsi_buf != NULL) {
+ if (strncmp(imsi_buf, new_imsi, 16) == 0) {
+ is_changed = FALSE;
+ dbg("Same sim");
+ } else {
+ dbg("Remove previous data");
+ ret = ckmc_remove_alias(alias);
+ if (CKMC_ERROR_NONE != ret)
+ warn("ckmc_remove_alias failed. ret=[%d]", ret);
+ }
+ ckmc_buffer_free(ckmc_buffer);
+ }
+
+ if (is_changed) {
+ ckmc_policy_s policy;
+ ckmc_raw_buffer_s store_buffer;
+
+ policy.password = passwd;
+ policy.extractable = true;
+ store_buffer.data = (unsigned char*)new_imsi;
+ store_buffer.size = strlen(new_imsi) + 1;
+
+ dbg("NEW SIM");
+ /* Update file */
+ ret_val = ckmc_save_data(alias, store_buffer, policy);
+ if (ret_val != CKMC_ERROR_NONE)
+ err("ckmc_save_data failed. ret_val=[%d]", ret_val);
+ }
+
+ /* Update sim identification */
+ tcore_sim_set_identification(co_sim, is_changed);
+
+ if (alias)
+ free(alias);
+
+ return TRUE;
+}
+
+static gboolean __sim_timer_handler_cb(gpointer data)
+{
+ struct atd_sim_property *sp = data;
+
+ dbg("Entry");
+ if (!sp) {
+ err("invalid user data");
+ return FALSE;
+ }
+
+ get_sim_status(sp->plugin);
+ sp->sim_init_retry_src_id = 0;
+
+ /* return FALSE to stop cycle */
+ return FALSE;
+}
+
+static void on_response_get_imsi(TcorePending *p,
+ int data_len, const void *data, void *user_data)
+{
+ const TcoreATResponse *resp = data;
+ GSList *tokens = NULL;
+ const char *line;
+ const char *imsi_str;
+ const char *error_str;
+ CoreObject *co_sim = NULL;
+ TcorePlugin *plugin = NULL;
+ struct tel_sim_imsi *imsi = NULL;
+
+ co_sim = tcore_pending_ref_core_object(p);
+ if (co_sim == NULL) {
+ err("CoreObject is NULL");
+ return;
+ }
+
+ if (resp && resp->success) {
+ dbg("RESPONSE OK");
+ if (resp->lines) {
+ int plmn_digits = 5;
+ char *plmn = NULL;
+
+ line = (const char *) resp->lines->data;
+ dbg("Line : [%s]", line);
+ tokens = tcore_at_tok_new(line);
+ if (g_slist_length(tokens) != 1) {
+ err("invalid message.");
+ goto OUT;
+ }
+
+ /* Copy IMSI */
+ imsi = g_try_new0(struct tel_sim_imsi, 1);
+ if (imsi == NULL) {
+ err("IMSI allocation failed.");
+ goto OUT;
+ }
+
+ imsi_str = g_slist_nth_data(tokens, 0);
+
+ /* Determine # of PLMN digits (5 or 6) */
+ plmn = g_strndup(imsi_str, 6 + 1);
+ if (plmn) {
+ plmn[6] = '\0';
+ if (tcore_sim_check_plmn_having_3digits_mnc(plmn))
+ plmn_digits = 6;
+
+ g_free(plmn);
+ }
+
+ memcpy(imsi->plmn, imsi_str, plmn_digits);
+ imsi->plmn[plmn_digits] = '\0';
+ memcpy(imsi->msin, imsi_str+plmn_digits,
+ strlen(imsi_str) - plmn_digits);
+ imsi->msin[strlen(imsi_str) - plmn_digits] = '\0';
+ dbg("imsi len[%d], imsi->plmn[%s], imsi->msin[%s]",
+ strlen(imsi_str), imsi->plmn, imsi->msin);
+
+ __sim_check_identity(co_sim, imsi);
+ tcore_sim_set_imsi(co_sim, imsi);
+
+ /* Update status */
+ __sim_status_update(co_sim, SIM_STATUS_INIT_COMPLETED);
+ }
+ } else {
+ err("RESPONSE NOK");
+ if (!resp)
+ goto OUT;
+
+ line = (const char *)resp->final_response;
+ tokens = tcore_at_tok_new(line);
+ if (g_slist_length(tokens) < 1) {
+ err("Unknown Error OR Corrupted string");
+ goto OUT;
+ }
+
+ error_str = g_slist_nth_data(tokens, 0);
+ dbg("Error: [%s]", error_str);
+ if (g_str_has_prefix(error_str, "SIM PIN") == FALSE
+ && g_str_has_prefix(error_str, "SIM PUK") == FALSE)
+ goto OUT;
+
+ /* When it is failed, we need to check sim status again */
+ plugin = tcore_object_ref_plugin(co_sim);
+ if (!plugin) {
+ err("Failed to get plugin");
+ goto OUT;
+ }
+ get_sim_status(plugin);
+ }
+OUT:
+ /* Free resources */
+ tcore_at_tok_free(tokens);
+}
+
+static gboolean __get_sim_type(CoreObject *co_sim)
+{
+ enum tel_sim_type sim_type = SIM_TYPE_UNKNOWN;
+
+ dbg("Entry");
+
+ /* set SIM type to USIM because there is no AT command */
+ sim_type = SIM_TYPE_USIM;
+ dbg("SIM Type is %d", sim_type);
+
+ tcore_sim_set_type(co_sim, sim_type);
+ tcore_sim_set_app_list(co_sim, SIM_APP_TYPE_USIM);
+
+ return TRUE;
+}
+
+static gboolean __get_sim_imsi(CoreObject *co_sim)
+{
+ TReturn ret = TCORE_RETURN_FAILURE;
+
+ dbg("Entry");
+
+ ret = tcore_prepare_and_send_at_request(co_sim,
+ "AT+CIMI", NULL,
+ TCORE_AT_NUMERIC, NULL,
+ on_response_get_imsi, NULL,
+ NULL, NULL,
+ 0, NULL, NULL);
+ if (ret != TCORE_RETURN_SUCCESS) {
+ err("ret: [0x%x]", ret);
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+static int __sim_get_current_pin_facility(atd_sim_sec_op_e op)
+{
+ int ret_type = 0;
+
+ dbg("current sec_op[%d]", op);
+
+ switch (op) {
+ case SEC_PIN1_VERIFY:
+ ret_type = SIM_PTYPE_PIN1;
+ break;
+
+ case SEC_PIN2_VERIFY:
+ ret_type = SIM_PTYPE_PIN2;
+ break;
+
+ case SEC_PUK1_VERIFY:
+ ret_type = SIM_PTYPE_PUK1;
+ break;
+
+ case SEC_PUK2_VERIFY:
+ ret_type = SIM_PTYPE_PUK2;
+ break;
+
+ case SEC_PIN1_STATUS:
+ ret_type = SIM_FACILITY_SC;
+ break;
+
+ case SEC_SIM_STATUS:
+ ret_type = SIM_FACILITY_PS;
+ break;
+
+ default:
+ err("not handled current sec op[%d]", op);
+ break;
+ }
+
+ return ret_type;
+}
+
+static void on_response_get_lock_info(TcorePending *p,
+ int data_len, const void *data, void *user_data)
+{
+ const TcoreATResponse *resp = data;
+ UserRequest *ur = NULL;
+ CoreObject *co_sim = NULL;
+ struct atd_sim_property *sp = NULL;
+ GSList *tokens = NULL;
+
+ dbg("Entry");
+
+ co_sim = tcore_pending_ref_core_object(p);
+ sp = tcore_sim_ref_userdata(co_sim);
+ if (!sp) {
+ err("invalid user data");
+ return;
+ }
+ ur = tcore_pending_ref_user_request(p);
+
+ if (resp && resp->success > 0) {
+ const char *line = NULL;
+ char *res_type = NULL;
+
+ dbg("RESPONSE OK");
+ if (!resp->lines) {
+ err("invalid message");
+ return;
+ }
+
+ line = (const char *)resp->lines->data;
+ tokens = tcore_at_tok_new(line);
+
+ res_type = g_slist_nth_data(tokens, 0);
+ dbg("Lock type: [%s]", res_type);
+
+ switch (sp->current_sec_op) {
+ case SEC_LOCK_INFO: {
+ struct tresp_sim_get_lock_info v_info = {0, };
+ v_info.type = SIM_FACILITY_SC;
+
+ if (g_strcmp0(res_type, "SIM PIN") == 0) {
+ dbg("SIM PIN LOCK");
+
+ v_info.result = SIM_PIN_OPERATION_SUCCESS;
+ v_info.retry_count = 0;
+ v_info.lock_status = SIM_LOCK_STATUS_PIN;
+ } else if (g_strcmp0(res_type, "SIM PIN2") == 0) {
+ dbg("SIM PIN2 LOCK");
+
+ v_info.result = SIM_PIN_OPERATION_SUCCESS;
+ v_info.retry_count = 0;
+ v_info.lock_status = SIM_LOCK_STATUS_PIN2;
+ } else if (g_strcmp0(res_type, "SIM PUK") == 0) {
+ dbg("SIM PUK LOCK");
+
+ v_info.result = SIM_PIN_OPERATION_SUCCESS;
+ v_info.retry_count = 0;
+ v_info.lock_status = SIM_LOCK_STATUS_PUK;
+ } else if (g_strcmp0(res_type, "SIM PUK2") == 0) {
+ dbg("SIM PUK2 LOCK");
+
+ v_info.result = SIM_PIN_OPERATION_SUCCESS;
+ v_info.retry_count = 0;
+ v_info.lock_status = SIM_LOCK_STATUS_PUK2;
+ } else if (g_strcmp0(res_type, "READY") == 0) {
+ dbg("SIM IS NOT LOCKED");
+
+ v_info.result = SIM_PIN_OPERATION_SUCCESS;
+ v_info.retry_count = 0;
+ v_info.lock_status = SIM_LOCK_STATUS_UNLOCKED;
+ } else {
+ err("UNKNOWN LOCK");
+
+ v_info.result = SIM_CARD_ERROR;
+ v_info.retry_count = 0;
+ v_info.lock_status = SIM_LOCK_STATUS_PERM_BLOCKED;
+ }
+
+ tcore_user_request_send_response(ur, __find_resp_command(ur),
+ sizeof(struct tresp_sim_get_lock_info), &v_info);
+ }
+ break;
+
+ default:
+ err("not handled sec op[%d]", sp->current_sec_op);
+ break;
+ }
+
+ /* Free tokens */
+ tcore_at_tok_free(tokens);
+ }
+}
+
+static void on_response_verify_pins(TcorePending *p,
+ int data_len, const void *data, void *user_data)
+{
+ const TcoreATResponse *resp = data;
+ UserRequest *ur = NULL;
+ CoreObject *co_sim = NULL;
+ const struct treq_sim_verify_pins *req_data = NULL;
+ struct tresp_sim_verify_pins res = {0,};
+
+ dbg("Entry");
+
+ co_sim = tcore_pending_ref_core_object(p);
+ ur = tcore_pending_ref_user_request(p);
+
+ res.result = SIM_CARD_ERROR;
+
+ req_data = tcore_user_request_ref_data(ur, NULL);
+ if (!req_data) {
+ err("Invalid data");
+ goto OUT;
+ }
+
+ if (resp && resp->success > 0) {
+ struct atd_sim_property *sp = NULL;
+ dbg("RESPONSE OK");
+
+ sp = tcore_sim_ref_userdata(co_sim);
+ if (!sp) {
+ err("invalid user data");
+ goto OUT;
+ }
+
+ /* Get PIN facility */
+ res.pin_type = __sim_get_current_pin_facility(sp->current_sec_op);
+ res.result = SIM_PIN_OPERATION_SUCCESS;
+
+ get_sim_status(tcore_object_ref_plugin(co_sim));
+ } else {
+ GSList *tokens = NULL;
+ const char *line;
+ int err = 0;
+
+ err("RESPONSE NOK");
+ if (!resp) {
+ err("invalid data");
+ goto OUT;
+ }
+
+ line = (const char *)resp->final_response;
+ tokens = tcore_at_tok_new(line);
+ if (g_slist_length(tokens) < 1) {
+ err("Unknown Error OR Corrupted string");
+ tcore_at_tok_free(tokens);
+ goto OUT;
+ } else {
+ const char *check = NULL;
+ check = g_slist_nth_data(tokens, 0);
+ if (check)
+ err = atoi(check);
+ err("Error: [%d]", err);
+
+ if (err == SIM_PIN_INCORRRECT_PASSWORD)
+ res.result = SIM_INCORRECT_PASSWORD;
+ }
+
+ /* Free tokens */
+ tcore_at_tok_free(tokens);
+
+ return;
+ }
+
+OUT:
+ tcore_user_request_send_response(ur, __find_resp_command(ur),
+ sizeof(struct tresp_sim_verify_pins), &res);
+}
+
+static void on_response_verify_puks(TcorePending *p,
+ int data_len, const void *data, void *user_data)
+{
+ const TcoreATResponse *resp = data;
+ UserRequest *ur = NULL;
+ CoreObject *co_sim = NULL;
+ const struct treq_sim_verify_puks *req_data;
+ struct tresp_sim_verify_puks res = {0,};
+
+ dbg("Entry");
+
+ co_sim = tcore_pending_ref_core_object(p);
+ ur = tcore_pending_ref_user_request(p);
+
+ res.result = SIM_CARD_ERROR;
+
+ req_data = tcore_user_request_ref_data(ur, NULL);
+ if (!req_data) {
+ err("Invalid data");
+ goto OUT;
+ }
+
+ if (resp && resp->success > 0) {
+ struct atd_sim_property *sp = NULL;
+
+ dbg("RESPONSE OK");
+ sp = tcore_sim_ref_userdata(co_sim);
+ if (!sp) {
+ err("invalid user data");
+ goto OUT;
+ }
+
+ /* Get PIN facility */
+ res.pin_type = __sim_get_current_pin_facility(sp->current_sec_op);
+ res.result = SIM_PIN_OPERATION_SUCCESS;
+
+ get_sim_status(tcore_object_ref_plugin(co_sim));
+ } else {
+ GSList *tokens = NULL;
+ const char *line;
+ int err = 0;
+
+ err("RESPONSE NOK");
+ if (!resp) {
+ err("invalid data");
+ goto OUT;
+ }
+
+ line = (const char *)resp->final_response;
+ tokens = tcore_at_tok_new(line);
+
+ if (g_slist_length(tokens) < 1) {
+ err("Unknown Error OR String corrupted");
+ tcore_at_tok_free(tokens);
+ goto OUT;
+
+ } else {
+ const char *check = NULL;
+ check = g_slist_nth_data(tokens, 0);
+ if (check)
+ err = atoi(check);
+ err("Error: [%d]", err);
+
+ if (err == SIM_PIN_INCORRRECT_PASSWORD)
+ res.result = SIM_INCORRECT_PASSWORD;
+ }
+ tcore_at_tok_free(tokens);
+ }
+ return;
+OUT:
+ tcore_user_request_send_response(ur,
+ __find_resp_command(ur),
+ sizeof(struct tresp_sim_verify_puks), &res);
+}
+
+static void on_response_get_facility_status(TcorePending *p,
+ int data_len, const void *data, void *user_data)
+{
+ const TcoreATResponse *resp = data;
+ struct tresp_sim_get_facility_status res = {0,};
+ UserRequest *ur = NULL;
+ GSList *tokens = NULL;
+
+ dbg("Entry");
+
+ res.type = (int)user_data;
+ res.result = SIM_INCOMPATIBLE_PIN_OPERATION;
+
+ if (resp && resp->success > 0) {
+ const char *line;
+ const char *check = NULL;
+ dbg("RESPONSE OK");
+ if (resp->lines) {
+ line = (const char *)resp->lines->data;
+ tokens = tcore_at_tok_new(line);
+ if (g_slist_length(tokens) != 1) {
+ dbg("Invalid message");
+
+ goto OUT;
+ }
+
+ check = g_slist_nth_data(tokens, 0);
+ if (check)
+ res.b_enable = atoi(check);
+
+ res.result = SIM_PIN_OPERATION_SUCCESS;
+ }
+
+ } else {
+ err("RESPONSE NOK");
+ }
+
+OUT:
+ /* Send Response */
+ ur = tcore_pending_ref_user_request(p);
+ if (ur) {
+ tcore_user_request_send_response(ur,
+ __find_resp_command(ur),
+ sizeof(struct tresp_sim_get_facility_status), &res);
+ }
+
+ tcore_at_tok_free(tokens);
+}
+
+static void on_response_enable_facility(TcorePending *p, int data_len,
+ const void *data, void *user_data)
+{
+ const TcoreATResponse *resp = data;
+ UserRequest *ur = NULL;
+ CoreObject *co_sim = NULL;
+ struct atd_sim_property *sp = NULL;
+ GSList *tokens = NULL;
+ struct tresp_sim_enable_facility res;
+
+ dbg("Entry");
+
+ co_sim = tcore_pending_ref_core_object(p);
+ sp = tcore_sim_ref_userdata(co_sim);
+
+ memset(&res, 0, sizeof(struct tresp_sim_enable_facility));
+
+ res.type = (int)user_data;
+ res.result = SIM_CARD_ERROR;
+
+ if (resp && resp->success > 0) {
+ const char *line;
+ dbg("RESPONSE OK");
+ if (resp->lines) {
+ line = (const char *)resp->lines->data;
+ tokens = tcore_at_tok_new(line);
+
+ if (g_slist_length(tokens) != 1) {
+ dbg("Invalid message");
+
+ /* Send Response */
+ tcore_user_request_send_response(ur,
+ __find_resp_command(ur),
+ sizeof(struct tresp_sim_enable_facility), &res);
+ tcore_at_tok_free(tokens);
+ return;
+ }
+ }
+
+ res.result = SIM_PIN_OPERATION_SUCCESS;
+ /* Get SIM status */
+ get_sim_status(tcore_object_ref_plugin(co_sim));
+
+ /* Free tokens */
+ tcore_at_tok_free(tokens);
+ } else {
+ dbg("RESPONSE NOK");
+ }
+
+ /* Send Response */
+ ur = tcore_pending_ref_user_request(p);
+ if (ur) {
+ tcore_user_request_send_response(ur, __find_resp_command(ur),
+ sizeof(struct tresp_sim_enable_facility), &res);
+ }
+
+ dbg("Exit");
+}
+
+static void on_response_disable_facility(TcorePending *p, int data_len,
+ const void *data, void *user_data)
+{
+ const TcoreATResponse *resp = data;
+ UserRequest *ur = NULL;
+ CoreObject *co_sim = NULL;
+ struct atd_sim_property *sp = NULL;
+ GSList *tokens = NULL;
+ struct tresp_sim_disable_facility res;
+
+ dbg("Entry");
+
+ co_sim = tcore_pending_ref_core_object(p);
+ sp = tcore_sim_ref_userdata(co_sim);
+
+ memset(&res, 0, sizeof(struct tresp_sim_disable_facility));
+
+ res.type = (int)user_data;
+ res.result = SIM_CARD_ERROR;
+
+ if (resp && resp->success > 0) {
+ const char *line;
+ dbg("RESPONSE OK");
+ if (resp->lines) {
+ line = (const char *)resp->lines->data;
+ tokens = tcore_at_tok_new(line);
+
+ if (g_slist_length(tokens) != 1) {
+ dbg("Invalid message");
+
+ /* Send Response */
+ tcore_user_request_send_response(ur,
+ __find_resp_command(ur),
+ sizeof(struct tresp_sim_disable_facility), &res);
+ tcore_at_tok_free(tokens);
+ return;
+ }
+ }
+
+ res.result = SIM_PIN_OPERATION_SUCCESS;
+
+ /* Free tokens */
+ tcore_at_tok_free(tokens);
+ } else {
+ dbg("RESPONSE NOK");
+ }
+
+ /* Send Response */
+ ur = tcore_pending_ref_user_request(p);
+ if (ur) {
+ tcore_user_request_send_response(ur, __find_resp_command(ur),
+ sizeof(struct tresp_sim_disable_facility), &res);
+ }
+
+ dbg("Exit");
+}
+
+static void on_response_get_sim_status(TcorePending *p,
+ int data_len, const void *data, void *user_data)
+{
+ const TcoreATResponse *resp = data;
+ GSList *tokens = NULL;
+ const char *line;
+ const char *sim_status;
+ enum tel_sim_status sim_state = SIM_STATUS_UNKNOWN;
+ CoreObject *co_sim = NULL;
+
+ TcorePlugin *plugin;
+ enum tel_sim_type sim_type = SIM_TYPE_UNKNOWN;
+ struct tel_sim_imsi *sim_imsi = NULL;
+ struct atd_sim_property *sp = NULL;
+
+ co_sim = tcore_pending_ref_core_object(p);
+ if (co_sim == NULL) {
+ err("CoreObject is NULL");
+ goto OUT;
+ }
+ plugin = tcore_object_ref_plugin(co_sim);
+ if (plugin == NULL) {
+ err("plugin is NULL");
+ goto OUT;
+ }
+ sp = tcore_sim_ref_userdata(co_sim);
+ if (!sp) {
+ err("invalid user data");
+ return;
+ }
+
+ if (resp && resp->success) {
+ dbg("RESPONSE OK");
+ if (resp->lines) {
+ line = (const char *) resp->lines->data;
+ dbg("Line : [%s]", line);
+ tokens = tcore_at_tok_new(line);
+ /* Response strcuture would be -
+ * <CR><LF>+CPIN: <code><CR><LF>
+ * <CR><LF>OK<CR><LF>
+ */
+ sim_status = g_slist_nth_data(tokens, 0);
+
+ if (!sim_status) {
+ err("Invalid data");
+ goto OUT;
+ }
+
+ dbg("status: [%s]", sim_status);
+ if (g_strcmp0(sim_status, "READY") == 0) {
+ sim_state = SIM_STATUS_INIT_COMPLETED;
+ dbg("VALID SIM STATE");
+ } else if (g_strcmp0(sim_status, "SIM PIN") == 0) {
+ sim_state = SIM_STATUS_PIN_REQUIRED;
+ dbg("SIM PIN REQUIRED");
+ } else if (g_strcmp0(sim_status, "SIM PIN2") == 0) {
+ sim_state = SIM_STATUS_PIN_REQUIRED;
+ dbg("SIM PIN2 REQUIRED");
+ } else if (g_strcmp0(sim_status, "SIM PUK") == 0) {
+ sim_state = SIM_STATUS_PUK_REQUIRED;
+ dbg("SIM PUK REQUIRED");
+ } else if (g_strcmp0(sim_status, "SIM PUK2") == 0) {
+ sim_state = SIM_STATUS_PUK_REQUIRED;
+ dbg("SIM PUK2 REQUIRED");
+ }
+
+ switch (sim_state) {
+ case SIM_STATUS_INIT_COMPLETED:
+ dbg("[SIM] SIM INIT COMPLETED");
+ /* TODO: Check SIM type - 2G / 3G */
+ sim_type = tcore_sim_get_type(co_sim);
+ sim_imsi = tcore_sim_get_imsi(co_sim);
+ if (!sim_imsi) {
+ err("[SIM] imsi is NULL");
+ goto OUT;
+ }
+ if (sim_type == SIM_TYPE_UNKNOWN
+ || strlen(sim_imsi->plmn) == 0) {
+ __get_sim_type(co_sim);
+ __get_sim_imsi(co_sim);
+ } else {
+ /* Update status */
+ __sim_status_update(co_sim, sim_state);
+ }
+ sp->sim_status_counter = 0;
+ g_free(sim_imsi);
+ goto OUT;
+ break;
+
+ case SIM_STATUS_PIN_REQUIRED:
+ dbg("SIM PIN is required !");
+ __sim_status_update(co_sim, sim_state);
+ goto OUT;
+ break;
+
+ case SIM_STATUS_PUK_REQUIRED:
+ dbg("SIM PUK is required !");
+ __sim_status_update(co_sim, sim_state);
+ goto OUT;
+ break;
+
+ case SIM_STATUS_CARD_NOT_PRESENT:
+ err("[SIM] SIM CARD NOT PRESENT");
+ tcore_sim_set_type(co_sim, SIM_TYPE_UNKNOWN);
+ break;
+
+ case SIM_STATUS_UNKNOWN:
+ err("[SIM] SIM CARD IS INVALID");
+ tcore_sim_set_type(co_sim, SIM_TYPE_UNKNOWN);
+ break;
+
+ case SIM_STATUS_INITIALIZING:
+ err("[SIM] SIM STATUS INITIALIZING");
+ tcore_sim_set_type(co_sim, SIM_TYPE_UNKNOWN);
+ break;
+ default:
+ err("SIM Status: [0x%02x]", sim_state);
+ break;
+ }
+
+ /* Update status */
+ __sim_status_update(co_sim, sim_state);
+ dbg("SIM init not completed");
+ /* Poll for 240 seconds to get SIM status*/
+ if (sp->sim_status_counter < MAX_SIM_RETRY_COUNT) {
+ sp->plugin = plugin;
+ if (sp->sim_init_retry_src_id) {
+ dbg("Remove timer source id");
+ g_source_remove(sp->sim_init_retry_src_id);
+ sp->sim_init_retry_src_id = 0;
+ }
+ sp->sim_init_retry_src_id = g_timeout_add(1000,
+ __sim_timer_handler_cb, sp);
+
+ sp->sim_status_counter++;
+ dbg("counter[%d]", sp->sim_status_counter);
+ } else {
+ sp->sim_status_counter = 0;
+ err("Failed to get sim state !");
+ err("[SIM] SIM CARD NOT PRESENT");
+ tcore_sim_set_type(co_sim, SIM_TYPE_UNKNOWN);
+ __sim_status_update(co_sim, SIM_STATUS_CARD_NOT_PRESENT);
+ }
+ } else {
+ err("has no lines !!");
+ get_sim_status(plugin);
+ }
+ } else {
+ err("RESPONSE NOK");
+ get_sim_status(plugin);
+ }
+OUT:
+ /* Free resources */
+ tcore_at_tok_free(tokens);
+}
+
+gboolean get_sim_status(TcorePlugin *plugin)
+{
+ CoreObject *co;
+ TReturn ret;
+
+ dbg("Entry");
+ co = tcore_plugin_ref_core_object(plugin, CORE_OBJECT_TYPE_SIM);
+
+ /* Get SIM status */
+ ret = tcore_prepare_and_send_at_request(co,
+ "AT+CPIN?", "+CPIN:",
+ TCORE_AT_SINGLELINE,
+ NULL,
+ on_response_get_sim_status, NULL,
+ NULL, NULL,
+ 0, NULL, NULL);
+ dbg("ret: [0x%x]", ret);
+ if (ret != TCORE_RETURN_SUCCESS)
+ return FALSE;
+
+ return TRUE;
+}
+
+static TReturn atd_verify_pins(CoreObject *co_sim, UserRequest *ur)
+{
+ char *cmd_str = NULL;
+ const struct treq_sim_verify_pins *req_data = NULL;
+ struct atd_sim_property *sp = NULL;
+ TReturn ret = TCORE_RETURN_FAILURE;
+
+ dbg("Entry");
+
+ if ((co_sim == NULL) || (ur == NULL)) {
+ err("co: [%p] ur: [%p]", co_sim);
+ return TCORE_RETURN_EINVAL;
+ }
+
+ sp = tcore_sim_ref_userdata(co_sim);
+ if (!sp) {
+ err("sim property is NULL");
+ return TCORE_RETURN_EINVAL;
+ }
+ req_data = tcore_user_request_ref_data(ur, NULL);
+
+ if (req_data->pin_type == SIM_PTYPE_PIN1) {
+ sp->current_sec_op = SEC_PIN1_VERIFY;
+ } else if (req_data->pin_type == SIM_PTYPE_PIN2) {
+ sp->current_sec_op = SEC_PIN2_VERIFY;
+ } else {
+ err("Invalid PIN type: [%d]", req_data->pin_type);
+ return TCORE_RETURN_EINVAL;
+ }
+
+ cmd_str = g_strdup_printf("AT+CPIN=\"%s\"", req_data->pin);
+ ret = tcore_prepare_and_send_at_request(co_sim,
+ cmd_str, NULL,
+ TCORE_AT_NO_RESULT,
+ ur,
+ on_response_verify_pins, NULL,
+ NULL, NULL,
+ 0, NULL, NULL);
+ dbg("ret: [0x%x]", ret);
+ g_free(cmd_str);
+
+ return ret;
+}
+
+static TReturn atd_verify_puks(CoreObject *co_sim, UserRequest *ur)
+{
+ char *cmd_str = NULL;
+ const struct treq_sim_verify_puks *req_data;
+ struct atd_sim_property *sp = NULL;
+ TReturn ret = TCORE_RETURN_FAILURE;
+
+ dbg("Entry");
+
+ if ((co_sim == NULL) || (ur == NULL)) {
+ err("co: [%p] ur: [%p]", co_sim);
+ return TCORE_RETURN_EINVAL;
+ }
+
+ sp = tcore_sim_ref_userdata(co_sim);
+ if (!sp) {
+ err("sim property is NULL");
+ return TCORE_RETURN_EINVAL;
+ }
+ req_data = tcore_user_request_ref_data(ur, NULL);
+
+ if (req_data->puk_type == SIM_PTYPE_PUK1) {
+ sp->current_sec_op = SEC_PUK1_VERIFY;
+ } else if (req_data->puk_type == SIM_PTYPE_PUK2) {
+ sp->current_sec_op = SEC_PUK2_VERIFY;
+ } else {
+ err("Invalid PUK type: [%d]", req_data->puk_type);
+ return TCORE_RETURN_EINVAL;
+ }
+
+ cmd_str = g_strdup_printf("AT+CPIN=\"%s\", \"%s\"",
+ req_data->puk, req_data->pin);
+ ret = tcore_prepare_and_send_at_request(co_sim,
+ cmd_str, NULL,
+ TCORE_AT_NO_RESULT,
+ ur,
+ on_response_verify_puks, NULL,
+ NULL, NULL,
+ 0, NULL, NULL);
+ dbg("ret: [0x%x]", ret);
+ g_free(cmd_str);
+
+ return ret;
+}
+
+static TReturn atd_get_facility_status(CoreObject *co_sim, UserRequest *ur)
+{
+ char *cmd_str = NULL;
+ const struct treq_sim_get_facility_status *req_data;
+ int mode = 2; /* 0:unblock, 1:lock, 2:query state */
+ TReturn ret = TCORE_RETURN_FAILURE;
+
+ dbg("Entry");
+
+ if ((co_sim == NULL) || (ur == NULL)) {
+ err("co: [%p] ur: [%p]", co_sim);
+ return TCORE_RETURN_EINVAL;
+ }
+
+ req_data = tcore_user_request_ref_data(ur, NULL);
+ if (!req_data) {
+ err("invalid ref data");
+ return TCORE_RETURN_EINVAL;
+ }
+
+ /* supports only faclity type "SC" */
+ if (req_data->type != SIM_FACILITY_SC) {
+ err("Invalid facility type");
+ return TCORE_RETURN_EINVAL;
+ }
+
+ cmd_str = g_strdup_printf("AT+CLCK=\"SC\", %d", mode);
+ ret = tcore_prepare_and_send_at_request(co_sim,
+ cmd_str, "+CLCK:",
+ TCORE_AT_SINGLELINE,
+ ur,
+ on_response_get_facility_status, (void *)req_data->type,
+ NULL, NULL,
+ 0, NULL, NULL);
+ dbg("ret: [0x%x]", ret);
+ g_free(cmd_str);
+
+ return ret;
+}
+
+static TReturn atd_enable_facility(CoreObject *co_sim, UserRequest *ur)
+{
+ char *cmd_str = NULL;
+ const struct treq_sim_enable_facility *req_data;
+ int mode = 1; /* 0:unlock, 1:lock, 2:query*/
+ TReturn ret = TCORE_RETURN_FAILURE;
+
+ dbg("Entry");
+
+ if ((co_sim == NULL) || (ur == NULL)) {
+ err("co: [%p] ur: [%p]", co_sim);
+ return TCORE_RETURN_EINVAL;
+ }
+
+ req_data = tcore_user_request_ref_data(ur, NULL);
+ if (!req_data) {
+ err("invalid ref data");
+ return TCORE_RETURN_EINVAL;
+ }
+
+ /* supports only faclity type "SC" */
+ if (req_data->type != SIM_FACILITY_SC) {
+ err("Invalid facility type");
+ return TCORE_RETURN_EINVAL;
+ }
+
+ cmd_str = g_strdup_printf("AT+CLCK=\"SC\", %d, \"%s\"",
+ mode, req_data->password);
+
+ ret = tcore_prepare_and_send_at_request(co_sim,
+ cmd_str, "+CLCK:",
+ TCORE_AT_SINGLELINE,
+ ur,
+ on_response_enable_facility, (void *)req_data->type,
+ NULL, NULL,
+ 0, NULL, NULL);
+ dbg("ret: [0x%x]", ret);
+ g_free(cmd_str);
+
+ return ret;
+}
+
+static TReturn atd_disable_facility(CoreObject *co_sim, UserRequest *ur)
+{
+ char *cmd_str = NULL;
+ const struct treq_sim_disable_facility *req_data;
+ int mode = 0; /* 0:unlock, 1:lock, 2:query*/
+ TReturn ret = TCORE_RETURN_FAILURE;
+
+ dbg("Entry");
+
+ if ((co_sim == NULL) || (ur == NULL)) {
+ err("co: [%p] ur: [%p]", co_sim);
+ return TCORE_RETURN_EINVAL;
+ }
+
+ req_data = tcore_user_request_ref_data(ur, NULL);
+ if (!req_data) {
+ err("invalid ref data");
+ return TCORE_RETURN_EINVAL;
+ }
+
+ /* supports only faclity type "SC" */
+ if (req_data->type != SIM_FACILITY_SC) {
+ err("Invalid facility type");
+ return TCORE_RETURN_EINVAL;
+ }
+
+ cmd_str = g_strdup_printf("AT+CLCK=\"SC\", %d, \"%s\"",
+ mode, req_data->password);
+
+ ret = tcore_prepare_and_send_at_request(co_sim,
+ cmd_str, "+CLCK:",
+ TCORE_AT_SINGLELINE,
+ ur,
+ on_response_disable_facility, (void *)req_data->type,
+ NULL, NULL,
+ 0, NULL, NULL);
+ dbg("ret: [0x%x]", ret);
+ g_free(cmd_str);
+
+ return ret;
+}
+
+static TReturn atd_get_lock_info(CoreObject *co_sim, UserRequest *ur)
+{
+ const struct treq_sim_get_lock_info *req_data;
+ struct atd_sim_property *sp = NULL;
+ TReturn ret = TCORE_RETURN_FAILURE;
+
+ dbg("Entry");
+
+ if ((co_sim == NULL) || (ur == NULL)) {
+ err("co: [%p] ur: [%p]", co_sim);
+ return TCORE_RETURN_EINVAL;
+ }
+
+ sp = tcore_sim_ref_userdata(co_sim);
+ if (!sp) {
+ err("sim property is NULL");
+ return TCORE_RETURN_EINVAL;
+ }
+
+ sp->current_sec_op = SEC_LOCK_INFO;
+ req_data = tcore_user_request_ref_data(ur, NULL);
+ if (!req_data) {
+ err("Invalid ref data");
+ return TCORE_RETURN_EINVAL;
+ }
+
+ /* supports only facilty type "SC" */
+ if (req_data->type != SIM_FACILITY_SC) {
+ err("Invalid facility type");
+ return TCORE_RETURN_EINVAL;
+ }
+
+ ret = tcore_prepare_and_send_at_request(co_sim,
+ "AT+CPIN?", "+CPIN:",
+ TCORE_AT_SINGLELINE,
+ ur,
+ on_response_get_lock_info, NULL,
+ NULL, NULL,
+ 0, NULL, NULL);
+ dbg("ret: [0x%x]", ret);
+
+ return ret;
+}
+
+static TReturn atd_read_file(CoreObject *co_sim, UserRequest *ur)
+{
+ TReturn api_ret = TCORE_RETURN_SUCCESS;
+ enum tcore_request_command command;
+
+ dbg("Entry");
+
+ if ((co_sim == NULL) || (ur == NULL))
+ return TCORE_RETURN_EINVAL;
+
+ command = tcore_user_request_get_command(ur);
+ if (FALSE == tcore_hal_get_power_state(tcore_object_get_hal(co_sim))) {
+ err("CP NOT READY");
+ return TCORE_RETURN_ENOSYS;
+ }
+
+ switch (command) {
+ default:
+ dbg("error - not handled read treq command[%d]", command);
+ api_ret = TCORE_RETURN_ENOSYS;
+ break;
+ }
+ dbg("Exit");
+ return api_ret;
+}
+
+/**< SIM Operations */
+static struct tcore_sim_operations sim_ops = {
+ .verify_pins = atd_verify_pins,
+ .verify_puks = atd_verify_puks,
+ .change_pins = NULL,
+ .get_facility_status = atd_get_facility_status,
+ .enable_facility = atd_enable_facility,
+ .disable_facility = atd_disable_facility,
+ .get_lock_info = atd_get_lock_info,
+ .read_file = atd_read_file,
+ .update_file = NULL,
+ .transmit_apdu = NULL,
+ .get_atr = NULL,
+ .req_authentication = NULL,
+ .set_powerstate = NULL,
+};
+
+/**< SIM init function */
+gboolean atd_sim_init(TcorePlugin *p, CoreObject *co_sim)
+{
+ struct atd_sim_property *file_meta;
+
+ dbg("Entry");
+
+ file_meta = g_try_new0(struct atd_sim_property, 1);
+ if (file_meta == NULL)
+ return FALSE;
+
+ tcore_sim_link_userdata(co_sim, file_meta);
+
+ /* Set operations */
+ tcore_sim_set_ops(co_sim, &sim_ops, TCORE_OPS_TYPE_CP);
+ dbg("Exit");
+ return TRUE;
+}
+
+/**< SIM exit function */
+void atd_sim_exit(TcorePlugin *p, CoreObject *co_sim)
+{
+ struct atd_sim_property *file_meta;
+
+ dbg("Entry");
+
+ /* Unset 'ops' */
+ tcore_sim_set_ops(co_sim, NULL, TCORE_OPS_TYPE_CP);
+
+ file_meta = tcore_sim_ref_userdata(co_sim);
+ if (file_meta->sim_init_retry_src_id) {
+ dbg("Stop fetching sim status");
+ g_source_remove(file_meta->sim_init_retry_src_id);
+ }
+ g_free(file_meta);
+
+ dbg("Exit");
+}
--- /dev/null
+/*
+ * tel-plugin-atdongle
+ *
+ * Copyright (c) 2014 Samsung Electronics Co., Ltd. All rights reserved.
+ *
+ * Contact:
+ *
+ * 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 <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <glib.h>
+
+#include <tcore.h>
+#include <server.h>
+#include <plugin.h>
+#include <core_object.h>
+#include <hal.h>
+#include <at.h>
+
+#include "atd_modem.h"
+#include "atd_sim.h"
+#include "atd_network.h"
+#include "atd_ps.h"
+
+#define STATUS_INFO_RESP "CPAS"
+
+static void __initialize_device_retry(TcorePlugin *plugin);
+
+static TReturn __send_at_request_immediately(CoreObject *co,
+ const char *at_cmd,
+ const char *at_cmd_prefix,
+ enum tcore_at_command_type at_cmd_type,
+ UserRequest *ur,
+ TcorePendingResponseCallback resp_cb,
+ void *resp_cb_data,
+ TcorePendingSendCallback send_cb,
+ void *send_cb_data,
+ unsigned int timeout,
+ TcorePendingTimeoutCallback timeout_cb,
+ void *timeout_cb_data)
+{
+ TcorePending *pending = NULL;
+ TcoreATRequest *req = NULL;
+ TcoreHal *hal = NULL;
+ TReturn ret = TCORE_RETURN_FAILURE;
+
+ hal = tcore_object_get_hal(co);
+ if (!hal) {
+ dbg("HAL is NULL");
+ return ret;
+ }
+ dbg("hal: [0x%x]", hal);
+
+ /* Create Pending Request */
+ pending = tcore_pending_new(co, 0);
+ if (!pending) {
+ dbg("Pending is NULL");
+ return ret;
+ }
+
+ /* Create AT-Command Request */
+ req = tcore_at_request_new(at_cmd, at_cmd_prefix, at_cmd_type);
+ if (req == NULL) {
+ dbg("Request is NULL");
+ tcore_pending_free(pending);
+ return ret;
+ }
+ dbg("AT Command: [%s], Prefix(if any): [%s], AT-Command length: [%d]",
+ req->cmd, req->prefix, strlen(req->cmd));
+
+ tcore_pending_set_request_data(pending, 0, req);
+ tcore_pending_set_response_callback(pending, resp_cb, resp_cb_data);
+ tcore_pending_set_send_callback(pending, send_cb, send_cb_data);
+ tcore_pending_set_priority(pending, TCORE_PENDING_PRIORITY_HIGH);
+ tcore_pending_set_timeout(pending, timeout);
+ tcore_pending_set_timeout_callback(pending, timeout_cb, timeout_cb_data);
+ tcore_pending_link_user_request(pending, ur);
+
+ ret = tcore_hal_send_request(hal, pending);
+ dbg("ret: [0x%x]", ret);
+ return ret;
+}
+
+static void on_response_sync_for_resp(TcorePending *p,
+ int data_len, const void *data, void *user_data)
+{
+ const TcoreATResponse *at_resp = data;
+ gboolean ret = FALSE;
+
+ if (at_resp && at_resp->success) {
+ dbg("Received - [Success]");
+ /* tty device works successfully. proceed */
+ /* Modem Power on */
+ ret = modem_power_on(user_data);
+ dbg("Modem Power ON: [%s]", (ret == TRUE ? "SUCCESS" : "FAIL"));
+ } else
+ err("Received - [Fail]");
+}
+
+static void __sync_for_resp(TcorePlugin *plugin)
+{
+ CoreObject *co_modem;
+ TReturn ret;
+
+ if (plugin == NULL) {
+ err("Invalid plugin !");
+ return;
+ }
+
+ /* Modem Core object */
+ co_modem = tcore_plugin_ref_core_object(plugin, CORE_OBJECT_TYPE_MODEM);
+
+ /* Some Dongle returns late response
+ * due to its late initialize.
+ *
+ * Send 'meaningless' command
+ * and sync sequence AT response for unexpected response */
+ ret = __send_at_request_immediately(co_modem,
+ "\n", NULL,
+ TCORE_AT_NO_RESULT,
+ NULL,
+ on_response_sync_for_resp, plugin,
+ NULL, NULL,
+ 0, NULL, NULL);
+
+ dbg("ret: [0x%x]", ret);
+}
+
+static void on_timeout_initialize_device(TcorePending *p, void *user_data)
+{
+ dbg("Timeout! pending:[%p]", p);
+ dbg(" Try another command to tty device again..");
+
+ /* When AT device doesn't responded with 'AT+CPAS' command,
+ * need to send 'AT' command and check its response..
+ */
+ __initialize_device_retry(user_data);
+}
+
+static void on_response_initialize_device(TcorePending *p,
+ int data_len, const void *data, void *user_data)
+{
+ const TcoreATResponse *at_resp = data;
+ gboolean ret = FALSE;
+
+ if (at_resp && at_resp->success) {
+ GSList *lines;
+ const char *line;
+ gboolean expected = FALSE;
+
+ dbg("Initialize for AT device - [Success] / Lines: %d",
+ g_slist_length(at_resp->lines));
+
+ lines = at_resp->lines;
+ while (lines) {
+ line = (const char *)lines->data;
+
+ /* Check if response contains 'CPAS' - valid response */
+ if (g_strrstr(line, STATUS_INFO_RESP)) {
+ expected = TRUE;
+ break;
+ }
+
+ /* Move to next line */
+ lines = lines->next;
+ }
+
+ /* Send empty command and wait for valid response */
+ if (expected == FALSE) {
+ TcorePlugin *plugin = tcore_pending_ref_plugin(p);
+ dbg("Response contains unexpected lines.");
+ dbg("Sync AT response");
+ __sync_for_resp(plugin);
+ } else {
+ /* tty device works successfully. proceed */
+ /* Modem Power on */
+ ret = modem_power_on(user_data);
+ dbg("Modem Power ON: [%s]", (ret == TRUE ? "SUCCESS" : "FAIL"));
+ }
+ } else {
+ dbg("Initialize for AT device - [Failed]");
+ }
+}
+
+static void on_response_initialize_device_retry(TcorePending *p,
+ int data_len, const void *data, void *user_data)
+{
+ const TcoreATResponse *at_resp = data;
+ gboolean ret = FALSE;
+
+ if (at_resp && at_resp->success) {
+ GSList *lines;
+ const char *line;
+ gboolean unexpected = FALSE;
+
+ dbg("Initialize for AT device - [Success] / Lines: %d",
+ g_slist_length(at_resp->lines));
+
+ lines = at_resp->lines;
+ while (lines) {
+ line = (const char *)lines->data;
+
+ /* Check if response contains 'CPAS' - invalid response */
+ if (g_strrstr(line, STATUS_INFO_RESP)) {
+ unexpected = TRUE;
+ break;
+ }
+
+ /* Move to next line */
+ lines = lines->next;
+ }
+
+ /* Send empty command and wait for valid response */
+ if (unexpected) {
+ TcorePlugin *plugin = tcore_pending_ref_plugin(p);
+ dbg("Response contains unexpected lines.");
+ dbg("Sync AT response");
+ __sync_for_resp(plugin);
+ } else {
+ /* tty device works successfully. proceed */
+ /* Modem Power on */
+ ret = modem_power_on(user_data);
+ dbg("Modem Power ON: [%s]", (ret == TRUE ? "SUCCESS" : "FAIL"));
+ }
+ } else {
+ dbg("Initialize for AT device - [Failed]");
+ }
+}
+
+static void __initialize_device(TcorePlugin *plugin)
+{
+ CoreObject *co_modem;
+ TReturn ret;
+
+ if (plugin == NULL) {
+ err("Invalid plugin !");
+ return;
+ }
+
+ /* Modem Core object */
+ co_modem = tcore_plugin_ref_core_object(plugin, CORE_OBJECT_TYPE_MODEM);
+
+ /* Send command and set timeout to 5 sec */
+ ret = tcore_prepare_and_send_at_request(co_modem,
+ "AT+CPAS", NULL,
+ TCORE_AT_MULTILINE,
+ NULL,
+ on_response_initialize_device, plugin,
+ NULL, NULL,
+ 5, on_timeout_initialize_device, plugin);
+
+ dbg("ret: [0x%x]", ret);
+}
+
+static void __initialize_device_retry(TcorePlugin *plugin)
+{
+ CoreObject *co_modem;
+ TReturn ret;
+
+ if (plugin == NULL) {
+ err("Invalid plugin !");
+ return;
+ }
+
+ /* Modem Core object */
+ co_modem = tcore_plugin_ref_core_object(plugin, CORE_OBJECT_TYPE_MODEM);
+
+ /* Send 'AT' command and set timeout to 5 sec */
+ ret = tcore_prepare_and_send_at_request(co_modem,
+ "AT", NULL,
+ TCORE_AT_MULTILINE,
+ NULL,
+ on_response_initialize_device_retry, plugin,
+ NULL, NULL,
+ 5, on_timeout_initialize_device, plugin);
+
+ dbg("ret: [0x%x]", ret);
+}
+
+/**< Initializer Table */
+struct object_initializer init_table = {
+ .modem_init = atd_modem_init,
+ .sim_init = atd_sim_init,
+ .network_init = atd_network_init,
+ .ps_init = atd_ps_init,
+ .sat_init = NULL,
+ .sap_init = NULL,
+ .call_init = NULL,
+ .ss_init = NULL,
+ .sms_init = NULL,
+ .phonebook_init = NULL,
+ .gps_init = NULL,
+};
+
+/**< Deinitializer Table */
+struct object_deinitializer deinit_table = {
+ .modem_deinit = atd_modem_exit,
+ .sim_deinit = atd_sim_exit,
+ .ps_deinit = atd_ps_exit,
+ .network_deinit = atd_network_exit,
+ .sat_deinit = NULL,
+ .sap_deinit = NULL,
+ .call_deinit = NULL,
+ .ss_deinit = NULL,
+ .sms_deinit = NULL,
+ .phonebook_deinit = NULL,
+ .gps_deinit = NULL,
+};
+
+static gboolean on_load()
+{
+ dbg("Load!!!");
+
+ return TRUE;
+}
+
+static gboolean on_init(TcorePlugin *p)
+{
+ dbg("Init!!!");
+
+ if (p == NULL) {
+ err("invalid plugin!!");
+ return FALSE;
+ }
+
+ /* Initialize Modules (Core Objects) */
+ if (tcore_object_init_objects(p, &init_table)
+ != TCORE_RETURN_SUCCESS) {
+ err("Failed to initialize Core Objects");
+ return FALSE;
+ }
+
+ /* Initialize tty device */
+ __initialize_device(p);
+
+ dbg("Init - Complete!");
+
+ return TRUE;
+}
+
+static void on_unload(TcorePlugin *p)
+{
+ dbg("Unload!!!");
+
+ /* Deinitialize Modules (Core Objects) */
+ tcore_object_deinit_objects(p, &deinit_table);
+}
+
+/* AT Dongle - Modem Plug-in Descriptor */
+struct tcore_plugin_define_desc plugin_define_desc = {
+ .name = "ATDONGLE",
+ .priority = TCORE_PLUGIN_PRIORITY_MID,
+ .version = 1,
+ .load = on_load,
+ .init = on_init,
+ .unload = on_unload
+};
--- /dev/null
+<manifest>
+ <request>
+ <domain name="_"/>
+ </request>
+</manifest>