Network: Added registration state (both SOL and UNSOL) support
[hayes-ril.git] / network.c
index d47545e..349d7a9 100644 (file)
--- a/network.c
+++ b/network.c
 
 #include <hayes-ril.h>
 
+/*
+ * Signal Strength
+ */
+
 void at2ril_signal_strength(RIL_SignalStrength *ss, int *values)
 {
        const int dbm_table[8] = {135,125,115,105,95,85,75,70};
@@ -86,7 +90,9 @@ int at_csq_expect(struct at_response *response, void *data, RIL_Token t)
        return AT_RESPONSE_HANDLED_OK;
 
 error_data:
-       LOGE("No valid data fiven to AT+CSQ response!");
+       LOGE("No valid data given to AT+CSQ response!");
+
+       RIL_onRequestComplete(t, RIL_E_GENERIC_FAILURE, NULL, 0);
 
        at_response_data_free(response_data, response_data_count);
        return AT_RESPONSE_HANDLED_ERROR;
@@ -96,3 +102,199 @@ void ril_request_signal_strength(RIL_Token t, void *data, size_t length)
 {
        at_send_expect_to_func("AT+CSQ", NULL, NULL, t, at_csq_expect);
 }
+
+/*
+ * Network registration
+ */
+
+int at_creg_handle(char **registration_state, struct at_response_data **response_data, int response_data_count)
+{
+       int state = 0;
+       char *lac = NULL;
+       char *cid = NULL;
+
+       if(registration_state == NULL || response_data == NULL || response_data_count <= 0)
+               return -1;
+
+       /*
+        * Ok you have to be careful here
+        * The solicited version of the CREG response is
+        * +CREG: n, stat, [lac, cid]
+        * and the unsolicited version is
+        * +CREG: stat, [lac, cid]
+        * The <n> parameter is basically "is unsolicited creg on?"
+        * which it should always be
+        *
+        * Also since the LAC and CID are only reported when registered,
+        * we can have 1, 2, 3, or 4 arguments here
+        *
+        * finally, a +CGREG: answer may have a fifth value that corresponds
+        * to the network type, as in;
+        *
+        *   +CGREG: n, stat [,lac, cid [,networkType]]
+        */
+
+       // Only handle CREG responses
+       switch(response_data_count) {
+               case 1: // UNSOL response, unregistered
+                       if(response_data[0]->type != AT_RESPONSE_DATA_NUMERIC)
+                               return -1;
+                       state = response_data[0]->value.n;
+
+                       break;
+               case 2: // SOL response, unregistered
+                       // Skip first argument
+                       if(response_data[1]->type != AT_RESPONSE_DATA_NUMERIC)
+                               return -1;
+                       state = response_data[1]->value.n;
+
+                       break;
+               case 3: // UNSOL response, registered
+                       if(response_data[0]->type != AT_RESPONSE_DATA_NUMERIC)
+                               return -1;
+                       state = response_data[0]->value.n;
+
+                       if(response_data[1]->type != AT_RESPONSE_DATA_STRING)
+                               return -1;
+                       lac = response_data[1]->value.s;
+
+                       if(response_data[2]->type != AT_RESPONSE_DATA_STRING)
+                               return -1;
+                       cid = response_data[2]->value.s;
+
+                       break;
+               case 4: // SOL response, registered
+                       if(response_data[1]->type != AT_RESPONSE_DATA_NUMERIC)
+                               return -1;
+                       state = response_data[1]->value.n;
+
+                       if(response_data[2]->type != AT_RESPONSE_DATA_STRING)
+                               return -1;
+                       lac = response_data[2]->value.s;
+
+                       if(response_data[3]->type != AT_RESPONSE_DATA_STRING)
+                               return -1;
+                       cid = response_data[3]->value.s;
+
+                       break;
+               default:
+                       return -1;
+       }
+
+       asprintf(&(registration_state[0]), "%d", state);
+       registration_state[1] = strdup(lac);
+       registration_state[2] = strdup(cid);
+
+       return 0;
+}
+
+void ril_registration_state_free(char **registration_state)
+{
+       int i;
+
+       for(i=0 ; i < 14 ; i++) {
+               if(registration_state[i] != NULL)
+                       free(registration_state[i]);
+       }
+
+
+       free(registration_state);
+}
+
+int at_creg_expect(struct at_response *response, void *data, RIL_Token t)
+{
+       struct at_response_data **response_data = NULL;
+       int response_data_count = 0;
+
+       char **registration_state = NULL;
+       int rc;
+
+       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+CREG response!");
+               return AT_RESPONSE_HANDLED_ERROR;
+       }
+
+       LOGE("Caught valid AT+CREG response!");
+
+       response_data_count = at_response_data_process(&response_data, response->data[0], strlen(response->data[0]));
+       if(response_data_count <= 0)
+               goto error_data;
+
+       registration_state = calloc(1, sizeof(char *) * 15);
+       rc = at_creg_handle(registration_state, response_data, response_data_count);
+       if(rc < 0) {
+               ril_registration_state_free(registration_state);
+               goto error_data;
+       }
+
+       RIL_onRequestComplete(t, RIL_E_SUCCESS, registration_state,  sizeof(char *) * 15);
+
+       ril_registration_state_free(registration_state);
+       at_response_data_free(response_data, response_data_count);
+
+       return AT_RESPONSE_HANDLED_OK;
+
+error_data:
+       LOGE("No valid data given to AT+CREG response!");
+
+       RIL_onRequestComplete(t, RIL_E_GENERIC_FAILURE, NULL, 0);
+
+       at_response_data_free(response_data, response_data_count);
+
+       return AT_RESPONSE_HANDLED_ERROR;
+}
+
+void at_creg_unexpect(struct at_response *response)
+{
+       struct at_response_data **response_data = NULL;
+       int response_data_count = 0;
+
+       char **registration_state = NULL;
+       int rc;
+
+       if(response->data == NULL || response->data_count <= 0) {
+               LOGE("No data given to AT+CREG unsol!");
+               return;
+       }
+
+       LOGE("Caught valid AT+CREG unsol!");
+
+       response_data_count = at_response_data_process(&response_data, response->data[0], strlen(response->data[0]));
+       if(response_data_count <= 0)
+               goto error_data;
+
+       registration_state = calloc(1, sizeof(char *) * 15);
+       rc = at_creg_handle(registration_state, response_data, response_data_count);
+       if(rc < 0) {
+               ril_registration_state_free(registration_state);
+               goto error_data;
+       }
+
+       if(ril_globals.registration_state != NULL)
+               ril_registration_state_free(ril_globals.registration_state);
+
+       ril_globals.registration_state = registration_state;
+
+       RIL_onUnsolicitedResponse(RIL_UNSOL_RESPONSE_NETWORK_STATE_CHANGED, NULL, 0);
+
+error_data:
+       LOGE("No valid data given to AT+CREG unsol!");
+
+       at_response_data_free(response_data, response_data_count);
+}
+
+void ril_request_registration_state(RIL_Token t, void *data, size_t length)
+{
+       if(ril_globals.registration_state != NULL) {
+               // There is UNSOL data there
+               RIL_onRequestComplete(t, RIL_E_SUCCESS, ril_globals.registration_state,  sizeof(char *) * 15);
+
+               ril_registration_state_free(ril_globals.registration_state);
+               ril_globals.registration_state = NULL;
+       } else {
+               at_send_expect_to_func("AT+CREG?", NULL, NULL, t, at_creg_expect);
+       }
+}