diff --git a/include/stk.h b/include/stk.h index ad3f6c56..638da9cb 100644 --- a/include/stk.h +++ b/include/stk.h @@ -65,6 +65,8 @@ void *ofono_stk_get_data(struct ofono_stk *stk); void ofono_stk_proactive_command_notify(struct ofono_stk *stk, int length, const unsigned char *pdu); +void ofono_stk_proactive_session_end_notify(struct ofono_stk *stk); + #ifdef __cplusplus } #endif diff --git a/src/stk.c b/src/stk.c index b5c69197..66b0a6b6 100644 --- a/src/stk.c +++ b/src/stk.c @@ -43,48 +43,144 @@ struct ofono_stk { const struct ofono_stk_driver *driver; void *driver_data; struct ofono_atom *atom; + struct stk_command *pending_cmd; + void (*cancel_cmd)(struct ofono_stk *stk); + gboolean cancelled; }; +static int stk_respond(struct ofono_stk *stk, struct stk_response *rsp, + ofono_stk_generic_cb_t cb) +{ + const guint8 *tlv; + unsigned int tlv_len; + + if (stk->driver->terminal_response == NULL) + return -ENOSYS; + + rsp->src = STK_DEVICE_IDENTITY_TYPE_TERMINAL; + rsp->dst = STK_DEVICE_IDENTITY_TYPE_UICC; + rsp->number = stk->pending_cmd->number; + rsp->type = stk->pending_cmd->type; + rsp->qualifier = stk->pending_cmd->qualifier; + + tlv = stk_pdu_from_response(rsp, &tlv_len); + if (!tlv) + return -EINVAL; + + stk_command_free(stk->pending_cmd); + stk->pending_cmd = NULL; + + stk->driver->terminal_response(stk, tlv_len, tlv, cb, stk); + + return 0; +} + +static int stk_send_envelope(struct ofono_stk *stk, struct stk_envelope *e, + ofono_stk_envelope_cb_t cb) +{ + const guint8 *tlv; + unsigned int tlv_len; + + if (stk->driver->envelope == NULL) + return -ENOSYS; + + e->dst = STK_DEVICE_IDENTITY_TYPE_UICC; + + tlv = stk_pdu_from_envelope(e, &tlv_len); + if (!tlv) + return -EINVAL; + + stk->driver->envelope(stk, tlv_len, tlv, cb, stk); + + return 0; +} + static void stk_cbs_download_cb(const struct ofono_error *error, const unsigned char *data, int len, void *user) { if (error->type != OFONO_ERROR_TYPE_NO_ERROR) { ofono_error("CellBroadcast download to UICC failed"); - /* "The ME may retry to deliver the same Cell Broadcast - * page." */ + /* + * "The ME may retry to deliver the same Cell Broadcast + * page." + */ return; } + if (len) + ofono_error("CellBroadcast download returned %i bytes of data", + len); + DBG("CellBroadcast download to UICC reported no error"); } void __ofono_cbs_sim_download(struct ofono_stk *stk, const struct cbs *msg) { - const guint8 *tlv; - unsigned int tlv_len; + struct ofono_error error = { .type = OFONO_ERROR_TYPE_FAILURE }; struct stk_envelope e; + int err; - if (stk->driver->envelope == NULL) - return; + memset(&e, 0, sizeof(e)); e.type = STK_ENVELOPE_TYPE_CBS_PP_DOWNLOAD; e.src = STK_DEVICE_IDENTITY_TYPE_NETWORK; - e.dst = STK_DEVICE_IDENTITY_TYPE_UICC; memcpy(&e.cbs_pp_download.page, msg, sizeof(msg)); - tlv = stk_pdu_from_envelope(&e, &tlv_len); - if (!tlv) + err = stk_send_envelope(stk, &e, stk_cbs_download_cb); + if (err) + stk_cbs_download_cb(&error, NULL, -1, stk); +} + +static void stk_command_cb(const struct ofono_error *error, void *data) +{ + if (error->type != OFONO_ERROR_TYPE_NO_ERROR) { + ofono_error("TERMINAL RESPONSE to a UICC command failed"); + /* "The ME may retry to deliver the same Cell Broadcast + * page." */ + return; + } + + DBG("TERMINAL RESPONSE to a command reported no errors"); +} + +static void stk_proactive_command_cancel(struct ofono_stk *stk) +{ + if (!stk->pending_cmd) return; - stk->driver->envelope(stk, tlv_len, tlv, stk_cbs_download_cb, stk); + stk->cancelled = TRUE; + + stk->cancel_cmd(stk); + + if (stk->pending_cmd) { + stk_command_free(stk->pending_cmd); + stk->pending_cmd = NULL; + } +} + +void ofono_stk_proactive_session_end_notify(struct ofono_stk *stk) +{ + stk_proactive_command_cancel(stk); } void ofono_stk_proactive_command_notify(struct ofono_stk *stk, int length, const unsigned char *pdu) { - struct stk_command *cmd; + struct ofono_error error = { .type = OFONO_ERROR_TYPE_FAILURE }; + struct stk_response rsp; char *buf; - int i; + int i, err; + gboolean respond = TRUE; + + /* + * Depending on the hardware we may have received a new + * command before we managed to send a TERMINAL RESPONSE to + * the previous one. 3GPP says in the current revision only + * one command can be executing at any time, so assume that + * the previous one is being cancelled and the card just + * expects a response to the new one. + */ + stk_proactive_command_cancel(stk); buf = g_try_malloc(length * 2 + 1); if (!buf) @@ -93,18 +189,53 @@ void ofono_stk_proactive_command_notify(struct ofono_stk *stk, for (i = 0; i < length; i ++) sprintf(buf + i * 2, "%02hhx", pdu[i]); - cmd = stk_command_new_from_pdu(pdu, length); - if (!cmd) { + stk->cancelled = FALSE; + + stk->pending_cmd = stk_command_new_from_pdu(pdu, length); + if (!stk->pending_cmd) { ofono_error("Can't parse proactive command: %s", buf); - /* TODO: return TERMINAL RESPONSE with permanent error */ + /* + * Nothing we can do, we'd need at least Command Details + * to be able to respond with an error. + */ goto done; } - /* TODO: execute */ - ofono_info("Proactive command PDU: %s", buf); + ofono_debug("Proactive command PDU: %s", buf); - stk_command_free(cmd); + memset(&rsp, 0, sizeof(rsp)); + + switch (stk->pending_cmd->status) { + case STK_PARSE_RESULT_OK: + switch (stk->pending_cmd->type) { + default: + rsp.result.type = + STK_RESULT_TYPE_COMMAND_NOT_UNDERSTOOD; + break; + } + + if (respond) + break; + return; + + case STK_PARSE_RESULT_MISSING_VALUE: + rsp.result.type = STK_RESULT_TYPE_MINIMUM_NOT_MET; + break; + + case STK_PARSE_RESULT_DATA_NOT_UNDERSTOOD: + rsp.result.type = STK_RESULT_TYPE_DATA_NOT_UNDERSTOOD; + break; + + case STK_PARSE_RESULT_TYPE_NOT_UNDERSTOOD: + default: + rsp.result.type = STK_RESULT_TYPE_COMMAND_NOT_UNDERSTOOD; + break; + } + + err = stk_respond(stk, &rsp, stk_command_cb); + if (err) + stk_command_cb(&error, stk); done: g_free(buf); @@ -131,6 +262,12 @@ void ofono_stk_driver_unregister(const struct ofono_stk_driver *d) static void stk_unregister(struct ofono_atom *atom) { + struct ofono_stk *stk = __ofono_atom_get_data(atom); + + if (stk->pending_cmd) { + stk_command_free(stk->pending_cmd); + stk->pending_cmd = NULL; + } } static void stk_remove(struct ofono_atom *atom)