/* * ST-Ericsson U300 RIL * * Copyright (C) ST-Ericsson AB 2008-2011 * Copyright 2006, The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * * Based on reference-ril by The Android Open Source Project. * * Heavily modified for ST-Ericsson modems. * Author: Christian Bejram * Author: Sverre Vegge */ #include #include #include #include #include #include #include #ifndef CAIF_SOCKET_SUPPORT_DISABLED #include #include #include "u300-ril-netif.h" #endif #include "u300-ril.h" #include "u300-ril-pdp.h" #include "u300-ril-information.h" #define LOG_TAG "RILV" #include typedef struct managerArgs { int channels; RILRequestGroup *parsedGroups[RIL_MAX_NR_OF_CHANNELS]; char *type; char *args[RIL_MAX_NR_OF_CHANNELS]; char *xarg; } managerArgs; static struct managerArgs mgrArgs; pthread_t s_tid_managerRunner; static void releaseCommandThreads(void) { pthread_mutex_lock(&ril_manager_queue_startup_mutex); g_managerRelease = true; pthread_cond_broadcast(&ril_manager_queue_startup_cond); LOGD("%s() released command execution queue thread(s)", __func__); pthread_mutex_unlock(&ril_manager_queue_startup_mutex); } static void haltCommandThreads(void) { pthread_mutex_lock(&ril_manager_queue_startup_mutex); g_managerRelease = false; LOGD("%s() halted command execution queue thread(s)", __func__); pthread_mutex_unlock(&ril_manager_queue_startup_mutex); } #ifndef CAIF_SOCKET_SUPPORT_DISABLED static bool createNetworkInterface(const char *ifname, int connection_id) { int ret; int ifindex = -1; char loop = 0; char ifnamecpy[MAX_IFNAME_LEN]; bool success = true; strncpy(ifnamecpy, ifname, MAX_IFNAME_LEN); ifnamecpy[MAX_IFNAME_LEN - 1] = '\0'; ret = rtnl_create_caif_interface(IFLA_CAIF_IPV4_CONNID, connection_id, ifnamecpy, &ifindex, loop); if (!ret) LOGI("%s() created CAIF net-interface: Name = %s, connection ID = %d, " "Index = %d", __func__, ifnamecpy, connection_id, ifindex); else if (ret == -EEXIST) /* Use the existing interface, NOT an error. */ LOGI("%s() found existing CAIF net-interface with same name, reusing: " "Name = %s, connection ID = %d, Index = %d", __func__, ifnamecpy, connection_id, ifindex ); else { LOGE("%s() failed creating CAIF net-interface. errno: %d (%s)!", __func__, errno, strerror(errno)); success = false; } if (strncmp(ifnamecpy, ifname, MAX_IFNAME_LEN) != 0) { LOGE("%s() did not get required interface name. Suggested %s but got " "%s. This is considered an error.", __func__, ifname, ifnamecpy); success = false; } return success; } #endif /****************************************************************************** * Start section that includes DBUS communication with MID module * ******************************************************************************/ #ifndef EXTERNAL_MODEM_CONTROL_MODULE_DISABLED #include #include #define BUF_MID_RESPONSE_SIZE 32 #define DBUS_MAX_WATCHERS 2 /* 1 for reading, 1 for writing. */ #define DBUS_CONNECTION_NAME "com.stericsson.mid" #define DBUS_OBJECT_PATH "/com/stericsson/mid" #define DBUS_OBJECT_INTERFACE "com.stericsson.mid.Modem" #define DBUS_CONNECT_DELAY 2 /* Retry timeout waiting for Dbus and MID */ static DBusWatch *used_watches[DBUS_MAX_WATCHERS]; static DBusWatch *unused_watches[DBUS_MAX_WATCHERS]; static int dbus_used_fds = 0; static int dbus_not_used_fds = 0; static struct pollfd dbus_used_pollfds_tab[DBUS_MAX_WATCHERS]; static struct pollfd dbus_not_used_pollfds_tab[DBUS_MAX_WATCHERS]; static pthread_mutex_t s_dbus_watch_mutex = PTHREAD_MUTEX_INITIALIZER; pthread_t s_tid_dbusRunner; #define UNUSED(expr) do { (void)(expr); } while (0) static void signalShutdown(void) { int err; if ((err = pthread_mutex_lock(&ril_system_shutdown_mutex)) != 0) { LOGE("%s() failed to take shutdown mutex: %s", __func__, strerror(err)); assert(0); } g_offSignalReceived = true; if ((err = pthread_cond_signal(&ril_system_shutdown_cond)) != 0) { LOGE("%s() failed to signal shutdown cond: %s",__func__, strerror(err)); } if ((err = pthread_mutex_unlock(&ril_system_shutdown_mutex)) != 0) { LOGE("%s() failed to release shutdown mutex: %s!", __func__, strerror(err)); } } /* MID signal message handler */ static DBusHandlerResult midSignalHandler(DBusConnection *dbcon, DBusMessage *msg, void *data) { DBusMessageIter args; DBusPendingCall* pending; DBusMessage *dbmsg; UNUSED(data); char *signame = NULL; const char *property = "sys.shutdown.requested"; char systemShutdown[PROPERTY_VALUE_MAX + 1]; int getRet = -1; int err; /* * g_managerRelease is here used a state indication of QueueRunners. * TRUE = Indicates queuethreads are executing and running normally. * Any event from the queueRunners can be considered a trigger * for changing state. * FALSE = Indicates queuethreads are stopping / stopped. Any event * from the queueRunners could be ignored as we are already * restarting them. */ if (dbus_message_is_signal(msg, "com.stericsson.mid.Modem", "StateChange")) { if (!dbus_message_iter_init(msg, &args)) { LOGD("%s(): Message has no arguments!", __func__); } else if (DBUS_TYPE_STRING != dbus_message_iter_get_arg_type(&args)) { LOGD("%s(): Argument is not string!", __func__); } else { dbus_message_iter_get_basic(&args, &signame); LOGD("%s(): Got Signal with value \"%s\"", __func__, signame); if (strncmp(signame, "on", 2) == 0) { if (g_managerRelease) { LOGD("%s() Already running state. Signal ignored...", __func__); } else { LOGD("%s() Releasing queueRunner threads..." , __func__); releaseCommandThreads(); } } else if (strncmp(signame, "prepare_off", 11) == 0) { if (g_managerRelease) { LOGD("%s(): Signal queueRunner threads and prepare to go " "back to initial state...", __func__); haltCommandThreads(); signalCloseQueues(); } else { LOGD("%s(): queueRunner threads (already) stopped waiting " "for \"on\"...", __func__); } } else if (strncmp(signame, "off", 3) == 0) { /* Check if Android property is set, and signal for shutdown. */ getRet = property_get(property, systemShutdown, NULL); if (getRet > 0) { signalShutdown(); } g_shutdownCompleted = true; if (g_managerRelease) { LOGD("%s(): Signal queueRunner threads and prepare to go " "back to initial state...", __func__); haltCommandThreads(); signalCloseQueues(); } else { LOGD("%s(): queueRunner threads (already) stopped waiting " "for \"on\"...", __func__); } } else LOGD("%s(): message \"%s\" ignored.", __func__, signame); return DBUS_HANDLER_RESULT_HANDLED; } } return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; } static dbus_bool_t addWatch(DBusWatch *watch, void *data) { UNUSED(data); short cond = POLLHUP | POLLERR; int fd; dbus_bool_t ret = TRUE; unsigned int flags; dbus_bool_t res; int mutexRes; fd = dbus_watch_get_fd(watch); flags = dbus_watch_get_flags(watch); if (flags & DBUS_WATCH_READABLE) { cond |= POLLIN; LOGD("%s(): RIL get new dbus watch for READABLE condition", __func__); } if (flags & DBUS_WATCH_WRITABLE) { cond |= POLLOUT; LOGD("%s(): RIL get new dbus watch for WRITABLE condition", __func__); } mutexRes = pthread_mutex_lock(&s_dbus_watch_mutex); if (mutexRes != 0) LOGE("%s(): Unable to take dbus watch mutex", __func__); res = dbus_watch_get_enabled(watch); if (res) { if (dbus_used_fds < DBUS_MAX_WATCHERS) { LOGD("%s(): RIL new dbus watch id: %d is marked USED", __func__, dbus_used_fds); used_watches[dbus_used_fds] = watch; dbus_used_pollfds_tab[dbus_used_fds].fd = fd; dbus_used_pollfds_tab[dbus_used_fds].events = cond; dbus_used_fds++; } else { LOGE("%s(): new dbus watch id: %d is marked USED. BUT can not be " "added", __func__, dbus_used_fds); goto error; } } else { if (dbus_not_used_fds < DBUS_MAX_WATCHERS) { LOGD("%s(): RIL new dbus watch id: %d is marked UNUSED", __func__, dbus_not_used_fds); unused_watches[dbus_not_used_fds] = watch; dbus_not_used_pollfds_tab[dbus_not_used_fds].fd = fd; dbus_not_used_pollfds_tab[dbus_not_used_fds].events = cond; dbus_not_used_fds++; } else { LOGE("%s(): new dbus watch id: %d is marked UNUSED. BUT can not be " "added", __func__, dbus_not_used_fds); goto error; } } goto exit; error: ret = FALSE; exit: mutexRes = pthread_mutex_unlock(&s_dbus_watch_mutex); if (mutexRes != 0) LOGE("%s(): Unable to release dbus watch mutex.", __func__); return ret; } static void removeWatch(DBusWatch *watch, void *data) { UNUSED(data); int i, res, index; bool found = false; int mutexRes; LOGD("%s(): RIL Removing dbus watch", __func__); mutexRes = pthread_mutex_lock(&s_dbus_watch_mutex); if (mutexRes != 0) LOGE("%s(): Unable to take dbus watch mutex", __func__); for (i = 0; i < dbus_not_used_fds; i++) { if (unused_watches[i] == watch) { found = true; index = i; break; } } if (!found) { LOGD("%s(): RIL watch %p not found in unused pool, try used pool...", __func__, (void*)watch); for (i = 0; i < dbus_used_fds; i++) { if (used_watches[i] == watch) { found = true; index = i; break; } } if (!found) { LOGE("%s(): RIL watch %p not found in any pool...", __func__, (void*)watch); goto exit; } else { LOGD("%s(): RIL watch %p found in used pool. Removed", __func__, (void*)watch); for (i = index; i < (dbus_used_fds - 1); i++) { used_watches[i] = used_watches[i + 1]; memcpy(&dbus_used_pollfds_tab[i], &dbus_used_pollfds_tab[i + 1], sizeof(dbus_used_pollfds_tab[i + 1])); } used_watches[i] = NULL; memset(&dbus_used_pollfds_tab[i], 0, sizeof(dbus_used_pollfds_tab[i])); dbus_used_fds--; } } else { LOGD("%s(): RIL watch %p found in unused pool. Removed", __func__, (void*)watch); for (i = index; i < (dbus_not_used_fds - 1); i++) { unused_watches[i] = unused_watches[i + 1]; memcpy(&dbus_not_used_pollfds_tab[i], &dbus_not_used_pollfds_tab[i + 1], sizeof(dbus_not_used_pollfds_tab[i + 1])); } unused_watches[i] = NULL; memset(&dbus_not_used_pollfds_tab[i], 0, sizeof(dbus_not_used_pollfds_tab[i])); dbus_not_used_fds--; } exit: mutexRes = pthread_mutex_unlock(&s_dbus_watch_mutex); if (mutexRes != 0) LOGE("%s(): Unable to release dbus watch mutex.", __func__); } static void notifyForEvent(int index, short event) { unsigned int flags = 0; if (event & POLLIN) flags |= DBUS_WATCH_READABLE; if (event & POLLOUT) flags |= DBUS_WATCH_WRITABLE; if (event & POLLHUP) flags |= DBUS_WATCH_HANGUP; if (event & POLLERR) flags |= DBUS_WATCH_ERROR; while (!dbus_watch_handle(used_watches[index], flags)) { LOGD("%s(): dbus_watch_handle needs more memory. Spinning", __func__); sleep(1); } LOGD("%s(): used id: %d dbus_watch_handle selected for DBUS operation", __func__, index); } static void flushDbusEvents(DBusConnection *dbcon) { DBusMessageIter args; DBusMessage *dbmsg; char *signame = NULL; int res = 0; for (;;) { res = dbus_connection_get_dispatch_status(dbcon); switch (res) { case DBUS_DISPATCH_COMPLETE: return; case DBUS_DISPATCH_NEED_MEMORY: LOGD("%s(): needs more memory. spinning.", __func__); sleep(1); break; case DBUS_DISPATCH_DATA_REMAINS: dbmsg = dbus_connection_pop_message(dbcon); if (dbus_message_is_signal(dbmsg, "com.stericsson.mid.Modem", "StateChange")) { if (!dbus_message_iter_init(dbmsg, &args)) { LOGD("%s(): Message has no arguments!", __func__); } else if (DBUS_TYPE_STRING != dbus_message_iter_get_arg_type(&args)) { LOGD("%s(): Argument is not string!", __func__); } else { dbus_message_iter_get_basic(&args, &signame); LOGD("%s(): Flushing Signal with value \"%s\"", __func__, signame); } } break; default: /* This should not happen */ break; } } } static void processDbusEvent(DBusConnection *dbcon) { int res = 0; for (;;) { res = dbus_connection_dispatch(dbcon); switch (res) { case DBUS_DISPATCH_COMPLETE: return; case DBUS_DISPATCH_NEED_MEMORY: LOGD("%s(): dbus_connection_dispatch needs more memory." "spinning", __func__); sleep(1); break; case DBUS_DISPATCH_DATA_REMAINS: LOGD("%s(): dispatch: remaining data for DBUS operation." "Spinning", __func__); break; default: /* This should not happen */ break; } } } static int requestMIDWithResponse(DBusConnection *dbcon, char *requestMethod, char *response) { char *return_val = NULL; int ret = 0; DBusPendingCall *pending = NULL; DBusMessage *dbmsg = NULL; DBusMessageIter args; dbmsg = dbus_message_new_method_call(DBUS_CONNECTION_NAME, DBUS_OBJECT_PATH, DBUS_OBJECT_INTERFACE, requestMethod); if (dbmsg == NULL) { LOGE("%s(): Failed to create a method call", __func__); goto error; } if (!dbus_connection_send_with_reply(dbcon, dbmsg, &pending, -1)) { LOGE("%s(): Failed to send method call", __func__); goto error; } if (pending == NULL) { LOGE("%s(): Failed to send method call, connection closed", __func__); goto error; } dbus_connection_flush(dbcon); dbus_message_unref(dbmsg); dbus_pending_call_block(pending); dbmsg = dbus_pending_call_steal_reply(pending); dbus_pending_call_unref(pending); if (dbmsg == NULL) { LOGE("%s(): Error on the received message.", __func__); goto error; } if (!dbus_message_iter_init(dbmsg, &args)) { LOGE(" %s(): Received message has no arguments!", __func__); goto error; } else if (DBUS_TYPE_STRING != dbus_message_iter_get_arg_type(&args)) { LOGD("%s(): Argument is not a string!", __func__); goto error; } else { dbus_message_iter_get_basic(&args, &return_val); strncpy(response, return_val, BUF_MID_RESPONSE_SIZE-1); LOGD("%s(): Got message: \"%s\", response: \"%s\"", __func__, return_val, response); } goto exit; error: ret = -1; exit: dbus_message_unref(dbmsg); return ret; } static bool queryModemOn(DBusConnection *dbcon) { bool res = false; char responseArray[BUF_MID_RESPONSE_SIZE]; char *pResponse = responseArray; memset(pResponse, 0x0, BUF_MID_RESPONSE_SIZE); if (requestMIDWithResponse(dbcon, "GetState", pResponse) != 0) { LOGE("%s(): Failed to query state of MID.", __func__); } else { if (strncmp(pResponse, "on", 2) == 0) { res = true; } else LOGD("%s(): %s returned and ignored.", __func__, pResponse); } return res; } static void *dbusAndThreadRunner(void *param) { DBusConnection *dbcon = (DBusConnection *)param; DBusError err; int ret; int i; for (;;) { ret = poll(dbus_used_pollfds_tab, DBUS_MAX_WATCHERS, -1); if (ret > 0) { for (i = 0; i < DBUS_MAX_WATCHERS; i++) { if (dbus_used_pollfds_tab[i].revents) { notifyForEvent(i, dbus_used_pollfds_tab[i].revents); processDbusEvent(dbcon); } } } } return 0; } #endif /* EXTERNAL_MODEM_CONTROL_MODULE_DISABLED */ /****************************************************************************** * End section that includes DBUS communication with MID module * ******************************************************************************/ static void *managerRunner(void *param) { int ret; int i; pthread_attr_t attr; pthread_t s_tid_queueRunner[RIL_MAX_NR_OF_CHANNELS]; struct queueArgs *queueArgs[RIL_MAX_NR_OF_CHANNELS] = { NULL, NULL }; #ifndef EXTERNAL_MODEM_CONTROL_MODULE_DISABLED /***************************************************** * Dbus-connection for listening: * Loop until we have connection to Dbus daemon * *****************************************************/ DBusError dbusErr; DBusConnection *listenDbcon = NULL; DBusConnection *queryDbcon = NULL; bool dbusIsHere = false; /* Initial shutdown flag when Dbus enabled*/ g_shutdownCompleted = false; /* Connect to system dbus */ do { dbus_error_init(&dbusErr); listenDbcon = dbus_bus_get(DBUS_BUS_SYSTEM, &dbusErr); if (!listenDbcon || dbus_error_is_set(&dbusErr)) { LOGI("[DBUS]: connect to DBUS(listenDbcon) daemon returned error (%s)." "Will try again in %d second(s)", dbusErr.name, DBUS_CONNECT_DELAY); dbus_error_free(&dbusErr); sleep(DBUS_CONNECT_DELAY); } else dbusIsHere = true; } while (!dbusIsHere); LOGI("[DBUS]: listenDbcon Connected to system dbus."); ret = pthread_attr_init(&attr); if (ret != 0) LOGW("%s(): Failed to initialize dbus pthread attribute: %s", __func__, strerror(ret)); ret = pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED); if (ret != 0) LOGW("%s(): Failed to set the dbus PTHREAD_CREATE_DETACHED " "attribute: %s", __func__, strerror(ret)); /************************************************* * Dbus-connection for listening: * Configure Dbus Connection. * *************************************************/ if (!dbus_connection_set_watch_functions(listenDbcon, addWatch, removeWatch, NULL, NULL, NULL)) { LOGE("%s(): dbus_connection_set_watch_functions failed.", __func__); } dbus_error_init(&dbusErr); /* * Adds a match rule to match messages going through the message bus * Listen only signal from com.stericsson.mid.Modem interface (MID * state changes) */ dbus_bus_add_match(listenDbcon, "type='signal', interface='com.stericsson.mid.Modem'", &dbusErr); if (dbus_error_is_set(&dbusErr)) { LOGE("%s(): DBUS match error %s: %s.", __func__, dbusErr.name, dbusErr.message); dbus_error_free(&dbusErr); } /* Add a message filter to process incoming messages */ if (!dbus_connection_add_filter(listenDbcon, (DBusHandleMessageFunction)midSignalHandler, NULL, NULL)) { LOGE("%s(): DBUS filter error.", __func__); } /***************************************************** * Dbus-connection for query: * Loop until we have connection to Dbus daemon * *****************************************************/ dbusIsHere = false; /* Connect to system dbus */ do { dbus_error_init(&dbusErr); queryDbcon = dbus_bus_get(DBUS_BUS_SYSTEM, &dbusErr); if (!queryDbcon || dbus_error_is_set(&dbusErr)) { LOGI("[DBUS]: connect to DBUS(queryDbcon) daemon returned error (%s)." "Will try again in %d second(s)", dbusErr.name, DBUS_CONNECT_DELAY); dbus_error_free(&dbusErr); sleep(DBUS_CONNECT_DELAY); } else dbusIsHere = true; } while (!dbusIsHere); LOGI("[DBUS]: queryDbcon Connected to system dbus."); /********************************************************** * Need to be sure CAIF interfaces are up. Wait for * * "on" message, and release command threads immediately. * * ToDo: Add mutex, and wait for 'on' instead. * **********************************************************/ while (!queryModemOn(queryDbcon)) sleep(DBUS_CONNECT_DELAY); /** * Need to empty the listen-buffer initially. * This must be done before we release command threads. */ flushDbusEvents(listenDbcon); ret = pthread_create(&s_tid_dbusRunner, &attr, dbusAndThreadRunner, (void *)listenDbcon); if (ret != 0) { LOGE("%s(): Failed to create dbus runner thread: %s. Asserting." , __func__, strerror(ret)); assert(0); } #endif /* EXTERNAL_MODEM_CONTROL_MODULE_DISABLED */ /* Modem is up. QueueRunners can start directly. */ releaseCommandThreads(); #ifndef CAIF_SOCKET_SUPPORT_DISABLED /********************************************** * Create CAIF interfaces for PDP contexts * **********************************************/ if (strncasecmp(mgrArgs.type, "CAIF", 4) == 0) { for (i = 0; i < RIL_MAX_NUMBER_OF_PDP_CONTEXTS; i++) { char ifaceName[MAX_IFNAME_LEN]; snprintf(ifaceName, MAX_IFNAME_LEN, "%s%d", ril_iface, i); if (!createNetworkInterface(ifaceName, i + RIL_FIRST_CID_INDEX)) { LOGE("%s(): Failed to create Caif interface. Asserting." , __func__); assert(0); } } } #else if (mgrArgs.type == NULL || strcmp(mgrArgs.type, "") == 0) { LOGE("%s(): AT/Data channel type was not supplied!", __func__); assert(0); } #endif for(;;) { ret = 0; /* Reset static state variables in RIL queue Runners */ initializeStateVariables(); for (i = 0; i < mgrArgs.channels; i++) { int err; queueArgs[i] = malloc(sizeof(struct queueArgs)); memset(queueArgs[i], 0, sizeof(struct queueArgs)); queueArgs[i]->channels = mgrArgs.channels; queueArgs[i]->group = mgrArgs.parsedGroups[i]; queueArgs[i]->type = mgrArgs.type; queueArgs[i]->index = i; queueArgs[i]->arg = mgrArgs.args[i]; queueArgs[i]->xarg = mgrArgs.xarg; if ((err = pthread_attr_init(&attr)) != 0) LOGE("%s() failed to initialize pthread attribute: %s", __func__, strerror(err)); if ((err = pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE)) != 0) LOGE("%s() failed to set the " "PTHREAD_CREATE_DETACHED attribute: %s", __func__, strerror(err)); if ((err = pthread_create(&s_tid_queueRunner[i], &attr, queueRunner, queueArgs[i])) != 0) { LOGE("%s() failed to create queue runner thread: %s", __func__, strerror(err)); assert(0); } /* Arg. memory is freed within the queuerunner */ queueArgs[i] = NULL; } /* Use cond as an indication that one or more threads want to quit */ ret = pthread_mutex_lock(&ril_manager_queue_exit_mutex); if (ret != 0) LOGE("%s(): Failed to get mutex lock. err: %s", __func__, strerror(-ret)); ret = pthread_cond_wait(&ril_manager_queue_exit_cond, &ril_manager_queue_exit_mutex); if (ret != 0) LOGE("%s(): pthread_cond_wait Failed. err: %s", __func__, strerror(-ret)); ret = pthread_mutex_unlock(&ril_manager_queue_exit_mutex); if (ret != 0) LOGE("%s(): Failed to unlock mutex. err: %s", __func__, strerror(-ret)); /* wait for all threads to exit */ for (i = 0; i < mgrArgs.channels; i++) { ret = pthread_join(s_tid_queueRunner[i], NULL); if (ret != 0) { LOGE("%s() failed joining with queuRunner threads. Errno: %s" "Asserting", __func__, strerror(errno)); assert(0); } LOGI("%s() detected queueRunner %d ended", __func__, i); } LOGI("%s() detected all queueRunner threads ended. Restarting...", __func__); #ifndef EXTERNAL_MODEM_CONTROL_MODULE_DISABLED char responseArray[BUF_MID_RESPONSE_SIZE]; char *pResponse = responseArray; if (g_managerRelease) { /* Signal MID to restart Modem */ if (requestMIDWithResponse(queryDbcon, "Reboot", pResponse) == 0) { if (strncmp(pResponse, "OK", 2) == 0) { LOGI("%s(): MID \"reboot\" request returned %s. " "Modem restarting!", __func__, pResponse); /* Instruct QueueRunners to wait for 'on' from MID */ haltCommandThreads(); } else { /* * In the event we are not allowed to do a modem reboot we * have little to do but try a direct restart of the * queuerunners. AT channels will anyway be re-opened. */ LOGE("%s(): MID \"reboot\" request returned %s. " "Continuing letting queue runners execute immediatly.", __func__, pResponse); } } else { LOGE("%s(): Failed to reboot modem." "Continuing letting queue runners execute immediatly.", __func__); } } #endif /* EXTERNAL_MODEM_CONTROL_MODULE_DISABLED */ } return NULL; } static void usage(char *s) { fprintf(stderr, "usage: %s [-c ]" "[-c ] " "[-g ] " "[-p ] " "[-s ] " "[-x ] " "[-i ]\n", s); exit(-1); } const RIL_RadioFunctions *RIL_Init(const struct RIL_Env *env, int argc, char **argv) { int opt; int i; int err; char *groups = NULL; pthread_attr_t attr; /* Initialize manager arguments */ mgrArgs.channels = 2; mgrArgs.type = NULL; mgrArgs.xarg = NULL; /* Initialize global flags */ g_managerRelease = false; g_shutdownCompleted = true; LOGI("**************************************************\n" "Starting ST-Ericsson RIL...\n" "**************************************************"); LOGI("%s()", __func__); s_rilenv = env; while (-1 != (opt = getopt(argc, argv, "c:n:g:p:s:x:i:"))) { switch (opt) { case 'c': mgrArgs.type = optarg; LOGI("Using channel type %s.", mgrArgs.type); break; case 'n': LOGW("-n is deprecated. Use -g instead."); break; case 'g': groups = optarg; mgrArgs.channels = parseGroups(groups, mgrArgs.parsedGroups); LOGI("RIL command group(s) " "(DEFAULT and AUXILIARY may be omitted): %s", groups); break; case 'p': mgrArgs.args[0] = optarg; LOGI("Primary AT channel: %s", mgrArgs.args[0]); break; case 's': mgrArgs.args[1] = optarg; LOGI("Secondary AT channel: %s", mgrArgs.args[1]); break; case 'x': mgrArgs.xarg = optarg; LOGI("Extra argument %s.", mgrArgs.xarg); break; case 'i': strncpy(ril_iface, optarg, MAX_IFNAME_LEN); ril_iface[MAX_IFNAME_LEN - 1] = '\0'; LOGI("Using network interface %s as prefix for data channel.", ril_iface); break; default: goto error; } } if (groups == NULL || strcmp(groups, "") == 0) { LOGI("%s(): RIL command groups was not supplied. Using default " "configuration DEFAULT and AUXILIARY groups (2 AT channels).", __func__); mgrArgs.channels = parseGroups("", mgrArgs.parsedGroups); } if (ril_iface == NULL || strcmp(ril_iface, "") == 0) { LOGW("%s(): Network interface was not supplied." " Falling back to rmnet!", __func__); strcpy(ril_iface, "rmnet"); } #ifndef CAIF_SOCKET_SUPPORT_DISABLED if (mgrArgs.type == NULL || strncasecmp(mgrArgs.type, "", 1) == 0) { LOGW("%s: AT/Data channel type was not supplied." " Falling back to CAIF!", __func__); mgrArgs.type = "CAIF"; } #endif /* Start Manager thread. */ err = pthread_attr_init(&attr); if (err != 0) LOGW("%s(): Failed to initialize RIL Manager pthread attribute: %s", __func__, strerror(err)); err = pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED); if (err != 0) LOGW("%s(): Failed to set the RIL Manager PTHREAD_CREATE_DETACHED " "attribute: %s", __func__, strerror(err)); err = pthread_create(&s_tid_managerRunner, &attr, managerRunner, NULL); if (err != 0) LOGE("%s(): Failed to create RIL manager runner thread: %s", __func__, strerror(err)); return &g_callbacks; error: LOGE("%s() failed to parse RIL command line!", __func__); usage(argv[0]); return NULL; }