diff options
Diffstat (limited to 'guest/hals/ril/reference-ril/atchannel.c')
-rw-r--r-- | guest/hals/ril/reference-ril/atchannel.c | 972 |
1 files changed, 0 insertions, 972 deletions
diff --git a/guest/hals/ril/reference-ril/atchannel.c b/guest/hals/ril/reference-ril/atchannel.c deleted file mode 100644 index 6ea051fff..000000000 --- a/guest/hals/ril/reference-ril/atchannel.c +++ /dev/null @@ -1,972 +0,0 @@ -/* //device/system/reference-ril/atchannel.c -** -** 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 "atchannel.h" -#include "at_tok.h" - -#include <stdio.h> -#include <string.h> -#include <pthread.h> -#include <ctype.h> -#include <stdlib.h> -#include <errno.h> -#include <fcntl.h> -#include <sys/time.h> -#include <time.h> -#include <unistd.h> - -#define LOG_NDEBUG 0 -#define LOG_TAG "AT" -#include <utils/Log.h> - -#include "misc.h" - - -#define NUM_ELEMS(x) (sizeof(x)/sizeof((x)[0])) - -#define MAX_AT_RESPONSE (8 * 1024) -#define HANDSHAKE_RETRY_COUNT 8 -#define HANDSHAKE_TIMEOUT_MSEC 250 - -static pthread_t s_tid_reader; -static int s_fd = -1; /* fd of the AT channel */ -static ATUnsolHandler s_unsolHandler; - -/* for input buffering */ - -static char s_ATBuffer[MAX_AT_RESPONSE+1]; -static char *s_ATBufferCur = s_ATBuffer; - -#if AT_DEBUG -void AT_DUMP(const char* prefix __unused, const char* buff, int len) -{ - if (len < 0) - len = strlen(buff); - RLOGD("%.*s", len, buff); -} -#endif - -/* - * There is one reader thread |s_tid_reader| and potentially multiple writer - * threads. |s_commandmutex| and |s_commandcond| are used to maintain the - * condition that the writer thread will not read from |sp_response| until the - * reader thread has signaled itself is finished, etc. |s_writeMutex| is used to - * prevent multiple writer threads from calling at_send_command_full_nolock - * function at the same time. - */ - -static pthread_mutex_t s_commandmutex = PTHREAD_MUTEX_INITIALIZER; -static pthread_cond_t s_commandcond = PTHREAD_COND_INITIALIZER; -static pthread_mutex_t s_writeMutex = PTHREAD_MUTEX_INITIALIZER; - -static ATCommandType s_type; -static const char *s_responsePrefix = NULL; -static const char *s_smsPDU = NULL; -static ATResponse *sp_response = NULL; - -static void (*s_onTimeout)(void) = NULL; -static void (*s_onReaderClosed)(void) = NULL; -static int s_readerClosed; - -static void onReaderClosed(); -static int writeCtrlZ (const char *s); -static int writeline (const char *s); - -#define NS_PER_S 1000000000 -static void setTimespecRelative(struct timespec *p_ts, long long msec) -{ - struct timeval tv; - - gettimeofday(&tv, (struct timezone *) NULL); - - p_ts->tv_sec = tv.tv_sec + (msec / 1000); - p_ts->tv_nsec = (tv.tv_usec + (msec % 1000) * 1000L ) * 1000L; - /* assuming tv.tv_usec < 10^6 */ - if (p_ts->tv_nsec >= NS_PER_S) { - p_ts->tv_sec++; - p_ts->tv_nsec -= NS_PER_S; - } -} - -static void sleepMsec(long long msec) -{ - struct timespec ts; - int err; - - ts.tv_sec = (msec / 1000); - ts.tv_nsec = (msec % 1000) * 1000 * 1000; - - do { - err = nanosleep (&ts, &ts); - } while (err < 0 && errno == EINTR); -} - - - -/** add an intermediate response to sp_response*/ -static void addIntermediate(const char *line) -{ - ATLine *p_new; - - p_new = (ATLine *) malloc(sizeof(ATLine)); - - p_new->line = strdup(line); - - /* note: this adds to the head of the list, so the list - will be in reverse order of lines received. the order is flipped - again before passing on to the command issuer */ - p_new->p_next = sp_response->p_intermediates; - sp_response->p_intermediates = p_new; -} - - -/** - * returns 1 if line is a final response indicating error - * See 27.007 annex B - * WARNING: NO CARRIER and others are sometimes unsolicited - */ -static const char * s_finalResponsesError[] = { - "ERROR", - "+CMS ERROR:", - "+CME ERROR:", - "NO CARRIER", /* sometimes! */ - "NO ANSWER", - "NO DIALTONE", -}; -static int isFinalResponseError(const char *line) -{ - size_t i; - - for (i = 0 ; i < NUM_ELEMS(s_finalResponsesError) ; i++) { - if (strStartsWith(line, s_finalResponsesError[i])) { - return 1; - } - } - - return 0; -} - -/** - * returns 1 if line is a final response indicating success - * See 27.007 annex B - * WARNING: NO CARRIER and others are sometimes unsolicited - */ -static const char * s_finalResponsesSuccess[] = { - "OK", - "CONNECT" /* some stacks start up data on another channel */ -}; -static int isFinalResponseSuccess(const char *line) -{ - size_t i; - - for (i = 0 ; i < NUM_ELEMS(s_finalResponsesSuccess) ; i++) { - if (strStartsWith(line, s_finalResponsesSuccess[i])) { - return 1; - } - } - - return 0; -} - -/** - * returns 1 if line is a final response, either error or success - * See 27.007 annex B - * WARNING: NO CARRIER and others are sometimes unsolicited - */ -static int isFinalResponse(const char *line) -{ - return isFinalResponseSuccess(line) || isFinalResponseError(line); -} - - -/** - * returns 1 if line is the first line in (what will be) a two-line - * SMS unsolicited response - */ -static const char * s_smsUnsoliciteds[] = { - "+CMT:", - "+CDS:", - "+CBM:" -}; -static int isSMSUnsolicited(const char *line) -{ - size_t i; - - for (i = 0 ; i < NUM_ELEMS(s_smsUnsoliciteds) ; i++) { - if (strStartsWith(line, s_smsUnsoliciteds[i])) { - return 1; - } - } - - return 0; -} - - -/** assumes s_commandmutex is held */ -static void handleFinalResponse(const char *line) -{ - sp_response->finalResponse = strdup(line); - - pthread_cond_signal(&s_commandcond); -} - -static void handleUnsolicited(const char *line) -{ - if (s_unsolHandler != NULL) { - s_unsolHandler(line, NULL); - } -} - -static void processLine(const char *line) -{ - pthread_mutex_lock(&s_commandmutex); - - if (sp_response == NULL) { - /* no command pending */ - handleUnsolicited(line); - } else if (isFinalResponseSuccess(line)) { - sp_response->success = 1; - handleFinalResponse(line); - } else if (isFinalResponseError(line)) { - sp_response->success = 0; - handleFinalResponse(line); - } else if (s_smsPDU != NULL && 0 == strcmp(line, "> ")) { - // See eg. TS 27.005 4.3 - // Commands like AT+CMGS have a "> " prompt - writeCtrlZ(s_smsPDU); - s_smsPDU = NULL; - } else switch (s_type) { - case NO_RESULT: - handleUnsolicited(line); - break; - case NUMERIC: - if (sp_response->p_intermediates == NULL - && isdigit(line[0]) - ) { - addIntermediate(line); - } else { - /* either we already have an intermediate response or - the line doesn't begin with a digit */ - handleUnsolicited(line); - } - break; - case SINGLELINE: - if (sp_response->p_intermediates == NULL - && strStartsWith (line, s_responsePrefix) - ) { - addIntermediate(line); - } else { - /* we already have an intermediate response */ - handleUnsolicited(line); - } - break; - case MULTILINE: - if (strStartsWith (line, s_responsePrefix)) { - addIntermediate(line); - } else { - handleUnsolicited(line); - } - break; - - default: /* this should never be reached */ - RLOGE("Unsupported AT command type %d\n", s_type); - handleUnsolicited(line); - break; - } - - pthread_mutex_unlock(&s_commandmutex); -} - - -/** - * Returns a pointer to the end of the next line - * special-cases the "> " SMS prompt - * - * returns NULL if there is no complete line - */ -static char * findNextEOL(char *cur) -{ - if (cur[0] == '>' && cur[1] == ' ' && cur[2] == '\0') { - /* SMS prompt character...not \r terminated */ - return cur+2; - } - - // Find next newline - while (*cur != '\0' && *cur != '\r' && *cur != '\n') cur++; - - return *cur == '\0' ? NULL : cur; -} - - -/** - * Reads a line from the AT channel, returns NULL on timeout. - * Assumes it has exclusive read access to the FD - * - * This line is valid only until the next call to readline - * - * This function exists because as of writing, android libc does not - * have buffered stdio. - */ - -static const char *readline() -{ - ssize_t count; - - char *p_read = NULL; - char *p_eol = NULL; - char *ret; - - /* this is a little odd. I use *s_ATBufferCur == 0 to - * mean "buffer consumed completely". If it points to a character, than - * the buffer continues until a \0 - */ - if (*s_ATBufferCur == '\0') { - /* empty buffer */ - s_ATBufferCur = s_ATBuffer; - *s_ATBufferCur = '\0'; - p_read = s_ATBuffer; - } else { /* *s_ATBufferCur != '\0' */ - /* there's data in the buffer from the last read */ - - // skip over leading newlines - while (*s_ATBufferCur == '\r' || *s_ATBufferCur == '\n') - s_ATBufferCur++; - - p_eol = findNextEOL(s_ATBufferCur); - - if (p_eol == NULL) { - /* a partial line. move it up and prepare to read more */ - size_t len; - - len = strlen(s_ATBufferCur); - - memmove(s_ATBuffer, s_ATBufferCur, len + 1); - p_read = s_ATBuffer + len; - s_ATBufferCur = s_ATBuffer; - } - /* Otherwise, (p_eol !- NULL) there is a complete line */ - /* that will be returned the while () loop below */ - } - - while (p_eol == NULL) { - if (0 == MAX_AT_RESPONSE - (p_read - s_ATBuffer)) { - RLOGE("ERROR: Input line exceeded buffer\n"); - /* ditch buffer and start over again */ - s_ATBufferCur = s_ATBuffer; - *s_ATBufferCur = '\0'; - p_read = s_ATBuffer; - } - - do { - count = read(s_fd, p_read, - MAX_AT_RESPONSE - (p_read - s_ATBuffer)); - } while (count < 0 && errno == EINTR); - - if (count > 0) { - AT_DUMP( "<< ", p_read, count ); - - p_read[count] = '\0'; - - // skip over leading newlines - while (*s_ATBufferCur == '\r' || *s_ATBufferCur == '\n') - s_ATBufferCur++; - - p_eol = findNextEOL(s_ATBufferCur); - p_read += count; - } else if (count <= 0) { - /* read error encountered or EOF reached */ - if(count == 0) { - RLOGD("atchannel: EOF reached"); - } else { - RLOGD("atchannel: read error %s", strerror(errno)); - } - return NULL; - } - } - - /* a full line in the buffer. Place a \0 over the \r and return */ - - ret = s_ATBufferCur; - *p_eol = '\0'; - s_ATBufferCur = p_eol + 1; /* this will always be <= p_read, */ - /* and there will be a \0 at *p_read */ - - RLOGD("AT< %s\n", ret); - return ret; -} - - -static void onReaderClosed() -{ - if (s_onReaderClosed != NULL && s_readerClosed == 0) { - - pthread_mutex_lock(&s_commandmutex); - - s_readerClosed = 1; - - pthread_cond_signal(&s_commandcond); - - pthread_mutex_unlock(&s_commandmutex); - - s_onReaderClosed(); - } -} - - -static void *readerLoop(void *arg __unused) -{ - for (;;) { - const char * line; - - line = readline(); - - if (line == NULL) { - break; - } - - if(isSMSUnsolicited(line)) { - char *line1; - const char *line2; - - // The scope of string returned by 'readline()' is valid only - // till next call to 'readline()' hence making a copy of line - // before calling readline again. - line1 = strdup(line); - line2 = readline(); - - if (line2 == NULL) { - free(line1); - break; - } - - if (s_unsolHandler != NULL) { - s_unsolHandler (line1, line2); - } - free(line1); - } else { - processLine(line); - } - } - - onReaderClosed(); - - return NULL; -} - -/** - * Sends string s to the radio with a \r appended. - * Returns AT_ERROR_* on error, 0 on success - * - * This function exists because as of writing, android libc does not - * have buffered stdio. - */ -static int writeline (const char *s) -{ - size_t cur = 0; - size_t len = strlen(s); - ssize_t written; - - if (s_fd < 0 || s_readerClosed > 0) { - return AT_ERROR_CHANNEL_CLOSED; - } - - RLOGD("AT> %s\n", s); - - AT_DUMP( ">> ", s, strlen(s) ); - - /* the main string */ - while (cur < len) { - do { - written = write (s_fd, s + cur, len - cur); - } while (written < 0 && errno == EINTR); - - if (written < 0) { - return AT_ERROR_GENERIC; - } - - cur += written; - } - - /* the \r */ - - do { - written = write (s_fd, "\r" , 1); - } while ((written < 0 && errno == EINTR) || (written == 0)); - - if (written < 0) { - return AT_ERROR_GENERIC; - } - - return 0; -} -static int writeCtrlZ (const char *s) -{ - size_t cur = 0; - size_t len = strlen(s); - ssize_t written; - - if (s_fd < 0 || s_readerClosed > 0) { - return AT_ERROR_CHANNEL_CLOSED; - } - - RLOGD("AT> %s^Z\n", s); - - AT_DUMP( ">* ", s, strlen(s) ); - - /* the main string */ - while (cur < len) { - do { - written = write (s_fd, s + cur, len - cur); - } while (written < 0 && errno == EINTR); - - if (written < 0) { - return AT_ERROR_GENERIC; - } - - cur += written; - } - - /* the ^Z */ - - do { - written = write (s_fd, "\032" , 1); - } while ((written < 0 && errno == EINTR) || (written == 0)); - - if (written < 0) { - return AT_ERROR_GENERIC; - } - - return 0; -} - -static void clearPendingCommand() -{ - if (sp_response != NULL) { - at_response_free(sp_response); - } - - sp_response = NULL; - s_responsePrefix = NULL; - s_smsPDU = NULL; -} - - -/** - * Starts AT handler on stream "fd' - * returns 0 on success, -1 on error - */ -int at_open(int fd, ATUnsolHandler h) -{ - int ret; - pthread_t tid; - pthread_attr_t attr; - - s_fd = fd; - s_unsolHandler = h; - s_readerClosed = 0; - - s_responsePrefix = NULL; - s_smsPDU = NULL; - sp_response = NULL; - - pthread_attr_init (&attr); - pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED); - - ret = pthread_create(&s_tid_reader, &attr, readerLoop, &attr); - - if (ret < 0) { - perror ("pthread_create"); - return -1; - } - - - return 0; -} - -/* FIXME is it ok to call this from the reader and the command thread? */ -void at_close() -{ - if (s_fd >= 0) { - close(s_fd); - } - s_fd = -1; - - pthread_mutex_lock(&s_commandmutex); - - s_readerClosed = 1; - - pthread_cond_signal(&s_commandcond); - - pthread_mutex_unlock(&s_commandmutex); - - /* the reader thread should eventually die */ -} - -static ATResponse * at_response_new() -{ - return (ATResponse *) calloc(1, sizeof(ATResponse)); -} - -void at_response_free(ATResponse *p_response) -{ - ATLine *p_line; - - if (p_response == NULL) return; - - p_line = p_response->p_intermediates; - - while (p_line != NULL) { - ATLine *p_toFree; - - p_toFree = p_line; - p_line = p_line->p_next; - - free(p_toFree->line); - free(p_toFree); - } - - free (p_response->finalResponse); - free (p_response); -} - -/** - * The line reader places the intermediate responses in reverse order - * here we flip them back - */ -static void reverseIntermediates(ATResponse *p_response) -{ - ATLine *pcur,*pnext; - - pcur = p_response->p_intermediates; - p_response->p_intermediates = NULL; - - while (pcur != NULL) { - pnext = pcur->p_next; - pcur->p_next = p_response->p_intermediates; - p_response->p_intermediates = pcur; - pcur = pnext; - } -} - -/** - * Internal send_command implementation - * Doesn't lock or call the timeout callback - * - * timeoutMsec == 0 means infinite timeout - */ - -static int at_send_command_full_nolock (const char *command, ATCommandType type, - const char *responsePrefix, const char *smspdu, - long long timeoutMsec, ATResponse **pp_outResponse) -{ - int err = 0; - struct timespec ts; - - if(sp_response != NULL) { - err = AT_ERROR_COMMAND_PENDING; - goto error; - } - - err = writeline (command); - - if (err < 0) { - goto error; - } - - s_type = type; - s_responsePrefix = responsePrefix; - s_smsPDU = smspdu; - sp_response = at_response_new(); - - if (timeoutMsec != 0) { - setTimespecRelative(&ts, timeoutMsec); - } - - while (sp_response->finalResponse == NULL && s_readerClosed == 0) { - if (timeoutMsec != 0) { - err = pthread_cond_timedwait(&s_commandcond, &s_commandmutex, &ts); - } else { - err = pthread_cond_wait(&s_commandcond, &s_commandmutex); - } - - if (err == ETIMEDOUT) { - err = AT_ERROR_TIMEOUT; - goto error; - } - } - - if (pp_outResponse == NULL) { - at_response_free(sp_response); - } else { - /* line reader stores intermediate responses in reverse order */ - reverseIntermediates(sp_response); - *pp_outResponse = sp_response; - } - - sp_response = NULL; - - if(s_readerClosed > 0) { - err = AT_ERROR_CHANNEL_CLOSED; - goto error; - } - - err = 0; -error: - clearPendingCommand(); - - return err; -} - -/** - * Internal send_command implementation - * - * timeoutMsec == 0 means infinite timeout - */ -static int at_send_command_full (const char *command, ATCommandType type, - const char *responsePrefix, const char *smspdu, - long long timeoutMsec, ATResponse **pp_outResponse) -{ - int err; - bool inEmulator; - - if (0 != pthread_equal(s_tid_reader, pthread_self())) { - /* cannot be called from reader thread */ - return AT_ERROR_INVALID_THREAD; - } - - pthread_mutex_lock(&s_writeMutex); - pthread_mutex_lock(&s_commandmutex); - - err = at_send_command_full_nolock(command, type, - responsePrefix, smspdu, - timeoutMsec, pp_outResponse); - - pthread_mutex_unlock(&s_commandmutex); - pthread_mutex_unlock(&s_writeMutex); - - - if (err == AT_ERROR_TIMEOUT && s_onTimeout != NULL) { - s_onTimeout(); - } - - return err; -} - - -/** - * Issue a single normal AT command with no intermediate response expected - * - * "command" should not include \r - * pp_outResponse can be NULL - * - * if non-NULL, the resulting ATResponse * must be eventually freed with - * at_response_free - */ -int at_send_command (const char *command, ATResponse **pp_outResponse) -{ - int err; - - err = at_send_command_full (command, NO_RESULT, NULL, - NULL, 0, pp_outResponse); - - return err; -} - - -int at_send_command_singleline (const char *command, - const char *responsePrefix, - ATResponse **pp_outResponse) -{ - int err; - - err = at_send_command_full (command, SINGLELINE, responsePrefix, - NULL, 0, pp_outResponse); - - if (err == 0 && pp_outResponse != NULL - && (*pp_outResponse)->success > 0 - && (*pp_outResponse)->p_intermediates == NULL - ) { - /* successful command must have an intermediate response */ - at_response_free(*pp_outResponse); - *pp_outResponse = NULL; - return AT_ERROR_INVALID_RESPONSE; - } - - return err; -} - - -int at_send_command_numeric (const char *command, - ATResponse **pp_outResponse) -{ - int err; - - err = at_send_command_full (command, NUMERIC, NULL, - NULL, 0, pp_outResponse); - - if (err == 0 && pp_outResponse != NULL - && (*pp_outResponse)->success > 0 - && (*pp_outResponse)->p_intermediates == NULL - ) { - /* successful command must have an intermediate response */ - at_response_free(*pp_outResponse); - *pp_outResponse = NULL; - return AT_ERROR_INVALID_RESPONSE; - } - - return err; -} - - -int at_send_command_sms (const char *command, - const char *pdu, - const char *responsePrefix, - ATResponse **pp_outResponse) -{ - int err; - - err = at_send_command_full (command, SINGLELINE, responsePrefix, - pdu, 0, pp_outResponse); - - if (err == 0 && pp_outResponse != NULL - && (*pp_outResponse)->success > 0 - && (*pp_outResponse)->p_intermediates == NULL - ) { - /* successful command must have an intermediate response */ - at_response_free(*pp_outResponse); - *pp_outResponse = NULL; - return AT_ERROR_INVALID_RESPONSE; - } - - return err; -} - - -int at_send_command_multiline (const char *command, - const char *responsePrefix, - ATResponse **pp_outResponse) -{ - int err; - - err = at_send_command_full (command, MULTILINE, responsePrefix, - NULL, 0, pp_outResponse); - - return err; -} - - -/** This callback is invoked on the command thread */ -void at_set_on_timeout(void (*onTimeout)(void)) -{ - s_onTimeout = onTimeout; -} - -/** - * This callback is invoked on the reader thread (like ATUnsolHandler) - * when the input stream closes before you call at_close - * (not when you call at_close()) - * You should still call at_close() - */ - -void at_set_on_reader_closed(void (*onClose)(void)) -{ - s_onReaderClosed = onClose; -} - - -/** - * Periodically issue an AT command and wait for a response. - * Used to ensure channel has start up and is active - */ - -int at_handshake() -{ - int i; - int err = 0; - bool inEmulator; - - if (0 != pthread_equal(s_tid_reader, pthread_self())) { - /* cannot be called from reader thread */ - return AT_ERROR_INVALID_THREAD; - } - inEmulator = isInEmulator(); - if (inEmulator) { - pthread_mutex_lock(&s_writeMutex); - } - pthread_mutex_lock(&s_commandmutex); - - for (i = 0 ; i < HANDSHAKE_RETRY_COUNT ; i++) { - /* some stacks start with verbose off */ - err = at_send_command_full_nolock ("ATE0Q0V1", NO_RESULT, - NULL, NULL, HANDSHAKE_TIMEOUT_MSEC, NULL); - - if (err == 0) { - break; - } - } - - if (err == 0) { - /* pause for a bit to let the input buffer drain any unmatched OK's - (they will appear as extraneous unsolicited responses) */ - - sleepMsec(HANDSHAKE_TIMEOUT_MSEC); - } - - pthread_mutex_unlock(&s_commandmutex); - if (inEmulator) { - pthread_mutex_unlock(&s_writeMutex); - } - - return err; -} - -/** - * Returns error code from response - * Assumes AT+CMEE=1 (numeric) mode - */ -AT_CME_Error at_get_cme_error(const ATResponse *p_response) -{ - int ret; - int err; - char *p_cur; - - if (p_response->success > 0) { - return CME_SUCCESS; - } - - if (p_response->finalResponse == NULL - || !strStartsWith(p_response->finalResponse, "+CME ERROR:") - ) { - return CME_ERROR_NON_CME; - } - - p_cur = p_response->finalResponse; - err = at_tok_start(&p_cur); - - if (err < 0) { - return CME_ERROR_NON_CME; - } - - err = at_tok_nextint(&p_cur, &ret); - - if (err < 0) { - return CME_ERROR_NON_CME; - } - - return (AT_CME_Error) ret; -} - |