/* * This file is part of QMI-RIL. * * Copyright (C) 2017 Wolfgang Wiedmeyer * * QMI-RIL is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * QMI-RIL is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with QMI-RIL. If not, see . */ #include #define LOG_TAG "RIL" #include #include #include static int qmi2ril_call_fail_cause(QmiWdsCallEndReason cer) { switch (cer) { case QMI_WDS_CALL_END_REASON_GSM_WCDMA_OPERATOR_DETERMINED_BARRING: return PDP_FAIL_OPERATOR_BARRED; case QMI_WDS_CALL_END_REASON_GSM_WCDMA_INSUFFICIENT_RESOURCES: return PDP_FAIL_INSUFFICIENT_RESOURCES; case QMI_WDS_CALL_END_REASON_GSM_WCDMA_APN_TYPE_CONFLICT: case QMI_WDS_CALL_END_REASON_GSM_WCDMA_UNKNOWN_APN: return PDP_FAIL_MISSING_UKNOWN_APN; case QMI_WDS_CALL_END_REASON_GSM_WCDMA_UNKNOWN_PDP: return PDP_FAIL_UNKNOWN_PDP_ADDRESS_TYPE; case QMI_WDS_CALL_END_REASON_GENERIC_AUTHENTICATION_FAILED: return PDP_FAIL_USER_AUTHENTICATION; case QMI_WDS_CALL_END_REASON_GSM_WCDMA_GGSN_REJECT: return PDP_FAIL_ACTIVATION_REJECT_GGSN; case QMI_WDS_CALL_END_REASON_GSM_WCDMA_ACTIVATION_REJECT: return PDP_FAIL_ACTIVATION_REJECT_UNSPECIFIED; case QMI_WDS_CALL_END_REASON_GSM_WCDMA_OPTION_NOT_SUPPORTED: return PDP_FAIL_SERVICE_OPTION_NOT_SUPPORTED; case QMI_WDS_CALL_END_REASON_GSM_WCDMA_OPTION_UNSUBSCRIBED: return PDP_FAIL_SERVICE_OPTION_NOT_SUBSCRIBED; case QMI_WDS_CALL_END_REASON_GENERIC_NO_SERVICE: case QMI_WDS_CALL_END_REASON_GSM_WCDMA_NO_SERVICE: case QMI_WDS_CALL_END_REASON_GSM_WCDMA_OPTION_TEMPORARILY_OUT_OF_ORDER: return PDP_FAIL_SERVICE_OPTION_OUT_OF_ORDER; case QMI_WDS_CALL_END_REASON_GSM_WCDMA_NSAPI_ALREADY_USED: return PDP_FAIL_NSAPI_IN_USE; case QMI_WDS_CALL_END_REASON_GENERIC_CLOSE_IN_PROGRESS: case QMI_WDS_CALL_END_REASON_GENERIC_RELEASE_NORMAL: case QMI_WDS_CALL_END_REASON_GSM_WCDMA_REGULAR_DEACTIVATION: return PDP_FAIL_REGULAR_DEACTIVATION; case QMI_WDS_CALL_END_REASON_GSM_WCDMA_PROTOCOL_ERROR: return PDP_FAIL_PROTOCOL_ERRORS; case QMI_WDS_CALL_END_REASON_GSM_WCDMA_MESSAGE_INCORRECT_SEMANTIC: return PDP_FAIL_VOICE_REGISTRATION_FAIL; case QMI_WDS_CALL_END_REASON_GENERIC_ACCESS_FAILURE: case QMI_WDS_CALL_END_REASON_GSM_WCDMA_NETWORK_FAILURE: return PDP_FAIL_DATA_REGISTRATION_FAIL; case QMI_WDS_CALL_END_REASON_GENERIC_FADE: return PDP_FAIL_SIGNAL_LOST; case QMI_WDS_CALL_END_REASON_GSM_WCDMA_REATTACH_REQUIRED: return PDP_FAIL_PREF_RADIO_TECH_CHANGED; case QMI_WDS_CALL_END_REASON_GENERIC_CLIENT_END: return PDP_FAIL_RADIO_POWER_OFF; case QMI_WDS_CALL_END_REASON_GSM_WCDMA_NETWORK_END: case QMI_WDS_CALL_END_REASON_GENERIC_INTERNAL_ERROR: case QMI_WDS_CALL_END_REASON_GENERIC_ACCESS_ATTEMPT_IN_PROGRESS: case QMI_WDS_CALL_END_REASON_GENERIC_UNSPECIFIED: default: return PDP_FAIL_ERROR_UNSPECIFIED; } } static void qmi2ril_addr4_string(guint32 buf, char **addr4) { struct in_addr in_addr_val; char buf4[INET_ADDRSTRLEN]; in_addr_val.s_addr = GUINT32_TO_BE(buf); memset(buf4, 0, sizeof(buf4)); inet_ntop(AF_INET, &in_addr_val, buf4, sizeof(buf4)); *addr4 = strdup(buf4); } static int qmi2ril_ipv4_netmask_prefix_length(in_addr_t mask) { int prefixLength = 0; uint32_t m = (uint32_t)ntohl(mask); while (m & 0x80000000) { prefixLength++; m = m << 1; } return prefixLength; } static void ril_setup_data_call_response(RIL_Token token) { RIL_Data_Call_Response_v6 response[3]; struct ril_data_connection data_connection; size_t size; data_connection = ril_data->data_connection; size = sizeof(RIL_Data_Call_Response_v6); memset(&response, 0, sizeof(response)); response[0].status = data_connection.status; // TODO: should this be set? It's not in Samsung-RIL /* response[0].suggestedRetryTime = -1; */ response[0].cid = data_connection.packet_data_handle; response[0].active = data_connection.active; response[0].type = data_connection.type; response[0].ifname = (char*) data_connection.iface; asprintf(&response[0].addresses, "%s/%d", data_connection.ip_addr, qmi2ril_ipv4_netmask_prefix_length(inet_addr(data_connection.subnet_mask))); RIL_LOGD("addresses for response: '%s'", response[0].addresses); if (data_connection.dns1 != NULL && data_connection.dns2 != NULL) asprintf(&response[0].dnses, "%s %s", data_connection.dns1, data_connection.dns2); response[0].gateways = data_connection.gateway; if (response[0].status == PDP_FAIL_NONE) ril_request_unsolicited(RIL_UNSOL_DATA_CALL_LIST_CHANGED, &response, size); ril_request_complete(token, RIL_E_SUCCESS, &response, size); } static int ril_setup_data_connection(struct ril_data_connection *data_connection) { in_addr_t ip_addr; in_addr_t gateway_addr; in_addr_t subnet_mask_addr; in_addr_t dns1_addr; in_addr_t dns2_addr; int rc; if (data_connection == NULL || data_connection->iface == NULL || data_connection->ip_addr == NULL || data_connection->gateway == NULL || data_connection->subnet_mask == NULL) return -1; ip_addr = inet_addr(data_connection->ip_addr); gateway_addr = inet_addr(data_connection->gateway); subnet_mask_addr = inet_addr(data_connection->subnet_mask); if (data_connection->dns1 != NULL) dns1_addr = inet_addr(data_connection->dns1); else dns1_addr = 0; if (data_connection->dns2 != NULL) dns2_addr = inet_addr(data_connection->dns2); else dns2_addr = 0; rc = ifc_configure(data_connection->iface, ip_addr, qmi2ril_ipv4_netmask_prefix_length(subnet_mask_addr), gateway_addr, dns1_addr, dns2_addr); if (rc < 0) return -1; RIL_LOGD("%s: Enabled data connection", __func__); return 0; } static void get_current_settings_ready(QmiClientWds *client, GAsyncResult *res, RIL_Token token) { GError *error = NULL; QmiMessageWdsGetCurrentSettingsOutput *output; QmiWdsIpFamily ip_family = QMI_WDS_IP_FAMILY_UNSPECIFIED; char *type = strdup("IP"); guint32 addr_buf = 0; char *ip_addr = NULL; char *gateway = NULL; char *subnet_mask = NULL; char *dns1 = NULL; char *dns2 = NULL; int rc; output = qmi_client_wds_get_current_settings_finish(client, res, &error); if (!output) { RIL_LOGE("%s: error: operation failed: %s", __func__, error->message); goto error; } if (!qmi_message_wds_get_current_settings_output_get_result( output, &error)) { RIL_LOGE("%s: error: couldn't get current settings: %s", __func__, error->message); goto error; } RIL_LOGD("Current data settings retrieved"); if (qmi_message_wds_get_current_settings_output_get_ip_family (output, &ip_family, NULL)) { if (ip_family == QMI_WDS_IP_FAMILY_IPV6) { type = strdup("IPV6"); RIL_LOGE("TODO: IPv6 support is missing"); goto error; } } ril_data->data_connection.type = strdup(type); if (qmi_message_wds_get_current_settings_output_get_ipv4_address( output, &addr_buf, NULL)) { qmi2ril_addr4_string(addr_buf, &ip_addr); RIL_LOGD("IPv4 address: %s", ip_addr); ril_data->data_connection.ip_addr = strdup(ip_addr); } else { RIL_LOGE("failed to retrieve IPv4 address"); goto error; } if (qmi_message_wds_get_current_settings_output_get_ipv4_gateway_subnet_mask( output, &addr_buf, NULL)) { qmi2ril_addr4_string(addr_buf, &subnet_mask); RIL_LOGD("IPv4 subnet mask: %s", subnet_mask); ril_data->data_connection.subnet_mask = strdup(subnet_mask); } else if (ip_addr != NULL) { asprintf(&subnet_mask, "255.255.255.255"); ril_data->data_connection.subnet_mask = strdup(subnet_mask); } if (qmi_message_wds_get_current_settings_output_get_ipv4_gateway_address( output, &addr_buf, NULL)) { qmi2ril_addr4_string(addr_buf, &gateway); RIL_LOGD("IPv4 gateway address: %s", gateway); ril_data->data_connection.gateway = strdup(gateway); } else if (ip_addr != NULL) { RIL_LOGE("failed to retrieve IPv4 gateway"); goto error; } if (qmi_message_wds_get_current_settings_output_get_primary_ipv4_dns_address( output, &addr_buf, NULL)) { qmi2ril_addr4_string(addr_buf, &dns1); RIL_LOGD("IPv4 primary DNS: %s", dns1); ril_data->data_connection.dns1 = strdup(dns1); } if (qmi_message_wds_get_current_settings_output_get_secondary_ipv4_dns_address( output, &addr_buf, NULL)) { qmi2ril_addr4_string(addr_buf, &dns2); RIL_LOGD("IPv4 secondary DNS: %s", dns2); ril_data->data_connection.dns2 = strdup(dns2); } rc = ril_setup_data_connection(&ril_data->data_connection); if (rc < 0) { RIL_LOGE("setting up the data connection failed"); ril_request_complete(token, RIL_E_GENERIC_FAILURE, NULL, 0); qmi_message_wds_get_current_settings_output_unref( output); return; } error: ril_setup_data_call_response(token); if (output) qmi_message_wds_get_current_settings_output_unref( output); if (error) g_error_free(error); } static void timeout_get_packet_service_status_ready(QmiClientWds *client, GAsyncResult *res) { GError *error = NULL; QmiMessageWdsGetPacketServiceStatusOutput *output; QmiWdsConnectionStatus status; output = qmi_client_wds_get_packet_service_status_finish( client, res, &error); if (!output) { RIL_LOGD("%s: error: operation failed: %s", __func__, error->message); g_error_free(error); return; } if (!qmi_message_wds_get_packet_service_status_output_get_result (output, &error)) { RIL_LOGD("%s: error: couldn't get packet service status: %s", __func__, error->message); g_error_free(error); qmi_message_wds_get_packet_service_status_output_unref(output); return; } qmi_message_wds_get_packet_service_status_output_get_connection_status( output, &status, NULL); RIL_LOGD("timout connection status: '%s'", qmi_wds_connection_status_get_string(status)); qmi_message_wds_get_packet_service_status_output_unref(output); } static gboolean packet_status_timeout(void) { qmi_client_wds_get_packet_service_status(ctx->WdsClient, NULL, 10, ctx->cancellable, (GAsyncReadyCallback)timeout_get_packet_service_status_ready, NULL); return TRUE; } static void get_packet_service_status_ready(QmiClientWds *client, GAsyncResult *res, RIL_Token token) { GError *error = NULL; QmiMessageWdsGetPacketServiceStatusOutput *output; QmiWdsConnectionStatus status; QmiMessageWdsGetCurrentSettingsInput *input; int active = 0; output = qmi_client_wds_get_packet_service_status_finish( client, res, &error); if (!output) { RIL_LOGE("%s: error: operation failed: %s", __func__, error->message); goto error; } if (!qmi_message_wds_get_packet_service_status_output_get_result(output, &error)) { RIL_LOGE("%s: error: couldn't get packet service status: %s", __func__, error->message); goto error; } qmi_message_wds_get_packet_service_status_output_get_connection_status( output, &status, NULL); RIL_LOGD("Connection status: '%s'", qmi_wds_connection_status_get_string(status)); if (status == QMI_WDS_CONNECTION_STATUS_CONNECTED) active = 2; error: ril_data->data_connection.active = active; input = qmi_message_wds_get_current_settings_input_new(); qmi_message_wds_get_current_settings_input_set_requested_settings( input, (QMI_WDS_GET_CURRENT_SETTINGS_REQUESTED_SETTINGS_DNS_ADDRESS | QMI_WDS_GET_CURRENT_SETTINGS_REQUESTED_SETTINGS_IP_ADDRESS | QMI_WDS_GET_CURRENT_SETTINGS_REQUESTED_SETTINGS_GATEWAY_INFO | QMI_WDS_GET_CURRENT_SETTINGS_REQUESTED_SETTINGS_IP_FAMILY), NULL); qmi_client_wds_get_current_settings(ctx->WdsClient, input, 10, ctx->cancellable, (GAsyncReadyCallback)get_current_settings_ready, token); qmi_message_wds_get_current_settings_input_unref(input); if (error) g_error_free(error); if (output) qmi_message_wds_get_packet_service_status_output_unref(output); } static void start_network_ready(QmiClientWds *client, GAsyncResult *res, RIL_Token token) { GError *error = NULL; int status = PDP_FAIL_ERROR_UNSPECIFIED; QmiMessageWdsStartNetworkOutput *output; QmiWdsCallEndReason cer; QmiWdsVerboseCallEndReasonType verbose_cer_type; gint16 verbose_cer_reason; output = qmi_client_wds_start_network_finish(client, res, &error); if (!output) { RIL_LOGE("%s: error: operation failed: %s", __func__, error->message); goto error; } if (!qmi_message_wds_start_network_output_get_result( output, &error)) { RIL_LOGE("%s: error: couldn't start network: %s", __func__, error->message); if (g_error_matches(error, QMI_PROTOCOL_ERROR, QMI_PROTOCOL_ERROR_CALL_FAILED)) { if (qmi_message_wds_start_network_output_get_call_end_reason( output, &cer, NULL)) { RIL_LOGE("call end reason (%u): %s", cer, qmi_wds_call_end_reason_get_string(cer)); status = qmi2ril_call_fail_cause(cer); } if (qmi_message_wds_start_network_output_get_verbose_call_end_reason( output, &verbose_cer_type, &verbose_cer_reason, NULL)) RIL_LOGE("verbose call end reason (%u,%d): [%s] %s", verbose_cer_type, verbose_cer_reason, qmi_wds_verbose_call_end_reason_type_get_string(verbose_cer_type), qmi_wds_verbose_call_end_reason_get_string(verbose_cer_type, verbose_cer_reason)); } goto error; } status = PDP_FAIL_NONE; qmi_message_wds_start_network_output_get_packet_data_handle( output, &ril_data->data_connection.packet_data_handle, NULL); qmi_message_wds_start_network_output_unref(output); RIL_LOGD("Network started, packet data handle: '%u'", (guint)ril_data->data_connection.packet_data_handle); error: ril_data->data_connection.status = status; ril_data->data_connection.iface = strdup(qmi_device_get_wwan_iface( ctx->device)); qmi_client_wds_get_packet_service_status(ctx->WdsClient, NULL, 10, ctx->cancellable, (GAsyncReadyCallback)get_packet_service_status_ready, token); // additionally check periodically the connection status ctx->packet_status_timeout_id = g_timeout_add_seconds( 10, (GSourceFunc)packet_status_timeout, NULL); if (error) g_error_free(error); } int ril_request_setup_data_call(void *data, size_t size, RIL_Token token) { struct ril_request *request; char *apn = NULL; char *username = NULL; char *password = NULL; char **values = NULL; QmiMessageWdsStartNetworkInput *input; int rc; if (data == NULL || size < 6 * sizeof(char *)) goto error; rc = ril_radio_state_check(RADIO_STATE_SIM_READY); if (rc < 0) { ril_request_complete(token, RIL_E_OP_NOT_ALLOWED_BEFORE_REG_TO_NW, NULL, 0); return RIL_REQUEST_COMPLETED; } request = ril_request_find_request_status(RIL_REQUEST_SETUP_DATA_CALL, RIL_REQUEST_HANDLED); if (request != NULL) return RIL_REQUEST_UNHANDLED; if (ril_data->data_connection.raw_ip_mode == FALSE) { RIL_LOGE("error: device is not in raw IP mode!"); goto error; } values = (char **) data; if (values[2] == NULL) { RIL_LOGE("%s: No APN was provided", __func__); goto error; } input = qmi_message_wds_start_network_input_new(); apn = strdup(values[2]); qmi_message_wds_start_network_input_set_apn(input, apn, NULL); if (values[3] != NULL) { username = strdup(values[3]); qmi_message_wds_start_network_input_set_username( input, username, NULL); } if (values[4] != NULL) { password = strdup(values[4]); qmi_message_wds_start_network_input_set_password( input, password, NULL); } qmi_client_wds_start_network(ctx->WdsClient, input, 45, ctx->cancellable, (GAsyncReadyCallback)start_network_ready, token); RIL_LOGD("Setting up data connection to APN: %s with username/password: %s/%s", apn, username, password); qmi_message_wds_start_network_input_unref(input); strings_array_free(values, size); values = NULL; rc = RIL_REQUEST_HANDLED; goto complete; error: if (values != NULL) strings_array_free(values, size); if (apn != NULL) free(apn); if (username != NULL) free(username); if (password != NULL) free(password); ril_request_complete(token, RIL_E_GENERIC_FAILURE, NULL, 0); rc = RIL_REQUEST_COMPLETED; complete: return rc; } static void set_data_format_ready(QmiClientWda *client, GAsyncResult *res) { QmiMessageWdaSetDataFormatOutput *output; GError *error = NULL; QmiWdaLinkLayerProtocol link_layer_protocol; output = qmi_client_wda_set_data_format_finish(client, res, &error); if (!output) { RIL_LOGE("%s: error: operation failed: %s\n", __func__, error->message); g_error_free(error); return; } if (!qmi_message_wda_set_data_format_output_get_result(output, &error)) { RIL_LOGE("%s: error: couldn't set data format: %s", __func__, error->message); g_error_free(error); qmi_message_wda_set_data_format_output_unref(output); return; } RIL_LOGD("[%s] Successfully set data format", qmi_device_get_path_display(ctx->device)); if (qmi_message_wda_set_data_format_output_get_link_layer_protocol( output, &link_layer_protocol, NULL)) RIL_LOGD("Link layer protocol: '%s'", qmi_wda_link_layer_protocol_get_string(link_layer_protocol)); ril_data->data_connection.raw_ip_mode = TRUE; qmi_message_wda_set_data_format_output_unref(output); } void qmi_set_raw_ip_mode(void) { QmiMessageWdaSetDataFormatInput *input; input = qmi_message_wda_set_data_format_input_new(); qmi_message_wda_set_data_format_input_set_link_layer_protocol( input, QMI_WDA_LINK_LAYER_PROTOCOL_RAW_IP, NULL); qmi_client_wda_set_data_format(ctx->WdaClient, input, 10, ctx->cancellable, (GAsyncReadyCallback)set_data_format_ready, NULL); qmi_message_wda_set_data_format_input_unref(input); }