Completed AT requests/responses support (queued sycn and async recv, send)
authorPaul Kocialkowski <contact@paulk.fr>
Sun, 22 Jul 2012 22:17:21 +0000 (00:17 +0200)
committerPaul Kocialkowski <contact@paulk.fr>
Sun, 22 Jul 2012 22:17:21 +0000 (00:17 +0200)
List of the precise changes introduced with this commit:
* Added responses handling engine
* Improved AT string utils
* Added status codes list
* Added responses queue, expect to function and expect to status recv
* Added various request utilities
* Added various send functions
* Added threaded dispatch loop
* Added log utils
* Added tag in ril device

Signed-off-by: Paul Kocialkowski <contact@paulk.fr>
Android.mk
at.c
at.h
device.c
hayes-ril.c
hayes-ril.h
utils.c [new file with mode: 0644]

index bfbd130..bea75ee 100644 (file)
@@ -20,7 +20,8 @@ include $(CLEAR_VARS)
 hayes_ril_files := \
        hayes-ril.c \
        at.c \
-       device.c
+       device.c \
+       utils.c
 
 ifeq ($(TARGET_DEVICE),gta04)
        hayes_ril_device_files := device/gta04/gta04.c
diff --git a/at.c b/at.c
index 8873a08..1a2b650 100644 (file)
--- a/at.c
+++ b/at.c
 
 #include <hayes-ril.h>
 
+/*
+ * Globals
+ */
+
+struct at_responses_handling at_responses_handling;
+
+/*
+ * Utilities
+ */
+
 int at_strings_compare(char *major, char *minor)
 {
-       if(strncmp(major, minor, strlen(major)) == 0)
+       int major_length = strlen(major);
+       int minor_length = strlen(minor);
+
+       // We can't check against the whole major string
+       if(major_length > minor_length)
+               return 0;
+
+       if(strncmp(major, minor, major_length) == 0)
                return 1;
        else
                return 0;
 }
 
+// Major is response string, minor is request (reference) string
+int at_commands_compare(char *major, char *minor)
+{
+       int rc;
+
+       // Assume major is corect
+
+       if(at_strings_compare(major, minor))
+               return 1;
+
+       // Compare without AT prefix
+       if(at_strings_compare("AT", minor) && !at_strings_compare("AT", major) && strlen(minor) > 2)
+               return at_strings_compare(major, minor + 2);
+
+       if(at_strings_compare("AT", major) && !at_strings_compare("AT", minor) && strlen(major) > 2)
+               return at_strings_compare(major + 2, minor);
+
+       return 0;
+}
+
 void at_string_clean(char *string, int length)
 {
        int i=0;
@@ -42,25 +79,59 @@ void at_string_clean(char *string, int length)
        }
 }
 
+int at_status_error(int status)
+{
+       switch(status) {
+               case AT_STATUS_CONNECT:
+                       return 0;
+               case AT_STATUS_OK:
+                       return 0;
+               default:
+                       return 1;
+       }
+}
+
+/*
+ * Line parsers
+ */
+
 int at_line_parse_status(char *data, int length, char **error_p)
 {
-       // TODO: defined rc, fill error_p
+       char *response_command = NULL;
+       char *response_error = NULL;
+       int response_error_length = 0;
 
        *error_p = NULL;
 
-       if(at_strings_compare("OK", data))
-               return 1;
+       if(at_strings_compare("OK", data)) {
+               *error_p = NULL;
+               return AT_STATUS_OK;
+       }
 
-       if(at_strings_compare("CONNECT", data))
-               return 2;
+       if(at_strings_compare("CONNECT", data)) {
+               *error_p = NULL;
+               return AT_STATUS_CONNECT;
+       }
 
-       if(at_strings_compare("ERROR", data))
-               return -1;
+       if(at_strings_compare("ERROR", data)) {
+               *error_p = NULL;
+               return AT_STATUS_ERROR;
+       }
+
+       if(at_strings_compare("+CME ERROR", data)) {
+               response_error_length = at_line_parse_command_data(data, length, &response_command, &response_error);
+               if(response_error != NULL && response_error_length > 0) {
+                       at_string_clean(response_error, strlen(response_error) + 1);
+                       *error_p = response_error;
+               }
 
-       // TODO: Other error codes
+               if(response_command != NULL)
+                       free(response_command);
 
-       // TODO: undefined magic
-       return 0;
+               return AT_STATUS_CME_ERROR;
+       }
+
+       return AT_STATUS_UNDEF;
 }
 
 int at_line_parse_command_data(char *data, int length, char **response_command, char **response_data)
@@ -143,6 +214,10 @@ int at_line_parse_command_data(char *data, int length, char **response_command,
        return 0;
 }
 
+/*
+ * Responses line processor
+ */
+
 int at_responses_process_line(struct at_response ***responses_p, int responses_count, char *data, int length)
 {
        struct at_response **responses = NULL;
@@ -170,10 +245,10 @@ int at_responses_process_line(struct at_response ***responses_p, int responses_c
        // Parse status
        response_status = at_line_parse_status(data, length, &response_error);
 
-       if(response_status != 0) {
+       if(response_status != AT_STATUS_UNDEF) {
                if(responses_count > 0 && responses != NULL) {
                        for(i=responses_count-1 ; i >= 0; i--) {
-                               if(responses[i]->status == 0) {
+                               if(responses[i]->status == AT_STATUS_UNDEF) {
                                        responses[i]->status = response_status;
                                        responses[i]->error = response_error;
 
@@ -221,7 +296,7 @@ int at_responses_process_line(struct at_response ***responses_p, int responses_c
                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)
+                                       if(at_commands_compare(responses[i]->command, response_command))
                                                // Do not alloc a new response
                                                index = i;
                                }
@@ -249,6 +324,7 @@ int at_responses_process_line(struct at_response ***responses_p, int responses_c
                        // Alloc new structure and copy obtained data
                        responses[index] = calloc(1, sizeof(struct at_response));
                        responses[index]->command = response_command;
+                       responses[index]->status = AT_STATUS_UNDEF;
                }
 
                if(response_data_length > 0 && response_data != NULL) {
@@ -276,6 +352,10 @@ int at_responses_process_line(struct at_response ***responses_p, int responses_c
        return count > responses_count ? count : responses_count;
 }
 
+/*
+ * Responses structures processing
+ */
+
 int at_responses_process(struct at_response ***responses_p, char *data, int length)
 {
        int responses_count = 0;
@@ -343,47 +423,623 @@ int at_responses_process(struct at_response ***responses_p, char *data, int leng
        return responses_count;
 }
 
-void at_responses_dump(struct at_response **responses, int responses_count)
+void at_response_free(struct at_response *response)
 {
        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]);
+       if(response->data_count > 0 && response->data != NULL) {
+               for(i=0 ; i < response->data_count ; i++) {
+                       if(response->data[i] != NULL) {
+                               free(response->data[i]);
+                               response->data[i] = NULL;
                        }
                }
+
+               free(response->data);
+               response->data_count = 0;
+       }
+
+       if(response->command != NULL) {
+               free(response->command);
+               response->command = NULL;
+       }
+
+       if(response->error != NULL) {
+               free(response->error);
+               response->error = NULL;
        }
 }
 
 void at_responses_free(struct at_response **responses, int responses_count)
 {
-       int i, j;
+       int i;
+
+       for(i=0 ; i < responses_count ; i++) {
+               at_response_free(responses[i]);
+       }
+}
+
+/*
+ * Responses handling
+ */
+
+void at_responses_handling_init(void)
+{
+       memset(&at_responses_handling, 0, sizeof(struct at_responses_handling));
+       pthread_mutex_init(&(at_responses_handling.sync_mutex), NULL);
+
+       // First lock
+       AT_SYNC_LOCK();
+       AT_RESPONSES_LOCK();
+}
+
+void at_responses_handling_sync_clean(void)
+{
+       if(at_responses_handling.sync_request != NULL) {
+               at_request_free(at_responses_handling.sync_request);
+               at_responses_handling.sync_request = NULL;
+       }
+
+       if(at_responses_handling.sync_response != NULL) {
+               at_response_free(at_responses_handling.sync_response);
+               at_responses_handling.sync_response = NULL;
+       }
+}
+
+/*
+ * Responses queue
+ */
+
+int at_response_queue(struct at_response *response)
+{
+       struct at_response **responses = NULL;
+       int index;
+       int count;
+
+       if(response == NULL) {
+               return -1;
+       }
+
+       // Index is the request index in the responses array
+       index = at_responses_handling.responses_queue.responses_count;
+       // Count is the total count of responses in the array
+       count = index + 1;
+
+       // Save the previous data pointer
+       responses = at_responses_handling.responses_queue.responses;
+
+       // Alloc the array with the new size
+       at_responses_handling.responses_queue.responses = malloc(sizeof(struct at_response *) * count);
+
+       // Copy and free previous data
+       if(responses != NULL) {
+               memcpy(at_responses_handling.responses_queue.responses, responses, sizeof(struct at_response *) * at_responses_handling.responses_queue.responses_count);
+               free(responses);
+       }
+
+       responses = at_responses_handling.responses_queue.responses;
+
+       // Alloc new structure and copy obtained data
+       responses[index] = response;
+
+       at_responses_handling.responses_queue.responses_count = count;
+
+       AT_RESPONSES_UNLOCK();
+
+       return 0;
+}
+
+struct at_response *at_response_dequeue(void)
+{
+       struct at_response *response = NULL;
+       struct at_response **responses = NULL;
+       int responses_count = 0;
+       int pos;
+       int i;
+
+       response = NULL;
+       responses = at_responses_handling.responses_queue.responses;
+       responses_count = at_responses_handling.responses_queue.responses_count;
+
+       if(responses_count <= 0 && responses == NULL) {
+               LOGE("No response queued, blocking!");
+               AT_RESPONSES_LOCK();
+
+               responses = at_responses_handling.responses_queue.responses;
+               responses_count = at_responses_handling.responses_queue.responses_count;
+       }
 
        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;
+               if(responses[i] != NULL) {
+                       response = responses[i];
+                       pos = i;
+                       break;
+               }
+       }
+
+       if(response == NULL) {
+               LOGD("Found no valid response, aborting!");
+               return NULL;
+       }
+
+       responses[pos] = NULL;
+
+       // Move the elements back
+       for(i=pos ; i < responses_count-1 ; i--) {
+               responses[i] = responses[i+1];
+       }
+
+       // Empty the latest element
+       if(pos != responses_count-1) {
+               responses[responses_count-1] = NULL;
+       }
+
+       at_responses_handling.responses_queue.responses_count--;
+
+       if(at_responses_handling.responses_queue.responses_count == 0) {
+               free(responses);
+               at_responses_handling.responses_queue.responses = NULL;
+       }
+
+       return response;
+}
+
+/*
+ * Expect to func
+ */
+
+void at_request_expect_to_func_release(struct at_async_request *request)
+{
+       struct at_async_request **requests = NULL;
+       int requests_count = 0;
+       int pos = -1;
+       int i;
+
+       if(request == NULL)
+               return;
+
+       requests = at_responses_handling.async_queue.requests;
+       requests_count = at_responses_handling.async_queue.requests_count;
+
+       for(i=0 ; i < requests_count ; i++) {
+               if(requests[i] == request) {
+                       pos = i;
+                       break;
+               }
+       }
+
+       if(pos == -1) {
+               LOGD("Found no matching request, aborting release!");
+               return;
+       }
+
+       if(requests[pos]->command != NULL)
+               free(requests[pos]->command);
+
+       free(requests[pos]);
+       requests[pos] = NULL;
+
+       // Move the elements back
+       for(i=pos ; i < requests_count-1 ; i--) {
+               requests[i] = requests[i+1];
+       }
+
+       // Empty the latest element
+       if(pos != requests_count-1) {
+               requests[requests_count-1] = NULL;
+       }
+
+       at_responses_handling.async_queue.requests_count--;
+
+       if(at_responses_handling.async_queue.requests_count == 0) {
+               free(requests);
+               at_responses_handling.async_queue.requests = NULL;
+       }
+}
+
+int at_request_expect_to_func(char *command, void *data, RIL_Token t, at_async_request_cb func)
+{
+       struct at_async_request **requests = NULL;
+       int index;
+       int count;
+
+       if(command == NULL || func == NULL) {
+               return -1;
+       }
+
+       AT_ASYNC_LOCK();
+
+       // Index is the request index in the requests array
+       index = at_responses_handling.async_queue.requests_count;
+       // Count is the total count of requests in the array
+       count = index + 1;
+
+       // Save the previous data pointer
+       requests = at_responses_handling.async_queue.requests;
+
+       // Alloc the array with the new size
+       at_responses_handling.async_queue.requests = malloc(sizeof(struct at_async_request *) * count);
+
+       // Copy and free previous data
+       if(requests != NULL) {
+               memcpy(at_responses_handling.async_queue.requests, requests, sizeof(struct at_async_request *) * at_responses_handling.async_queue.requests_count);
+               free(requests);
+       }
+
+       requests = at_responses_handling.async_queue.requests;
+
+       // Alloc new structure and copy obtained data
+       requests[index] = calloc(1, sizeof(struct at_async_request));
+       requests[index]->command = strdup(command);
+       requests[index]->data = data;
+       requests[index]->token = t;
+       requests[index]->func = func;
+
+       at_responses_handling.async_queue.requests_count = count;
+
+       LOGD("Registered async function for command: %s", command);
+
+       AT_ASYNC_UNLOCK();
+
+       return 0;
+}
+
+int at_response_expect_to_func(struct at_response *response)
+{
+       struct at_async_request *request = NULL;
+       struct at_async_request **requests = NULL;
+       int requests_count = 0;
+       int rc;
+       int i;
+
+       if(response == NULL)
+               return -1;
+
+       AT_ASYNC_LOCK();
+
+       request = NULL;
+       requests = at_responses_handling.async_queue.requests;
+       requests_count = at_responses_handling.async_queue.requests_count;
+
+       if(requests == NULL) {
+               AT_ASYNC_UNLOCK();
+               return -1;
+       }
+
+       for(i=0 ; i < requests_count ; i++) {
+               if(requests[i] != NULL) {
+                       if(requests[i]->command != NULL && response->command != NULL) {
+                               rc = at_commands_compare(response->command, requests[i]->command);
+                               if(rc) {
+                                       LOGD("Found a matching request!");
+                                       request = requests[i];
+                                       break;
                                }
                        }
-
-                       free(responses[i]->data);
-                       responses[i]->data_count = 0;
                }
+       }
+
+       if(request == NULL || request->func == NULL) {
+               LOGD("Found no valid matching request, aborting!");
+               AT_ASYNC_UNLOCK();
+               return -1;
+       }
+
+       AT_ASYNC_UNLOCK();
+
+       // This must run out of any mutex
+       rc = request->func(response, request->data, request->token);
+
+       AT_ASYNC_LOCK();
+       at_request_expect_to_func_release(request);
+       AT_ASYNC_UNLOCK();
+
+       return rc < 0 ? rc : 0;
+}
+
+/*
+ * Expect status
+ */
+
+void at_response_expect_status_release(void)
+{
+       AT_SYNC_UNLOCK();
+       at_responses_handling_sync_clean();
+       AT_SYNC_LOCK();
+}
+
+int at_request_expect_status(struct at_request *request)
+{
+       // Register the request
+
+       if(request == NULL)
+               return -1;
+
+       if(at_responses_handling.sync_request != NULL) {
+               LOGE("There is already an AT sync request waiting, aborting!");
+               return -1;
+       }
+
+       if(request->command == NULL) {
+               LOGE("No command was given, aborting!");
+               return -1;
+       }
+
+       at_responses_handling.sync_request = request;
 
-               if(responses[i]->command != NULL) {
-                       free(responses[i]->command);
-                       responses[i]->command = NULL;
+       return 0;
+}
+
+int at_response_expect_status(struct at_response *response)
+{
+       int rc;
+
+       if(response == NULL)
+               return -1;
+
+       if(at_responses_handling.sync_request == NULL) {
+               return -1;
+       }
+
+       if(response->status == AT_STATUS_UNDEF) {
+               // Only fail and clean if there is a status and command differs
+               return -1;
+       }
+
+       if(response->command != NULL) {
+               rc = at_commands_compare(response->command, at_responses_handling.sync_request->command);
+               if(!rc) {
+                       AT_SYNC_UNLOCK();
+                       return -1;
                }
+       }
+
+       at_responses_handling.sync_response = response;
 
-               if(responses[i]->error != NULL) {
-                       free(responses[i]->error);
-                       responses[i]->error = NULL;
+       AT_SYNC_UNLOCK();
+
+       return 0;
+}
+
+int at_expect_status(struct at_request *request)
+{
+       int rc;
+
+       if(request == NULL)
+               return -1;
+
+       // Second lock: block until sync_response is there
+       AT_SYNC_LOCK();
+
+       if(at_responses_handling.sync_response == NULL)
+               return -1;
+
+       if(at_responses_handling.sync_response->command != NULL) {
+               rc = at_commands_compare(at_responses_handling.sync_response->command, request->command);
+               if(!rc) {
+                       LOGE("Obtained command doesn't match, aborting!");
+                       return -1;
                }
        }
+
+       request->status = at_responses_handling.sync_response->status;
+       if(at_responses_handling.sync_response->error != NULL)
+               request->error = strdup(at_responses_handling.sync_response->error);
+
+       return 0;
+}
+
+/*
+ * Request
+ */
+
+int at_request_send(struct at_request *request)
+{
+       char *string = NULL;
+       int length = 0;
+
+       int offset_separator_begin = 0;
+       int length_separator_begin = 0;
+       int offset_command = 0;
+       int length_command = 0;
+       int offset_data_separator = 0;
+       int length_data_separator = 0;
+       int offset_data = 0;
+       int length_data = 0;
+       int offset_separator_end = 0;
+       int length_separator_end = 0;
+
+       int i, p;
+
+       // TODO: Do we send \r\n or only \r, begining/end or just end?
+       char separator[] = "\r\n";
+       char data_separator[] = "=";
+
+       if(request->command == NULL)
+               return -1;
+
+       offset_separator_begin = 0;
+       length_separator_begin = strlen(separator);
+       length += length_separator_begin;
+
+       offset_command = length;
+       length_command = strlen(request->command);
+       length += length_command;
+
+       if(request->data != NULL) {
+               offset_data_separator = length;
+               length_data_separator = strlen(data_separator);
+               length += length_data_separator;
+
+               offset_data = length;
+               length_data = strlen(request->data);
+               length += length_data;
+       }
+
+       offset_separator_end = length;
+       length_separator_end = strlen(separator);
+       length += length_separator_end;
+
+       // Alloc final string
+       string = calloc(1, length);
+
+       // Copy the data to the string
+       memcpy(string + offset_separator_begin, separator, length_separator_begin);
+       memcpy(string + offset_command, request->command, length_command);
+       if(request->data != NULL) {
+               memcpy(string + offset_data_separator, data_separator, length_data_separator);
+               memcpy(string + offset_data, request->data, length_data);
+       }
+       memcpy(string + offset_separator_end, separator, length_separator_end);
+
+       // Log request
+       ril_send_log(request);
+       ril_data_log(string, length);
+
+       ril_device_send(ril_device, string, length);
+
+       free(string);
+
+       return 0;
+}
+
+struct at_request *at_request_create(char *command, char *data)
+{
+       struct at_request *request = NULL;
+       int i;
+
+       if(command == NULL)
+               return NULL;
+
+       request = calloc(1, sizeof(struct at_request));
+
+       asprintf(&(request->command), "%s", command);
+
+       if(data != NULL) {
+               asprintf(&(request->data), "%s", data);
+       }
+
+       return request;
+}
+
+void at_request_free(struct at_request *request)
+{
+       int i;
+
+       if(request->command != NULL)
+               free(request->command);
+
+       if(request->data != NULL)
+               free(request->data);
+
+       if(request->error != NULL)
+               free(request->error);
+}
+
+/*
+ * Send
+ */
+
+int at_send(char *command, char *data)
+{
+       struct at_request *request = NULL;
+       int rc;
+
+       request = at_request_create(command, data);
+       if(request == NULL) {
+               LOGE("Unable to create request, aborting!");
+               return -1;
+       }
+
+       rc = at_request_send(request);
+       if(rc < 0) {
+               LOGE("Unable to send request, aborting!");
+               at_request_free(request);
+               return -1;
+       }
+
+       at_request_free(request);
+
+       return 0;
+}
+
+int at_send_command(char *command)
+{
+       return at_send(command, NULL);
+}
+
+int at_send_expect_status(char *command, char *data)
+{
+       struct at_request *request = NULL;
+       int status;
+       int rc;
+
+       if(command == NULL)
+               return -1;
+
+       request = at_request_create(command, data);
+       if(request == NULL) {
+               LOGE("Unable to create request, aborting!");
+               at_response_expect_status_release();
+               return AT_STATUS_INTERNAL_ERROR;
+       }
+
+       rc = at_request_send(request);
+       if(rc < 0) {
+               LOGE("Unable to send request, aborting!");
+               at_response_expect_status_release();
+               return AT_STATUS_INTERNAL_ERROR;
+       }
+
+       rc = at_request_expect_status(request);
+       if(rc < 0) {
+               LOGE("Unable to request expect status, aborting!");
+               at_response_expect_status_release();
+               return AT_STATUS_INTERNAL_ERROR;
+       }
+
+       // Block here
+       rc = at_expect_status(request);
+       if(rc < 0) {
+               LOGE("Unable to get expected status, aborting!");
+               at_response_expect_status_release();
+               return AT_STATUS_INTERNAL_ERROR;
+       }
+
+       status = request->status;
+
+       // Release sync structures and lock the mutex
+       at_response_expect_status_release();
+
+       return status;
+}
+
+int at_send_expect_to_func(char *command, char *data, void *pdata, RIL_Token t, at_async_request_cb func)
+{
+       struct at_request *request = NULL;
+       int rc;
+
+       if(command == NULL)
+               return -1;
+
+       request = at_request_create(command, data);
+       if(request == NULL) {
+               LOGE("Unable to create request, aborting!");
+               return -1;
+       }
+
+       rc = at_request_expect_to_func(command, pdata, t, func);
+       if(rc < 0) {
+               LOGE("Unable to request expect to func, aborting!");
+               at_request_free(request);
+       }
+
+       rc = at_request_send(request);
+       if(rc < 0) {
+               LOGE("Unable to send request, aborting!");
+               at_request_free(request);
+               return -1;
+       }
+
+       return 0;
 }
diff --git a/at.h b/at.h
index 4f4b35c..97c545a 100644 (file)
--- a/at.h
+++ b/at.h
  * limitations under the License.
  */
 
+#include <telephony/ril.h>
+
 #ifndef _HAYES_RIL_AT_H_
 #define _HAYES_RIL_AT_H_
 
+#define AT_SYNC_LOCK() pthread_mutex_lock(&(at_responses_handling.sync_mutex));
+#define AT_SYNC_UNLOCK() pthread_mutex_unlock(&(at_responses_handling.sync_mutex));
+#define AT_ASYNC_LOCK() pthread_mutex_lock(&(at_responses_handling.async_mutex));
+#define AT_ASYNC_UNLOCK() pthread_mutex_unlock(&(at_responses_handling.async_mutex));
+#define AT_RESPONSES_LOCK() pthread_mutex_lock(&(at_responses_handling.responses_queue_mutex));
+#define AT_RESPONSES_UNLOCK() pthread_mutex_unlock(&(at_responses_handling.responses_queue_mutex));
+
 #define AT_PARSE_SINGLE_QUOTE  (1 << 0)
 #define AT_PARSE_DOUBLE_QUOTE  (1 << 1)
 
@@ -32,8 +41,91 @@ struct at_response {
        char *error;
 };
 
+struct at_request {
+       char *command;
+       char *data;
+
+       int status;
+       char *error;
+};
+
+enum {
+       AT_STATUS_UNDEF,
+       AT_STATUS_OK,
+       AT_STATUS_CONNECT,
+       AT_STATUS_ERROR,
+       AT_STATUS_CME_ERROR,
+       AT_STATUS_INTERNAL_ERROR,
+};
+
+struct at_responses_queue {
+       struct at_response **responses;
+       int responses_count;
+};
+
+typedef int (*at_async_request_cb)(struct at_response *response, void *data, RIL_Token t);
+
+struct at_async_request {
+       char *command;
+       void *data;
+       RIL_Token token;
+
+       at_async_request_cb func;
+};
+
+struct at_async_queue {
+       struct at_async_request **requests;
+       int requests_count;
+};
+
+struct at_responses_handling {
+       struct at_responses_queue responses_queue;
+       pthread_mutex_t responses_queue_mutex;
+
+       struct at_async_queue async_queue;
+       pthread_mutex_t async_mutex;
+
+       struct at_request *sync_request;
+       struct at_response *sync_response;
+       pthread_mutex_t sync_mutex;
+};
+
+// Utilities
+int at_strings_compare(char *major, char *minor);
+int at_commands_compare(char *major, char *minor);
+void at_string_clean(char *string, int length);
+int at_status_error(int status);
+
+// Responses structures processing
 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_response_free(struct at_response *response);
 void at_responses_free(struct at_response **responses, int responses_count);
 
+// Responses handling
+void at_responses_handling_init(void);
+
+// Responses queue
+int at_response_queue(struct at_response *response);
+struct at_response *at_response_dequeue(void);
+
+// Expect to func
+int at_request_expect_to_func(char *command, void *data, RIL_Token t, at_async_request_cb func);
+int at_response_expect_to_func(struct at_response *response);
+
+// Expect status
+int at_request_expect_status(struct at_request *request);
+int at_response_expect_status(struct at_response *response);
+int at_expect_status(struct at_request *request);
+
+// Request
+int at_request_send(struct at_request *request);
+struct at_request *at_request_create(char *command, char *data);
+void at_request_free(struct at_request *request);
+
+// Send
+int at_send(char *command, char *data);
+int at_send_command(char *command);
+int at_send_expect_status(char *command, char *data);
+int at_send_expect_to_func(char *command, char *data, void *pdata, RIL_Token t, at_async_request_cb func);
+
 #endif
index f061af3..74b5707 100644 (file)
--- a/device.c
+++ b/device.c
@@ -371,7 +371,7 @@ int ril_device_recv_loop(struct ril_device *ril_device_p)
        char *data = NULL;
        int length = 0;
        int rc;
-       int i;
+       int i, j;
 
        // Return error after 5 consecutive poll/read failures
        for(i=5 ; i > 0 ; i--) {
@@ -385,7 +385,7 @@ int ril_device_recv_loop(struct ril_device *ril_device_p)
                        length = rc;
 
                        rc = ril_device_recv(ril_device_p, &data, length);
-                       if(rc <= 0) {
+                       if(rc <= 0 || data == NULL) {
                                LOGE("Reading from transport recv failed!");
                                break;
                        }
@@ -396,17 +396,27 @@ int ril_device_recv_loop(struct ril_device *ril_device_p)
                        if(i != 5)
                                 i = 5;
 
-                       LOGD("Read: %d bytes", length);
-
                        responses_count = at_responses_process(&responses, data, length);
-                       LOGD("Converted to %d AT structures", responses_count);
 
-                       free(data);
+                       // Good way to give the recv loop some sleep: call at_send_expect_status
+                       for(j=0 ; j < responses_count ; j++) {
+                               // Log response
+                               ril_recv_log(responses[j]);
+
+                               rc = at_response_expect_status(responses[j]);
+                               if(rc < 0) {
+                                       rc = at_response_queue(responses[j]);
+                                       if(rc < 0) {
+                                               LOGE("Failed to queue response, clearing!");
+                                               at_response_free(responses[j]);
+                                       }
+                               }
+                       }
 
-                       //TODO: Dispatch AT structures with multi-message handling (dispatch each)
+                       // Log response
+                       ril_data_log(data, length);
 
-                       at_responses_dump(responses, responses_count);
-                       at_responses_free(responses, responses_count);
+                       free(data);
                }
 
                // When poll/read failed, close and reopen the device
index 8690854..d37ea47 100644 (file)
 #define RIL_VERSION_STRING "Hayes RIL"
 
 struct ril_device *ril_device;
+pthread_t ril_dispatch_thread;
+
+struct ril_dispatch_unsol ril_dispatch_unsol[] = {};
+
+int ril_dispatch_unsol_count = sizeof(ril_dispatch_unsol) / sizeof(struct ril_dispatch_unsol);
 
 const char *ril_get_version(void)
 {
@@ -40,10 +45,74 @@ static const RIL_RadioFunctions ril_ops = {
        ril_get_version
 };
 
+void *ril_dispatch(void *data)
+{
+       struct at_response *response = NULL;
+       int rc;
+       int i, j;
+
+       for(i = 5 ; i > 0 ; i--) {
+               while(1) {
+                       response = at_response_dequeue();
+                       if(response == NULL)
+                               break;
+
+                       if(response->command == NULL) {
+                               at_response_free(response);
+                               continue;
+                       }
+
+                       // Dequeue works now
+                       if(i != 5)
+                               i = 5;
+
+                       rc = at_response_expect_to_func(response);
+                       if(rc < 0) {
+                               for(j=0 ; j < ril_dispatch_unsol_count ; j++) {
+                                       if(at_commands_compare(response->command, ril_dispatch_unsol[j].command) && ril_dispatch_unsol[j].func != NULL) {
+                                               ril_dispatch_unsol[j].func(response);
+                                       }
+                               }
+                       }
+
+                       at_response_free(response);
+               }
+
+               LOGE("RIL dispatch failed, retrying!");
+       }
+
+       LOGE("RIL dispatch failed too many times, aborting");
+
+       return NULL;
+}
+
+int ril_dispatch_thread_start(void)
+{
+       pthread_attr_t attr;
+       int rc;
+
+       pthread_attr_init(&attr);
+       pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
+
+       rc = pthread_create(&ril_dispatch_thread, &attr, ril_dispatch, NULL);
+       if(rc != 0) {
+               LOGE("Creating dispatch thread failed!");
+               return -1;
+       }
+
+       return 0;
+}
+
+void ril_globals_init(void)
+{
+       at_responses_handling_init();
+}
+
 const RIL_RadioFunctions *RIL_Init(const struct RIL_Env *env, int argc, char **argv)
 {
        int rc;
 
+       ril_globals_init();
        ril_device_register(&ril_device);
 
        LOGD("Starting %s for device: %s", RIL_VERSION_STRING, ril_device->name);
@@ -64,6 +133,14 @@ const RIL_RadioFunctions *RIL_Init(const struct RIL_Env *env, int argc, char **a
                return NULL;
        }
 
+       rc = ril_dispatch_thread_start();
+       if(rc < 0) {
+               LOGE("Failed to start dispatch thread!");
+               ril_device_deinit(ril_device);
+
+               return NULL;
+       }
+
        LOGD("Initialization complete");
 
        return &ril_ops;
index 00dcf08..09471b8 100644 (file)
@@ -72,12 +72,18 @@ struct ril_device_handlers {
 
 struct ril_device {
        char *name;
+       char *tag;
        void *sdata;
 
        enum ril_device_type type;
        struct ril_device_handlers *handlers;
 };
 
+struct ril_dispatch_unsol {
+       char *command;
+       void (*func)(struct at_response *response);
+};
+
 // Device
 void ril_device_register(struct ril_device **ril_device_p);
 int ril_device_init(struct ril_device *ril_device_p);
@@ -96,4 +102,10 @@ int ril_device_recv(struct ril_device *ril_device_p, void **data, int length);
 int ril_device_recv_poll(struct ril_device *ril_device_p);
 int ril_device_recv_thread_start(struct ril_device *ril_device_p);
 
+// Utils
+char *ril_status_string(int status);
+void ril_recv_log(struct at_response *response);
+void ril_send_log(struct at_request *request);
+void ril_data_log(char *data, int length);
+
 #endif
diff --git a/utils.c b/utils.c
new file mode 100644 (file)
index 0000000..1cfbb61
--- /dev/null
+++ b/utils.c
@@ -0,0 +1,170 @@
+/*
+ * This file is part of hayes-ril.
+ *
+ * Copyright (C) 2012 Paul Kocialkowski <contact@paulk.fr>
+ *
+ * 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"
+#include <utils/Log.h>
+
+#include <hayes-ril.h>
+
+char *ril_status_string(int status)
+{
+       switch(status) {
+               case AT_STATUS_OK:
+                       return "OK";
+               case AT_STATUS_CONNECT:
+                       return "CONNECT";
+               case AT_STATUS_ERROR:
+                       return "ERROR";
+               case AT_STATUS_CME_ERROR:
+                       return "CME_ERROR";
+               case AT_STATUS_INTERNAL_ERROR:
+                       return "INTERNAL_ERROR";
+               case AT_STATUS_UNDEF:
+               default:
+                       return "UNDEF";
+       }
+}
+
+void ril_recv_log(struct at_response *response)
+{
+       char *data_string = NULL;
+       char *status_string = NULL;
+       char *error_string = NULL;
+       char *string = NULL;
+       int i;
+
+       if(response->data_count > 0 && response->data != NULL) {
+               for(i=0 ; i < response->data_count ; i++) {
+                       if(i == 0) {
+                               asprintf(&data_string, "%s", response->data[i]);
+                       } else {
+                               string = data_string;
+                               asprintf(&data_string, "%s, %s", string, response->data[i]);
+                               free(string);
+                       }
+               }
+       }
+
+       if(response->status != AT_STATUS_UNDEF)
+               status_string = ril_status_string(response->status);
+
+       if(response->error != NULL)
+               error_string = response->error;
+
+       if(data_string && status_string && error_string)
+               asprintf(&string, "%s: AT RECV [\"%s\", {%s}, %s, %s]", ril_device->tag, response->command, data_string, status_string, error_string);
+       else if(data_string && status_string)
+               asprintf(&string, "%s: AT RECV [\"%s\", {%s}, %s]", ril_device->tag, response->command, data_string, status_string);
+       else if(data_string && !status_string)
+               asprintf(&string, "%s: AT RECV [\"%s\", {%s}]", ril_device->tag, response->command, data_string);
+       else if(!data_string && status_string && error_string)
+               asprintf(&string, "%s: AT RECV [\"%s\", %s, %s]", ril_device->tag, response->command, status_string, error_string);
+       else if(!data_string && status_string)
+               asprintf(&string, "%s: AT RECV [\"%s\", %s]", ril_device->tag, response->command, status_string);
+       else
+               asprintf(&string, "%s: AT RECV [\"%s\"]", ril_device->tag, response->command);
+
+       if(data_string)
+               free(data_string);
+
+       LOGD("\n");
+       LOGD("%s", string);
+       free(string);
+}
+
+void ril_send_log(struct at_request *request)
+{
+       char *data_string = NULL;
+       char *string = NULL;
+       int i;
+
+       if(request->data != NULL)
+               data_string = (char *) request->data;
+
+       if(data_string)
+               asprintf(&string, "%s: AT SEND [\"%s\", {%s}]", ril_device->tag, request->command, data_string);
+       else
+               asprintf(&string, "%s: AT SEND [\"%s\"]", ril_device->tag, request->command);
+
+       LOGD("\n");
+       LOGD("%s", string);
+       free(string);
+}
+
+void hex_dump(void *data, int size)
+{
+       /* dumps size bytes of *data to stdout. Looks like:
+        * [0000] 75 6E 6B 6E 6F 77 6E 20
+        *                                30 FF 00 00 00 00 39 00 unknown 0.....9.
+        * (in a single line of course)
+        */
+
+       unsigned char *p = data;
+       unsigned char c;
+       int n;
+       char bytestr[4] = {0};
+       char addrstr[10] = {0};
+       char hexstr[ 16*3 + 5] = {0};
+       char charstr[16*1 + 5] = {0};
+       for(n=1;n<=size;n++) {
+               if (n%16 == 1) {
+                       /* store address for this line */
+                       snprintf(addrstr, sizeof(addrstr), "%.4x",
+                          ((unsigned int)p-(unsigned int)data) );
+               }
+
+               c = *p;
+               if (isalnum(c) == 0) {
+                       c = '.';
+               }
+
+               /* store hex str (for left side) */
+               snprintf(bytestr, sizeof(bytestr), "%02X ", *p);
+               strncat(hexstr, bytestr, sizeof(hexstr)-strlen(hexstr)-1);
+
+               /* store char str (for right side) */
+               snprintf(bytestr, sizeof(bytestr), "%c", c);
+               strncat(charstr, bytestr, sizeof(charstr)-strlen(charstr)-1);
+
+               if(n%16 == 0) {
+                       /* line completed */
+                       LOGD("[%4.4s]   %-50.50s  %s", addrstr, hexstr, charstr);
+                       hexstr[0] = 0;
+                       charstr[0] = 0;
+               } else if(n%8 == 0) {
+                       /* half line: add whitespaces */
+                       strncat(hexstr, "  ", sizeof(hexstr)-strlen(hexstr)-1);
+                       strncat(charstr, " ", sizeof(charstr)-strlen(charstr)-1);
+               }
+               p++; /* next byte */
+       }
+
+       if (strlen(hexstr) > 0) {
+               /* print rest of buffer if not empty */
+               LOGD("[%4.4s]   %-50.50s  %s\n", addrstr, hexstr, charstr);
+       }
+}
+
+void ril_data_log(char *data, int length)
+{
+       LOGD("%s: ==== DATA DUMP: %d BYTES ====", ril_device->tag, length);
+       hex_dump(data, length);
+}