AT: Added AT response parsing engine
authorPaul Kocialkowski <contact@paulk.fr>
Thu, 19 Jul 2012 18:08:41 +0000 (20:08 +0200)
committerPaul Kocialkowski <contact@paulk.fr>
Thu, 19 Jul 2012 18:08:41 +0000 (20:08 +0200)
Signed-off-by: Paul Kocialkowski <contact@paulk.fr>
Android.mk
at.c [new file with mode: 0644]
at.h [new file with mode: 0644]
device.c
hayes-ril.h

index b664d34..bfbd130 100644 (file)
@@ -19,6 +19,7 @@ include $(CLEAR_VARS)
 
 hayes_ril_files := \
        hayes-ril.c \
+       at.c \
        device.c
 
 ifeq ($(TARGET_DEVICE),gta04)
@@ -37,7 +38,7 @@ ifeq ($(TARGET_DEVICE),dream_sapphire)
 endif
 
 LOCAL_SRC_FILES := $(hayes_ril_files) $(hayes_ril_device_files)
-LOCAL_SHARED_LIBRARIES += libcutils libutils
+LOCAL_SHARED_LIBRARIES += libcutils libutils libril
 LOCAL_PRELINK_MODULE := false
 
 LOCAL_C_INCLUDES += $(KERNEL_HEADERS) $(LOCAL_PATH)
diff --git a/at.c b/at.c
new file mode 100644 (file)
index 0000000..2cfd0e1
--- /dev/null
+++ b/at.c
@@ -0,0 +1,393 @@
+/**
+ * This file is part of hayes-ril.
+ *
+ * Copyright (C) 2012 Paul Kocialkowski <contact@paulk.fr>
+ *
+ * Based on htcgeneric-ril, reference-ril:
+ * Copyright 2006-2011, htcgeneric-ril contributors
+ * Copyright 2006, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <string.h>
+#include <ctype.h>
+
+#define LOG_TAG "RIL-AT"
+#include <utils/Log.h>
+
+#include <hayes-ril.h>
+
+int at_strings_compare(char *major, char *minor)
+{
+       if(strncmp(major, minor, strlen(major)) == 0)
+               return 1;
+       else
+               return 0;
+}
+
+void at_string_clean(char *string, int length)
+{
+       int i=0;
+
+       for(i=length-1; i >= 0 ; i--) {
+               if(!isprint(string[i]))
+                       string[i] = '\0';
+       }
+}
+
+int at_line_parse_status(char *data, int length, char **error_p)
+{
+       // TODO: defined rc, fill error_p
+
+       *error_p = NULL;
+
+       if(at_strings_compare("OK", data))
+               return 1;
+
+       if(at_strings_compare("CONNECT", data))
+               return 2;
+
+       if(at_strings_compare("ERROR", data))
+               return -1;
+
+       // TODO: Other error codes
+
+       // TODO: undefined magic
+       return 0;
+}
+
+int at_line_parse_command_data(char *data, int length, char **response_command, char **response_data)
+{
+       char *string = NULL;
+       int string_length = 0;
+       int close_your_eyes = 0;
+       int mark = 0;
+       int i;
+
+
+       for(i=0 ; i < length ; i++) {
+               if(data[i] == ':' && mark == 0) {
+                       if(i > 0) {
+                               data[i] = '\0';
+
+                               string_length = i + 1;
+                               string = strndup(data, string_length);
+
+                               if(!isprint(string[0])) {
+                                       free(string);
+                               } else {
+                                       *response_command = string;
+                               }
+
+                               mark = i + 1;
+                       }
+
+                       while(isspace(data[i+1])) {
+                               mark = i + 2;
+                               i++;
+                       }
+               }
+
+               if(data[i] == '"') {
+                       if(close_your_eyes & AT_PARSE_DOUBLE_QUOTE)
+                               close_your_eyes &= ~AT_PARSE_DOUBLE_QUOTE;
+                       else
+                               close_your_eyes |= AT_PARSE_DOUBLE_QUOTE;
+               }
+
+               if(data[i] == '\'') {
+                       if(close_your_eyes & AT_PARSE_SINGLE_QUOTE)
+                               close_your_eyes &= ~AT_PARSE_SINGLE_QUOTE;
+                       else
+                               close_your_eyes |= AT_PARSE_SINGLE_QUOTE;
+               }
+
+               // Found = or ? outside of any quotes: assume no data
+               if(!close_your_eyes && (data[i] == '=' || data[i] == '?') && mark == 0) {
+                       data[i] = '\0';
+
+                       string_length = i + 1;
+                       string = strndup(data, string_length);
+
+                       if(!isprint(string[0])) {
+                               free(string);
+                       } else {
+                               *response_command = string;
+                       }
+
+                       return 0;
+               }
+       }
+
+
+       if(length - mark > 0) {
+               string_length = length - mark;
+               string = strndup(data + mark, string_length);
+
+               if(mark == 0) {
+                       *response_command = string;
+                       return 0;
+               } else {
+                       *response_data = string;
+                       return string_length;
+               }
+       }
+
+       return 0;
+}
+
+int at_responses_process_line(struct at_response ***responses_p, int responses_count, char *data, int length)
+{
+       struct at_response **responses = NULL;
+
+       char *response_command = NULL;
+       char *response_data = NULL;
+       int response_data_length = 0;
+       char *response_error = NULL;
+       int response_status = 0;
+
+       char **response_previous_data = NULL;
+       int response_data_count = 0;
+
+       int index = -1;
+       int count = 0;
+       int i;
+
+       if(responses_p == NULL || data == NULL || length < 0) {
+               LOGE("Failed to process AT response: wrong arguments!");
+               return 0;
+       }
+
+       responses = *responses_p;
+
+       // Parse status
+       response_status = at_line_parse_status(data, length, &response_error);
+
+       if(response_status != 0) {
+               if(responses_count > 0 && responses != NULL) {
+                       for(i=responses_count-1 ; i >= 0; i--) {
+                               if(responses[i]->status == 0) {
+                                       responses[i]->status = response_status;
+                                       responses[i]->error = response_error;
+
+                                       // Do not alloc a new response
+                                       if(index == -1)
+                                               index = i;
+                               }
+                       }
+               }
+
+               // Alloc a new response
+               if(index == -1) {
+                       // Index is the request index in the requests array
+                       index = responses_count;
+                       // Count is the total count of requests in the array
+                       count = index + 1;
+
+                       // Alloc the array with the new size
+                       *responses_p = malloc(sizeof(struct at_response *) * count);
+
+                       // Copy and free previous data
+                       if(responses != NULL) {
+                               memcpy(*responses_p, responses, sizeof(struct at_response *) * responses_count);
+                               free(responses);
+                       }
+
+                       responses = *responses_p;
+
+                       // Alloc new structure and copy obtained data
+                       responses[index] = calloc(1, sizeof(struct at_response));
+                       responses[index]->status = response_status;
+                       responses[index]->error = response_error;
+               }
+       } else {
+               // Parse command and data
+               response_data_length = at_line_parse_command_data(data, length, &response_command, &response_data);
+
+               if(response_command == NULL) {
+                       LOGE("Failed to parse command!");
+                       return responses_count;
+               }
+
+               at_string_clean(response_command, strlen(response_command) + 1);
+
+               if(responses_count > 0 && responses != NULL) {
+                       for(i=responses_count-1 ; i >= 0; i--) {
+                               if(responses[i]->command != NULL) {
+                                       if(strcmp(responses[i]->command, response_command) == 0)
+                                               // Do not alloc a new response
+                                               index = i;
+                               }
+                       }
+               }
+
+               // Alloc a new response
+               if(index == -1) {
+                       // Index is the request index in the requests array
+                       index = responses_count;
+                       // Count is the total count of requests in the array
+                       count = index + 1;
+
+                       // Alloc the array with the new size
+                       *responses_p = malloc(sizeof(struct at_response *) * count);
+
+                       // Copy and free previous data
+                       if(responses != NULL) {
+                               memcpy(*responses_p, responses, sizeof(struct at_response *) * responses_count);
+                               free(responses);
+                       }
+
+                       responses = *responses_p;
+
+                       // Alloc new structure and copy obtained data
+                       responses[index] = calloc(1, sizeof(struct at_response));
+                       responses[index]->command = response_command;
+               }
+
+               if(response_data_length > 0 && response_data != NULL) {
+                       at_string_clean(response_data, response_data_length);
+
+                       response_previous_data = responses[index]->data;
+
+                       // Data count is the total count of elements in the request data
+                       response_data_count = responses[index]->data_count + 1;
+
+                       // Alloc the array with the new size
+                       responses[index]->data = malloc(sizeof(char *) * response_data_count);
+
+                       // Copy and free previous data
+                       if(response_previous_data != NULL) {
+                               memcpy(responses[index]->data, response_previous_data, sizeof(char *) * responses[index]->data_count);
+                               free(response_previous_data);
+                       }
+
+                       responses[index]->data_count = response_data_count;
+                       responses[index]->data[response_data_count - 1] = response_data;
+               }
+       }
+
+       return count > responses_count ? count : responses_count;
+}
+
+int at_responses_process(struct at_response ***responses_p, char *data, int length)
+{
+       int responses_count = 0;
+       int count = 0;
+
+       char *string = NULL;
+       int string_length = 0;
+       int mark = 0;
+       int i;
+
+       if(responses_p == NULL || data == NULL || length < 0) {
+               LOGE("Failed to process AT response: wrong arguments!");
+               return 0;
+       }
+
+       for(i=0 ; i < length ; i++) {
+               if(data[i] == '\r' || data[i] == ';') {
+                       if(i - mark > 0) {
+                               data[i] = '\0';
+
+                               string_length = i - mark + 1;
+                               string = strndup(data + mark, string_length);
+
+                               if(!isprint(string[0])) {
+                                       free(string);
+                               }
+                               else {
+                                       count = at_responses_process_line(responses_p, responses_count, string, string_length);
+                                       if(count > responses_count)
+                                               responses_count = count;
+
+                                       free(string);
+                               }
+
+                               mark = i + 1;
+                       }
+
+                       while(isspace(data[i+1])) {
+                               mark = i + 2;
+                               i++;
+                       }
+               }
+       }
+
+       if(length - mark > 0) {
+               for(i=mark ; i < length ; i++)
+                       if(!isprint(data[i]))
+                               break;
+
+               if(i - mark > 0) {
+                       string_length = i - mark + 1;
+                       string = calloc(1, string_length);
+
+                       memcpy(string, data + mark, string_length - 1);
+                       string[string_length - 1] = '\0';
+
+                       count = at_responses_process_line(responses_p, responses_count, string , string_length);
+                       if(count > responses_count)
+                               responses_count = count;
+
+                       free(string);
+               }
+       }
+
+       return responses_count;
+}
+
+void at_responses_dump(struct at_response **responses, int responses_count)
+{
+       int i;
+
+       LOGD("== AT RESPONSES DUMP ==\n");
+       for(i=0 ; i < responses_count ; i++) {
+               LOGD("#%d command=%s status=%d error=%s\n", i, responses[i]->command, responses[i]->status, responses[i]->error);
+               if(responses[i]->data_count > 0) {
+                       int j;
+                       for(j=0 ; j < responses[i]->data_count ; j++) {
+                               LOGD("    #%d data='%s'\n", j, responses[i]->data[j]);
+                       }
+               }
+       }
+}
+
+void at_responses_free(struct at_response **responses, int responses_count)
+{
+       int i, j;
+
+       for(i=0 ; i < responses_count ; i++) {
+               if(responses[i]->data_count > 0 && responses[i]->data != NULL) {
+                       for(j=0 ; j < responses[i]->data_count ; j++) {
+                               if(responses[i]->data[j] != NULL) {
+                                       free(responses[i]->data[j]);
+                                       responses[i]->data[j] = NULL;
+                               }
+                       }
+
+                       free(responses[i]->data);
+                       responses[i]->data_count = 0;
+               }
+
+               if(responses[i]->command != NULL) {
+                       free(responses[i]->command);
+                       responses[i]->command = NULL;
+               }
+
+               if(responses[i]->error != NULL) {
+                       free(responses[i]->error);
+                       responses[i]->error = NULL;
+               }
+       }
+}
diff --git a/at.h b/at.h
new file mode 100644 (file)
index 0000000..2a3d35f
--- /dev/null
+++ b/at.h
@@ -0,0 +1,43 @@
+/**
+ * This file is part of hayes-ril.
+ *
+ * Copyright (C) 2012 Paul Kocialkowski <contact@paulk.fr>
+ *
+ * Based on htcgeneric-ril, reference-ril:
+ * Copyright 2006-2011, htcgeneric-ril contributors
+ * Copyright 2006, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _HAYES_RIL_AT_H_
+#define _HAYES_RIL_AT_H_
+
+#define AT_PARSE_SINGLE_QUOTE  (1 << 0)
+#define AT_PARSE_DOUBLE_QUOTE  (1 << 1)
+
+struct at_response {
+       char *command;
+
+       char **data;
+       int data_count;
+
+       int status;
+       char *error;
+};
+
+int at_responses_process(struct at_response ***responses_p, char *data, int length);
+void at_responses_dump(struct at_response **responses, int responses_count);
+void at_responses_free(struct at_response **responses, int responses_count);
+
+#endif
index e4fd1f1..592bfdb 100644 (file)
--- a/device.c
+++ b/device.c
@@ -365,9 +365,11 @@ int ril_device_recv_poll(struct ril_device *ril_device_p)
 
 int ril_device_recv_loop(struct ril_device *ril_device_p)
 {
-       // TODO: AT message structure
-       char *data;
-       int count;
+       struct at_response **responses = NULL;
+       int responses_count = 0;
+
+       char *data = NULL;
+       int length = 0;
        int rc;
        int i;
 
@@ -380,26 +382,31 @@ int ril_device_recv_loop(struct ril_device *ril_device_p)
                                break;
                        }
 
-                       count = rc;
+                       length = rc;
 
-                       rc = ril_device_recv(ril_device_p, &data, count);
-                       if(rc < 0) {
+                       rc = ril_device_recv(ril_device_p, &data, length);
+                       if(rc <= 0) {
                                LOGE("Reading from transport recv failed!");
                                break;
                        }
-                       if(rc == 0) LOGE("WERE GOING MAD!!! READING 0 BYTES!!!"); //FIXME
+
+                       length = rc;
 
                        // Read works now
                        if(i != 5)
                                 i = 5;
 
-                       LOGD("GOT: '%s'", data);
+                       LOGD("Read: %d bytes", length);
+
+                       responses_count = at_responses_process(&responses, data, length);
+                       LOGD("Converted to %d AT structures", responses_count);
 
-                       //TODO: Convert AT string to AT structures
                        free(data);
 
                        //TODO: Dispatch AT structures with multi-message handling (dispatch each)
-                       //TODO: Free AT structures
+
+                       at_responses_dump(responses, responses_count);
+                       at_responses_free(responses, responses_count);
                }
 
                // When poll/read failed, close and reopen the device
index 1966ff6..4aeef63 100644 (file)
@@ -16,6 +16,8 @@
  * limitations under the License.
  */
 
+#include <at.h>
+
 #ifndef _HAYES_RIL_H_
 #define _HAYES_RIL_H_