static void at_cds_notify(GAtResult *result, gpointer user_data) { struct ofono_sms *sms = user_data; struct sms_data *data = ofono_sms_get_data(sms); long pdu_len; int tpdu_len; const char *hexpdu; unsigned char pdu[176]; if (!at_parse_pdu_common(result, "+CDS:", &hexpdu, &tpdu_len)) { ofono_error("Unable to parse CDS notification"); return; } if (strlen(hexpdu) > sizeof(pdu) * 2) { ofono_error("Bad PDU length in CDS notification"); return; } DBG("Got new Status-Report PDU via CDS: %s, %d", hexpdu, tpdu_len); /* Decode pdu and notify about new SMS status report */ decode_hex_own_buf(hexpdu, -1, &pdu_len, 0, pdu); ofono_sms_status_notify(sms, pdu, pdu_len, tpdu_len); if (data->cnma_enabled) at_ack_delivery(sms); }
static void ril_csca_set(struct ofono_sms *sms, const struct ofono_phone_number *sca, ofono_sms_sca_set_cb_t cb, void *user_data) { struct sms_data *data = ofono_sms_get_data(sms); struct cb_data *cbd = cb_data_new(cb, user_data); struct parcel rilp; int ret = -1; char number[OFONO_MAX_PHONE_NUMBER_LENGTH + 4]; if (sca->type == 129) snprintf(number, sizeof(number), "\"%s\"", sca->number); else snprintf(number, sizeof(number), "\"+%s\"", sca->number); DBG("Setting sca: %s", number); parcel_init(&rilp); parcel_w_string(&rilp, number); /* Send request to RIL */ ret = g_ril_send(data->ril, RIL_REQUEST_SET_SMSC_ADDRESS, rilp.data, rilp.size, ril_csca_set_cb, cbd, g_free); parcel_free(&rilp); /* In case of error free cbd and return the cb with failure */ if (ret <= 0) { g_free(cbd); CALLBACK_WITH_FAILURE(cb, user_data); } }
static void isi_bearer_set(struct ofono_sms *sms, int bearer, ofono_sms_bearer_set_cb_t cb, void *data) { struct sms_data *sd = ofono_sms_get_data(sms); struct isi_cb_data *cbd = isi_cb_data_new(sms, cb, data); const uint8_t msg[] = { SMS_SETTINGS_UPDATE_REQ, SMS_SETTING_TYPE_ROUTE, 1, /* Subblock count */ ISI_16BIT(SMS_SB_ROUTE_INFO), ISI_16BIT(8), /* Subblock length */ bearer_to_cs_pref(bearer), /* CS priority */ bearer_to_ps_pref(bearer), /* PS priority */ 0, 0, }; if (cbd == NULL) goto error; if (g_isi_client_send(sd->client, msg, sizeof(msg), bearer_set_resp_cb, cbd, g_free)) return; error: CALLBACK_WITH_FAILURE(cb, data); g_free(cbd); }
static void at_send_cmgr_cpms(struct ofono_sms *sms, int store, int index, gboolean expect_sr) { struct sms_data *data = ofono_sms_get_data(sms); if (store == data->store) { struct cpms_request req; req.sms = sms; req.store = store; req.index = index; req.expect_sr = expect_sr; at_cmgr_cpms_cb(TRUE, NULL, &req); } else { char buf[128]; const char *incoming = storages[data->incoming]; struct cpms_request *req = g_new(struct cpms_request, 1); req->sms = sms; req->store = store; req->index = index; req->expect_sr = expect_sr; snprintf(buf, sizeof(buf), "AT+CPMS=\"%s\",\"%s\",\"%s\"", storages[store], storages[store], incoming); g_at_chat_send(data->chat, buf, cpms_prefix, at_cmgr_cpms_cb, req, g_free); } }
static void sms_reachable_cb(const GIsiMessage *msg, void *data) { struct ofono_sms *sms = data; struct sms_data *sd = ofono_sms_get_data(sms); if (g_isi_msg_error(msg) < 0) { DBG("unable to find SMS resource"); ofono_sms_remove(sms); return; } if (sd == NULL) return; ISI_RESOURCE_DBG(msg); sd->version.major = g_isi_msg_version_major(msg); sd->version.minor = g_isi_msg_version_minor(msg); if (ISI_VERSION_AT_LEAST(&sd->version, 9, 1)) activate_reception(sd->client, sms, NULL); else set_routing(sd->client, sms, NULL); g_isi_client_verify(sd->sim, sim_reachable_cb, sms, NULL); }
static void at_cmgl_set_cpms(struct ofono_sms *sms, int store) { struct sms_data *data = ofono_sms_get_data(sms); if (store == data->store) { struct cpms_request req; req.sms = sms; req.store = store; at_cmgl_cpms_cb(TRUE, NULL, &req); } else { char buf[128]; const char *readwrite = storages[store]; const char *incoming = storages[data->incoming]; struct cpms_request *req = g_new(struct cpms_request, 1); req->sms = sms; req->store = store; snprintf(buf, sizeof(buf), "AT+CPMS=\"%s\",\"%s\",\"%s\"", readwrite, readwrite, incoming); g_at_chat_send(data->chat, buf, cpms_prefix, at_cmgl_cpms_cb, req, g_free); } }
static void at_query_cnmi(struct ofono_sms *sms) { struct sms_data *data = ofono_sms_get_data(sms); g_at_chat_send(data->chat, "AT+CNMI=?", cnmi_prefix, at_cnmi_query_cb, sms, NULL); }
static void at_cmgs(struct ofono_sms *sms, unsigned char *pdu, int pdu_len, int tpdu_len, int mms, ofono_sms_submit_cb_t cb, void *user_data) { struct sms_data *data = ofono_sms_get_data(sms); struct cb_data *cbd = cb_data_new(cb, user_data); char buf[512]; int len; if (mms) { snprintf(buf, sizeof(buf), "AT+CMMS=%d", mms); g_at_chat_send(data->chat, buf, none_prefix, NULL, NULL, NULL); } len = snprintf(buf, sizeof(buf), "AT+CMGS=%d\r", tpdu_len); encode_hex_own_buf(pdu, pdu_len, 0, buf+len); if (g_at_chat_send(data->chat, buf, cmgs_prefix, at_cmgs_cb, cbd, g_free) > 0) return; g_free(cbd); CALLBACK_WITH_FAILURE(cb, -1, user_data); }
static void at_csms_query_cb(gboolean ok, GAtResult *result, gpointer user_data) { struct ofono_sms *sms = user_data; struct sms_data *data = ofono_sms_get_data(sms); gboolean cnma_supported = FALSE; GAtResultIter iter; int status_min, status_max; char buf[128]; if (!ok) return at_sms_not_supported(sms); g_at_result_iter_init(&iter, result); if (!g_at_result_iter_next(&iter, "+CSMS:")) goto out; if (!g_at_result_iter_open_list(&iter)) goto out; while (g_at_result_iter_next_range(&iter, &status_min, &status_max)) if (status_min <= 1 && 1 <= status_max) cnma_supported = TRUE; DBG("CSMS query parsed successfully"); out: snprintf(buf, sizeof(buf), "AT+CSMS=%d", cnma_supported ? 1 : 0); g_at_chat_send(data->chat, buf, csms_prefix, at_csms_set_cb, sms, NULL); }
static void ril_ack_delivery(struct ofono_sms *sms) { struct sms_data *sd = ofono_sms_get_data(sms); struct parcel rilp; int ret; int request = RIL_REQUEST_SMS_ACKNOWLEDGE; parcel_init(&rilp); parcel_w_int32(&rilp, 2); /* Number of int32 values in array */ parcel_w_int32(&rilp, 1); /* Successful receipt */ parcel_w_int32(&rilp, 0); /* error code */ /* TODO: should ACK be sent for either of the error cases? */ /* ACK the incoming NEW_SMS */ ret = g_ril_send(sd->ril, request, rilp.data, rilp.size, ril_ack_delivery_cb, NULL, NULL); g_ril_append_print_buf(sd->ril, "(1,0)"); g_ril_print_request(sd->ril, ret, request); parcel_free(&rilp); }
static void at_cmgf_query_cb(gboolean ok, GAtResult *result, gpointer user_data) { struct ofono_sms *sms = user_data; struct sms_data *data = ofono_sms_get_data(sms); gboolean supported = FALSE; if (ok) { GAtResultIter iter; int mode; g_at_result_iter_init(&iter, result); if (!g_at_result_iter_next(&iter, "+CMGF:")) goto out; if (!g_at_result_iter_open_list(&iter)) goto out; /* Look for mode 0 (PDU mode) */ while (g_at_result_iter_next_number(&iter, &mode)) if (mode == 0) supported = TRUE; } out: if (!supported) return at_sms_not_supported(sms); g_at_chat_send(data->chat, "AT+CPMS=?", cpms_prefix, at_cpms_query_cb, sms, NULL); }
static void at_cmgl_done(struct ofono_sms *sms) { struct sms_data *data = ofono_sms_get_data(sms); DBG(""); if (data->incoming == AT_UTIL_SMS_STORE_MT && data->store == AT_UTIL_SMS_STORE_ME) { at_cmgl_set_cpms(sms, AT_UTIL_SMS_STORE_SM); return; } g_at_chat_register(data->chat, "+CMTI:", at_cmti_notify, FALSE, sms, NULL); g_at_chat_register(data->chat, "+CMT:", at_cmt_notify, TRUE, sms, NULL); g_at_chat_register(data->chat, "+CDS:", at_cds_notify, TRUE, sms, NULL); g_at_chat_register(data->chat, "+CDSI:", at_cdsi_notify, FALSE, sms, NULL); /* We treat CMGR just like a notification */ g_at_chat_register(data->chat, "+CMGR:", at_cmgr_notify, TRUE, sms, NULL); }
static void ril_cmgs(struct ofono_sms *sms, const unsigned char *pdu, int pdu_len, int tpdu_len, int mms, ofono_sms_submit_cb_t cb, void *user_data) { struct sms_data *sd = ofono_sms_get_data(sms); struct cb_data *cbd = cb_data_new(cb, user_data); struct parcel rilp; char *tpdu; int request = RIL_REQUEST_SEND_SMS; int ret, smsc_len; cbd->user = sd; DBG("pdu_len: %d, tpdu_len: %d mms: %d", pdu_len, tpdu_len, mms); /* TODO: if (mms) { ... } */ parcel_init(&rilp); parcel_w_int32(&rilp, 2); /* Number of strings */ /* SMSC address: * * smsc_len == 1, then zero-length SMSC was spec'd * RILD expects a NULL string in this case instead * of a zero-length string. */ smsc_len = pdu_len - tpdu_len; if (smsc_len > 1) { /* TODO: encode SMSC & write to parcel */ DBG("SMSC address specified (smsc_len %d); NOT-IMPLEMENTED", smsc_len); } parcel_w_string(&rilp, NULL); /* SMSC address; NULL == default */ /* TPDU: * * 'pdu' is a raw hexadecimal string * encode_hex() turns it into an ASCII/hex UTF8 buffer * parcel_w_string() encodes utf8 -> utf16 */ tpdu = encode_hex(pdu + smsc_len, tpdu_len, 0); parcel_w_string(&rilp, tpdu); ret = g_ril_send(sd->ril, request, rilp.data, rilp.size, submit_sms_cb, cbd, g_free); g_ril_append_print_buf(sd->ril, "(%s)", tpdu); g_ril_print_request(sd->ril, ret, request); parcel_free(&rilp); if (ret <= 0) { g_free(cbd); CALLBACK_WITH_FAILURE(cb, -1, user_data); } }
static void received_msg_ind_cb(const GIsiMessage *msg, void *data) { struct ofono_sms *sms = data; struct sms_data *sd = ofono_sms_get_data(sms); struct sms_common tpdu; struct sms_addr addr; GIsiSubBlockIter iter; uint8_t pdu[176]; uint8_t sbcount; DBG(""); if (g_isi_msg_id(msg) != SMS_RECEIVED_MSG_IND) return; if (!g_isi_msg_data_get_byte(msg, 1, &sbcount)) return; for (g_isi_sb_iter_init_full(&iter, msg, 2, TRUE, sbcount); g_isi_sb_iter_is_valid(&iter); g_isi_sb_iter_next(&iter)) { switch (g_isi_sb_iter_get_id(&iter)) { case SMS_ADDRESS: if (!parse_sms_address(&iter, 4, &addr)) return; if (addr.type != SMS_SMSC_ADDRESS) return; break; case SMS_SB_TPDU: if (!parse_sms_tpdu(&iter, 4, &tpdu)) return; break; } } if (tpdu.data == NULL || addr.data == NULL || tpdu.len + addr.len > sizeof(pdu)) return; memcpy(pdu, addr.data, addr.len); memcpy(pdu + addr.len, tpdu.data, tpdu.len); /* 23.040 9.2.3.1 */ if ((tpdu.data[0] & 0x03) == 0x02) ofono_sms_status_notify(sms, pdu, tpdu.len + addr.len, tpdu.len); else ofono_sms_deliver_notify(sms, pdu, tpdu.len + addr.len, tpdu.len); send_deliver_report(sd->client, TRUE, NULL, NULL); }
static void at_csms_set_cb(gboolean ok, GAtResult *result, gpointer user_data) { struct ofono_sms *sms = user_data; struct sms_data *data = ofono_sms_get_data(sms); g_at_chat_send(data->chat, "AT+CSMS?", csms_prefix, at_csms_status_cb, sms, NULL); }
static void sca_sim_query_resp_cb(const GIsiMessage *msg, void *data) { struct isi_cb_data *cbd = data; struct ofono_sms *sms = cbd->user; struct sms_data *sd = ofono_sms_get_data(sms); ofono_sms_sca_query_cb_t cb = cbd->cb; struct ofono_phone_number sca; struct sms_params *info; size_t len = sizeof(struct sms_params); uint8_t bcd_len; if (!check_sim(msg, SIM_SMS_RESP, READ_PARAMETER)) goto error; if (!g_isi_msg_data_get_struct(msg, 2, (const void **) &info, len)) goto error; if (info->alphalen > 17) info->alphalen = 17; else if (info->alphalen < 1) info->alphalen = 1; info->alpha[info->alphalen - 1] = '\0'; sd->params.absent = info->absent; sd->params.tp_pid = info->tp_pid; sd->params.tp_dcs = info->tp_dcs; sd->params.tp_vp = info->tp_vp; memcpy(sd->params.dst, info->dst, sizeof(sd->params.dst)); memcpy(sd->params.sca, info->sca, sizeof(sd->params.sca)); sd->params.alphalen = info->alphalen; memcpy(sd->params.alpha, info->alpha, sizeof(sd->params.alpha)); /* * Bitmask indicating absence of parameters -- * If second bit is set it indicates that the SCA is absent */ if (info->absent & 0x2) goto error; bcd_len = info->sca[0]; if (bcd_len == 0 || bcd_len > 12) goto error; extract_bcd_number(info->sca + 2, bcd_len - 1, sca.number); sca.type = 0x80 | info->sca[1]; CALLBACK_WITH_SUCCESS(cb, &sca, cbd->data); return; error: CALLBACK_WITH_FAILURE(cb, NULL, cbd->data); }
static void at_cmgl_notify(GAtResult *result, gpointer user_data) { struct ofono_sms *sms = user_data; struct sms_data *data = ofono_sms_get_data(sms); GAtResultIter iter; const char *hexpdu; unsigned char pdu[176]; long pdu_len; int tpdu_len; int index; int status; char buf[16]; DBG(""); g_at_result_iter_init(&iter, result); while (g_at_result_iter_next(&iter, "+CMGL:")) { if (!g_at_result_iter_next_number(&iter, &index)) goto err; if (!g_at_result_iter_next_number(&iter, &status)) goto err; if (!g_at_result_iter_skip_next(&iter)) goto err; if (!g_at_result_iter_next_number(&iter, &tpdu_len)) goto err; /* Only MT messages */ if (status != 0 && status != 1) continue; hexpdu = g_at_result_pdu(result); DBG("Found an old SMS PDU: %s, with len: %d", hexpdu, tpdu_len); if (strlen(hexpdu) > sizeof(pdu) * 2) continue; decode_hex_own_buf(hexpdu, -1, &pdu_len, 0, pdu); ofono_sms_deliver_notify(sms, pdu, pdu_len, tpdu_len); /* We don't buffer SMS on the SIM/ME, send along a CMGD */ snprintf(buf, sizeof(buf), "AT+CMGD=%d", index); g_at_chat_send(data->chat, buf, none_prefix, at_cmgd_cb, NULL, NULL); } return; err: ofono_error("Unable to parse CMGL response"); }
static void ril_sms_remove(struct ofono_sms *sms) { struct sms_data *data = ofono_sms_get_data(sms); DBG(""); g_ril_unref(data->ril); g_free(data); ofono_sms_set_data(sms, NULL); }
static gboolean set_cmgf(gpointer user_data) { struct ofono_sms *sms = user_data; struct sms_data *data = ofono_sms_get_data(sms); g_at_chat_send(data->chat, "AT+CMGF=0", cmgf_prefix, at_cmgf_set_cb, sms, NULL); data->timeout_source = 0; return FALSE; }
static void at_sms_initialized(struct ofono_sms *sms) { struct sms_data *data = ofono_sms_get_data(sms); /* Inspect and free the incoming SMS storage */ if (data->incoming == AT_UTIL_SMS_STORE_MT) at_cmgl_set_cpms(sms, AT_UTIL_SMS_STORE_ME); else at_cmgl_set_cpms(sms, data->incoming); ofono_sms_register(sms); }
static void at_csms_status_cb(gboolean ok, GAtResult *result, gpointer user_data) { struct ofono_sms *sms = user_data; struct sms_data *data = ofono_sms_get_data(sms); gboolean supported = FALSE; if (ok) { GAtResultIter iter; int service; int mt; int mo; g_at_result_iter_init(&iter, result); if (!g_at_result_iter_next(&iter, "+CSMS:")) goto out; switch (data->vendor) { case OFONO_VENDOR_HUAWEI: case OFONO_VENDOR_NOVATEL: g_at_result_iter_skip_next(&iter); service = 0; break; default: if (!g_at_result_iter_next_number(&iter, &service)) goto out; break; } if (!g_at_result_iter_next_number(&iter, &mt)) goto out; if (!g_at_result_iter_next_number(&iter, &mo)) goto out; if (service == 1) data->cnma_enabled = TRUE; if (mt == 1 && mo == 1) supported = TRUE; } out: if (!supported) return at_sms_not_supported(sms); /* Now query supported text format */ g_at_chat_send(data->chat, "AT+CMGF=?", cmgf_prefix, at_cmgf_query_cb, sms, NULL); }
static void at_sms_remove(struct ofono_sms *sms) { struct sms_data *data = ofono_sms_get_data(sms); g_free(data->cnma_ack_pdu); if (data->timeout_source > 0) g_source_remove(data->timeout_source); g_at_chat_unref(data->chat); g_free(data); ofono_sms_set_data(sms, NULL); }
static void at_cgsms_query(struct ofono_sms *sms, ofono_sms_bearer_query_cb_t cb, void *user_data) { struct sms_data *data = ofono_sms_get_data(sms); struct cb_data *cbd = cb_data_new(cb, user_data); if (g_at_chat_send(data->chat, "AT+CGSMS?", cgsms_prefix, at_cgsms_query_cb, cbd, g_free) > 0) return; g_free(cbd); CALLBACK_WITH_FAILURE(cb, -1, user_data); }
static gboolean ril_delayed_register(gpointer user_data) { struct ofono_sms *sms = user_data; struct sms_data *data = ofono_sms_get_data(sms); DBG(""); ofono_sms_register(sms); /* register to receive INCOMING_SMS */ g_ril_register(data->ril, RIL_UNSOL_RESPONSE_NEW_SMS, ril_sms_notify, sms); /* This makes the timeout a single-shot */ return FALSE; }
static void isi_sca_query(struct ofono_sms *sms, ofono_sms_sca_query_cb_t cb, void *data) { struct sms_data *sd = ofono_sms_get_data(sms); struct isi_cb_data *cbd = isi_cb_data_new(sms, cb, data); if (cbd == NULL || sd->sim == NULL) goto error; if (sca_sim_query(sd->sim, cbd, g_free)) return; error: CALLBACK_WITH_FAILURE(cb, NULL, data); g_free(cbd); }
static inline void at_ack_delivery(struct ofono_sms *sms) { struct sms_data *data = ofono_sms_get_data(sms); char buf[256]; DBG(""); /* We must acknowledge the PDU using CNMA */ if (data->cnma_ack_pdu) snprintf(buf, sizeof(buf), "AT+CNMA=1,%d\r%s", data->cnma_ack_pdu_len, data->cnma_ack_pdu); else /* Should be a safe fallback */ snprintf(buf, sizeof(buf), "AT+CNMA=0"); g_at_chat_send(data->chat, buf, none_prefix, at_cnma_cb, NULL, NULL); }
static void at_cpms_set_cb(gboolean ok, GAtResult *result, gpointer user_data) { struct ofono_sms *sms = user_data; struct sms_data *data = ofono_sms_get_data(sms); if (ok) return at_query_cnmi(sms); data->retries += 1; if (data->retries == MAX_CPMS_RETRIES) { ofono_error("Unable to set preferred storage"); return at_sms_not_supported(sms); } data->timeout_source = g_timeout_add_seconds(1, set_cpms, sms); }
static void at_cgsms_set(struct ofono_sms *sms, int bearer, ofono_sms_bearer_set_cb_t cb, void *user_data) { struct sms_data *data = ofono_sms_get_data(sms); struct cb_data *cbd = cb_data_new(cb, user_data); char buf[64]; snprintf(buf, sizeof(buf), "AT+CGSMS=%d", bearer); if (g_at_chat_send(data->chat, buf, none_prefix, at_cgsms_set_cb, cbd, g_free) > 0) return; g_free(cbd); CALLBACK_WITH_FAILURE(cb, user_data); }
static void at_cmgl_cpms_cb(gboolean ok, GAtResult *result, gpointer user_data) { struct cpms_request *req = user_data; struct ofono_sms *sms = req->sms; struct sms_data *data = ofono_sms_get_data(sms); if (!ok) { ofono_error("Initial CPMS request failed"); at_cmgl_done(sms); return; } data->store = req->store; g_at_chat_send_pdu_listing(data->chat, "AT+CMGL=4", cmgl_prefix, at_cmgl_notify, at_cmgl_cb, sms, NULL); }
static void isi_sca_set(struct ofono_sms *sms, const struct ofono_phone_number *sca, ofono_sms_sca_set_cb_t cb, void *data) { struct sms_data *sd = ofono_sms_get_data(sms); struct isi_cb_data *cbd = isi_cb_data_new(sms, cb, data); if (cbd == NULL || sd->sim == NULL) goto error; if (sca_sim_set(sd->sim, &sd->params, sca, cbd, g_free)) return; error: CALLBACK_WITH_FAILURE(cb, data); g_free(cbd); }