AT: Better failure handling and removed wrong unlock
[hayes-ril.git] / sim.c
diff --git a/sim.c b/sim.c
index ae9c6e7..694bf4e 100644 (file)
--- a/sim.c
+++ b/sim.c
 
 #include <hayes-ril.h>
 
-RIL_RadioState at2ril_cme_pin_status(int code)
+RIL_RadioState at2ril_sim_status(int status, char *error, char **data, int data_count)
 {
-       switch(code) {
-               case AT_CME_ERROR_PH_SIM_PIN_REQD:
-               case AT_CME_ERROR_PH_SIM_PUK_REQD:
-               case AT_CME_ERROR_PH_FSIM_PIN_REQD:
-               case AT_CME_ERROR_PH_FSIM_PUK_REQD:
-               case AT_CME_ERROR_SIM_NOT_INSERTED:
-               case AT_CME_ERROR_SIM_PIN_REQD:
-               case AT_CME_ERROR_SIM_PUK_REQD:
-               case AT_CME_ERROR_SIM_PIN2_REQD:
-               case AT_CME_ERROR_SIM_PUK2_REQD:
-               case AT_CME_ERROR_SIM_BLOCKED:
-                       return RADIO_STATE_SIM_LOCKED_OR_ABSENT;
-               case AT_CME_ERROR_SIM_FAILURE:
-               case AT_CME_ERROR_SIM_BUSY:
-               case AT_CME_ERROR_SIM_WRONG:
-               case AT_CME_ERROR_SIM_POWERED_DOWN:
-               default:
-                       return RADIO_STATE_SIM_NOT_READY;
+       int code = 0;
+       int i;
+
+       if(status == AT_STATUS_CME_ERROR && error != NULL) {
+               code = atoi(error);
+               switch(code) {
+                       case AT_CME_ERROR_PH_SIM_PIN_REQD:
+                       case AT_CME_ERROR_PH_SIM_PUK_REQD:
+                       case AT_CME_ERROR_PH_FSIM_PIN_REQD:
+                       case AT_CME_ERROR_PH_FSIM_PUK_REQD:
+                       case AT_CME_ERROR_SIM_NOT_INSERTED:
+                       case AT_CME_ERROR_SIM_PIN_REQD:
+                       case AT_CME_ERROR_SIM_PUK_REQD:
+                       case AT_CME_ERROR_SIM_PIN2_REQD:
+                       case AT_CME_ERROR_SIM_PUK2_REQD:
+                       case AT_CME_ERROR_SIM_BLOCKED:
+                       case AT_CME_ERROR_INCORRECT_PASSWORD:
+                               return RADIO_STATE_SIM_LOCKED_OR_ABSENT;
+                       case AT_CME_ERROR_SIM_FAILURE:
+                       case AT_CME_ERROR_SIM_BUSY:
+                       case AT_CME_ERROR_SIM_POWERED_DOWN:
+                       case AT_CME_ERROR_SIM_WRONG:
+                       default:
+                               return RADIO_STATE_SIM_NOT_READY;
+               }
+       } else if(status == AT_STATUS_OK) {
+               if(data != NULL && data_count > 0) {
+                       for(i=0 ; i < data_count ; i++) {
+                               if(at_strings_raw_compare("SIM PIN", data[i])) {
+                                       return RADIO_STATE_SIM_LOCKED_OR_ABSENT;
+                               } else if(at_strings_raw_compare("SIM PUK", data[i])) {
+                                       return RADIO_STATE_SIM_LOCKED_OR_ABSENT;
+                               } else if(at_strings_raw_compare("READY", data[i])) {
+                                       return RADIO_STATE_SIM_READY;
+                               }
+                       }
+               }
+
+               // Fallback
+               return RADIO_STATE_SIM_NOT_READY;
+       } else {
+               // Various error codes, not helping here, don't change anything
+               return ril_globals.radio_state;
        }
+
 }
 
-void at_cpin(struct at_response *response)
+RIL_RadioState at2ril_card_status(RIL_CardStatus *card_status, int status, char *error, char **data, int data_count)
 {
+       RIL_RadioState radio_state;
        int code = 0;
 
-       if(response->status == AT_STATUS_UNDEF)
-               return;
+       int app_status_array_length = 0;
+       int app_index = -1;
+       int i;
+
+       static RIL_AppStatus app_status_array[] = {
+               // SIM_ABSENT = 0
+               { RIL_APPTYPE_UNKNOWN, RIL_APPSTATE_UNKNOWN, RIL_PERSOSUBSTATE_UNKNOWN,
+               NULL, NULL, 0, RIL_PINSTATE_UNKNOWN, RIL_PINSTATE_UNKNOWN },
+               // SIM_NOT_READY = 1
+               { RIL_APPTYPE_SIM, RIL_APPSTATE_DETECTED, RIL_PERSOSUBSTATE_UNKNOWN,
+               NULL, NULL, 0, RIL_PINSTATE_UNKNOWN, RIL_PINSTATE_UNKNOWN },
+               // SIM_READY = 2
+               { RIL_APPTYPE_SIM, RIL_APPSTATE_READY, RIL_PERSOSUBSTATE_READY,
+               NULL, NULL, 0, RIL_PINSTATE_UNKNOWN, RIL_PINSTATE_UNKNOWN },
+               // SIM_PIN = 3
+               { RIL_APPTYPE_SIM, RIL_APPSTATE_PIN, RIL_PERSOSUBSTATE_UNKNOWN,
+               NULL, NULL, 0, RIL_PINSTATE_ENABLED_NOT_VERIFIED, RIL_PINSTATE_UNKNOWN },
+               // SIM_PUK = 4
+               { RIL_APPTYPE_SIM, RIL_APPSTATE_PUK, RIL_PERSOSUBSTATE_UNKNOWN,
+               NULL, NULL, 0, RIL_PINSTATE_ENABLED_BLOCKED, RIL_PINSTATE_UNKNOWN },
+               // SIM_BLOCKED = 5
+               { RIL_APPTYPE_SIM, RIL_APPSTATE_PUK, RIL_PERSOSUBSTATE_UNKNOWN,
+               NULL, NULL, 0, RIL_PINSTATE_ENABLED_PERM_BLOCKED, RIL_PINSTATE_UNKNOWN },
+               // SIM_NETWORK_PERSO = 6
+               { RIL_APPTYPE_SIM, RIL_APPSTATE_SUBSCRIPTION_PERSO, RIL_PERSOSUBSTATE_SIM_NETWORK,
+               NULL, NULL, 0, RIL_PINSTATE_ENABLED_NOT_VERIFIED, RIL_PINSTATE_UNKNOWN },
+               // SIM_NETWORK_SUBSET_PERSO = 7
+               { RIL_APPTYPE_SIM, RIL_APPSTATE_SUBSCRIPTION_PERSO, RIL_PERSOSUBSTATE_SIM_NETWORK_SUBSET,
+               NULL, NULL, 0, RIL_PINSTATE_ENABLED_NOT_VERIFIED, RIL_PINSTATE_UNKNOWN },
+               // SIM_CORPORATE_PERSO = 8
+               { RIL_APPTYPE_SIM, RIL_APPSTATE_SUBSCRIPTION_PERSO, RIL_PERSOSUBSTATE_SIM_CORPORATE,
+               NULL, NULL, 0, RIL_PINSTATE_ENABLED_NOT_VERIFIED, RIL_PINSTATE_UNKNOWN },
+               // SIM_SERVICE_PROVIDER_PERSO = 9
+               { RIL_APPTYPE_SIM, RIL_APPSTATE_SUBSCRIPTION_PERSO, RIL_PERSOSUBSTATE_SIM_SERVICE_PROVIDER,
+               NULL, NULL, 0, RIL_PINSTATE_ENABLED_NOT_VERIFIED, RIL_PINSTATE_UNKNOWN },
+       };
+
+       app_status_array_length = sizeof(app_status_array) / sizeof(RIL_AppStatus);
 
-       if(response->status == AT_STATUS_CME_ERROR && response->error != NULL) {
-               code = atoi(response->error);
-               if(code != 0) {
-                       LOGD("Updating radio state!");
-                       ril_globals.radio_state = at2ril_cme_pin_status(code);
-                       RIL_onUnsolicitedResponse(RIL_UNSOL_RESPONSE_RADIO_STATE_CHANGED, NULL, 0);
+       if(app_status_array_length > RIL_CARD_MAX_APPS)
+               app_status_array_length = RIL_CARD_MAX_APPS;
+
+       radio_state = at2ril_sim_status(status, error, data, data_count);
+
+       if(status == AT_STATUS_CME_ERROR && error != NULL) {
+               code = atoi(error);
+
+               switch(code) {
+                       case AT_CME_ERROR_SIM_NOT_INSERTED:
+                               card_status->card_state = RIL_CARDSTATE_ABSENT;
+                               card_status->universal_pin_state = RIL_PINSTATE_UNKNOWN;
+                               app_index = 0;
+                               break;
+                       case AT_CME_ERROR_PH_SIM_PIN_REQD:
+                       case AT_CME_ERROR_PH_FSIM_PIN_REQD:
+                       case AT_CME_ERROR_SIM_PIN_REQD:
+                       case AT_CME_ERROR_INCORRECT_PASSWORD:
+                               card_status->card_state = RIL_CARDSTATE_PRESENT;
+                               card_status->universal_pin_state = RIL_PINSTATE_ENABLED_NOT_VERIFIED;
+                               app_index = 3;
+                               break;
+                       case AT_CME_ERROR_PH_SIM_PUK_REQD:
+                       case AT_CME_ERROR_PH_FSIM_PUK_REQD:
+                       case AT_CME_ERROR_SIM_PUK_REQD:
+                               card_status->card_state = RIL_CARDSTATE_PRESENT;
+                               card_status->universal_pin_state = RIL_PINSTATE_ENABLED_BLOCKED;
+                               app_index = 4;
+                               break;
+                       case AT_CME_ERROR_SIM_PIN2_REQD:
+                       case AT_CME_ERROR_SIM_PUK2_REQD:
+                       case AT_CME_ERROR_SIM_BLOCKED:
+                               card_status->card_state = RIL_CARDSTATE_PRESENT;
+                               card_status->universal_pin_state = RIL_PINSTATE_ENABLED_PERM_BLOCKED;
+                               app_index = 5;
+                               break;
+                       case AT_CME_ERROR_SIM_FAILURE:
+                       case AT_CME_ERROR_SIM_BUSY:
+                       case AT_CME_ERROR_SIM_POWERED_DOWN:
+                       case AT_CME_ERROR_SIM_WRONG:
+                       default:
+                               card_status->card_state = RIL_CARDSTATE_PRESENT;
+                               card_status->universal_pin_state = RIL_PINSTATE_UNKNOWN;
+                               app_index = 1;
+                               break;
                }
+       } else if(status == AT_STATUS_OK) {
+               if(data != NULL && data_count > 0) {
+                       for(i=0 ; i < data_count ; i++) {
+                               if(at_strings_raw_compare("SIM PIN", data[i])) {
+                                       card_status->card_state = RIL_CARDSTATE_PRESENT;
+                                       card_status->universal_pin_state = RIL_PINSTATE_ENABLED_NOT_VERIFIED;
+                                       app_index = 3;
+                               } else if(at_strings_raw_compare("SIM PUK", data[i])) {
+                                       card_status->card_state = RIL_CARDSTATE_PRESENT;
+                                       card_status->universal_pin_state = RIL_PINSTATE_ENABLED_BLOCKED;
+                                       app_index = 4;
+                               } else if(at_strings_raw_compare("READY", data[i])) {
+                                       card_status->card_state = RIL_CARDSTATE_PRESENT;
+                                       card_status->universal_pin_state = RIL_PINSTATE_UNKNOWN;
+                                       app_index = 2;
+                               }
+                       }
+               }
+       }
+
+       // Fallback
+       if(app_index == -1) {
+               card_status->card_state = RIL_CARDSTATE_PRESENT;
+               card_status->universal_pin_state = RIL_PINSTATE_UNKNOWN;
+               app_index = 1;
+       }
+
+       // Initialize the apps
+       for (i = 0 ; i < app_status_array_length ; i++) {
+               memcpy((void *) &(card_status->applications[i]), (void *) &(app_status_array[i]), sizeof(RIL_AppStatus));
+       }
+       for(i = app_status_array_length ; i < RIL_CARD_MAX_APPS ; i++) {
+               memset((void *) &(card_status->applications[i]), 0, sizeof(RIL_AppStatus));
+       }
+
+       card_status->gsm_umts_subscription_app_index = app_index;
+       card_status->cdma_subscription_app_index = app_index;
+       card_status->num_applications = app_status_array_length;
+
+       LOGD("Selecting application #%d on %d", app_index, app_status_array_length);
+
+       return radio_state;
+}
+
+int at_cpin_expect(struct at_response *response, void *data, RIL_Token t)
+{
+       RIL_CardStatus card_status;
+       RIL_RadioState radio_state;
+
+       if(response->status == AT_STATUS_UNDEF)
+               return AT_RESPONSE_UNHANDELD_REASON_STATUS;
+
+       if(t != (RIL_Token) 0x0000) {
+               radio_state = at2ril_card_status(&card_status, response->status, response->error, response->data, response->data_count);
+               ril_globals.radio_state = radio_state;
+
+               RIL_onRequestComplete(t, RIL_E_SUCCESS, &card_status, sizeof(card_status));
+       } else {
+               radio_state = at2ril_sim_status(response->status, response->error, response->data, response->data_count);
+               ril_globals.radio_state = radio_state;
 
-               // TODO: gather more infos for ril_globals
-       } else if(response->status == AT_STATUS_OK) {
-               ril_globals.radio_state = RADIO_STATE_SIM_READY;
                RIL_onUnsolicitedResponse(RIL_UNSOL_RESPONSE_RADIO_STATE_CHANGED, NULL, 0);
-               // TODO: gather more infos for ril_globals
+       }
+
+       LOGD("Handled AT+CPIN response");
+
+       return AT_RESPONSE_HANDLED_OK;
+}
+
+int at_cpin_unlock_expect(struct at_response *response, void *data, RIL_Token t)
+{
+       int tries = -1;
+       int code = 0;
+
+       if(response->status == AT_STATUS_UNDEF)
+               return AT_RESPONSE_UNHANDELD_REASON_STATUS;
+
+       if(at_status_error(response->status)) {
+               if(response->status == AT_STATUS_CME_ERROR && response->error != NULL) {
+                       code = atoi(response->error);
+
+                       if(code == AT_CME_ERROR_INCORRECT_PASSWORD)
+                               RIL_onRequestComplete(t, RIL_E_PASSWORD_INCORRECT, &tries, sizeof(int *));
+               } else {
+                       RIL_onRequestComplete(t, RIL_E_GENERIC_FAILURE, NULL, 0);
+               }
        } else {
-               // Various error codes, not helping here
+               RIL_onRequestComplete(t, RIL_E_SUCCESS, &tries, sizeof(int *));
+       }
+
+       // Update radio state
+       at_send_expect_to_func("AT+CPIN?", NULL, NULL, 0x0000, at_cpin_expect);
+
+       return AT_RESPONSE_HANDLED_OK;
+}
+
+void ril_request_get_sim_status(RIL_Token t, void *data, size_t length)
+{
+       at_send_expect_to_func("AT+CPIN?", NULL, NULL, t, at_cpin_expect);
+}
+
+void ril_request_enter_sim_pin(RIL_Token t, void *data, size_t length)
+{
+       char *pin = ((char **) data)[0];
+
+       at_send_expect_to_func("AT+CPIN", pin, NULL, t, at_cpin_unlock_expect);
+}
+
+int at_crsm_expect(struct at_response *response, void *data, RIL_Token t)
+{
+       RIL_SIM_IO_Response sim_io_response;
+       struct at_response_data **response_data = NULL;
+       int response_data_count = 0;
+
+       if(response->status == AT_STATUS_UNDEF)
+               return AT_RESPONSE_UNHANDELD_REASON_STATUS;
+
+       if(response->data == NULL || response->data_count <= 0) {
+               LOGE("No data given to AT+CRSM response!");
+               return AT_RESPONSE_HANDLED_ERROR;
+       }
+
+       LOGE("Caught valid AT+CRSM response!");
+
+       response_data_count = at_response_data_process(&response_data, response->data[0], strlen(response->data[0]));
+       if(response_data_count < 3)
+               goto error_data;
+
+       memset(&sim_io_response, 0, sizeof(RIL_SIM_IO_Response));
+
+       if(response_data[0]->type != AT_RESPONSE_DATA_NUMERIC)
+               goto error_data;
+       sim_io_response.sw1 = response_data[0]->value.n;
+
+       if(response_data[1]->type != AT_RESPONSE_DATA_NUMERIC)
+               goto error_data;
+       sim_io_response.sw2 = response_data[1]->value.n;
+
+       if(response_data[2]->type != AT_RESPONSE_DATA_STRING)
+               goto error_data;
+       sim_io_response.simResponse = response_data[2]->value.s;
+
+       RIL_onRequestComplete(t, RIL_E_SUCCESS, &sim_io_response, sizeof(RIL_SIM_IO_Response));
+
+       at_response_data_free(response_data, response_data_count);
+
+       return AT_RESPONSE_HANDLED_OK;
+
+error_data:
+       LOGE("No valid data given to AT+CRSM response!");
+
+       at_response_data_free(response_data, response_data_count);
+       return AT_RESPONSE_HANDLED_ERROR;
+}
+
+void ril_request_sim_io(RIL_Token t, void *data, size_t length)
+{
+       RIL_SIM_IO *sim_io_request = NULL;
+       char *sim_io_data;
+
+       if(data == NULL || length <= 0) {
+               RIL_onRequestComplete(t, RIL_E_GENERIC_FAILURE, NULL, 0);
                return;
        }
 
-       // TODO: Store the data we have in ril_globals (pointer alloc here?), then call UNSOL
+       sim_io_request = (RIL_SIM_IO *) data;
 
-       LOGD("Handled AT+CPIN response");
+       // TODO: add delay if SIM is slow (from device flags)
+
+       if(sim_io_request->data == NULL) {
+               asprintf(&sim_io_data, "%d,%d,%d,%d,%d",
+                       sim_io_request->command, sim_io_request->fileid,
+                       sim_io_request->p1, sim_io_request->p2, sim_io_request->p3);
+       } else {
+               asprintf(&sim_io_data, "%d,%d,%d,%d,%d,%s",
+                       sim_io_request->command, sim_io_request->fileid,
+                       sim_io_request->p1, sim_io_request->p2, sim_io_request->p3, sim_io_request->data);
+       }
+
+       at_send_expect_to_func("AT+CRSM", sim_io_data, NULL, t, at_crsm_expect);
+
+       free(sim_io_data);
 }