diff spandsp-0.0.6pre17/src/at_interpreter.c @ 4:26cd8f1ef0b1

import spandsp-0.0.6pre17
author Peter Meerwald <pmeerw@cosy.sbg.ac.at>
date Fri, 25 Jun 2010 15:50:58 +0200
parents
children
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/spandsp-0.0.6pre17/src/at_interpreter.c	Fri Jun 25 15:50:58 2010 +0200
@@ -0,0 +1,5534 @@
+/*
+ * SpanDSP - a series of DSP components for telephony
+ *
+ * at_interpreter.c - AT command interpreter to V.251, V.252, V.253, T.31 and the 3GPP specs.
+ *
+ * Written by Steve Underwood <steveu@coppice.org>
+ *
+ * Special thanks to Lee Howard <faxguy@howardsilvan.com>
+ * for his great work debugging and polishing this code.
+ *
+ * Copyright (C) 2004, 2005, 2006 Steve Underwood
+ *
+ * All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License version 2.1,
+ * as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * $Id: at_interpreter.c,v 1.42.4.1 2009/12/23 14:18:32 steveu Exp $
+ */
+
+/*! \file */
+
+#if defined(HAVE_CONFIG_H)
+#include "config.h"
+#endif
+
+#if defined(__sun)
+#define __EXTENSIONS__
+#endif
+
+#include <inttypes.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <fcntl.h>
+#include <memory.h>
+#include <string.h>
+#include <ctype.h>
+#include <assert.h>
+
+#include "spandsp/telephony.h"
+#include "spandsp/logging.h"
+#include "spandsp/queue.h"
+#include "spandsp/power_meter.h"
+#include "spandsp/complex.h"
+#include "spandsp/tone_generate.h"
+#include "spandsp/async.h"
+#include "spandsp/hdlc.h"
+#include "spandsp/fsk.h"
+#include "spandsp/super_tone_rx.h"
+#include "spandsp/fax_modems.h"
+
+#include "spandsp/at_interpreter.h"
+
+#include "spandsp/private/logging.h"
+#include "spandsp/private/at_interpreter.h"
+
+#define MANUFACTURER            "www.soft-switch.org"
+#define SERIAL_NUMBER           "42"
+#define GLOBAL_OBJECT_IDENTITY  "42"
+
+enum
+{
+    ASCII_RESULT_CODES = 1,
+    NUMERIC_RESULT_CODES,
+    NO_RESULT_CODES
+};
+
+static at_profile_t profiles[3] =
+{
+    {
+#if defined(_MSC_VER)  ||  defined(__sunos)  ||  defined(__solaris)  ||  defined(__sun)
+        /*.echo =*/ TRUE,
+        /*.verbose =*/ TRUE,
+        /*.result_code_format =*/ ASCII_RESULT_CODES,
+        /*.pulse_dial =*/ FALSE,
+        /*.double_escape =*/ FALSE,
+        /*.adaptive_receive =*/ FALSE,
+        /*.s_regs[100] =*/ {0, 0, 0, '\r', '\n', '\b', 1, 60, 5, 0, 0}
+#else
+        .echo = TRUE,
+        .verbose = TRUE,
+        .result_code_format = ASCII_RESULT_CODES,
+        .pulse_dial = FALSE,
+        .double_escape = FALSE,
+        .adaptive_receive = FALSE,
+        .s_regs[0] = 0,
+        .s_regs[3] = '\r',
+        .s_regs[4] = '\n',
+        .s_regs[5] = '\b',
+        .s_regs[6] = 1,
+        .s_regs[7] = 60,
+        .s_regs[8] = 5,
+        .s_regs[10] = 0
+#endif
+    }
+};
+
+typedef const char *(*at_cmd_service_t)(at_state_t *s, const char *cmd);
+
+static const char *manufacturer = MANUFACTURER;
+static const char *model = PACKAGE;
+static const char *revision = VERSION;
+
+#define ETX 0x03
+#define DLE 0x10
+#define SUB 0x1A
+
+static const char *at_response_codes[] =
+{
+    "OK",
+    "CONNECT",
+    "RING",
+    "NO CARRIER",
+    "ERROR",
+    "???",
+    "NO DIALTONE",
+    "BUSY",
+    "NO ANSWER",
+    "+FCERROR",
+    "+FRH:3"
+};
+
+SPAN_DECLARE(void) at_set_at_rx_mode(at_state_t *s, int new_mode)
+{
+    /* The use of a DTE timeout is mode dependent. Set the timeout appropriately in
+       the modem. */
+    switch (new_mode)
+    {
+    case AT_MODE_HDLC:
+    case AT_MODE_STUFFED:
+        at_modem_control(s, s->dte_inactivity_timeout*1000, (void *) (intptr_t) s->dte_inactivity_timeout);
+        break;
+    default:
+        at_modem_control(s, AT_MODEM_CONTROL_DTE_TIMEOUT, NULL);
+        break;
+    }
+    s->at_rx_mode = new_mode;
+}
+/*- End of function --------------------------------------------------------*/
+
+SPAN_DECLARE(void) at_put_response(at_state_t *s, const char *t)
+{
+    uint8_t buf[3];
+    
+    buf[0] = s->p.s_regs[3];
+    buf[1] = s->p.s_regs[4];
+    buf[2] = '\0';
+    if (s->p.result_code_format == ASCII_RESULT_CODES)
+        s->at_tx_handler(s, s->at_tx_user_data, buf, 2);
+    s->at_tx_handler(s, s->at_tx_user_data, (uint8_t *) t, strlen(t));
+    s->at_tx_handler(s, s->at_tx_user_data, buf, 2);
+}
+/*- End of function --------------------------------------------------------*/
+
+SPAN_DECLARE(void) at_put_numeric_response(at_state_t *s, int val)
+{
+    char buf[20];
+
+    snprintf(buf, sizeof(buf), "%d", val);
+    at_put_response(s, buf);
+}
+/*- End of function --------------------------------------------------------*/
+
+SPAN_DECLARE(void) at_put_response_code(at_state_t *s, int code)
+{
+    uint8_t buf[20];
+
+    span_log(&s->logging, SPAN_LOG_FLOW, "Sending AT response code %s\n", at_response_codes[code]);
+    switch (s->p.result_code_format)
+    {
+    case ASCII_RESULT_CODES:
+        at_put_response(s, at_response_codes[code]);
+        break;
+    case NUMERIC_RESULT_CODES:
+        snprintf((char *) buf, sizeof(buf), "%d%c", code, s->p.s_regs[3]);
+        s->at_tx_handler(s, s->at_tx_user_data, buf, strlen((char *) buf));
+        break;
+    default:
+        /* No result codes */
+        break;
+    }
+}
+/*- End of function --------------------------------------------------------*/
+
+static int answer_call(at_state_t *s)
+{
+    if (at_modem_control(s, AT_MODEM_CONTROL_ANSWER, NULL) < 0)
+        return FALSE;
+    /* Answering should now be in progress. No AT response should be
+       issued at this point. */
+    s->do_hangup = FALSE;
+    return TRUE;
+}
+/*- End of function --------------------------------------------------------*/
+
+SPAN_DECLARE(void) at_call_event(at_state_t *s, int event)
+{
+    span_log(&s->logging, SPAN_LOG_FLOW, "Call event %d received\n", event);
+    switch (event)
+    {
+    case AT_CALL_EVENT_ALERTING:
+        at_modem_control(s, AT_MODEM_CONTROL_RNG, (void *) 1);
+        if (s->display_call_info  &&  !s->call_info_displayed)
+            at_display_call_info(s);
+        at_put_response_code(s, AT_RESPONSE_CODE_RING);
+        if ((++s->rings_indicated) >= s->p.s_regs[0]  &&  s->p.s_regs[0])
+        {
+            /* The modem is set to auto-answer now */
+            answer_call(s);
+        }
+        break;
+    case AT_CALL_EVENT_ANSWERED:
+        at_modem_control(s, AT_MODEM_CONTROL_RNG, (void *) 0);
+        if (s->fclass_mode == 0)
+        {
+            /* Normal data modem connection */
+            at_set_at_rx_mode(s, AT_MODE_CONNECTED);
+            /* TODO: */
+        }
+        else
+        {
+            /* FAX modem connection */
+            at_set_at_rx_mode(s, AT_MODE_DELIVERY);
+            at_modem_control(s, AT_MODEM_CONTROL_RESTART, (void *) FAX_MODEM_CED_TONE);
+        }
+        break;
+    case AT_CALL_EVENT_CONNECTED:
+        span_log(&s->logging, SPAN_LOG_FLOW, "Dial call - connected. FCLASS=%d\n", s->fclass_mode);
+        at_modem_control(s, AT_MODEM_CONTROL_RNG, (void *) 0);
+        if (s->fclass_mode == 0)
+        {
+            /* Normal data modem connection */
+            at_set_at_rx_mode(s, AT_MODE_CONNECTED);
+            /* TODO: */
+        }
+        else
+        {
+            /* FAX modem connection */
+            at_set_at_rx_mode(s, AT_MODE_DELIVERY);
+            if (s->silent_dial)
+                at_modem_control(s, AT_MODEM_CONTROL_RESTART, (void *) FAX_MODEM_NOCNG_TONE);
+            else
+                at_modem_control(s, AT_MODEM_CONTROL_RESTART, (void *) FAX_MODEM_CNG_TONE);
+            s->dte_is_waiting = TRUE;
+        }
+        break;
+    case AT_CALL_EVENT_BUSY:
+        at_set_at_rx_mode(s, AT_MODE_ONHOOK_COMMAND);
+        at_put_response_code(s, AT_RESPONSE_CODE_BUSY);
+        break;
+    case AT_CALL_EVENT_NO_DIALTONE:
+        at_set_at_rx_mode(s, AT_MODE_ONHOOK_COMMAND);
+        at_put_response_code(s, AT_RESPONSE_CODE_NO_DIALTONE);
+        break;
+    case AT_CALL_EVENT_NO_ANSWER:
+        at_set_at_rx_mode(s, AT_MODE_ONHOOK_COMMAND);
+        at_put_response_code(s, AT_RESPONSE_CODE_NO_ANSWER);
+        break;
+    case AT_CALL_EVENT_HANGUP:
+        span_log(&s->logging, SPAN_LOG_FLOW, "Hangup... at_rx_mode %d\n", s->at_rx_mode);
+        at_modem_control(s, AT_MODEM_CONTROL_ONHOOK, NULL);
+        if (s->dte_is_waiting)
+        {
+            if (s->ok_is_pending)
+            {
+                at_put_response_code(s, AT_RESPONSE_CODE_OK);
+                s->ok_is_pending = FALSE;
+            }
+            else
+            {
+                at_put_response_code(s, AT_RESPONSE_CODE_NO_CARRIER);
+            }
+            s->dte_is_waiting = FALSE;
+            at_set_at_rx_mode(s, AT_MODE_ONHOOK_COMMAND);
+        }
+        else if (s->fclass_mode  &&  s->rx_signal_present)
+        {
+            s->rx_data[s->rx_data_bytes++] = DLE;
+            s->rx_data[s->rx_data_bytes++] = ETX;
+            s->at_tx_handler(s, s->at_tx_user_data, s->rx_data, s->rx_data_bytes);
+            s->rx_data_bytes = 0;
+        }
+        if (s->at_rx_mode != AT_MODE_OFFHOOK_COMMAND  &&  s->at_rx_mode != AT_MODE_ONHOOK_COMMAND)
+            at_put_response_code(s, AT_RESPONSE_CODE_NO_CARRIER);
+        s->rx_signal_present = FALSE;
+        at_modem_control(s, AT_MODEM_CONTROL_RNG, (void *) 0);
+        at_set_at_rx_mode(s, AT_MODE_ONHOOK_COMMAND);
+        break;
+    default:
+        span_log(&s->logging, SPAN_LOG_WARNING, "Invalid call event %d received.\n", event);
+        break;
+    }
+}
+/*- End of function --------------------------------------------------------*/
+
+SPAN_DECLARE(void) at_reset_call_info(at_state_t *s)
+{
+    at_call_id_t *call_id;
+    at_call_id_t *next;
+ 
+    for (call_id = s->call_id;  call_id;  call_id = next)
+    {
+        next = call_id->next;
+        free(call_id);
+    }
+    s->call_id = NULL;
+    s->rings_indicated = 0;
+    s->call_info_displayed = FALSE;
+}
+/*- End of function --------------------------------------------------------*/
+
+SPAN_DECLARE(void) at_set_call_info(at_state_t *s, char const *id, char const *value)
+{
+    at_call_id_t *new_call_id;
+    at_call_id_t *call_id;
+
+    /* TODO: We should really not merely ignore a failure to malloc */
+    if ((new_call_id = (at_call_id_t *) malloc(sizeof(*new_call_id))) == NULL)
+        return;
+    call_id = s->call_id;
+    /* If these strdups fail its pretty harmless. We just appear to not
+       have the relevant field. */
+    new_call_id->id = (id)  ?  strdup(id)  :  NULL;
+    new_call_id->value = (value)  ?  strdup(value)  :  NULL;
+    new_call_id->next = NULL;
+
+    if (call_id)
+    {
+        while (call_id->next)
+            call_id = call_id->next;
+        call_id->next = new_call_id;
+    }
+    else
+    {
+        s->call_id = new_call_id;
+    }
+}
+/*- End of function --------------------------------------------------------*/
+
+SPAN_DECLARE(void) at_display_call_info(at_state_t *s)
+{
+    char buf[132 + 1];
+    at_call_id_t *call_id = s->call_id;
+
+    while (call_id)
+    {
+        snprintf(buf, sizeof(buf), "%s=%s", 
+                 call_id->id ? call_id->id : "NULL", call_id->value ? call_id->value : "<NONE>");
+        at_put_response(s, buf);
+        call_id = call_id->next;
+    }
+    s->call_info_displayed = TRUE;
+}
+/*- End of function --------------------------------------------------------*/
+
+static int parse_num(const char **s, int max_value)
+{
+    int i;
+    
+    /* The spec. says no digits is valid, and should be treated as zero. */
+    i = 0;
+    while (isdigit(**s))
+    {
+        i = i*10 + ((**s) - '0');
+        (*s)++;
+    }
+    if (i > max_value)
+        i = -1;
+    return i;
+}
+/*- End of function --------------------------------------------------------*/
+
+static int parse_hex_num(const char **s, int max_value)
+{
+    int i;
+    
+    /* The spec. says a hex value is always 2 digits, and the alpha digits are
+       upper case. */
+    i = 0;
+    if (isdigit(**s))
+        i = **s - '0';
+    else if (**s >= 'A'  &&  **s <= 'F')
+        i = **s - 'A';
+    else
+        return -1;
+    (*s)++;
+
+    if (isdigit(**s))
+        i = (i << 4)  | (**s - '0');
+    else if (**s >= 'A'  &&  **s <= 'F')
+        i = (i << 4)  | (**s - 'A');
+    else
+        return -1;
+    (*s)++;
+    if (i > max_value)
+        i = -1;
+    return i;
+}
+/*- End of function --------------------------------------------------------*/
+
+static int match_element(const char **variant, const char *variants)
+{
+    int i;
+    size_t len;
+    char const *s;
+    char const *t;
+
+    s = variants;
+    for (i = 0;  *s;  i++)
+    {
+        if ((t = strchr(s, ',')))
+            len = t - s;
+        else
+            len = strlen(s);
+        if (len == (int) strlen(*variant)  &&  memcmp(*variant, s, len) == 0)
+        {
+            *variant += len;
+            return  i;
+        }
+        s += len;
+        if (*s == ',')
+            s++;
+    }
+    return  -1;
+}
+/*- End of function --------------------------------------------------------*/
+
+static int parse_out(at_state_t *s, const char **t, int *target, int max_value, const char *prefix, const char *def)
+{
+    char buf[100];
+    int val;
+
+    switch (*(*t)++)
+    {
+    case '=':
+        switch (**t)
+        {
+        case '?':
+            /* Show possible values */
+            (*t)++;
+            snprintf(buf, sizeof(buf), "%s%s", (prefix)  ?  prefix  :  "", def);
+            at_put_response(s, buf);
+            break;
+        default:
+            /* Set value */
+            if ((val = parse_num(t, max_value)) < 0)
+                return FALSE;
+            if (target)
+                *target = val;
+            break;
+        }
+        break;
+    case '?':
+        /* Show current value */
+        val = (target)  ?  *target  :  0;
+        snprintf(buf, sizeof(buf), "%s%d", (prefix)  ?  prefix  :  "", val);
+        at_put_response(s, buf);
+        break;
+    default:
+        return FALSE;
+    }
+    return TRUE;
+}
+/*- End of function --------------------------------------------------------*/
+
+static int parse_2_out(at_state_t *s, const char **t, int *target1, int max_value1, int *target2, int max_value2, const char *prefix, const char *def)
+{
+    char buf[100];
+    int val1;
+    int val2;
+
+    switch (*(*t)++)
+    {
+    case '=':
+        switch (**t)
+        {
+        case '?':
+            /* Show possible values */
+            (*t)++;
+            snprintf(buf, sizeof(buf), "%s%s", (prefix)  ?  prefix  :  "", def);
+            at_put_response(s, buf);
+            break;
+        default:
+            /* Set value */
+            if ((val1 = parse_num(t, max_value1)) < 0)
+                return FALSE;
+            if (target1)
+                *target1 = val1;
+            if (**t == ',')
+            {
+                (*t)++;
+                if ((val2 = parse_num(t, max_value2)) < 0)
+                    return FALSE;
+                if (target2)
+                    *target2 = val2;
+            }
+            break;
+        }
+        break;
+    case '?':
+        /* Show current value */
+        val1 = (target1)  ?  *target1  :  0;
+        val2 = (target2)  ?  *target2  :  0;
+        snprintf(buf, sizeof(buf), "%s%d,%d", (prefix)  ?  prefix  :  "", val1, val2);
+        at_put_response(s, buf);
+        break;
+    default:
+        return FALSE;
+    }
+    return TRUE;
+}
+/*- End of function --------------------------------------------------------*/
+
+static int parse_n_out(at_state_t *s,
+                       const char **t,
+                       int *targets[],
+                       const int max_values[],
+                       int entries,
+                       const char *prefix,
+                       const char *def)
+{
+    char buf[100];
+    int val;
+    int len;
+    int i;
+
+    switch (*(*t)++)
+    {
+    case '=':
+        switch (**t)
+        {
+        case '?':
+            /* Show possible values */
+            (*t)++;
+            snprintf(buf, sizeof(buf), "%s%s", (prefix)  ?  prefix  :  "", def);
+            at_put_response(s, buf);
+            break;
+        default:
+            /* Set value */
+            for (i = 0;  i < entries;  i++)
+            {
+                if ((val = parse_num(t, max_values[i])) < 0)
+                    return FALSE;
+                if (targets[i])
+                    *targets[i] = val;
+                if (**t != ',')
+                    break;
+                (*t)++;
+            }
+            break;
+        }
+        break;
+    case '?':
+        /* Show current value */
+        len = snprintf(buf, sizeof(buf), "%s", (prefix)  ?  prefix  :  "");
+        for (i = 0;  i < entries;  i++)
+        {
+            if (i > 0)
+                len += snprintf(&buf[len], sizeof(buf) - len, ",");
+            val = (targets[i])  ?  *targets[i]  :  0;
+            len += snprintf(&buf[len], sizeof(buf) - len, "%d", val);
+        }
+        at_put_response(s, buf);
+        break;
+    default:
+        return FALSE;
+    }
+    return TRUE;
+}
+/*- End of function --------------------------------------------------------*/
+
+static int parse_hex_out(at_state_t *s, const char **t, int *target, int max_value, const char *prefix, const char *def)
+{
+    char buf[100];
+    int val;
+
+    switch (*(*t)++)
+    {
+    case '=':
+        switch (**t)
+        {
+        case '?':
+            /* Show possible values */
+            (*t)++;
+            snprintf(buf, sizeof(buf), "%s%s", (prefix)  ?  prefix  :  "", def);
+            at_put_response(s, buf);
+            break;
+        default:
+            /* Set value */
+            if ((val = parse_hex_num(t, max_value)) < 0)
+                return FALSE;
+            if (target)
+                *target = val;
+            break;
+        }
+        break;
+    case '?':
+        /* Show current value */
+        val = (target)  ?  *target  :  0;
+        snprintf(buf, sizeof(buf), "%s%02X", (prefix)  ?  prefix  :  "", val);
+        at_put_response(s, buf);
+        break;
+    default:
+        return FALSE;
+    }
+    return TRUE;
+}
+/*- End of function --------------------------------------------------------*/
+
+static int parse_string_list_out(at_state_t *s, const char **t, int *target, int max_value, const char *prefix, const char *def)
+{
+    char buf[100];
+    int val;
+    size_t len;
+    char *tmp;
+
+    switch (*(*t)++)
+    {
+    case '=':
+        switch (**t)
+        {
+        case '?':
+            /* Show possible values */
+            (*t)++;
+            snprintf(buf, sizeof(buf), "%s%s", (prefix)  ?  prefix  :  "", def);
+            at_put_response(s, buf);
+            break;
+        default:
+            /* Set value */
+            if ((val = match_element(t, def)) < 0)
+                return FALSE;
+            if (target)
+                *target = val;
+            break;
+        }
+        break;
+    case '?':
+        /* Show current index value from def */
+        val = (target)  ?  *target  :  0;
+        while (val--  &&  (def = strchr(def, ',')))
+            def++;
+        if ((tmp = strchr(def, ',')))
+            len = tmp - def;
+        else
+            len = strlen(def);
+        snprintf(buf, sizeof(buf), "%s%.*s", (prefix)  ?  prefix  :  "", (int) len, def);
+        at_put_response(s, buf);
+        break;
+    default:
+        return FALSE;
+    }
+    return TRUE;
+}
+/*- End of function --------------------------------------------------------*/
+
+static int parse_string_out(at_state_t *s, const char **t, char **target, const char *prefix)
+{
+    char buf[100];
+
+    switch (*(*t)++)
+    {
+    case '=':
+        switch (**t)
+        {
+        case '?':
+            /* Show possible values */
+            (*t)++;
+            snprintf(buf, sizeof(buf), "%s", (prefix)  ?  prefix  :  "");
+            at_put_response(s, buf);
+            break;
+        default:
+            /* Set value */
+            if (*target)
+                free(*target);
+            /* If this strdup fails, it should be harmless */
+            *target = strdup(*t);
+            break;
+        }
+        break;
+    case '?':
+        /* Show current index value */
+        at_put_response(s, (*target)  ?  *target  :  "");
+        break;
+    default:
+        return FALSE;
+    }
+    while (*t)
+        t++;
+    return TRUE;
+}
+/*- End of function --------------------------------------------------------*/
+
+static const char *s_reg_handler(at_state_t *s, const char *t, int reg)
+{
+    int val;
+    int b;
+    char buf[4];
+
+    /* Set or get an S register */
+    switch (*t++)
+    {
+    case '=':
+        switch (*t)
+        {
+        case '?':
+            t++;
+            snprintf(buf, sizeof(buf), "%3.3d", 0);
+            at_put_response(s, buf);
+            break;
+        default:
+            if ((val = parse_num(&t, 255)) < 0)
+                return NULL;
+            s->p.s_regs[reg] = (uint8_t) val;
+            break;
+        }
+        break;
+    case '?':
+        snprintf(buf, sizeof(buf), "%3.3d", s->p.s_regs[reg]);
+        at_put_response(s, buf);
+        break;
+    case '.':
+        if ((b = parse_num(&t, 7)) < 0)
+            return NULL;
+        switch (*t++)
+        {
+        case '=':
+            switch (*t)
+            {
+            case '?':
+                t++;
+                at_put_numeric_response(s, 0);
+                break;
+            default:
+                if ((val = parse_num(&t, 1)) < 0)
+                    return NULL;
+                if (val)
+                    s->p.s_regs[reg] |= (1 << b);
+                else
+                    s->p.s_regs[reg] &= ~(1 << b);
+                break;
+            }
+            break;
+        case '?':
+            at_put_numeric_response(s, (int) ((s->p.s_regs[reg] >> b) & 1));
+            break;
+        default:
+            return NULL;
+        }
+        break;
+    default:
+        return NULL;
+    }
+    return t;
+}
+/*- End of function --------------------------------------------------------*/
+
+static int process_class1_cmd(at_state_t *s, const char **t)
+{
+    int val;
+    int operation;
+    int direction;
+    int result;
+    const char *allowed;
+
+    direction = (*(*t + 2) == 'T');
+    operation = *(*t + 3);
+    /* Step past the "+Fxx" */
+    *t += 4;
+    switch (operation)
+    {
+    case 'S':
+        allowed = "0-255";
+        break;
+    case 'H':
+        allowed = "3";
+        break;
+    default:
+        allowed = "24,48,72,73,74,96,97,98,121,122,145,146";
+        break;
+    }
+    
+    val = -1;
+    if (!parse_out(s, t, &val, 255, NULL, allowed))
+        return TRUE;
+    if (val < 0)
+    {
+        /* It was just a query */
+        return  TRUE;
+    }
+    /* All class 1 FAX commands are supposed to give an ERROR response, if the phone
+       is on-hook. */
+    if (s->at_rx_mode == AT_MODE_ONHOOK_COMMAND)
+        return FALSE;
+
+    result = TRUE;
+    if (s->class1_handler)
+        result = s->class1_handler(s, s->class1_user_data, direction, operation, val);
+    switch (result)
+    {
+    case 0:
+        /* Inhibit an immediate response.  (These commands should not be part of a multi-command entry.) */
+        *t = (const char *) -1;
+        return TRUE;
+    case -1:
+        return FALSE;
+    }
+    return TRUE;
+}
+/*- End of function --------------------------------------------------------*/
+
+static const char *at_cmd_dummy(at_state_t *s, const char *t)
+{
+    /* Dummy routine to absorb delimiting characters from a command string */
+    return t + 1;
+}
+/*- End of function --------------------------------------------------------*/
+
+static const char *at_cmd_A(at_state_t *s, const char *t)
+{
+    /* V.250 6.3.5 - Answer (abortable) */ 
+    t += 1;
+    if (!answer_call(s))
+        return NULL;
+    return (const char *) -1;
+}
+/*- End of function --------------------------------------------------------*/
+
+static const char *at_cmd_D(at_state_t *s, const char *t)
+{
+    int ok;
+    char *u;
+    const char *w;
+    char num[100 + 1];
+    char ch;
+
+    /* V.250 6.3.1 - Dial (abortable) */ 
+    at_reset_call_info(s);
+    s->do_hangup = FALSE;
+    s->silent_dial = FALSE;
+    t += 1;
+    ok = FALSE;
+    /* There are a numbers of options in a dial command string.
+       Many are completely irrelevant in this application. */
+    w = t;
+    u = num;
+    for (  ;  (ch = *t);  t++)
+    {
+        if (isdigit(ch))
+        {
+            /* V.250 6.3.1.1 Basic digit set */
+            *u++ = ch;
+        }
+        else
+        {
+            switch (ch)
+            {
+            case 'A':
+            case 'B':
+            case 'C':
+            case 'D':
+            case '*':
+            case '#':
+                /* V.250 6.3.1.1 Full DTMF repertoire */
+                if (!s->p.pulse_dial)
+                    *u++ = ch;
+                break;
+            case ' ':
+            case '-':
+                /* Ignore spaces and dashes */
+                /* This is not a standards based thing. It just improves
+                   compatibility with some other modems. */
+                break;
+            case '+':
+                /* V.250 6.3.1.1 International access code */
+                /* TODO: */
+                break;
+            case ',':
+                /* V.250 6.3.1.2 Pause */
+                /* Pass these through to the application to handle. */
+                *u++ = ch;
+                break;
+            case 'T':
+                /* V.250 6.3.1.3 Tone dial */
+                s->p.pulse_dial = FALSE;
+                break;
+            case 'P':
+                /* V.250 6.3.1.4 Pulse dial */
+                s->p.pulse_dial = TRUE;
+                break;
+            case '!':
+                /* V.250 6.3.1.5 Hook flash, register recall */
+                /* TODO: */
+                break;
+            case 'W':
+                /* V.250 6.3.1.6 Wait for dial tone */
+                /* TODO: */
+                break;
+            case '@':
+                /* V.250 6.3.1.7 Wait for quiet answer */
+                s->silent_dial = TRUE;
+                break;
+            case 'S':
+                /* V.250 6.3.1.8 Invoke stored string */
+                /* S=<location> */
+                /* TODO: */
+                break;
+            case 'G':
+            case 'g':
+                /* GSM07.07 6.2 - Control the CUG supplementary service for this call */
+                /* TODO: */
+                break;
+            case 'I':
+            case 'i':
+                /* GSM07.07 6.2 - Override Calling Line Identification Restriction (CLIR) */
+                /* TODO: */
+                break;
+            case ';':
+                /* V.250 6.3.1 - Dial string terminator - make voice call and remain in command mode */
+                /* TODO: */
+                break;
+            case '>':
+                /* GSM07.07 6.2 - Direct dialling from phone book supplementary service subscription
+                                  default value for this call */
+                /* TODO: */
+                break;
+            default:
+                return NULL;
+            }
+        }
+    }
+    *u = '\0';
+    if ((ok = at_modem_control(s, AT_MODEM_CONTROL_CALL, num)) < 0)
+        return NULL;
+    /* Dialing should now be in progress. No AT response should be
+       issued at this point. */
+    return (const char *) -1;
+}
+/*- End of function --------------------------------------------------------*/
+
+static const char *at_cmd_E(at_state_t *s, const char *t)
+{
+    int val;
+
+    /* V.250 6.2.4 - Command echo */ 
+    t += 1;
+    if ((val = parse_num(&t, 1)) < 0)
+        return NULL;
+    s->p.echo = val;
+    return t;
+}
+/*- End of function --------------------------------------------------------*/
+
+static const char *at_cmd_H(at_state_t *s, const char *t)
+{
+    int val;
+
+    /* V.250 6.3.6 - Hook control */ 
+    t += 1;
+    if ((val = parse_num(&t, 1)) < 0)
+        return NULL;
+    if (val)
+    {
+        /* Take the receiver off-hook, effectively busying-out the modem. */
+        if (s->at_rx_mode != AT_MODE_ONHOOK_COMMAND  &&  s->at_rx_mode != AT_MODE_OFFHOOK_COMMAND)
+            return NULL;
+        at_modem_control(s, AT_MODEM_CONTROL_OFFHOOK, NULL);
+        at_set_at_rx_mode(s, AT_MODE_OFFHOOK_COMMAND);
+        return t;
+    }
+    at_reset_call_info(s);
+    if (s->at_rx_mode != AT_MODE_ONHOOK_COMMAND  &&  s->at_rx_mode != AT_MODE_OFFHOOK_COMMAND)
+    {
+        /* Push out the last of the audio (probably by sending a short silence). */
+        at_modem_control(s, AT_MODEM_CONTROL_RESTART, (void *) FAX_MODEM_FLUSH);
+        s->do_hangup = TRUE;
+        at_set_at_rx_mode(s, AT_MODE_CONNECTED);
+        return (const char *) -1;
+    }
+    at_modem_control(s, AT_MODEM_CONTROL_HANGUP, NULL);
+    at_set_at_rx_mode(s, AT_MODE_ONHOOK_COMMAND);
+    return t;
+}
+/*- End of function --------------------------------------------------------*/
+
+static const char *at_cmd_I(at_state_t *s, const char *t)
+{
+    int val;
+
+    /* V.250 6.1.3 - Request identification information */ 
+    /* N.B. The information supplied in response to an ATIx command is very
+       variable. It was widely used in different ways before the AT command
+       set was standardised by the ITU. */
+    t += 1;
+    switch (val = parse_num(&t, 255))
+    {
+    case 0:
+        at_put_response(s, model);
+        break;
+    case 3:
+        at_put_response(s, manufacturer);
+        break;
+    default:
+        return NULL;
+    }
+    return t;
+}
+/*- End of function --------------------------------------------------------*/
+
+static const char *at_cmd_L(at_state_t *s, const char *t)
+{
+    int val;
+
+    /* V.250 6.3.13 - Monitor speaker loudness */
+    /* Just absorb this command, as we have no speaker */
+    t += 1;
+    if ((val = parse_num(&t, 255)) < 0)
+        return NULL;
+    s->speaker_volume = val;
+    return t;
+}
+/*- End of function --------------------------------------------------------*/
+
+static const char *at_cmd_M(at_state_t *s, const char *t)
+{
+    int val;
+
+    /* V.250 6.3.14 - Monitor speaker mode */ 
+    /* Just absorb this command, as we have no speaker */
+    t += 1;
+    if ((val = parse_num(&t, 255)) < 0)
+        return NULL;
+    s->speaker_mode = val;
+    return t;
+}
+/*- End of function --------------------------------------------------------*/
+
+static const char *at_cmd_O(at_state_t *s, const char *t)
+{
+    int val;
+
+    /* V.250 6.3.7 - Return to online data state */ 
+    t += 1;
+    if ((val = parse_num(&t, 1)) < 0)
+        return NULL;
+    if (val == 0)
+    {
+        at_set_at_rx_mode(s, AT_MODE_CONNECTED);
+        at_put_response_code(s, AT_RESPONSE_CODE_CONNECT);
+    }
+    return t;
+}
+/*- End of function --------------------------------------------------------*/
+
+static const char *at_cmd_P(at_state_t *s, const char *t)
+{
+    /* V.250 6.3.3 - Select pulse dialling (command) */ 
+    t += 1;
+    s->p.pulse_dial = TRUE;
+    return t;
+}
+/*- End of function --------------------------------------------------------*/
+
+static const char *at_cmd_Q(at_state_t *s, const char *t)
+{
+    int val;
+
+    /* V.250 6.2.5 - Result code suppression */ 
+    t += 1;
+    if ((val = parse_num(&t, 1)) < 0)
+        return NULL;
+    switch (val)
+    {
+    case 0:
+        s->p.result_code_format = (s->p.verbose)  ?  ASCII_RESULT_CODES  :  NUMERIC_RESULT_CODES;
+        break;
+    case 1:
+        s->p.result_code_format = NO_RESULT_CODES;
+        break;
+    }
+    return t;
+}
+/*- End of function --------------------------------------------------------*/
+
+static const char *at_cmd_S0(at_state_t *s, const char *t)
+{
+    /* V.250 6.3.8 - Automatic answer */ 
+    t += 2;
+    return s_reg_handler(s, t, 0);
+}
+/*- End of function --------------------------------------------------------*/
+
+static const char *at_cmd_S10(at_state_t *s, const char *t)
+{
+    /* V.250 6.3.12 - Automatic disconnect delay */ 
+    t += 3;
+    return s_reg_handler(s, t, 10);
+}
+/*- End of function --------------------------------------------------------*/
+
+static const char *at_cmd_S3(at_state_t *s, const char *t)
+{
+    /* V.250 6.2.1 - Command line termination character */ 
+    t += 2;
+    return s_reg_handler(s, t, 3);
+}
+/*- End of function --------------------------------------------------------*/
+
+static const char *at_cmd_S4(at_state_t *s, const char *t)
+{
+    /* V.250 6.2.2 - Response formatting character */ 
+    t += 2;
+    return s_reg_handler(s, t, 4);
+}
+/*- End of function --------------------------------------------------------*/
+
+static const char *at_cmd_S5(at_state_t *s, const char *t)
+{
+    /* V.250 6.2.3 - Command line editing character */ 
+    t += 2;
+    return s_reg_handler(s, t, 5);
+}
+/*- End of function --------------------------------------------------------*/
+
+static const char *at_cmd_S6(at_state_t *s, const char *t)
+{
+    /* V.250 6.3.9 - Pause before blind dialling */ 
+    t += 2;
+    return s_reg_handler(s, t, 6);
+}
+/*- End of function --------------------------------------------------------*/
+
+static const char *at_cmd_S7(at_state_t *s, const char *t)
+{
+    /* V.250 6.3.10 - Connection completion timeout */ 
+    t += 2;
+    return s_reg_handler(s, t, 7);
+}
+/*- End of function --------------------------------------------------------*/
+
+static const char *at_cmd_S8(at_state_t *s, const char *t)
+{
+    /* V.250 6.3.11 - Comma dial modifier time */ 
+    t += 2;
+    return s_reg_handler(s, t, 8);
+}
+/*- End of function --------------------------------------------------------*/
+
+static const char *at_cmd_T(at_state_t *s, const char *t)
+{
+    /* V.250 6.3.2 - Select tone dialling (command) */ 
+    t += 1;
+    s->p.pulse_dial = FALSE;
+    return t;
+}
+/*- End of function --------------------------------------------------------*/
+
+static const char *at_cmd_V(at_state_t *s, const char *t)
+{
+    int val;
+
+    /* V.250 6.2.6 - DCE response format */ 
+    t += 1;
+    if ((val = parse_num(&t, 1)) < 0)
+        return NULL;
+    s->p.verbose = val;
+    if (s->p.result_code_format != NO_RESULT_CODES)
+        s->p.result_code_format = (s->p.verbose)  ?  ASCII_RESULT_CODES  :  NUMERIC_RESULT_CODES;
+    return t;
+}
+/*- End of function --------------------------------------------------------*/
+
+static const char *at_cmd_X(at_state_t *s, const char *t)
+{
+    int val;
+
+    /* V.250 6.2.7 - Result code selection and call progress monitoring control */
+    /* 0    CONNECT result code is given upon entering online data state.
+            Dial tone and busy detection are disabled.
+       1    CONNECT <text> result code is given upon entering online data state.
+            Dial tone and busy detection are disabled.
+       2    CONNECT <text> result code is given upon entering online data state.
+            Dial tone detection is enabled, and busy detection is disabled.
+       3    CONNECT <text> result code is given upon entering online data state.
+            Dial tone detection is disabled, and busy detection is enabled.
+       4    CONNECT <text> result code is given upon entering online data state.
+            Dial tone and busy detection are both enabled. */
+    t += 1;
+    if ((val = parse_num(&t, 4)) < 0)
+        return NULL;
+    s->result_code_mode = val;
+    return t;
+}
+/*- End of function --------------------------------------------------------*/
+
+static const char *at_cmd_Z(at_state_t *s, const char *t)
+{
+    int val;
+
+    /* V.250 6.1.1 - Reset to default configuration */ 
+    t += 1;
+    if ((val = parse_num(&t, sizeof(profiles)/sizeof(profiles[0]) - 1)) < 0)
+        return NULL;
+    /* Just make sure we are on hook */
+    at_modem_control(s, AT_MODEM_CONTROL_HANGUP, NULL);
+    at_set_at_rx_mode(s, AT_MODE_ONHOOK_COMMAND);
+    s->p = profiles[val];
+    at_reset_call_info(s);
+    return t;
+}
+/*- End of function --------------------------------------------------------*/
+
+static const char *at_cmd_amp_C(at_state_t *s, const char *t)
+{
+    int val;
+
+    /* V.250 6.2.8 - Circuit 109 (received line signal detector) behaviour */ 
+    /* We have no RLSD pin, so just absorb this. */
+    t += 2;
+    if ((val = parse_num(&t, 1)) < 0)
+        return NULL;
+    s->rlsd_behaviour = val;
+    return t;
+}
+/*- End of function --------------------------------------------------------*/
+
+static const char *at_cmd_amp_D(at_state_t *s, const char *t)
+{
+    int val;
+
+    /* V.250 6.2.9 - Circuit 108 (data terminal ready) behaviour */ 
+    t += 2;
+    if ((val = parse_num(&t, 2)) < 0)
+        return NULL;
+    /* TODO: We have no DTR pin, but we need this to get into online
+             command state. */
+    s->dtr_behaviour = val;
+    return t;
+}
+/*- End of function --------------------------------------------------------*/
+
+static const char *at_cmd_amp_F(at_state_t *s, const char *t)
+{
+    t += 2;
+
+    /* V.250 6.1.2 - Set to factory-defined configuration */ 
+    /* Just make sure we are on hook */
+    at_modem_control(s, AT_MODEM_CONTROL_HANGUP, NULL);
+    at_set_at_rx_mode(s, AT_MODE_ONHOOK_COMMAND);
+    s->p = profiles[0];
+    return t;
+}
+/*- End of function --------------------------------------------------------*/
+
+static const char *at_cmd_plus_A8A(at_state_t *s, const char *t)
+{
+    /* V.251 6.3 - V.8 calling tone indication */
+    return t;
+}
+/*- End of function --------------------------------------------------------*/
+
+static const char *at_cmd_plus_A8C(at_state_t *s, const char *t)
+{
+    /* V.251 6.2 - V.8 answer signal indication */
+    return t;
+}
+/*- End of function --------------------------------------------------------*/
+
+static const char *at_cmd_plus_A8E(at_state_t *s, const char *t)
+{
+    int val;
+
+    /* V.251 5.1 - V.8 and V.8bis operation controls */
+    /* Syntax: +A8E=<v8o>,<v8a>,<v8cf>[,<v8b>][,<cfrange>][,<protrange>] */
+    /* <v8o>=0  Disable V.8 origination negotiation
+       <v8o>=1  Enable DCE-controlled V.8 origination negotiation
+       <v8o>=2  Enable DTE-controlled V.8 origination negotiation, send V.8 CI only
+       <v8o>=3  Enable DTE-controlled V.8 origination negotiation, send 1100Hz CNG only
+       <v8o>=4  Enable DTE-controlled V.8 origination negotiation, send 1300Hz CT only
+       <v8o>=5  Enable DTE-controlled V.8 origination negotiation, send no tones
+       <v8o>=6  Enable DCE-controlled V.8 origination negotiation, issue +A8x indications
+       <v8a>=0  Disable V.8 answer negotiation
+       <v8a>=1  Enable DCE-controlled V.8 answer negotiation
+       <v8a>=2  Enable DTE-controlled V.8 answer negotiation, send ANSam
+       <v8a>=3  Enable DTE-controlled V.8 answer negotiation, send no signal
+       <v8a>=4  Disable DTE-controlled V.8 answer negotiation, send ANS
+       <v8a>=5  Enable DCE-controlled V.8 answer negotiation, issue +A8x indications
+       <v8cf>=X..Y Set the V.8 CI signal call function to the hexadecimal octet value X..Y
+       <v8b>=0  Disable V.8bis negotiation
+       <v8b>=1  Enable DCE-controlled V.8bis negotiation
+       <v8b>=2  Enable DTE-controlled V.8bis negotiation
+       <cfrange>="<string of values>"   Set to alternative list of call function "option bit"
+                                        values that the answering DCE shall accept from the caller
+       <protrange>="<string of values>" Set to alternative list of protocol "option bit" values that
+                                        the answering DCE shall accept from the caller
+    */
+    /* TODO: */
+    t += 4;
+    if (!parse_out(s, &t, &val, 6, "+A8E:", "(0-6),(0-5),(00-FF)"))
+        return NULL;
+    if (*t != ',')
+        return t;
+    if ((val = parse_num(&t, 5)) < 0)
+        return NULL;
+    if (*t != ',')
+        return t;
+    return t;
+}
+/*- End of function --------------------------------------------------------*/
+
+static const char *at_cmd_plus_A8I(at_state_t *s, const char *t)
+{
+    /* V.251 6.1 - V.8 CI signal indication */
+    return t;
+}
+/*- End of function --------------------------------------------------------*/
+
+static const char *at_cmd_plus_A8J(at_state_t *s, const char *t)
+{
+    /* V.251 6.4 - V.8 negotiation complete */
+    return t;
+}
+/*- End of function --------------------------------------------------------*/
+
+static const char *at_cmd_plus_A8M(at_state_t *s, const char *t)
+{
+    /* V.251 5.2 - Send V.8 menu signals */
+    /* Syntax: +A8M=<hexadecimal coded CM or JM octet string>  */
+    /* TODO: */
+    t += 4;
+    return t;
+}
+/*- End of function --------------------------------------------------------*/
+
+static const char *at_cmd_plus_A8R(at_state_t *s, const char *t)
+{
+    /* V.251 6.6 - V.8bis signal and message reporting */
+    return t;
+}
+/*- End of function --------------------------------------------------------*/
+
+static const char *at_cmd_plus_A8T(at_state_t *s, const char *t)
+{
+    int val;
+
+    /* V.251 5.3 - Send V.8bis signal and/or message(s) */
+    /* Syntax: +A8T=<signal>[,<1st message>][,<2nd message>][,<sig_en>][,<msg_en>][,<supp_delay>] */
+    /*  <signal>=0  None
+        <signal>=1  Initiating Mre
+        <signal>=2  Initiating MRd
+        <signal>=3  Initiating CRe, low power
+        <signal>=4  Initiating CRe, high power
+        <signal>=5  Initiating CRd
+        <signal>=6  Initiating Esi
+        <signal>=7  Responding MRd, low power
+        <signal>=8  Responding MRd, high power
+        <signal>=9  Responding CRd
+        <signal>=10 Responding Esr
+    */
+    /* TODO: */
+    t += 4;
+    if (!parse_out(s, &t, &val, 10, "+A8T:", "(0-10)"))
+        return NULL;
+    s->v8bis_signal = val;
+    if (*t != ',')
+        return t;
+    if ((val = parse_num(&t, 255)) < 0)
+        return NULL;
+    s->v8bis_1st_message = val;
+    if (*t != ',')
+        return t;
+    if ((val = parse_num(&t, 255)) < 0)
+        return NULL;
+    s->v8bis_2nd_message = val;
+    if (*t != ',')
+        return t;
+    if ((val = parse_num(&t, 255)) < 0)
+        return NULL;
+    s->v8bis_sig_en = val;
+    if (*t != ',')
+        return t;
+    if ((val = parse_num(&t, 255)) < 0)
+        return NULL;
+    s->v8bis_msg_en = val;
+    if (*t != ',')
+        return t;
+    if ((val = parse_num(&t, 255)) < 0)
+        return NULL;
+    s->v8bis_supp_delay = val;
+    return t;
+}
+/*- End of function --------------------------------------------------------*/
+
+static const char *at_cmd_plus_ASTO(at_state_t *s, const char *t)
+{
+    /* V.250 6.3.15 - Store telephone number */ 
+    /* TODO: */
+    t += 5;
+    if (!parse_out(s, &t, NULL, 1, "+ASTO:", ""))
+        return NULL;
+    return t;
+}
+/*- End of function --------------------------------------------------------*/
+
+static const char *at_cmd_plus_CAAP(at_state_t *s, const char *t)
+{
+    /* 3GPP TS 27.007 7.25 - Automatic answer for eMLPP Service */
+    /* TODO: */
+    t += 5;
+    if (!parse_2_out(s, &t, NULL, 65535, NULL, 65535, "+CAAP:", ""))
+        return NULL;
+    return t;
+}
+/*- End of function --------------------------------------------------------*/
+
+static const char *at_cmd_plus_CACM(at_state_t *s, const char *t)
+{
+    /* 3GPP TS 27.007 8.25 - Accumulated call meter */
+    /* TODO: */
+    t += 5;
+    if (!parse_out(s, &t, NULL, 1, "+CACM:", ""))
+        return NULL;
+    return t;
+}
+/*- End of function --------------------------------------------------------*/
+
+static const char *at_cmd_plus_CACSP(at_state_t *s, const char *t)
+{
+    /* 3GPP TS 27.007 11.1.7 - Voice Group or Voice Broadcast Call State Attribute Presentation */
+    /* TODO: */
+    t += 6;
+    if (!parse_out(s, &t, NULL, 1, "+CACSP:", ""))
+        return NULL;
+    return t;
+}
+/*- End of function --------------------------------------------------------*/
+
+static const char *at_cmd_plus_CAD(at_state_t *s, const char *t)
+{
+    /* IS-99 5.6.3 - Query analogue or digital service */
+    /* TODO: */
+    t += 4;
+    return t;
+}
+/*- End of function --------------------------------------------------------*/
+
+static const char *at_cmd_plus_CAEMLPP(at_state_t *s, const char *t)
+{
+    /* 3GPP TS 27.007 7.22 - eMLPP Priority Registration and Interrogation */
+    /* TODO: */
+    t += 8;
+    if (!parse_out(s, &t, NULL, 1, "+CAEMLPP:", ""))
+        return NULL;
+    return t;
+}
+/*- End of function --------------------------------------------------------*/
+
+static const char *at_cmd_plus_CAHLD(at_state_t *s, const char *t)
+{
+    /* 3GPP TS 27.007 11.1.3 - Leave an ongoing Voice Group or Voice Broadcast Call */
+    /* TODO: */
+    t += 6;
+    if (!parse_out(s, &t, NULL, 1, "+CAHLD:", ""))
+        return NULL;
+    return t;
+}
+/*- End of function --------------------------------------------------------*/
+
+static const char *at_cmd_plus_CAJOIN(at_state_t *s, const char *t)
+{
+    /* 3GPP TS 27.007 11.1.1 - Accept an incoming Voice Group or Voice Broadcast Call */
+    /* TODO: */
+    t += 7;
+    if (!parse_out(s, &t, NULL, 1, "+CAJOIN:", ""))
+        return NULL;
+    return t;
+}
+/*- End of function --------------------------------------------------------*/
+
+static const char *at_cmd_plus_CALA(at_state_t *s, const char *t)
+{
+    /* 3GPP TS 27.007 8.16 - Alarm */
+    /* TODO: */
+    t += 5;
+    if (!parse_out(s, &t, NULL, 1, "+CALA:", ""))
+        return NULL;
+    return t;
+}
+/*- End of function --------------------------------------------------------*/
+
+static const char *at_cmd_plus_CALCC(at_state_t *s, const char *t)
+{
+    /* 3GPP TS 27.007 11.1.6 - List current Voice Group and Voice Broadcast Calls */
+    /* TODO: */
+    t += 6;
+    if (!parse_out(s, &t, NULL, 1, "+CALCC:", ""))
+        return NULL;
+    return t;
+}
+/*- End of function --------------------------------------------------------*/
+
+static const char *at_cmd_plus_CALD(at_state_t *s, const char *t)
+{
+    /* 3GPP TS 27.007 8.38 - Delete alarm */
+    /* TODO: */
+    t += 5;
+    if (!parse_out(s, &t, NULL, 1, "+CALD:", ""))
+        return NULL;
+    return t;
+}
+/*- End of function --------------------------------------------------------*/
+
+static const char *at_cmd_plus_CALM(at_state_t *s, const char *t)
+{
+    /* 3GPP TS 27.007 8.20 - Alert sound mode */
+    /* TODO: */
+    t += 5;
+    if (!parse_out(s, &t, NULL, 1, "+CALM:", ""))
+        return NULL;
+    return t;
+}
+/*- End of function --------------------------------------------------------*/
+
+static const char *at_cmd_plus_CAMM(at_state_t *s, const char *t)
+{
+    /* 3GPP TS 27.007 8.26 - Accumulated call meter maximum */
+    /* TODO: */
+    t += 5;
+    if (!parse_out(s, &t, NULL, 1, "+CAMM:", ""))
+        return NULL;
+    return t;
+}
+/*- End of function --------------------------------------------------------*/
+
+static const char *at_cmd_plus_CANCHEV(at_state_t *s, const char *t)
+{
+    /* 3GPP TS 27.007 11.1.8 - NCH Support Indication */
+    /* TODO: */
+    t += 8;
+    if (!parse_out(s, &t, NULL, 1, "+CANCHEV:", ""))
+        return NULL;
+    return t;
+}
+/*- End of function --------------------------------------------------------*/
+
+static const char *at_cmd_plus_CAOC(at_state_t *s, const char *t)
+{
+    /* 3GPP TS 27.007 7.16 - Advice of Charge */
+    /* TODO: */
+    t += 5;
+    if (!parse_out(s, &t, NULL, 1, "+CAOC:", ""))
+        return NULL;
+    return t;
+}
+/*- End of function --------------------------------------------------------*/
+
+static const char *at_cmd_plus_CAPD(at_state_t *s, const char *t)
+{
+    /* 3GPP TS 27.007 8.39 - Postpone or dismiss an alarm */
+    /* TODO: */
+    t += 5;
+    if (!parse_out(s, &t, NULL, 1, "+CAPD:", ""))
+        return NULL;
+    return t;
+}
+/*- End of function --------------------------------------------------------*/
+
+static const char *at_cmd_plus_CAPTT(at_state_t *s, const char *t)
+{
+    /* 3GPP TS 27.007 11.1.4 - Talker Access for Voice Group Call */
+    /* TODO: */
+    t += 6;
+    if (!parse_out(s, &t, NULL, 1, "+CAPTT:", ""))
+        return NULL;
+    return t;
+}
+/*- End of function --------------------------------------------------------*/
+
+static const char *at_cmd_plus_CAREJ(at_state_t *s, const char *t)
+{
+    /* 3GPP TS 27.007 11.1.2 - Reject an incoming Voice Group or Voice Broadcast Call */
+    /* TODO: */
+    t += 6;
+    if (!parse_out(s, &t, NULL, 1, "+CAREJ:", ""))
+        return NULL;
+    return t;
+}
+/*- End of function --------------------------------------------------------*/
+
+static const char *at_cmd_plus_CAULEV(at_state_t *s, const char *t)
+{
+    /* 3GPP TS 27.007 11.1.5 - Voice Group Call Uplink Status Presentation */
+    /* TODO: */
+    t += 7;
+    if (!parse_out(s, &t, NULL, 1, "+CAULEV:", ""))
+        return NULL;
+    return t;
+}
+/*- End of function --------------------------------------------------------*/
+
+static const char *at_cmd_plus_CBC(at_state_t *s, const char *t)
+{
+    /* 3GPP TS 27.007 8.4 - Battery charge */
+    /* TODO: */
+    t += 4;
+    if (!parse_out(s, &t, NULL, 1, "+CBC:", ""))
+        return NULL;
+    return t;
+}
+/*- End of function --------------------------------------------------------*/
+
+static const char *at_cmd_plus_CBCS(at_state_t *s, const char *t)
+{
+    /* 3GPP TS 27.007 11.3.2 - VBS subscriptions and GId status */
+    /* TODO: */
+    t += 5;
+    if (!parse_out(s, &t, NULL, 1, "+CBCS:", ""))
+        return NULL;
+    return t;
+}
+/*- End of function --------------------------------------------------------*/
+
+static const char *at_cmd_plus_CBIP(at_state_t *s, const char *t)
+{
+    /* IS-99 5.6 - Base station IP address */
+    /* TODO: */
+    t += 5;
+    return t;
+}
+/*- End of function --------------------------------------------------------*/
+
+static const char *at_cmd_plus_CBST(at_state_t *s, const char *t)
+{
+    /* 3GPP TS 27.007 6.7 - Select bearer service type */
+    /* TODO: */
+    t += 5;
+    if (!parse_out(s, &t, NULL, 1, "+CBST:", ""))
+        return NULL;
+    return t;
+}
+/*- End of function --------------------------------------------------------*/
+
+static const char *at_cmd_plus_CCFC(at_state_t *s, const char *t)
+{
+    /* 3GPP TS 27.007 7.11 - Call forwarding number and conditions */
+    /* TODO: */
+    t += 5;
+    if (!parse_out(s, &t, NULL, 1, "+CCFC:", ""))
+        return NULL;
+    return t;
+}
+/*- End of function --------------------------------------------------------*/
+
+static const char *at_cmd_plus_CCLK(at_state_t *s, const char *t)
+{
+    /* 3GPP TS 27.007 8.15 - Clock */
+    /* TODO: */
+    t += 5;
+    if (!parse_out(s, &t, NULL, 1, "+CCLK:", ""))
+        return NULL;
+    return t;
+}
+/*- End of function --------------------------------------------------------*/
+
+static const char *at_cmd_plus_CCS(at_state_t *s, const char *t)
+{
+    /* IS-135 4.1.22 - Compression status */
+    /* TODO: */
+    t += 4;
+    return t;
+}
+/*- End of function --------------------------------------------------------*/
+
+static const char *at_cmd_plus_CCUG(at_state_t *s, const char *t)
+{
+    /* 3GPP TS 27.007 7.10 - Closed user group */
+    /* TODO: */
+    t += 5;
+    if (!parse_out(s, &t, NULL, 1, "+CCUG:", ""))
+        return NULL;
+    return t;
+}
+/*- End of function --------------------------------------------------------*/
+
+static const char *at_cmd_plus_CCWA(at_state_t *s, const char *t)
+{
+    /* 3GPP TS 27.007 7.12 - Call waiting */
+    /* TODO: */
+    t += 5;
+    if (!parse_out(s, &t, NULL, 1, "+CCWA:", ""))
+        return NULL;
+    return t;
+}
+/*- End of function --------------------------------------------------------*/
+
+static const char *at_cmd_plus_CCWE(at_state_t *s, const char *t)
+{
+    /* 3GPP TS 27.007 8.28 - Call Meter maximum event */
+    /* TODO: */
+    t += 5;
+    if (!parse_out(s, &t, NULL, 1, "+CCWE:", ""))
+        return NULL;
+    return t;
+}
+/*- End of function --------------------------------------------------------*/
+
+static const char *at_cmd_plus_CDIP(at_state_t *s, const char *t)
+{
+    /* 3GPP TS 27.007 7.9 - Called line identification presentation */
+    /* TODO: */
+    t += 5;
+    if (!parse_out(s, &t, NULL, 1, "+CDIP:", ""))
+        return NULL;
+    return t;
+}
+/*- End of function --------------------------------------------------------*/
+
+static const char *at_cmd_plus_CDIS(at_state_t *s, const char *t)
+{
+    /* 3GPP TS 27.007 8.8 - Display control */
+    /* TODO: */
+    t += 5;
+    if (!parse_out(s, &t, NULL, 1, "+CDIS:", ""))
+        return NULL;
+    return t;
+}
+/*- End of function --------------------------------------------------------*/
+
+static const char *at_cmd_plus_CDV(at_state_t *s, const char *t)
+{
+    /* IS-99 5.6 - Dial command for voice call */
+    /* TODO: */
+    t += 4;
+    return t;
+}
+/*- End of function --------------------------------------------------------*/
+
+static const char *at_cmd_plus_CEER(at_state_t *s, const char *t)
+{
+    /* 3GPP TS 27.007 6.10 - Extended error report */
+    /* TODO: */
+    t += 5;
+    if (!parse_out(s, &t, NULL, 1, "+CEER:", ""))
+        return NULL;
+    return t;
+}
+/*- End of function --------------------------------------------------------*/
+
+static const char *at_cmd_plus_CESP(at_state_t *s, const char *t)
+{
+    /* GSM07.05  3.2.4 - Enter SMS block mode protocol */
+    /* TODO: */
+    t += 5;
+    return t;
+}
+/*- End of function --------------------------------------------------------*/
+
+static const char *at_cmd_plus_CFCS(at_state_t *s, const char *t)
+{
+    /* 3GPP TS 27.007 7.24 - Fast call setup conditions */
+    /* TODO: */
+    t += 5;
+    if (!parse_out(s, &t, NULL, 1, "+CFCS:", ""))
+        return NULL;
+    return t;
+}
+/*- End of function --------------------------------------------------------*/
+
+static const char *at_cmd_plus_CFG(at_state_t *s, const char *t)
+{
+    /* IS-99 5.6 - Configuration string */
+    /* TODO: */
+    t += 4;
+    return t;
+}
+/*- End of function --------------------------------------------------------*/
+
+static const char *at_cmd_plus_CFUN(at_state_t *s, const char *t)
+{
+    /* 3GPP TS 27.007 8.2 - Set phone functionality */
+    /* TODO: */
+    t += 5;
+    if (!parse_out(s, &t, NULL, 1, "+CFUN:", ""))
+        return NULL;
+    return t;
+}
+/*- End of function --------------------------------------------------------*/
+
+static const char *at_cmd_plus_CGACT(at_state_t *s, const char *t)
+{
+    /* 3GPP TS 27.007 10.1.10 - PDP context activate or deactivate */
+    /* TODO: */
+    t += 6;
+    if (!parse_out(s, &t, NULL, 1, "+CGACT:", ""))
+        return NULL;
+    return t;
+}
+/*- End of function --------------------------------------------------------*/
+
+static const char *at_cmd_plus_CGANS(at_state_t *s, const char *t)
+{
+    /* 3GPP TS 27.007 10.1.16 - Manual response to a network request for PDP context activation */
+    /* TODO: */
+    t += 6;
+    if (!parse_out(s, &t, NULL, 1, "+CGANS:", ""))
+        return NULL;
+    return t;
+}
+/*- End of function --------------------------------------------------------*/
+
+static const char *at_cmd_plus_CGATT(at_state_t *s, const char *t)
+{
+    /* 3GPP TS 27.007 10.1.9 - PS attach or detach */
+    /* TODO: */
+    t += 6;
+    if (!parse_out(s, &t, NULL, 1, "+CGATT:", ""))
+        return NULL;
+    return t;
+}
+/*- End of function --------------------------------------------------------*/
+
+static const char *at_cmd_plus_CGAUTO(at_state_t *s, const char *t)
+{
+    /* 3GPP TS 27.007 10.1.15 - Automatic response to a network request for PDP context activation */
+    /* TODO: */
+    t += 7;
+    if (!parse_out(s, &t, NULL, 1, "+CGAUTO:", ""))
+        return NULL;
+    return t;
+}
+/*- End of function --------------------------------------------------------*/
+
+static const char *at_cmd_plus_CGCAP(at_state_t *s, const char *t)
+{
+    /* IS-99 5.6 - Request complete capabilities list */
+    /* TODO: */
+    t += 6;
+    return t;
+}
+/*- End of function --------------------------------------------------------*/
+
+static const char *at_cmd_plus_CGCLASS(at_state_t *s, const char *t)
+{
+    /* 3GPP TS 27.007 10.1.17 - GPRS mobile station class (GPRS only) */
+    /* TODO: */
+    t += 8;
+    if (!parse_out(s, &t, NULL, 1, "+CGCLASS:", ""))
+        return NULL;
+    return t;
+}
+/*- End of function --------------------------------------------------------*/
+
+static const char *at_cmd_plus_CGCLOSP(at_state_t *s, const char *t)
+{
+    /* 3GPP TS 27.007 10.1.13 - Configure local Octet Stream PAD parameters (Obsolete) */
+    /* TODO: */
+    t += 8;
+    if (!parse_out(s, &t, NULL, 1, "+CGCLOSP:", ""))
+        return NULL;
+    return t;
+}
+/*- End of function --------------------------------------------------------*/
+
+static const char *at_cmd_plus_CGCLPAD(at_state_t *s, const char *t)
+{
+    /* 3GPP TS 27.007 10.1.12 - Configure local triple-X PAD parameters (GPRS only) (Obsolete) */
+    /* TODO: */
+    t += 8;
+    if (!parse_out(s, &t, NULL, 1, "+CGCLPAD:", ""))
+        return NULL;
+    return t;
+}
+/*- End of function --------------------------------------------------------*/
+
+static const char *at_cmd_plus_CGCMOD(at_state_t *s, const char *t)
+{
+    /* 3GPP TS 27.007 10.1.11 - PDP Context Modify */
+    /* TODO: */
+    t += 7;
+    if (!parse_out(s, &t, NULL, 1, "+CGCMOD:", ""))
+        return NULL;
+    return t;
+}
+/*- End of function --------------------------------------------------------*/
+
+static const char *at_cmd_plus_CGCS(at_state_t *s, const char *t)
+{
+    /* 3GPP TS 27.007 11.3.1 - VGCS subscriptions and GId status */
+    /* TODO: */
+    t += 5;
+    if (!parse_out(s, &t, NULL, 1, "+CGCS:", ""))
+        return NULL;
+    return t;
+}
+/*- End of function --------------------------------------------------------*/
+
+static const char *at_cmd_plus_CGDATA(at_state_t *s, const char *t)
+{
+    /* 3GPP TS 27.007 10.1.12 - Enter data state */
+    /* TODO: */
+    t += 7;
+    if (!parse_out(s, &t, NULL, 1, "+CGDATA:", ""))
+        return NULL;
+    return t;
+}
+/*- End of function --------------------------------------------------------*/
+
+static const char *at_cmd_plus_CGDCONT(at_state_t *s, const char *t)
+{
+    /* 3GPP TS 27.007 10.1.1 - Define PDP Context */
+    /* TODO: */
+    t += 8;
+    if (!parse_out(s, &t, NULL, 1, "+CGDCONT:", ""))
+        return NULL;
+    return t;
+}
+/*- End of function --------------------------------------------------------*/
+
+static const char *at_cmd_plus_CGDSCONT(at_state_t *s, const char *t)
+{
+    /* 3GPP TS 27.007 10.1.2 - Define Secondary PDP Context */
+    /* TODO: */
+    t += 9;
+    if (!parse_out(s, &t, NULL, 1, "+CGDSCONT:", ""))
+        return NULL;
+    return t;
+}
+/*- End of function --------------------------------------------------------*/
+
+static const char *at_cmd_plus_CGEQMIN(at_state_t *s, const char *t)
+{
+    /* 3GPP TS 27.007 10.1.7 - 3G Quality of Service Profile (Minimum acceptable) */
+    /* TODO: */
+    t += 8;
+    if (!parse_out(s, &t, NULL, 1, "+CGEQMIN:", ""))
+        return NULL;
+    return t;
+}
+/*- End of function --------------------------------------------------------*/
+
+static const char *at_cmd_plus_CGEQNEG(at_state_t *s, const char *t)
+{
+    /* 3GPP TS 27.007 10.1.8 - 3G Quality of Service Profile (Negotiated) */
+    /* TODO: */
+    t += 8;
+    if (!parse_out(s, &t, NULL, 1, "+CGEQNEG:", ""))
+        return NULL;
+    return t;
+}
+/*- End of function --------------------------------------------------------*/
+
+static const char *at_cmd_plus_CGEQREQ(at_state_t *s, const char *t)
+{
+    /* 3GPP TS 27.007 10.1.6 - 3G Quality of Service Profile (Requested) */
+    /* TODO: */
+    t += 8;
+    if (!parse_out(s, &t, NULL, 1, "+CGEQREQ:", ""))
+        return NULL;
+    return t;
+}
+/*- End of function --------------------------------------------------------*/
+
+static const char *at_cmd_plus_CGEREP(at_state_t *s, const char *t)
+{
+    /* 3GPP TS 27.007 10.1.18 - Packet Domain event reporting */
+    /* TODO: */
+    t += 7;
+    if (!parse_out(s, &t, NULL, 1, "+CGEREP:", ""))
+        return NULL;
+    return t;
+}
+/*- End of function --------------------------------------------------------*/
+
+static const char *at_cmd_plus_CGMI(at_state_t *s, const char *t)
+{
+    /* 3GPP TS 27.007 5.1 - Request manufacturer identification */
+    /* TODO: */
+    t += 5;
+    if (!parse_out(s, &t, NULL, 1, "+CGMI:", ""))
+        return NULL;
+    return t;
+}
+/*- End of function --------------------------------------------------------*/
+
+static const char *at_cmd_plus_CGMM(at_state_t *s, const char *t)
+{
+    /* 3GPP TS 27.007 5.2 - Request model identification */
+    /* TODO: */
+    t += 5;
+    if (!parse_out(s, &t, NULL, 1, "+CGMM:", ""))
+        return NULL;
+    return t;
+}
+/*- End of function --------------------------------------------------------*/
+
+static const char *at_cmd_plus_CGMR(at_state_t *s, const char *t)
+{
+    /* 3GPP TS 27.007 5.3 - Request revision identification */
+    /* TODO: */
+    t += 5;
+    if (!parse_out(s, &t, NULL, 1, "+CGMR:", ""))
+        return NULL;
+    return t;
+}
+/*- End of function --------------------------------------------------------*/
+
+static const char *at_cmd_plus_CGOI(at_state_t *s, const char *t)
+{
+    /* IS-99 5.6 - Request global object identification */
+    /* TODO: */
+    t += 5;
+    return t;
+}
+/*- End of function --------------------------------------------------------*/
+
+static const char *at_cmd_plus_CGPADDR(at_state_t *s, const char *t)
+{
+    /* 3GPP TS 27.007 10.1.14 - Show PDP address */
+    /* TODO: */
+    t += 8;
+    if (!parse_out(s, &t, NULL, 1, "+CGPADDR:", ""))
+        return NULL;
+    return t;
+}
+/*- End of function --------------------------------------------------------*/
+
+static const char *at_cmd_plus_CGQMIN(at_state_t *s, const char *t)
+{
+    /* 3GPP TS 27.007 10.1.5 - Quality of Service Profile (Minimum acceptable) */
+    /* TODO: */
+    t += 7;
+    if (!parse_out(s, &t, NULL, 1, "+CGQMIN:", ""))
+        return NULL;
+    return t;
+}
+/*- End of function --------------------------------------------------------*/
+
+static const char *at_cmd_plus_CGQREQ(at_state_t *s, const char *t)
+{
+    /* 3GPP TS 27.007 10.1.4 - Quality of Service Profile (Requested) */
+    /* TODO: */
+    t += 7;
+    if (!parse_out(s, &t, NULL, 1, "+CGQREQ:", ""))
+        return NULL;
+    return t;
+}
+/*- End of function --------------------------------------------------------*/
+
+static const char *at_cmd_plus_CGREG(at_state_t *s, const char *t)
+{
+    /* 3GPP TS 27.007 10.1.19 - GPRS network registration status */
+    /* TODO: */
+    t += 6;
+    if (!parse_out(s, &t, NULL, 1, "+CGREG:", ""))
+        return NULL;
+    return t;
+}
+/*- End of function --------------------------------------------------------*/
+
+static const char *at_cmd_plus_CGSMS(at_state_t *s, const char *t)
+{
+    /* 3GPP TS 27.007 10.1.20 - Select service for MO SMS messages */
+    /* TODO: */
+    t += 6;
+    if (!parse_out(s, &t, NULL, 1, "+CGSMS:", ""))
+        return NULL;
+    return t;
+}
+/*- End of function --------------------------------------------------------*/
+
+static const char *at_cmd_plus_CGSN(at_state_t *s, const char *t)
+{
+    /* 3GPP TS 27.007 5.4 - Request product serial number identification */
+    /* TODO: */
+    t += 5;
+    if (!parse_out(s, &t, NULL, 1, "+CGSN:", ""))
+        return NULL;
+    return t;
+}
+/*- End of function --------------------------------------------------------*/
+
+static const char *at_cmd_plus_CGTFT(at_state_t *s, const char *t)
+{
+    /* 3GPP TS 27.007 10.1.3 - Traffic Flow Template */
+    /* TODO: */
+    t += 6;
+    if (!parse_out(s, &t, NULL, 1, "+CGTFT:", ""))
+        return NULL;
+    return t;
+}
+/*- End of function --------------------------------------------------------*/
+
+static const char *at_cmd_plus_CHLD(at_state_t *s, const char *t)
+{
+    /* 3GPP TS 27.007 7.13 - Call related supplementary services */
+    /* TODO: */
+    t += 5;
+    if (!parse_out(s, &t, NULL, 1, "+CHLD:", ""))
+        return NULL;
+    return t;
+}
+/*- End of function --------------------------------------------------------*/
+
+static const char *at_cmd_plus_CHSA(at_state_t *s, const char *t)
+{
+    /* 3GPP TS 27.007 6.18 - HSCSD non-transparent asymmetry configuration */
+    /* TODO: */
+    t += 5;
+    if (!parse_out(s, &t, NULL, 1, "+CHSA:", ""))
+        return NULL;
+    return t;
+}
+/*- End of function --------------------------------------------------------*/
+
+static const char *at_cmd_plus_CHSC(at_state_t *s, const char *t)
+{
+    /* 3GPP TS 27.007 6.15 - HSCSD current call parameters */
+    /* TODO: */
+    t += 5;
+    if (!parse_out(s, &t, NULL, 1, "+CHSC:", ""))
+        return NULL;
+    return t;
+}
+/*- End of function --------------------------------------------------------*/
+
+static const char *at_cmd_plus_CHSD(at_state_t *s, const char *t)
+{
+    /* 3GPP TS 27.007 6.12 - HSCSD device parameters */
+    /* TODO: */
+    t += 5;
+    if (!parse_out(s, &t, NULL, 1, "+CHSD:", ""))
+        return NULL;
+    return t;
+}
+/*- End of function --------------------------------------------------------*/
+
+static const char *at_cmd_plus_CHSN(at_state_t *s, const char *t)
+{
+    /* 3GPP TS 27.007 6.14 - HSCSD non-transparent call configuration */
+    /* TODO: */
+    t += 5;
+    if (!parse_out(s, &t, NULL, 1, "+CHSN:", ""))
+        return NULL;
+    return t;
+}
+/*- End of function --------------------------------------------------------*/
+
+static const char *at_cmd_plus_CHSR(at_state_t *s, const char *t)
+{
+    /* 3GPP TS 27.007 6.16 - HSCSD parameters report */
+    /* TODO: */
+    t += 5;
+    if (!parse_out(s, &t, NULL, 1, "+CHSR:", ""))
+        return NULL;
+    return t;
+}
+/*- End of function --------------------------------------------------------*/
+
+static const char *at_cmd_plus_CHST(at_state_t *s, const char *t)
+{
+    /* 3GPP TS 27.007 6.13 - HSCSD transparent call configuration */
+    /* TODO: */
+    t += 5;
+    if (!parse_out(s, &t, NULL, 1, "+CHST:", ""))
+        return NULL;
+    return t;
+}
+/*- End of function --------------------------------------------------------*/
+
+static const char *at_cmd_plus_CHSU(at_state_t *s, const char *t)
+{
+    /* 3GPP TS 27.007 6.17 - HSCSD automatic user initiated upgrading */
+    /* TODO: */
+    t += 5;
+    if (!parse_out(s, &t, NULL, 1, "+CHSU:", ""))
+        return NULL;
+    return t;
+}
+/*- End of function --------------------------------------------------------*/
+
+static const char *at_cmd_plus_CHUP(at_state_t *s, const char *t)
+{
+    /* 3GPP TS 27.007 6.5 - Hangup call */
+    /* TODO: */
+    t += 5;
+    if (!parse_out(s, &t, NULL, 1, "+CHUP:", ""))
+        return NULL;
+    return t;
+}
+/*- End of function --------------------------------------------------------*/
+
+static const char *at_cmd_plus_CHV(at_state_t *s, const char *t)
+{
+    /* IS-99 5.6 - Hang-up voice */
+    /* TODO: */
+    t += 4;
+    return t;
+}
+/*- End of function --------------------------------------------------------*/
+
+static const char *at_cmd_plus_CIMI(at_state_t *s, const char *t)
+{
+    /* 3GPP TS 27.007 5.6 - Request international mobile subscriber identity */
+    /* TODO: */
+    t += 5;
+    if (!parse_out(s, &t, NULL, 1, "+CIMI:", ""))
+        return NULL;
+    return t;
+}
+/*- End of function --------------------------------------------------------*/
+
+static const char *at_cmd_plus_CIND(at_state_t *s, const char *t)
+{
+    /* 3GPP TS 27.007 8.9 - Indicator control */
+    /* TODO: */
+    t += 5;
+    if (!parse_out(s, &t, NULL, 1, "+CIND:", ""))
+        return NULL;
+    return t;
+}
+/*- End of function --------------------------------------------------------*/
+
+static const char *at_cmd_plus_CIT(at_state_t *s, const char *t)
+{
+    /* IS-99 5.6 - Command state inactivity timer */
+    /* TODO: */
+    t += 4;
+    return t;
+}
+/*- End of function --------------------------------------------------------*/
+
+static const char *at_cmd_plus_CKPD(at_state_t *s, const char *t)
+{
+    /* 3GPP TS 27.007 8.7 - Keypad control */
+    /* TODO: */
+    t += 5;
+    if (!parse_out(s, &t, NULL, 1, "+CKPD:", ""))
+        return NULL;
+    return t;
+}
+/*- End of function --------------------------------------------------------*/
+
+static const char *at_cmd_plus_CLAC(at_state_t *s, const char *t)
+{
+    /* 3GPP TS 27.007 8.37 - List all available AT commands */
+    /* TODO: */
+    t += 5;
+    if (!parse_out(s, &t, NULL, 1, "+CLAC:", ""))
+        return NULL;
+    return t;
+}
+/*- End of function --------------------------------------------------------*/
+
+static const char *at_cmd_plus_CLAE(at_state_t *s, const char *t)
+{
+    /* 3GPP TS 27.007 8.31 - Language Event */
+    /* TODO: */
+    t += 5;
+    if (!parse_out(s, &t, NULL, 1, "+CLAE:", ""))
+        return NULL;
+    return t;
+}
+/*- End of function --------------------------------------------------------*/
+
+static const char *at_cmd_plus_CLAN(at_state_t *s, const char *t)
+{
+    /* 3GPP TS 27.007 8.30 - Set Language */
+    /* TODO: */
+    t += 5;
+    if (!parse_out(s, &t, NULL, 1, "+CLAN:", ""))
+        return NULL;
+    return t;
+}
+/*- End of function --------------------------------------------------------*/
+
+static const char *at_cmd_plus_CLCC(at_state_t *s, const char *t)
+{
+    /* 3GPP TS 27.007 7.18 - List current calls */
+    /* TODO: */
+    t += 5;
+    if (!parse_out(s, &t, NULL, 1, "+CLCC:", ""))
+        return NULL;
+    return t;
+}
+/*- End of function --------------------------------------------------------*/
+
+static const char *at_cmd_plus_CLCK(at_state_t *s, const char *t)
+{
+    /* 3GPP TS 27.007 7.4 - Facility lock */
+    /* TODO: */
+    t += 5;
+    if (!parse_out(s, &t, NULL, 1, "+CLCK:", ""))
+        return NULL;
+    return t;
+}
+/*- End of function --------------------------------------------------------*/
+
+static const char *at_cmd_plus_CLIP(at_state_t *s, const char *t)
+{
+    /* 3GPP TS 27.007 7.6 - Calling line identification presentation */
+    /* TODO: */
+    t += 5;
+    if (!parse_out(s, &t, NULL, 1, "+CLIP:", ""))
+        return NULL;
+    return t;
+}
+/*- End of function --------------------------------------------------------*/
+
+static const char *at_cmd_plus_CLIR(at_state_t *s, const char *t)
+{
+    /* 3GPP TS 27.007 7.7 - Calling line identification restriction */
+    /* TODO: */
+    t += 5;
+    if (!parse_out(s, &t, NULL, 1, "+CLIR:", ""))
+        return NULL;
+    return t;
+}
+/*- End of function --------------------------------------------------------*/
+
+static const char *at_cmd_plus_CLVL(at_state_t *s, const char *t)
+{
+    /* 3GPP TS 27.007 8.23 - Loudspeaker volume level */
+    /* TODO: */
+    t += 5;
+    if (!parse_out(s, &t, NULL, 1, "+CLVL:", ""))
+        return NULL;
+    return t;
+}
+/*- End of function --------------------------------------------------------*/
+
+static const char *at_cmd_plus_CMAR(at_state_t *s, const char *t)
+{
+    /* 3GPP TS 27.007 8.36 - Master Reset */
+    /* TODO: */
+    t += 5;
+    if (!parse_out(s, &t, NULL, 1, "+CMAR:", ""))
+        return NULL;
+    return t;
+}
+/*- End of function --------------------------------------------------------*/
+
+static const char *at_cmd_plus_CMEC(at_state_t *s, const char *t)
+{
+    /* 3GPP TS 27.007 8.6 - Mobile Termination control mode */
+    /* TODO: */
+    t += 5;
+    if (!parse_out(s, &t, NULL, 1, "+CMEC:", ""))
+        return NULL;
+    return t;
+}
+/*- End of function --------------------------------------------------------*/
+
+static const char *at_cmd_plus_CMEE(at_state_t *s, const char *t)
+{
+    /* GSM07.07 9.1 - Report mobile equipment error */
+    /* TODO: */
+    t += 5;
+    return t;
+}
+/*- End of function --------------------------------------------------------*/
+
+static const char *at_cmd_plus_CMER(at_state_t *s, const char *t)
+{
+    /* 3GPP TS 27.007 8.10 - Mobile Termination event reporting */
+    /* TODO: */
+    t += 5;
+    if (!parse_out(s, &t, NULL, 1, "+CMER:", ""))
+        return NULL;
+    return t;
+}
+/*- End of function --------------------------------------------------------*/
+
+static const char *at_cmd_plus_CMGC(at_state_t *s, const char *t)
+{
+    /* GSM07.05 3.5.5/4.5 - Send command */
+    /* TODO: */
+    t += 5;
+    return t;
+}
+/*- End of function --------------------------------------------------------*/
+
+static const char *at_cmd_plus_CMGD(at_state_t *s, const char *t)
+{
+    /* GSM07.05 3.5.4 - Delete message */
+    /* TODO: */
+    t += 5;
+    return t;
+}
+/*- End of function --------------------------------------------------------*/
+
+static const char *at_cmd_plus_CMGF(at_state_t *s, const char *t)
+{
+    /* GSM07.05 3.2.3 - Message Format */
+    /* TODO: */
+    t += 5;
+    return t;
+}
+/*- End of function --------------------------------------------------------*/
+
+static const char *at_cmd_plus_CMGL(at_state_t *s, const char *t)
+{
+    /* GSM07.05 3.4.2/4.1 -  List messages */
+    /* TODO: */
+    t += 5;
+    return t;
+}
+/*- End of function --------------------------------------------------------*/
+
+static const char *at_cmd_plus_CMGR(at_state_t *s, const char *t)
+{
+    /* GSM07.05 3.4.3/4.2 - Read message */
+    /* TODO: */
+    t += 5;
+    return t;
+}
+/*- End of function --------------------------------------------------------*/
+
+static const char *at_cmd_plus_CMGS(at_state_t *s, const char *t)
+{
+    /* GSM07.05 3.5.1/4.3 - Send message */
+    /* TODO: */
+    t += 5;
+    return t;
+}
+/*- End of function --------------------------------------------------------*/
+
+static const char *at_cmd_plus_CMGW(at_state_t *s, const char *t)
+{
+    /* GSM07.05 3.5.3/4.4 - Write message to memory */
+    /* TODO: */
+    t += 5;
+    return t;
+}
+/*- End of function --------------------------------------------------------*/
+
+static const char *at_cmd_plus_CMIP(at_state_t *s, const char *t)
+{
+    /* IS-99 5.6 - Mobile station IP address */
+    /* TODO: */
+    t += 5;
+    return t;
+}
+/*- End of function --------------------------------------------------------*/
+
+static const char *at_cmd_plus_CMM(at_state_t *s, const char *t)
+{
+    /* IS-135 4.1.23 - Menu map */
+    /* TODO: */
+    t += 4;
+    return t;
+}
+/*- End of function --------------------------------------------------------*/
+
+static const char *at_cmd_plus_CMMS(at_state_t *s, const char *t)
+{
+    /* GSM07.05 3.5.6 - More messages to send */
+    /* TODO: */
+    t += 5;
+    return t;
+}
+/*- End of function --------------------------------------------------------*/
+
+static const char *at_cmd_plus_CMOD(at_state_t *s, const char *t)
+{
+    /* 3GPP TS 27.007 6.4 - Call mode */
+    /* TODO: */
+    t += 5;
+    if (!parse_out(s, &t, NULL, 1, "+CMOD:", ""))
+        return NULL;
+    return t;
+}
+/*- End of function --------------------------------------------------------*/
+
+static const char *at_cmd_plus_CMSS(at_state_t *s, const char *t)
+{
+    /* GSM07.05 3.5.2/4.7 - Send message from storage */
+    /* TODO: */
+    t += 5;
+    return t;
+}
+/*- End of function --------------------------------------------------------*/
+
+static const char *at_cmd_plus_CMUT(at_state_t *s, const char *t)
+{
+    /* 3GPP TS 27.007 8.24 - Mute control */
+    /* TODO: */
+    t += 5;
+    if (!parse_out(s, &t, NULL, 1, "+CMUT:", ""))
+        return NULL;
+    return t;
+}
+/*- End of function --------------------------------------------------------*/
+
+static const char *at_cmd_plus_CNMA(at_state_t *s, const char *t)
+{
+    /* GSM07.05 3.4.4/4.6 - New message acknowledgement to terminal adapter */
+    /* TODO: */
+    t += 5;
+    return t;
+}
+/*- End of function --------------------------------------------------------*/
+
+static const char *at_cmd_plus_CNMI(at_state_t *s, const char *t)
+{
+    /* GSM07.05 3.4.1 - New message indications to terminal equipment */
+    /* TODO: */
+    t += 5;
+    return t;
+}
+/*- End of function --------------------------------------------------------*/
+
+static const char *at_cmd_plus_CMUX(at_state_t *s, const char *t)
+{
+    /* 3GPP TS 27.007 5.7 - Multiplexing mode */
+    /* TODO: */
+    t += 5;
+    if (!parse_out(s, &t, NULL, 1, "+CMUX:", ""))
+        return NULL;
+    return t;
+}
+/*- End of function --------------------------------------------------------*/
+
+static const char *at_cmd_plus_CNUM(at_state_t *s, const char *t)
+{
+    /* 3GPP TS 27.007 7.1 - Subscriber number */
+    /* TODO: */
+    t += 5;
+    if (!parse_out(s, &t, NULL, 1, "+CNUM:", ""))
+        return NULL;
+    return t;
+}
+/*- End of function --------------------------------------------------------*/
+
+static const char *at_cmd_plus_COLP(at_state_t *s, const char *t)
+{
+    /* 3GPP TS 27.007 7.8 - Connected line identification presentation */
+    /* TODO: */
+    t += 5;
+    if (!parse_out(s, &t, NULL, 1, "+COLP:", ""))
+        return NULL;
+    return t;
+}
+/*- End of function --------------------------------------------------------*/
+
+static const char *at_cmd_plus_COPN(at_state_t *s, const char *t)
+{
+    /* 3GPP TS 27.007 7.21 - Read operator names */
+    /* TODO: */
+    t += 5;
+    if (!parse_out(s, &t, NULL, 1, "+COPN:", ""))
+        return NULL;
+    return t;
+}
+/*- End of function --------------------------------------------------------*/
+
+static const char *at_cmd_plus_COPS(at_state_t *s, const char *t)
+{
+    /* 3GPP TS 27.007 7.3 - PLMN selection */
+    /* TODO: */
+    t += 5;
+    if (!parse_out(s, &t, NULL, 1, "+COPS:", ""))
+        return NULL;
+    return t;
+}
+/*- End of function --------------------------------------------------------*/
+
+static const char *at_cmd_plus_COS(at_state_t *s, const char *t)
+{
+    /* IS-135 4.1.24 - Originating service */
+    /* TODO: */
+    t += 4;
+    return t;
+}
+/*- End of function --------------------------------------------------------*/
+
+static const char *at_cmd_plus_COTDI(at_state_t *s, const char *t)
+{
+    /* 3GPP TS 27.007 11.1.9 - Originator to Dispatcher Information */
+    /* TODO: */
+    t += 6;
+    if (!parse_out(s, &t, NULL, 1, "+COTDI:", ""))
+        return NULL;
+    return t;
+}
+/*- End of function --------------------------------------------------------*/
+
+static const char *at_cmd_plus_CPAS(at_state_t *s, const char *t)
+{
+    /* 3GPP TS 27.007 8.1 - Phone activity status */
+    /* TODO: */
+    t += 5;
+    if (!parse_out(s, &t, NULL, 1, "+CPAS:", ""))
+        return NULL;
+    return t;
+}
+/*- End of function --------------------------------------------------------*/
+
+static const char *at_cmd_plus_CPBF(at_state_t *s, const char *t)
+{
+    /* 3GPP TS 27.007 8.13 - Find phonebook entries */
+    /* TODO: */
+    t += 5;
+    if (!parse_out(s, &t, NULL, 1, "+CPBF:", ""))
+        return NULL;
+    return t;
+}
+/*- End of function --------------------------------------------------------*/
+
+static const char *at_cmd_plus_CPBR(at_state_t *s, const char *t)
+{
+    /* 3GPP TS 27.007 8.12 - Read phonebook entries */
+    /* TODO: */
+    t += 5;
+    if (!parse_out(s, &t, NULL, 1, "+CPBR:", ""))
+        return NULL;
+    return t;
+}
+/*- End of function --------------------------------------------------------*/
+
+static const char *at_cmd_plus_CPBS(at_state_t *s, const char *t)
+{
+    /* 3GPP TS 27.007 8.11 - Select phonebook memory storage */
+    /* TODO: */
+    t += 5;
+    if (!parse_out(s, &t, NULL, 1, "+CPBS:", ""))
+        return NULL;
+    return t;
+}
+/*- End of function --------------------------------------------------------*/
+
+static const char *at_cmd_plus_CPBW(at_state_t *s, const char *t)
+{
+    /* 3GPP TS 27.007 8.14 - Write phonebook entry */
+    /* TODO: */
+    t += 5;
+    if (!parse_out(s, &t, NULL, 1, "+CPBW:", ""))
+        return NULL;
+    return t;
+}
+/*- End of function --------------------------------------------------------*/
+
+static const char *at_cmd_plus_CPIN(at_state_t *s, const char *t)
+{
+    /* 3GPP TS 27.007 8.3 - Enter PIN */
+    /* TODO: */
+    t += 5;
+    if (!parse_out(s, &t, NULL, 1, "+CPIN:", ""))
+        return NULL;
+    return t;
+}
+/*- End of function --------------------------------------------------------*/
+
+static const char *at_cmd_plus_CPLS(at_state_t *s, const char *t)
+{
+    /* 3GPP TS 27.007 7.20 - Selection of preferred PLMN list */
+    /* TODO: */
+    t += 5;
+    if (!parse_out(s, &t, NULL, 1, "+CPLS:", ""))
+        return NULL;
+    return t;
+}
+/*- End of function --------------------------------------------------------*/
+
+static const char *at_cmd_plus_CPMS(at_state_t *s, const char *t)
+{
+    /* GSM07.05 3.2.2 - Preferred message storage */
+    /* TODO: */
+    t += 5;
+    return t;
+}
+/*- End of function --------------------------------------------------------*/
+
+static const char *at_cmd_plus_CPOL(at_state_t *s, const char *t)
+{
+    /* 3GPP TS 27.007 7.19 - Preferred PLMN list */
+    /* TODO: */
+    t += 5;
+    if (!parse_out(s, &t, NULL, 1, "+CPOL:", ""))
+        return NULL;
+    return t;
+}
+/*- End of function --------------------------------------------------------*/
+
+static const char *at_cmd_plus_CPPS(at_state_t *s, const char *t)
+{
+    /* 3GPP TS 27.007 7.23 - eMLPP subscriptions */
+    /* TODO: */
+    t += 5;
+    if (!parse_out(s, &t, NULL, 1, "+CPPS:", ""))
+        return NULL;
+    return t;
+}
+/*- End of function --------------------------------------------------------*/
+
+static const char *at_cmd_plus_CPROT(at_state_t *s, const char *t)
+{
+    /* 3GPP TS 27.007 8.42 - Enter protocol mode */
+    /* TODO: */
+    t += 6;
+    if (!parse_out(s, &t, NULL, 1, "+CPROT:", ""))
+        return NULL;
+    return t;
+}
+/*- End of function --------------------------------------------------------*/
+
+static const char *at_cmd_plus_CPUC(at_state_t *s, const char *t)
+{
+    /* 3GPP TS 27.007 8.27 - Price per unit and currency table */
+    /* TODO: */
+    t += 5;
+    if (!parse_out(s, &t, NULL, 1, "+CPUC:", ""))
+        return NULL;
+    return t;
+}
+/*- End of function --------------------------------------------------------*/
+
+static const char *at_cmd_plus_CPWC(at_state_t *s, const char *t)
+{
+    /* 3GPP TS 27.007 8.29 - Power class */
+    /* TODO: */
+    t += 5;
+    if (!parse_out(s, &t, NULL, 1, "+CPWC:", ""))
+        return NULL;
+    return t;
+}
+/*- End of function --------------------------------------------------------*/
+
+static const char *at_cmd_plus_CPWD(at_state_t *s, const char *t)
+{
+    /* 3GPP TS 27.007 7.5 - Change password */
+    /* TODO: */
+    t += 5;
+    if (!parse_out(s, &t, NULL, 1, "+CPWD:", ""))
+        return NULL;
+    return t;
+}
+/*- End of function --------------------------------------------------------*/
+
+static const char *at_cmd_plus_CQD(at_state_t *s, const char *t)
+{
+    /* IS-135 4.1.25 - Query disconnect timer */
+    /* TODO: */
+    t += 4;
+    return t;
+}
+/*- End of function --------------------------------------------------------*/
+
+static const char *at_cmd_plus_CR(at_state_t *s, const char *t)
+{
+    /* 3GPP TS 27.007 6.9 - Service reporting control */
+    /* TODO: */
+    t += 3;
+    if (!parse_out(s, &t, NULL, 1, "+CR:", ""))
+        return NULL;
+    return t;
+}
+/*- End of function --------------------------------------------------------*/
+
+static const char *at_cmd_plus_CRC(at_state_t *s, const char *t)
+{
+    /* 3GPP TS 27.007 6.11 - Cellular result codes */
+    /* TODO: */
+    t += 4;
+    if (!parse_out(s, &t, NULL, 1, "+CRC:", ""))
+        return NULL;
+    return t;
+}
+/*- End of function --------------------------------------------------------*/
+
+static const char *at_cmd_plus_CREG(at_state_t *s, const char *t)
+{
+    /* 3GPP TS 27.007 7.2 - Network registration */
+    /* TODO: */
+    t += 5;
+    if (!parse_out(s, &t, NULL, 1, "+CREG:", ""))
+        return NULL;
+    return t;
+}
+/*- End of function --------------------------------------------------------*/
+
+static const char *at_cmd_plus_CRES(at_state_t *s, const char *t)
+{
+    /* GSM07.05 3.3.6 - Restore Settings */
+    /* TODO: */
+    t += 5;
+    if (!parse_out(s, &t, NULL, 1, "+CRLP:", ""))
+        return NULL;
+    return t;
+}
+/*- End of function --------------------------------------------------------*/
+
+static const char *at_cmd_plus_CRLP(at_state_t *s, const char *t)
+{
+    /* 3GPP TS 27.007 6.8 - Radio link protocol */
+    /* TODO: */
+    t += 5;
+    if (!parse_out(s, &t, NULL, 1, "+CRLP:", ""))
+        return NULL;
+    return t;
+}
+/*- End of function --------------------------------------------------------*/
+
+static const char *at_cmd_plus_CRM(at_state_t *s, const char *t)
+{
+    /* IS-99 5.6 - Set rm interface protocol */
+    /* TODO: */
+    t += 4;
+    return t;
+}
+/*- End of function --------------------------------------------------------*/
+
+static const char *at_cmd_plus_CRMC(at_state_t *s, const char *t)
+{
+    /* 3GPP TS 27.007 8.34 - Ring Melody Control */
+    /* TODO: */
+    t += 5;
+    if (!parse_out(s, &t, NULL, 1, "+CRMC:", ""))
+        return NULL;
+    return t;
+}
+/*- End of function --------------------------------------------------------*/
+
+static const char *at_cmd_plus_CRMP(at_state_t *s, const char *t)
+{
+    /* 3GPP TS 27.007 8.35 - Ring Melody Playback */
+    /* TODO: */
+    t += 5;
+    if (!parse_out(s, &t, NULL, 1, "+CRMP:", ""))
+        return NULL;
+    return t;
+}
+/*- End of function --------------------------------------------------------*/
+
+static const char *at_cmd_plus_CRSL(at_state_t *s, const char *t)
+{
+    /* 3GPP TS 27.007 8.21 - Ringer sound level */
+    /* TODO: */
+    t += 5;
+    if (!parse_out(s, &t, NULL, 1, "+CRSL:", ""))
+        return NULL;
+    return t;
+}
+/*- End of function --------------------------------------------------------*/
+
+static const char *at_cmd_plus_CRSM(at_state_t *s, const char *t)
+{
+    /* 3GPP TS 27.007 8.18 - Restricted SIM access */
+    /* TODO: */
+    t += 5;
+    if (!parse_out(s, &t, NULL, 1, "+CRSM:", ""))
+        return NULL;
+    return t;
+}
+/*- End of function --------------------------------------------------------*/
+
+static const char *at_cmd_plus_CSAS(at_state_t *s, const char *t)
+{
+    /* GSM07.05 3.3.5 - Save settings */
+    /* TODO: */
+    t += 5;
+    return t;
+}
+/*- End of function --------------------------------------------------------*/
+
+static const char *at_cmd_plus_CSCA(at_state_t *s, const char *t)
+{
+    /* GSM07.05 3.3.1 - Service centre address */
+    /* TODO: */
+    t += 5;
+    return t;
+}
+/*- End of function --------------------------------------------------------*/
+
+static const char *at_cmd_plus_CSCB(at_state_t *s, const char *t)
+{
+    /* GSM07.05 3.3.4 - Select cell broadcast message types */
+    /* TODO: */
+    t += 5;
+    return t;
+}
+/*- End of function --------------------------------------------------------*/
+
+static const char *at_cmd_plus_CSCC(at_state_t *s, const char *t)
+{
+    /* 3GPP TS 27.007 8.19 - Secure control command */
+    /* TODO: */
+    t += 5;
+    if (!parse_out(s, &t, NULL, 1, "+CSCC:", ""))
+        return NULL;
+    return t;
+}
+/*- End of function --------------------------------------------------------*/
+
+static const char *at_cmd_plus_CSCS(at_state_t *s, const char *t)
+{
+    /* 3GPP TS 27.007 5.5 - Select TE character set */
+    /* TODO: */
+    t += 5;
+    if (!parse_out(s, &t, NULL, 1, "+CSCS:", ""))
+        return NULL;
+    return t;
+}
+/*- End of function --------------------------------------------------------*/
+
+static const char *at_cmd_plus_CSDF(at_state_t *s, const char *t)
+{
+    /* 3GPP TS 27.007 6.22 - Settings date format */
+    /* TODO: */
+    t += 5;
+    if (!parse_out(s, &t, NULL, 1, "+CSDF:", ""))
+        return NULL;
+    return t;
+}
+/*- End of function --------------------------------------------------------*/
+
+static const char *at_cmd_plus_CSDH(at_state_t *s, const char *t)
+{
+    /* GSM07.05 3.3.3 - Show text mode parameters */
+    /* TODO: */
+    t += 5;
+    return t;
+}
+/*- End of function --------------------------------------------------------*/
+
+static const char *at_cmd_plus_CSGT(at_state_t *s, const char *t)
+{
+    /* 3GPP TS 27.007 8.32 - Set Greeting Text */
+    /* TODO: */
+    t += 5;
+    if (!parse_out(s, &t, NULL, 1, "+CSGT:", ""))
+        return NULL;
+    return t;
+}
+/*- End of function --------------------------------------------------------*/
+
+static const char *at_cmd_plus_CSIL(at_state_t *s, const char *t)
+{
+    /* 3GPP TS 27.007 6.23 - Silence Command */
+    /* TODO: */
+    t += 5;
+    if (!parse_out(s, &t, NULL, 1, "+CSIL:", ""))
+        return NULL;
+    return t;
+}
+/*- End of function --------------------------------------------------------*/
+
+static const char *at_cmd_plus_CSIM(at_state_t *s, const char *t)
+{
+    /* 3GPP TS 27.007 8.17 - Generic SIM access */
+    /* TODO: */
+    t += 5;
+    if (!parse_out(s, &t, NULL, 1, "+CSIM:", ""))
+        return NULL;
+    return t;
+}
+/*- End of function --------------------------------------------------------*/
+
+static const char *at_cmd_plus_CSMP(at_state_t *s, const char *t)
+{
+    /* GSM07.05 3.3.2 - Set text mode parameters */
+    /* TODO: */
+    t += 5;
+    return t;
+}
+/*- End of function --------------------------------------------------------*/
+
+static const char *at_cmd_plus_CSMS(at_state_t *s, const char *t)
+{
+    /* GSM07.05 3.2.1 - Select Message Service */
+    /* TODO: */
+    t += 5;
+    return t;
+}
+/*- End of function --------------------------------------------------------*/
+
+static const char *at_cmd_plus_CSNS(at_state_t *s, const char *t)
+{
+    /* 3GPP TS 27.007 6.19 - Single numbering scheme */
+    /* TODO: */
+    t += 5;
+    if (!parse_out(s, &t, NULL, 1, "+CSNS:", ""))
+        return NULL;
+    return t;
+}
+/*- End of function --------------------------------------------------------*/
+
+static const char *at_cmd_plus_CSQ(at_state_t *s, const char *t)
+{
+    /* 3GPP TS 27.007 8.5 - Signal quality */
+    /* TODO: */
+    t += 4;
+    if (!parse_out(s, &t, NULL, 1, "+CSQ:", ""))
+        return NULL;
+    return t;
+}
+/*- End of function --------------------------------------------------------*/
+
+static const char *at_cmd_plus_CSS(at_state_t *s, const char *t)
+{
+    /* IS-135 4.1.28 - Serving system identification */
+    /* TODO: */
+    t += 4;
+    return t;
+}
+/*- End of function --------------------------------------------------------*/
+
+static const char *at_cmd_plus_CSSN(at_state_t *s, const char *t)
+{
+    /* 3GPP TS 27.007 7.17 - Supplementary service notifications */
+    /* TODO: */
+    t += 5;
+    if (!parse_out(s, &t, NULL, 1, "+CSSN:", ""))
+        return NULL;
+    return t;
+}
+/*- End of function --------------------------------------------------------*/
+
+static const char *at_cmd_plus_CSTA(at_state_t *s, const char *t)
+{
+    /* 3GPP TS 27.007 6.1 - Select type of address */
+    /* TODO: */
+    t += 5;
+    if (!parse_out(s, &t, NULL, 1, "+CSTA:", ""))
+        return NULL;
+    return t;
+}
+/*- End of function --------------------------------------------------------*/
+
+static const char *at_cmd_plus_CSTF(at_state_t *s, const char *t)
+{
+    /* 3GPP TS 27.007 6.24 - Settings time format */
+    /* TODO: */
+    t += 5;
+    if (!parse_out(s, &t, NULL, 1, "+CSTF:", ""))
+        return NULL;
+    return t;
+}
+/*- End of function --------------------------------------------------------*/
+
+static const char *at_cmd_plus_CSVM(at_state_t *s, const char *t)
+{
+    /* 3GPP TS 27.007 8.33 - Set Voice Mail Number */
+    /* TODO: */
+    t += 5;
+    if (!parse_out(s, &t, NULL, 1, "+CSVM:", ""))
+        return NULL;
+    return t;
+}
+/*- End of function --------------------------------------------------------*/
+
+static const char *at_cmd_plus_CTA(at_state_t *s, const char *t)
+{
+    /* IS-135 4.1.29 - MT-Terminated async. Data calls */
+    /* TODO: */
+    t += 4;
+    return t;
+}
+/*- End of function --------------------------------------------------------*/
+
+static const char *at_cmd_plus_CTF(at_state_t *s, const char *t)
+{
+    /* IS-135 4.1.30 - MT-Terminated FAX calls */
+    /* TODO: */
+    t += 4;
+    return t;
+}
+/*- End of function --------------------------------------------------------*/
+
+static const char *at_cmd_plus_CTFR(at_state_t *s, const char *t)
+{
+    /* 3GPP TS 27.007 7.14 - Call deflection */
+    /* TODO: */
+    t += 5;
+    if (!parse_out(s, &t, NULL, 1, "+CTFR:", ""))
+        return NULL;
+    return t;
+}
+/*- End of function --------------------------------------------------------*/
+
+static const char *at_cmd_plus_CTZR(at_state_t *s, const char *t)
+{
+    /* 3GPP TS 27.007 8.41 - Time Zone Reporting */
+    /* TODO: */
+    t += 5;
+    if (!parse_out(s, &t, NULL, 1, "+CTZR:", ""))
+        return NULL;
+    return t;
+}
+/*- End of function --------------------------------------------------------*/
+
+static const char *at_cmd_plus_CTZU(at_state_t *s, const char *t)
+{
+    /* 3GPP TS 27.007 8.40 - Automatic Time Zone Update */
+    /* TODO: */
+    t += 5;
+    if (!parse_out(s, &t, NULL, 1, "+CTZU:", ""))
+        return NULL;
+    return t;
+}
+/*- End of function --------------------------------------------------------*/
+
+static const char *at_cmd_plus_CUSD(at_state_t *s, const char *t)
+{
+    /* 3GPP TS 27.007 7.15 - Unstructured supplementary service data */
+    /* TODO: */
+    t += 5;
+    if (!parse_out(s, &t, NULL, 1, "+CUSD:", ""))
+        return NULL;
+    return t;
+}
+/*- End of function --------------------------------------------------------*/
+
+static const char *at_cmd_plus_CUUS1(at_state_t *s, const char *t)
+{
+    /* 3GPP TS 27.007 7.26 - User to User Signalling Service 1 */
+    /* TODO: */
+    t += 6;
+    if (!parse_out(s, &t, NULL, 1, "+CUUS1:", ""))
+        return NULL;
+    return t;
+}
+/*- End of function --------------------------------------------------------*/
+
+static const char *at_cmd_plus_CV120(at_state_t *s, const char *t)
+{
+    /* 3GPP TS 27.007 6.21 - V.120 rate adaption protocol */
+    /* TODO: */
+    t += 6;
+    if (!parse_out(s, &t, NULL, 1, "+CV120:", ""))
+        return NULL;
+    return t;
+}
+/*- End of function --------------------------------------------------------*/
+
+static const char *at_cmd_plus_CVHU(at_state_t *s, const char *t)
+{
+    /* 3GPP TS 27.007 6.20 - Voice Hangup Control */
+    /* TODO: */
+    t += 5;
+    if (!parse_out(s, &t, NULL, 1, "+CVHU:", ""))
+        return NULL;
+    return t;
+}
+/*- End of function --------------------------------------------------------*/
+
+static const char *at_cmd_plus_CVIB(at_state_t *s, const char *t)
+{
+    /* 3GPP TS 27.007 8.22 - Vibrator mode */
+    /* TODO: */
+    t += 5;
+    if (!parse_out(s, &t, NULL, 1, "+CVIB:", ""))
+        return NULL;
+    return t;
+}
+/*- End of function --------------------------------------------------------*/
+
+static const char *at_cmd_plus_CXT(at_state_t *s, const char *t)
+{
+    /* IS-99 5.6 - Cellular extension */
+    /* TODO: */
+    t += 4;
+    return t;
+}
+/*- End of function --------------------------------------------------------*/
+
+static const char *at_cmd_plus_DR(at_state_t *s, const char *t)
+{
+    /* V.250 6.6.2 - Data compression reporting */ 
+    /* TODO: */
+    t += 3;
+    if (!parse_out(s, &t, NULL, 1, "+DR:", ""))
+        return NULL;
+    return t;
+}
+/*- End of function --------------------------------------------------------*/
+
+static const char *at_cmd_plus_DS(at_state_t *s, const char *t)
+{
+    /* V.250 6.6.1 - Data compression */ 
+    /* TODO: */
+    t += 3;
+    if (!parse_out(s, &t, NULL, 1, "+DS:", ""))
+        return NULL;
+    return t;
+}
+/*- End of function --------------------------------------------------------*/
+
+static const char *at_cmd_plus_DS44(at_state_t *s, const char *t)
+{
+    /* V.250 6.6.2 - V.44 data compression */
+    /* TODO: */
+    t += 5;
+    return t;
+}
+/*- End of function --------------------------------------------------------*/
+
+static const char *at_cmd_plus_EB(at_state_t *s, const char *t)
+{
+    /* V.250 6.5.2 - Break handling in error control operation */ 
+    /* TODO: */
+    t += 3;
+    if (!parse_out(s, &t, NULL, 1, "+EB:", ""))
+        return NULL;
+    return t;
+}
+/*- End of function --------------------------------------------------------*/
+
+static const char *at_cmd_plus_EFCS(at_state_t *s, const char *t)
+{
+    /* V.250 6.5.4 - 32-bit frame check sequence */ 
+    /* TODO: */
+    t += 5;
+    if (!parse_out(s, &t, NULL, 2, "+EFCS:", "(0-2)"))
+        return NULL;
+    return t;
+}
+/*- End of function --------------------------------------------------------*/
+
+static const char *at_cmd_plus_EFRAM(at_state_t *s, const char *t)
+{
+    /* V.250 6.5.8 - Frame length */ 
+    /* TODO: */
+    t += 6;
+    if (!parse_2_out(s, &t, NULL, 65535, NULL, 65535, "+EFRAM:", "(1-65535),(1-65535)"))
+        return NULL;
+    return t;
+}
+/*- End of function --------------------------------------------------------*/
+
+static const char *at_cmd_plus_ER(at_state_t *s, const char *t)
+{
+    /* V.250 6.5.5 - Error control reporting */ 
+    /*  0   Error control reporting disabled (no +ER intermediate result code transmitted)
+        1   Error control reporting enabled (+ER intermediate result code transmitted) */
+    /* TODO: */
+    t += 3;
+    if (!parse_out(s, &t, NULL, 1, "+ER:", "(0,1)"))
+        return NULL;
+    return t;
+}
+/*- End of function --------------------------------------------------------*/
+
+static const char *at_cmd_plus_ES(at_state_t *s, const char *t)
+{
+    static const int maxes[3] =
+    {
+        7, 4, 9
+    };
+    int *locations[3];
+
+    /* V.250 6.5.1 - Error control selection */ 
+
+    /* orig_rqst
+        0:  Direct mode
+        1:  Initiate call with Buffered mode only
+        2:  Initiate V.42 without Detection Phase. If Rec. V.8 is in use, this is a request to disable V.42 Detection Phase
+        3:  Initiate V.42 with Detection Phase
+        4:  Initiate Altemative Protocol
+        5:  Initiate Synchronous Mode when connection is completed, immediately after the entire CONNECT result code
+            is delivered. V.24 circuits 113 and 115 are activated when Data State is entered
+        6:  Initiate Synchronous Access Mode when connection is completed, and Data State is entered
+        7:  Initiate Frame Tunnelling Mode when connection is completed, and Data State is entered
+
+       orig_fbk
+        0:  Error control optional (either LAPM or Alternative acceptable); if error control not established, maintain
+            DTE-DCE data rate and use V.14 buffered mode with flow control during non-error-control operation
+        1:  Error control optional (either LAPM or Alternative acceptable); if error control not established, change
+            DTE-DCE data rate to match line rate and use Direct mode
+        2:  Error control required (either LAPM or Alternative acceptable); if error control not established, disconnect
+        3:  Error control required (only LAPM acceptable); if error control not established, disconnect
+        4:  Error control required (only Altemative protocol acceptable); if error control not established, disconnect
+
+       ans_fbk
+        0:  Direct mode
+        1:  Error control disabled, use Buffered mode
+        2:  Error control optional (either LAPM or Alternative acceptable); if error control not established, maintain
+            DTE-DCE data rate and use local buffering and flow control during non-error-control operation
+        3:  Error control optional (either LAPM or Alternative acceptable); if error control not established, change
+            DTE-DCE data rate to match line rate and use Direct mode
+        4:  Error control required (either LAPM or Alternative acceptable); if error control not established, disconnect
+        5:  Error control required (only LAPM acceptable); if error control not established, disconnect
+        6:  Error control required (only Alternative protocol acceptable); if error control not established, disconnect
+        7:  Initiate Synchronous Mode when connection is completed, immediately after the entire CONNECT result code
+            is delivered. V.24 cicuits 113 and 115 are activated when Data State is entered
+        8:  Initiate Synchronous Access Mode when connection is completed, and Data State is entered
+        9:  Initiate Frame Tunnelling Mode when connection is completed, and Data State is entered */
+
+    /* TODO: */
+    t += 3;
+    locations[0] = NULL;
+    locations[1] = NULL;
+    locations[2] = NULL;
+    if (!parse_n_out(s, &t, locations, maxes, 3, "+ES:", "(0-7),(0-4),(0-9)"))
+        return NULL;
+    return t;
+}
+/*- End of function --------------------------------------------------------*/
+
+static const char *at_cmd_plus_ESA(at_state_t *s, const char *t)
+{
+    static const int maxes[8] =
+    {
+        2, 1, 1, 1, 2, 1, 255, 255
+    };
+    int *locations[8];
+    int i;
+
+    /* V.80 8.2 - Synchronous access mode configuration */
+    /* TODO: */
+    t += 4;
+    for (i = 0;  i < 8;  i++)
+        locations[i] = NULL;
+    if (!parse_n_out(s, &t, locations, maxes, 8, "+ESA:", "(0-2),(0-1),(0-1),(0-1),(0-2),(0-1),(0-255),(0-255)"))
+        return NULL;
+    return t;
+}
+/*- End of function --------------------------------------------------------*/
+
+static const char *at_cmd_plus_ESR(at_state_t *s, const char *t)
+{
+    /* V.250 6.5.3 - Selective repeat */ 
+    /* TODO: */
+    t += 4;
+    return t;
+}
+/*- End of function --------------------------------------------------------*/
+
+static const char *at_cmd_plus_ETBM(at_state_t *s, const char *t)
+{
+    /* V.250 6.5.6 - Call termination buffer management */ 
+    /* TODO: */
+    t += 5;
+    if (!parse_2_out(s, &t, NULL, 2, NULL, 2, "+ETBM:", "(0-2),(0-2),(0-30)"))
+        return NULL;
+    return t;
+}
+/*- End of function --------------------------------------------------------*/
+
+static const char *at_cmd_plus_EWIND(at_state_t *s, const char *t)
+{
+    /* V.250 6.5.7 - Window size */ 
+    /* TODO: */
+    t += 6;
+    if (!parse_2_out(s, &t, &s->rx_window, 127, &s->tx_window, 127, "+EWIND:", "(1-127),(1-127)"))
+        return NULL;
+    return t;
+}
+/*- End of function --------------------------------------------------------*/
+
+static const char *at_cmd_plus_F34(at_state_t *s, const char *t)
+{
+    static const int maxes[5] =
+    {
+        14, 14, 2, 14, 14
+    };
+    int *locations[5];
+    int i;
+
+    /* T.31 B.6.1 - Initial V.34 rate controls for FAX */
+    /* Syntax: +F34=[<maxp>][,[<minp>][,<prefc>][,<maxp2>][,<minp2]] */
+    /* TODO */
+    t += 4;
+    for (i = 0;  i < 5;  i++)
+        locations[i] = NULL;
+    if (!parse_n_out(s, &t, locations, maxes, 5, "+F34:", "(0-14),(0-14),(0-2),(0-14),(0-14)"))
+        return NULL;
+    return t;
+}
+/*- End of function --------------------------------------------------------*/
+
+static const char *at_cmd_plus_FAA(at_state_t *s, const char *t)
+{
+    /* T.32 8.5.2.5 - Adaptive answer parameter */
+    /* TODO */
+    t += 4;
+    return t;
+}
+/*- End of function --------------------------------------------------------*/
+
+static const char *at_cmd_plus_FAP(at_state_t *s, const char *t)
+{
+    /* T.32 8.5.1.12 - Addressing and polling capabilities parameter */
+    /* TODO */
+    t += 4;
+    return t;
+}
+/*- End of function --------------------------------------------------------*/
+
+static const char *at_cmd_plus_FAR(at_state_t *s, const char *t)
+{
+    /* T.31 8.5.1 - Adaptive reception control */ 
+    t += 4;
+    if (!parse_out(s, &t, &s->p.adaptive_receive, 1, NULL, "0,1"))
+         return NULL;
+    return t;
+}
+/*- End of function --------------------------------------------------------*/
+
+static const char *at_cmd_plus_FBO(at_state_t *s, const char *t)
+{
+    /* T.32 8.5.3.4 - Phase C data bit order */
+    /* TODO */
+    t += 4;
+    return t;
+}
+/*- End of function --------------------------------------------------------*/
+
+static const char *at_cmd_plus_FBS(at_state_t *s, const char *t)
+{
+    /* T.32 8.5.3.2 - Buffer Size, read only parameter */
+    /* TODO */
+    t += 4;
+    return t;
+}
+/*- End of function --------------------------------------------------------*/
+
+static const char *at_cmd_plus_FBU(at_state_t *s, const char *t)
+{
+    /* T.32 8.5.1.10 - HDLC Frame Reporting parameter */
+    /* TODO */
+    t += 4;
+    return t;
+}
+/*- End of function --------------------------------------------------------*/
+
+static const char *at_cmd_plus_FCC(at_state_t *s, const char *t)
+{
+    /* T.32 8.5.1.1 - DCE capabilities parameters */
+    /* TODO */
+    t += 4;
+    return t;
+}
+/*- End of function --------------------------------------------------------*/
+
+static const char *at_cmd_plus_FCL(at_state_t *s, const char *t)
+{
+    /* T.31 8.5.2 - Carrier loss timeout */ 
+    t += 4;
+    if (!parse_out(s, &t, &s->carrier_loss_timeout, 255, NULL, "(0-255)"))
+         return NULL;
+    return t;
+}
+/*- End of function --------------------------------------------------------*/
+
+static const char *at_cmd_plus_FCLASS(at_state_t *s, const char *t)
+{
+    /* T.31 8.2 - Capabilities identification and control */ 
+    t += 7;
+    /* T.31 says the reply string should be "0,1.0", however making
+       it "0,1,1.0" makes things compatible with a lot more software
+       that may be expecting a pre-T.31 modem. */
+    if (!parse_string_list_out(s, &t, &s->fclass_mode, 1, NULL, "0,1,1.0"))
+        return NULL;
+    return t;
+}
+/*- End of function --------------------------------------------------------*/
+
+static const char *at_cmd_plus_FCQ(at_state_t *s, const char *t)
+{
+    /* T.32 8.5.2.3 -  Copy quality checking parameter */
+    /* TODO */
+    t += 4;
+    return t;
+}
+/*- End of function --------------------------------------------------------*/
+
+static const char *at_cmd_plus_FCR(at_state_t *s, const char *t)
+{
+    /* T.32 8.5.1.9 - Capability to receive parameter */
+    /* TODO */
+    t += 4;
+    return t;
+}
+/*- End of function --------------------------------------------------------*/
+
+static const char *at_cmd_plus_FCS(at_state_t *s, const char *t)
+{
+    /* T.32 8.5.1.3 - Current Session Results parameters */
+    /* TODO */
+    t += 4;
+    return t;
+}
+/*- End of function --------------------------------------------------------*/
+
+static const char *at_cmd_plus_FCT(at_state_t *s, const char *t)
+{
+    /* T.32 8.5.2.6 - DTE phase C timeout parameter */
+    /* TODO */
+    t += 4;
+    return t;
+}
+/*- End of function --------------------------------------------------------*/
+
+static const char *at_cmd_plus_FDD(at_state_t *s, const char *t)
+{
+    /* T.31 8.5.3 - Double escape character replacement */ 
+    t += 4;
+    if (!parse_out(s, &t, &s->p.double_escape, 1, NULL, "(0,1)"))
+        return NULL;
+    return t;
+}
+/*- End of function --------------------------------------------------------*/
+
+static const char *at_cmd_plus_FDR(at_state_t *s, const char *t)
+{
+    /* T.32 8.3.4 - Data reception command */
+    /* TODO */
+    t += 4;
+    return t;
+}
+/*- End of function --------------------------------------------------------*/
+
+static const char *at_cmd_plus_FDT(at_state_t *s, const char *t)
+{
+    /* T.32 8.3.3 - Data transmission command */
+    /* TODO */
+    t += 4;
+    return t;
+}
+/*- End of function --------------------------------------------------------*/
+
+static const char *at_cmd_plus_FEA(at_state_t *s, const char *t)
+{
+    /* T.32 8.5.3.5 - Phase C received EOL alignment parameter */
+    /* TODO */
+    t += 4;
+    return t;
+}
+/*- End of function --------------------------------------------------------*/
+
+static const char *at_cmd_plus_FFC(at_state_t *s, const char *t)
+{
+    /* T.32 8.5.3.6 - Format conversion parameter */
+    /* TODO */
+    t += 4;
+    return t;
+}
+/*- End of function --------------------------------------------------------*/
+
+static const char *at_cmd_plus_FFD(at_state_t *s, const char *t)
+{
+    /* T.32 8.5.1.14 - File diagnostic message parameter */
+    /* TODO */
+    t += 4;
+    return t;
+}
+/*- End of function --------------------------------------------------------*/
+
+static const char *at_cmd_plus_FHS(at_state_t *s, const char *t)
+{
+    /* T.32 8.5.2.7 - Call termination status parameter */
+    /* TODO */
+    t += 4;
+    return t;
+}
+/*- End of function --------------------------------------------------------*/
+
+static const char *at_cmd_plus_FIE(at_state_t *s, const char *t)
+{
+    /* T.32 8.5.2.1 - Procedure interrupt enable parameter */
+    /* TODO */
+    t += 4;
+    return t;
+}
+/*- End of function --------------------------------------------------------*/
+
+static const char *at_cmd_plus_FIP(at_state_t *s, const char *t)
+{
+    /* T.32 8.3.6 - Initialize Facsimile Parameters */
+    t += 4;
+    return t;
+}
+/*- End of function --------------------------------------------------------*/
+
+static const char *at_cmd_plus_FIS(at_state_t *s, const char *t)
+{
+    /* T.32 8.5.1.2 -  Current session parameters */
+    /* TODO */
+    t += 4;
+    return t;
+}
+/*- End of function --------------------------------------------------------*/
+
+static const char *at_cmd_plus_FIT(at_state_t *s, const char *t)
+{
+    /* T.31 8.5.4 - DTE inactivity timeout */ 
+    t += 4;
+    if (!parse_2_out(s, &t, &s->dte_inactivity_timeout, 255, &s->dte_inactivity_action, 1, "+FIT:", "(0-255),(0-1)"))
+        return NULL;
+    return t;
+}
+/*- End of function --------------------------------------------------------*/
+
+static const char *at_cmd_plus_FKS(at_state_t *s, const char *t)
+{
+    /* T.32 8.3.5 - Session termination command */
+    /* TODO: */
+    t += 4;
+    return t;
+}
+/*- End of function --------------------------------------------------------*/
+
+static const char *at_cmd_plus_FLI(at_state_t *s, const char *t)
+{
+    /* T.32 8.5.1.5 - Local ID string parameter, TSI or CSI */
+    /* TODO: */
+    t += 4;
+    return t;
+}
+/*- End of function --------------------------------------------------------*/
+
+static const char *at_cmd_plus_FLO(at_state_t *s, const char *t)
+{
+    /* T.31 Annex A */ 
+    /* Implement something similar to the V.250 +IFC command */ 
+    /* TODO: */
+    t += 4;
+    return t;
+}
+/*- End of function --------------------------------------------------------*/
+
+static const char *at_cmd_plus_FLP(at_state_t *s, const char *t)
+{
+    /* T.32 8.5.1.7 - Indicate document to poll parameter */
+    /* TODO: */
+    t += 4;
+    return t;
+}
+/*- End of function --------------------------------------------------------*/
+
+static const char *at_cmd_plus_FMI(at_state_t *s, const char *t)
+{
+    /* T.31 says to duplicate +GMI */ 
+    t += 4;
+    if (t[0] == '?')
+    {
+        at_put_response(s, manufacturer);
+        t += 1;
+    }
+    return t;
+}
+/*- End of function --------------------------------------------------------*/
+
+static const char *at_cmd_plus_FMM(at_state_t *s, const char *t)
+{
+    /* T.31 says to duplicate +GMM */ 
+    t += 4;
+    if (t[0] == '?')
+    {
+        at_put_response(s, model);
+        t += 1;
+    }
+    return t;
+}
+/*- End of function --------------------------------------------------------*/
+
+static const char *at_cmd_plus_FMR(at_state_t *s, const char *t)
+{
+    /* T.31 says to duplicate +GMR */ 
+    t += 4;
+    if (t[0] == '?')
+    {
+        at_put_response(s, revision);
+        t += 1;
+    }
+    return t;
+}
+/*- End of function --------------------------------------------------------*/
+
+static const char *at_cmd_plus_FMS(at_state_t *s, const char *t)
+{
+    /* T.32 8.5.2.9 - Minimum phase C speed parameter */
+    t += 4;
+    return t;
+}
+/*- End of function --------------------------------------------------------*/
+
+static const char *at_cmd_plus_FND(at_state_t *s, const char *t)
+{
+    /* T.32 8.5.2.10 - Non-Standard Message Data Indication parameter */
+    t += 4;
+    return t;
+}
+/*- End of function --------------------------------------------------------*/
+
+static const char *at_cmd_plus_FNR(at_state_t *s, const char *t)
+{
+    /* T.32 8.5.1.11 - Negotiation message reporting control parameters */
+    t += 4;
+    return t;
+}
+/*- End of function --------------------------------------------------------*/
+
+static const char *at_cmd_plus_FNS(at_state_t *s, const char *t)
+{
+    /* T.32 8.5.1.6 - Non-Standard Frame FIF parameter */
+    t += 4;
+    return t;
+}
+/*- End of function --------------------------------------------------------*/
+
+static const char *at_cmd_plus_FPA(at_state_t *s, const char *t)
+{
+    /* T.32 8.5.1.13 - Selective polling address parameter */
+    t += 4;
+    return t;
+}
+/*- End of function --------------------------------------------------------*/
+
+static const char *at_cmd_plus_FPI(at_state_t *s, const char *t)
+{
+    /* T.32 8.5.1.5 - Local Polling ID String parameter */
+    t += 4;
+    return t;
+}
+/*- End of function --------------------------------------------------------*/
+
+static const char *at_cmd_plus_FPP(at_state_t *s, const char *t)
+{
+    /* T.32 8.5.3 - Facsimile packet protocol */
+    t += 4;
+    return t;
+}
+/*- End of function --------------------------------------------------------*/
+
+static const char *at_cmd_plus_FPR(at_state_t *s, const char *t)
+{
+    /* T.31 Annex A */ 
+    /* Implement something similar to the V.250 +IPR command */ 
+    t += 4;
+    if (!parse_out(s, &t, &s->dte_rate, 115200, NULL, "115200"))
+        return NULL;
+    return t;
+}
+/*- End of function --------------------------------------------------------*/
+
+static const char *at_cmd_plus_FPS(at_state_t *s, const char *t)
+{
+    /* T.32 8.5.2.2 - Page Status parameter */
+    t += 4;
+    return t;
+}
+/*- End of function --------------------------------------------------------*/
+
+static const char *at_cmd_plus_FPW(at_state_t *s, const char *t)
+{
+    /* T.32 8.5.1.13 - PassWord parameter (Sending or Polling) */
+    t += 4;
+    return t;
+}
+/*- End of function --------------------------------------------------------*/
+
+static const char *at_cmd_plus_FRH(at_state_t *s, const char *t)
+{
+    /* T.31 8.3.6 - HDLC receive */ 
+    if (!process_class1_cmd(s, &t))
+        return NULL;
+    return t;
+}
+/*- End of function --------------------------------------------------------*/
+
+static const char *at_cmd_plus_FRM(at_state_t *s, const char *t)
+{
+    /* T.31 8.3.4 - Facsimile receive */ 
+    if (!process_class1_cmd(s, &t))
+        return NULL;
+    return t;
+}
+/*- End of function --------------------------------------------------------*/
+
+static const char *at_cmd_plus_FRQ(at_state_t *s, const char *t)
+{
+    /* T.32 8.5.2.4 - Receive Quality Thresholds parameters */
+    /* TODO */
+    t += 4;
+    return t;
+}
+/*- End of function --------------------------------------------------------*/
+
+static const char *at_cmd_plus_FRS(at_state_t *s, const char *t)
+{
+    /* T.31 8.3.2 - Receive silence */ 
+    if (!process_class1_cmd(s, &t))
+        return NULL;
+    return t;
+}
+/*- End of function --------------------------------------------------------*/
+
+static const char *at_cmd_plus_FRY(at_state_t *s, const char *t)
+{
+    /* T.32 8.5.2.8 - ECM Retry Value parameter */
+    /* TODO */
+    t += 4;
+    return t;
+}
+/*- End of function --------------------------------------------------------*/
+
+static const char *at_cmd_plus_FSA(at_state_t *s, const char *t)
+{
+    /* T.32 8.5.1.13 - Subaddress parameter */
+    /* TODO */
+    t += 4;
+    return t;
+}
+/*- End of function --------------------------------------------------------*/
+
+static const char *at_cmd_plus_FSP(at_state_t *s, const char *t)
+{
+    /* T.32 8.5.1.8 - Request to poll parameter */
+    /* TODO */
+    t += 4;
+    return t;
+}
+/*- End of function --------------------------------------------------------*/
+
+static const char *at_cmd_plus_FTH(at_state_t *s, const char *t)
+{
+    /* T.31 8.3.5 - HDLC transmit */ 
+    if (!process_class1_cmd(s, &t))
+        return NULL;
+    return t;
+}
+/*- End of function --------------------------------------------------------*/
+
+static const char *at_cmd_plus_FTM(at_state_t *s, const char *t)
+{
+    /* T.31 8.3.3 - Facsimile transmit */ 
+    if (!process_class1_cmd(s, &t))
+        return NULL;
+    return t;
+}
+/*- End of function --------------------------------------------------------*/
+
+static const char *at_cmd_plus_FTS(at_state_t *s, const char *t)
+{
+    /* T.31 8.3.1 - Transmit silence */ 
+    if (!process_class1_cmd(s, &t))
+        return NULL;
+    return t;
+}
+/*- End of function --------------------------------------------------------*/
+
+static const char *at_cmd_plus_GCAP(at_state_t *s, const char *t)
+{
+    /* V.250 6.1.9 - Request complete capabilities list */
+    t += 5;
+    /* Response elements
+       +FCLASS     +F (FAX) commands
+       +MS         +M (modulation control) commands +MS and +MR
+       +MV18S      +M (modulation control) commands +MV18S and +MV18R
+       +ES         +E (error control) commands +ES, +EB, +ER, +EFCS, and +ETBM
+       +DS         +D (data compression) commands +DS and +DR */
+    /* TODO: make this adapt to the configuration we really have. */
+    if (t[0] == '?')
+    {
+        at_put_response(s, "+GCAP:+FCLASS");
+        t += 1;
+    }
+    return t;
+}
+/*- End of function --------------------------------------------------------*/
+
+static const char *at_cmd_plus_GCI(at_state_t *s, const char *t)
+{
+    /* V.250 6.1.10 - Country of installation, */ 
+    t += 4;
+    if (!parse_hex_out(s, &t, &s->country_of_installation, 255, "+GCI:", "(00-FF)"))
+        return NULL;
+    return t;
+}
+/*- End of function --------------------------------------------------------*/
+
+static const char *at_cmd_plus_GMI(at_state_t *s, const char *t)
+{
+    /* V.250 6.1.4 - Request manufacturer identification */ 
+    t += 4;
+    if (t[0] == '?')
+    {
+        at_put_response(s, manufacturer);
+        t += 1;
+    }
+    return t;
+}
+/*- End of function --------------------------------------------------------*/
+
+static const char *at_cmd_plus_GMM(at_state_t *s, const char *t)
+{
+    /* V.250 6.1.5 - Request model identification */ 
+    t += 4;
+    if (t[0] == '?')
+    {
+        at_put_response(s, model);
+        t += 1;
+    }
+    return t;
+}
+/*- End of function --------------------------------------------------------*/
+
+static const char *at_cmd_plus_GMR(at_state_t *s, const char *t)
+{
+    /* V.250 6.1.6 - Request revision identification */ 
+    t += 4;
+    if (t[0] == '?')
+    {
+        at_put_response(s, revision);
+        t += 1;
+    }
+    return t;
+}
+/*- End of function --------------------------------------------------------*/
+
+static const char *at_cmd_plus_GOI(at_state_t *s, const char *t)
+{
+    /* V.250 6.1.8 - Request global object identification */ 
+    /* TODO: */
+    t += 4;
+    if (t[0] == '?')
+    {
+        at_put_response(s, GLOBAL_OBJECT_IDENTITY);
+        t += 1;
+    }
+    return t;
+}
+/*- End of function --------------------------------------------------------*/
+
+static const char *at_cmd_plus_GSN(at_state_t *s, const char *t)
+{
+    /* V.250 6.1.7 - Request product serial number identification */ 
+    /* TODO: */
+    t += 4;
+    if (t[0] == '?')
+    {
+        at_put_response(s, SERIAL_NUMBER);
+        t += 1;
+    }
+    return t;
+}
+/*- End of function --------------------------------------------------------*/
+
+static const char *at_cmd_plus_IBC(at_state_t *s, const char *t)
+{
+    static const int maxes[13] =
+    {
+        2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1
+    };
+    int *locations[13];
+    int i;
+
+    /* V.80 7.9 - Control of in-band control */
+    /* TODO: */
+    t += 4;
+    /* 0: In-band control service disabled
+       1: In-band control service enabled, 7-bit codes allowed, and top bit insignificant
+       2; In-band control service enabled, 7-bit codes allowed, and 8-bit codes available
+    
+       Circuits 105, 106, 107, 108, 109, 110, 125, 132, 133, 135, 142 in that order. For each one:
+       0: disabled
+       1: enabled
+       
+       DCE line connect status reports:
+       0: disabled
+       1: enabled */
+    for (i = 0;  i < 13;  i++)
+        locations[i] = NULL;
+    if (!parse_n_out(s, &t, locations, maxes, 13, "+IBC:", "(0-2),(0,1),(0,1),(0,1),(0,1),(0,1),(0,1),(0,1),(0,1),(0,1),(0,1),(0.1),(0,1)"))
+        return NULL;
+    return t;
+}
+/*- End of function --------------------------------------------------------*/
+
+static const char *at_cmd_plus_IBM(at_state_t *s, const char *t)
+{
+    static const int maxes[3] =
+    {
+        7, 255, 255
+    };
+    int *locations[3];
+
+    /* V.80 7.10 - In-band MARK idle reporting control */
+    /* TODO: */
+    t += 4;
+    /* Report control
+        0: No reports
+        1: Report only once when <T1 > expires
+        2: Report each time <T2> expires
+        3: Report once when <T1> expires, and then each time <T2> expires
+        4: Report only when the Mark-ldle Period ends; T3 = the entire interval
+        5: Report the first time when <T1> is exceeded, and then once more when the mark idle period ends
+        6: Report each time when <T2> is exceeded, and then once more when the mark idle period ends;
+           T3 = entire interval -- N*T2
+        7: report the first time when <T1> is exceeded, and then each time <T2> is exceeded, and then once
+           more when the mark idle period ends; T3 = entire mark idle period -- N*T2 - T1
+           
+       T1 in units of 10ms
+        
+       T2 in units of 10ms */
+    locations[0] = NULL;
+    locations[1] = NULL;
+    locations[2] = NULL;
+    if (!parse_n_out(s, &t, locations, maxes, 3, "+IBM:", "(0-7),(0-255),(0-255)"))
+        return NULL;
+    return t;
+}
+/*- End of function --------------------------------------------------------*/
+
+static const char *at_cmd_plus_ICF(at_state_t *s, const char *t)
+{
+    /* V.250 6.2.11 - DTE-DCE character framing */ 
+    t += 4;
+    /* Character format
+        0:  auto detect
+        1:  8 data 2 stop
+        2:  8 data 1 parity 1 stop
+        3:  8 data 1 stop
+        4:  7 data 2 stop
+        5:  7 data 1 parity 1 stop
+        6:  7 data 1 stop
+    
+       Parity
+        0:  Odd
+        1:  Even
+        2:  Mark
+        3:  Space */
+    if (!parse_2_out(s, &t, &s->dte_char_format, 6, &s->dte_parity, 3, "+ICF:", "(0-6),(0-3)"))
+        return NULL;
+    return t;
+}
+/*- End of function --------------------------------------------------------*/
+
+static const char *at_cmd_plus_ICLOK(at_state_t *s, const char *t)
+{
+    /* V.250 6.2.14 - Select sync transmit clock source */ 
+    t += 6;
+    if (!parse_out(s, &t, &s->sync_tx_clock_source, 2, "+ICLOK:", "(0-2)"))
+        return NULL;
+    return t;
+}
+/*- End of function --------------------------------------------------------*/
+
+static const char *at_cmd_plus_IDSR(at_state_t *s, const char *t)
+{
+    /* V.250 6.2.16 - Select data set ready option */ 
+    t += 5;
+    if (!parse_out(s, &t, &s->dsr_option, 2, "+IDSR:", "(0-2)"))
+        return NULL;
+    return t;
+}
+/*- End of function --------------------------------------------------------*/
+
+static const char *at_cmd_plus_IFC(at_state_t *s, const char *t)
+{
+    /* V.250 6.2.12 - DTE-DCE local flow control */ 
+    /* TODO: */
+    t += 4;
+    return t;
+}
+/*- End of function --------------------------------------------------------*/
+
+static const char *at_cmd_plus_ILRR(at_state_t *s, const char *t)
+{
+    /* V.250 6.2.13 - DTE-DCE local rate reporting */ 
+    /* TODO: */
+    t += 5;
+    return t;
+}
+/*- End of function --------------------------------------------------------*/
+
+static const char *at_cmd_plus_ILSD(at_state_t *s, const char *t)
+{
+    /* V.250 6.2.15 - Select long space disconnect option */ 
+    t += 5;
+    if (!parse_out(s, &t, &s->long_space_disconnect_option, 2, "+ILSD:", "(0,1)"))
+        return NULL;
+    return t;
+}
+/*- End of function --------------------------------------------------------*/
+
+static const char *at_cmd_plus_IPR(at_state_t *s, const char *t)
+{
+    /* V.250 6.2.10 - Fixed DTE rate */ 
+    /* TODO: */
+    t += 4;
+    if (!parse_out(s, &t, &s->dte_rate, 115200, "+IPR:", "(115200),(115200)"))
+        return NULL;
+    return t;
+}
+/*- End of function --------------------------------------------------------*/
+
+static const char *at_cmd_plus_IRTS(at_state_t *s, const char *t)
+{
+    /* V.250 6.2.17 - Select synchronous mode RTS option */ 
+    t += 5;
+    if (!parse_out(s, &t, NULL, 1, "+IRTS:", "(0,1)"))
+        return NULL;
+    return t;
+}
+/*- End of function --------------------------------------------------------*/
+
+static const char *at_cmd_plus_ITF(at_state_t *s, const char *t)
+{
+    /* V.80 8.4 - Transmit flow control thresholds */
+    /* TODO: */
+    t += 5;
+    return t;
+}
+/*- End of function --------------------------------------------------------*/
+
+static const char *at_cmd_plus_MA(at_state_t *s, const char *t)
+{
+    /* V.250 6.4.2 - Modulation automode control */ 
+    /* TODO: */
+    t += 3;
+    return t;
+}
+/*- End of function --------------------------------------------------------*/
+
+static const char *at_cmd_plus_MR(at_state_t *s, const char *t)
+{
+    /* V.250 6.4.3 - Modulation reporting control */ 
+    /*  0    Disables reporting of modulation connection (+MCR: and +MRR: are not transmitted)
+        1    Enables reporting of modulation connection (+MCR: and +MRR: are transmitted) */
+    /* TODO: */
+    t += 3;
+    if (!parse_out(s, &t, NULL, 1, "+MR:", "(0,1)"))
+        return NULL;
+    return t;
+}
+/*- End of function --------------------------------------------------------*/
+
+static const char *at_cmd_plus_MS(at_state_t *s, const char *t)
+{
+    /* V.250 6.4.1 - Modulation selection */ 
+    /* TODO: */
+    t += 3;
+    return t;
+}
+/*- End of function --------------------------------------------------------*/
+
+static const char *at_cmd_plus_MSC(at_state_t *s, const char *t)
+{
+    /* V.250 6.4.8 - Seamless rate change enable */ 
+    /*  0   Disables V.34 seamless rate change 
+        1   Enables V.34 seamless rate change */
+    /* TODO: */
+    t += 4;
+    if (!parse_out(s, &t, NULL, 1, "+MSC:", "(0,1)"))
+        return NULL;
+    return t;
+}
+/*- End of function --------------------------------------------------------*/
+
+static const char *at_cmd_plus_MV18AM(at_state_t *s, const char *t)
+{
+    /* V.250 6.4.6 - V.18 answering message editing */ 
+    /* TODO: */
+    t += 7;
+    return t;
+}
+/*- End of function --------------------------------------------------------*/
+
+static const char *at_cmd_plus_MV18P(at_state_t *s, const char *t)
+{
+    /* V.250 6.4.7 - Order of probes */ 
+    /*  2    Send probe message in 5-bit (Baudot) mode
+        3    Send probe message in DTMF mode
+        4    Send probe message in EDT mode
+        5    Send Rec. V.21 carrier as a probe
+        6    Send Rec. V.23 carrier as a probe
+        7    Send Bell 103 carrier as a probe */
+    /* TODO: */
+    t += 6;
+    if (!parse_out(s, &t, NULL, 7, "+MV18P:", "(2-7)"))
+        return NULL;
+    return t;
+}
+/*- End of function --------------------------------------------------------*/
+
+static const char *at_cmd_plus_MV18R(at_state_t *s, const char *t)
+{
+    /* V.250 6.4.5 - V.18 reporting control */ 
+    /* TODO: */
+    t += 6;
+    if (!parse_out(s, &t, NULL, 1, "+MV18R:", "(0,1)"))
+        return NULL;
+    return t;
+}
+/*- End of function --------------------------------------------------------*/
+
+static const char *at_cmd_plus_MV18S(at_state_t *s, const char *t)
+{
+    /* V.250 6.4.4 - V.18 selection */ 
+    /*  mode:
+        0    Disables V.18 operation
+        1    V.18 operation, auto detect mode
+        2    V.18 operation, connect in 5-bit (Baudot) mode
+        3    V.18 operation, connect in DTMF mode
+        4    V.18 operation, connect in EDT mode
+        5    V.18 operation, connect in V.21 mode
+        6    V.18 operation, connect in V.23 mode
+        7    V.18 operation, connect in Bell 103-type mode
+
+        dflt_ans_mode:
+        0    Disables V.18 answer operation
+        1    No default specified (auto detect)
+        2    V.18 operation connect in 5-bit (Baudot) mode
+        3    V.18 operation connect in DTMF mode
+        4    V.18 operation connect in EDT mode
+
+        fbk_time_enable:
+        0    Disable
+        1    Enable
+
+        ans_msg_enable
+        0    Disable
+        1    Enable
+
+        probing_en
+        0    Disable probing
+        1    Enable probing
+        2    Initiate probing */
+    /* TODO: */
+    t += 6;
+    return t;
+}
+/*- End of function --------------------------------------------------------*/
+
+static const char *at_cmd_plus_PCW(at_state_t *s, const char *t)
+{
+    /* V.250 6.8.1 - Call waiting enable (V.92 DCE) */
+    /* TODO: */
+    t += 4;
+    return t;
+}
+/*- End of function --------------------------------------------------------*/
+
+static const char *at_cmd_plus_PIG(at_state_t *s, const char *t)
+{
+    /* V.250 6.8.5 - PCM upstream ignore */
+    /* TODO: */
+    t += 4;
+    return t;
+}
+/*- End of function --------------------------------------------------------*/
+
+static const char *at_cmd_plus_PMH(at_state_t *s, const char *t)
+{
+    /* V.250 6.8.2 - Modem on hold enable */
+    /* TODO: */
+    t += 4;
+    return t;
+}
+/*- End of function --------------------------------------------------------*/
+
+static const char *at_cmd_plus_PMHF(at_state_t *s, const char *t)
+{
+    /* V.250 6.8.6 - V.92 Modem on hold hook flash */
+    /* TODO: */
+    t += 5;
+    return t;
+}
+/*- End of function --------------------------------------------------------*/
+
+static const char *at_cmd_plus_PMHR(at_state_t *s, const char *t)
+{
+    /* V.250 6.8.4 - Initiate modem on hold */
+    /* TODO: */
+    t += 5;
+    return t;
+}
+/*- End of function --------------------------------------------------------*/
+
+static const char *at_cmd_plus_PMHT(at_state_t *s, const char *t)
+{
+    /* V.250 6.8.3 - Modem on hold timer */
+    /* TODO: */
+    t += 5;
+    return t;
+}
+/*- End of function --------------------------------------------------------*/
+
+static const char *at_cmd_plus_PQC(at_state_t *s, const char *t)
+{
+    /* V.250 6.8.7 - V.92 Phase 1 and Phase 2 Control */
+    /* TODO: */
+    t += 4;
+    return t;
+}
+/*- End of function --------------------------------------------------------*/
+
+static const char *at_cmd_plus_PSS(at_state_t *s, const char *t)
+{
+    /* V.250 6.8.8 - V.92 Use Short Sequence */
+    /* TODO: */
+    t += 4;
+    return t;
+}
+/*- End of function --------------------------------------------------------*/
+
+static const char *at_cmd_plus_SAC(at_state_t *s, const char *t)
+{
+    /* V.252 3.4 - Audio transmit configuration */
+    /* TODO: */
+    t += 4;
+    return t;
+}
+/*- End of function --------------------------------------------------------*/
+
+static const char *at_cmd_plus_SAM(at_state_t *s, const char *t)
+{
+    /* V.252 3.5 - Audio receive mode */
+    /* TODO: */
+    t += 4;
+    return t;
+}
+/*- End of function --------------------------------------------------------*/
+
+static const char *at_cmd_plus_SAR(at_state_t *s, const char *t)
+{
+    /* V.252 5.3 - Audio receive channel indication */
+    /* TODO: */
+    t += 4;
+    return t;
+}
+/*- End of function --------------------------------------------------------*/
+
+static const char *at_cmd_plus_SARR(at_state_t *s, const char *t)
+{
+    /* V.252 3.9 - Audio indication reporting */
+    /* TODO: */
+    t += 5;
+    return t;
+}
+/*- End of function --------------------------------------------------------*/
+
+static const char *at_cmd_plus_SAT(at_state_t *s, const char *t)
+{
+    /* V.252 5.4 - Audio transmit channel indication */
+    /* TODO: */
+    t += 4;
+    return t;
+}
+/*- End of function --------------------------------------------------------*/
+
+static const char *at_cmd_plus_SCRR(at_state_t *s, const char *t)
+{
+    /* V.252 3.11 - Capabilities indication reporting */
+    /* TODO: */
+    t += 5;
+    return t;
+}
+/*- End of function --------------------------------------------------------*/
+
+static const char *at_cmd_plus_SDC(at_state_t *s, const char *t)
+{
+    /* V.252 3.3 - Data configuration */
+    /* TODO: */
+    t += 4;
+    return t;
+}
+/*- End of function --------------------------------------------------------*/
+
+static const char *at_cmd_plus_SDI(at_state_t *s, const char *t)
+{
+    /* V.252 5.2 - Data channel identification */
+    /* TODO: */
+    t += 4;
+    return t;
+}
+/*- End of function --------------------------------------------------------*/
+
+static const char *at_cmd_plus_SDR(at_state_t *s, const char *t)
+{
+    /* V.252 3.8 - Data indication reporting */
+    /* TODO: */
+    t += 4;
+    return t;
+}
+/*- End of function --------------------------------------------------------*/
+
+static const char *at_cmd_plus_SRSC(at_state_t *s, const char *t)
+{
+    /* V.252 5.1.2 - Remote terminal simultaneous capability indication */
+    /* TODO: */
+    t += 5;
+    return t;
+}
+/*- End of function --------------------------------------------------------*/
+
+static const char *at_cmd_plus_STC(at_state_t *s, const char *t)
+{
+    /* V.252 3.1 - Terminal configuration */
+    /* TODO: */
+    t += 4;
+    return t;
+}
+/*- End of function --------------------------------------------------------*/
+
+static const char *at_cmd_plus_STH(at_state_t *s, const char *t)
+{
+    /* V.252 3.2 - Close logical channel */
+    /* TODO: */
+    t += 4;
+    return t;
+}
+/*- End of function --------------------------------------------------------*/
+
+static const char *at_cmd_plus_SVC(at_state_t *s, const char *t)
+{
+    /* V.252 3.6 - Video transmit configuration */
+    /* TODO: */
+    t += 4;
+    return t;
+}
+/*- End of function --------------------------------------------------------*/
+
+static const char *at_cmd_plus_SVM(at_state_t *s, const char *t)
+{
+    /* V.252 3.7 - Video receive mode */
+    /* TODO: */
+    t += 4;
+    return t;
+}
+/*- End of function --------------------------------------------------------*/
+
+static const char *at_cmd_plus_SVR(at_state_t *s, const char *t)
+{
+    /* V.252 5.5 - Video receive channel indication */
+    /* TODO: */
+    t += 4;
+    return t;
+}
+/*- End of function --------------------------------------------------------*/
+
+static const char *at_cmd_plus_SVRR(at_state_t *s, const char *t)
+{
+    /* V.252 3.10 - Video indication reporting */
+    /* TODO: */
+    t += 5;
+    return t;
+}
+/*- End of function --------------------------------------------------------*/
+
+static const char *at_cmd_plus_SVT(at_state_t *s, const char *t)
+{
+    /* V.252 5.6 - Video transmit channel indication */
+    /* TODO: */
+    t += 4;
+    return t;
+}
+/*- End of function --------------------------------------------------------*/
+
+static const char *at_cmd_plus_TADR(at_state_t *s, const char *t)
+{
+    /* V.250 6.7.2.9 - Local V.54 address */ 
+    /* TODO: */
+    t += 5;
+    return t;
+}
+/*- End of function --------------------------------------------------------*/
+
+static const char *at_cmd_plus_TAL(at_state_t *s, const char *t)
+{
+    /* V.250 6.7.2.15 - Local analogue loop */ 
+    /* Action
+        0   Disable analogue loop
+        1   Enable analogue loop
+       Band
+        0   Low frequency band
+        1   High frequency band */
+    /* TODO: */
+    t += 4;
+    if (!parse_2_out(s, &t, NULL, 1, NULL, 1, "+TAL:", "(0,1),(0,1)"))
+        return NULL;
+    return t;
+}
+/*- End of function --------------------------------------------------------*/
+
+static const char *at_cmd_plus_TALS(at_state_t *s, const char *t)
+{
+    /* V.250 6.7.2.6 - Analogue loop status */ 
+    /*  0   Inactive
+        1   V.24 circuit 141 invoked
+        2   Front panel invoked
+        3   Network management system invoked */
+    /* TODO: */
+    t += 5;
+    if (!parse_out(s, &t, NULL, 3, "+TALS:", "(0-3)"))
+        return NULL;
+    return t;
+}
+/*- End of function --------------------------------------------------------*/
+
+static const char *at_cmd_plus_TDLS(at_state_t *s, const char *t)
+{
+    /* V.250 6.7.2.7 - Local digital loop status */ 
+    /*  0   Disabled
+        1   Enabled, inactive
+        2   Front panel invoked
+        3   Network management system invoked
+        4   Remote invoked */
+    /* TODO: */
+    t += 5;
+    if (!parse_out(s, &t, NULL, 3, "+TDLS:", "(0-4)"))
+        return NULL;
+    return t;
+}
+/*- End of function --------------------------------------------------------*/
+
+static const char *at_cmd_plus_TE140(at_state_t *s, const char *t)
+{
+    /* V.250 6.7.2.1 - Enable ckt 140 */ 
+    /*  0   Disabled
+        1   Enabled */
+    /* TODO: */
+    t += 6;
+    if (!parse_out(s, &t, NULL, 1, "+TE140:", "(0,1)"))
+        return NULL;
+    return t;
+}
+/*- End of function --------------------------------------------------------*/
+
+static const char *at_cmd_plus_TE141(at_state_t *s, const char *t)
+{
+    /* V.250 6.7.2.2 - Enable ckt 141 */ 
+    /*  0   Response is disabled
+        1   Response is enabled */
+    /* TODO: */
+    t += 6;
+    if (!parse_out(s, &t, NULL, 1, "+TE141:", "(0,1)"))
+        return NULL;
+    return t;
+}
+/*- End of function --------------------------------------------------------*/
+
+static const char *at_cmd_plus_TEPAL(at_state_t *s, const char *t)
+{
+    /* V.250 6.7.2.5 - Enable front panel analogue loop */ 
+    /*  0   Disabled
+        1   Enabled */
+    /* TODO: */
+    t += 6;
+    if (!parse_out(s, &t, NULL, 1, "+TEPAL:", "(0,1)"))
+        return NULL;
+    return t;
+}
+/*- End of function --------------------------------------------------------*/
+
+static const char *at_cmd_plus_TEPDL(at_state_t *s, const char *t)
+{
+    /* V.250 6.7.2.4 - Enable front panel RDL */ 
+    /*  0   Disabled
+        1   Enabled */
+    /* TODO: */
+    t += 6;
+    if (!parse_out(s, &t, NULL, 1, "+TEPDL:", "(0,1)"))
+        return NULL;
+    return t;
+}
+/*- End of function --------------------------------------------------------*/
+
+static const char *at_cmd_plus_TERDL(at_state_t *s, const char *t)
+{
+    /* V.250 6.7.2.3 - Enable RDL from remote */ 
+    /*  0   Local DCE will ignore command from remote
+        1   Local DCE will obey command from remote */
+    /* TODO: */
+    t += 6;
+    if (!parse_out(s, &t, NULL, 1, "+TERDL:", "(0,1)"))
+        return NULL;
+    return t;
+}
+/*- End of function --------------------------------------------------------*/
+
+static const char *at_cmd_plus_TLDL(at_state_t *s, const char *t)
+{
+    /* V.250 6.7.2.13 - Local digital loop */
+    /*  0   Stop test
+        1   Start test */
+    /* TODO: */
+    t += 5;
+    if (!parse_out(s, &t, NULL, 1, "+TLDL:", "(0,1)"))
+        return NULL;
+    return t;
+}
+/*- End of function --------------------------------------------------------*/
+
+static const char *at_cmd_plus_TMO(at_state_t *s, const char *t)
+{
+    /* V.250 6.9 - V.59 command */
+    /* TODO: */
+    t += 4;
+    return t;
+}
+/*- End of function --------------------------------------------------------*/
+
+static const char *at_cmd_plus_TMODE(at_state_t *s, const char *t)
+{
+    /* V.250 6.7.2.10 - Set V.54 mode */ 
+    /* TODO: */
+    t += 6;
+    if (!parse_out(s, &t, NULL, 1, "+TMODE:", "(0,1)"))
+        return NULL;
+    return t;
+}
+/*- End of function --------------------------------------------------------*/
+
+static const char *at_cmd_plus_TNUM(at_state_t *s, const char *t)
+{
+    /* V.250 6.7.2.12 - Errored bit and block counts */ 
+    /* TODO: */
+    t += 5;
+    return t;
+}
+/*- End of function --------------------------------------------------------*/
+
+static const char *at_cmd_plus_TRDL(at_state_t *s, const char *t)
+{
+    /* V.250 6.7.2.14 - Request remote digital loop */ 
+    /*  0   Stop RDL
+        1   Start RDL */
+    /* TODO: */
+    t += 5;
+    if (!parse_out(s, &t, NULL, 1, "+TRDL:", "(0,1)"))
+        return NULL;
+    return t;
+}
+/*- End of function --------------------------------------------------------*/
+
+static const char *at_cmd_plus_TRDLS(at_state_t *s, const char *t)
+{
+    /* V.250 6.7.2.8 - Remote digital loop status */ 
+    /* TODO: */
+    t += 6;
+    return t;
+}
+/*- End of function --------------------------------------------------------*/
+
+static const char *at_cmd_plus_TRES(at_state_t *s, const char *t)
+{
+    /* V.250 6.7.2.17 - Self test result */ 
+    /*  0   No test
+        1   Pass
+        2   Fail */
+    /* TODO: */
+    t += 5;
+    if (!parse_out(s, &t, NULL, 1, "+TRES:", "(0-2)"))
+        return NULL;
+    return t;
+}
+/*- End of function --------------------------------------------------------*/
+
+static const char *at_cmd_plus_TSELF(at_state_t *s, const char *t)
+{
+    /* V.250 6.7.2.16 - Self test */ 
+    /*  0   Intrusive full test
+        1   Safe partial test */
+    /* TODO: */
+    t += 6;
+    if (!parse_out(s, &t, NULL, 1, "+TSELF:", "(0,1)"))
+        return NULL;
+    return t;
+}
+/*- End of function --------------------------------------------------------*/
+
+static const char *at_cmd_plus_TTER(at_state_t *s, const char *t)
+{
+    /* V.250 6.7.2.11 - Test error rate */ 
+    /* TODO: */
+    t += 5;
+    if (!parse_2_out(s, &t, NULL, 65535, NULL, 65535, "+TTER:", "(0-65535),(0-65535)"))
+        return NULL;
+    return t;
+}
+/*- End of function --------------------------------------------------------*/
+
+static const char *at_cmd_plus_VAC(at_state_t *s, const char *t)
+{
+    /* V.252 4.1 - Set audio code */
+    return t;
+}
+/*- End of function --------------------------------------------------------*/
+
+static const char *at_cmd_plus_VACR(at_state_t *s, const char *t)
+{
+    /* V.252 6.1 - Audio code report */
+    /* TODO: */
+    t += 5;
+    return t;
+}
+/*- End of function --------------------------------------------------------*/
+
+static const char *at_cmd_plus_VBT(at_state_t *s, const char *t)
+{
+    /* 3GPP TS 27.007 C.2.2 - Buffer threshold setting */
+    /* TODO: */
+    t += 4;
+    return t;
+}
+/*- End of function --------------------------------------------------------*/
+
+static const char *at_cmd_plus_VCID(at_state_t *s, const char *t)
+{
+    /* 3GPP TS 27.007 C.2.3 - Calling number ID presentation */
+    /* TODO: */
+    t += 5;
+    if (!parse_out(s, &t, &s->display_call_info, 1, NULL, "0,1"))
+         return NULL;
+    return t;
+}
+/*- End of function --------------------------------------------------------*/
+
+static const char *at_cmd_plus_VCIDR(at_state_t *s, const char *t)
+{
+    /* V.252 6.2 - Caller ID report */
+    /* TODO: */
+    t += 6;
+    return t;
+}
+/*- End of function --------------------------------------------------------*/
+
+static const char *at_cmd_plus_VDID(at_state_t *s, const char *t)
+{
+    /* V.253 9.2.4 - DID service */
+    /* TODO: */
+    t += 5;
+    return t;
+}
+/*- End of function --------------------------------------------------------*/
+
+static const char *at_cmd_plus_VDIDR(at_state_t *s, const char *t)
+{
+    /* V.252 6.2 - DID report */
+    /* TODO: */
+    t += 6;
+    return t;
+}
+/*- End of function --------------------------------------------------------*/
+
+static const char *at_cmd_plus_VDR(at_state_t *s, const char *t)
+{
+    /* V.253 10.3.1 - Distinctive ring (ring cadence reporting) */
+    /* TODO: */
+    t += 4;
+    return t;
+}
+/*- End of function --------------------------------------------------------*/
+
+static const char *at_cmd_plus_VDT(at_state_t *s, const char *t)
+{
+    /* V.253 10.3.2 - Control tone cadence reporting */
+    /* TODO: */
+    t += 4;
+    return t;
+}
+/*- End of function --------------------------------------------------------*/
+
+static const char *at_cmd_plus_VDX(at_state_t *s, const char *t)
+{
+    /* V.253 10.5.6 - Speakerphone duplex mode */
+    /* TODO: */
+    t += 4;
+    return t;
+}
+/*- End of function --------------------------------------------------------*/
+
+static const char *at_cmd_plus_VEM(at_state_t *s, const char *t)
+{
+    /* V.253 10.5.7 - Deliver event reports */
+    /* TODO: */
+    t += 4;
+    return t;
+}
+/*- End of function --------------------------------------------------------*/
+
+static const char *at_cmd_plus_VGM(at_state_t *s, const char *t)
+{
+    /* V.253 10.5.2 - Microphone gain */
+    /* TODO: */
+    t += 4;
+    return t;
+}
+/*- End of function --------------------------------------------------------*/
+
+static const char *at_cmd_plus_VGR(at_state_t *s, const char *t)
+{
+    /* V.253 10.2.1 - Receive gain selection */
+    /* TODO: */
+    t += 4;
+    return t;
+}
+/*- End of function --------------------------------------------------------*/
+
+static const char *at_cmd_plus_VGS(at_state_t *s, const char *t)
+{
+    /* V.253 10.5.3 - Speaker gain */
+    /* TODO: */
+    t += 4;
+    return t;
+}
+/*- End of function --------------------------------------------------------*/
+
+static const char *at_cmd_plus_VGT(at_state_t *s, const char *t)
+{
+    /* V.253 10.2.2 - Volume selection */
+    /* TODO: */
+    t += 4;
+    return t;
+}
+/*- End of function --------------------------------------------------------*/
+
+static const char *at_cmd_plus_VHC(at_state_t *s, const char *t)
+{
+    /* V.252 4.12 - Telephony port hook control */
+    /* TODO: */
+    t += 4;
+    return t;
+}
+/*- End of function --------------------------------------------------------*/
+
+static const char *at_cmd_plus_VIP(at_state_t *s, const char *t)
+{
+    /* V.253 10.1.1 - Initialize voice parameters */
+    /* TODO: */
+    t += 4;
+    return t;
+}
+/*- End of function --------------------------------------------------------*/
+
+static const char *at_cmd_plus_VIT(at_state_t *s, const char *t)
+{
+    /* V.253 10.2.3 - DTE/DCE inactivity timer */
+    /* TODO: */
+    t += 4;
+    return t;
+}
+/*- End of function --------------------------------------------------------*/
+
+static const char *at_cmd_plus_VLS(at_state_t *s, const char *t)
+{
+    /* V.253 10.2.4 - Analogue source/destination selection */
+    /* TODO: */
+    t += 4;
+    return t;
+}
+/*- End of function --------------------------------------------------------*/
+
+static const char *at_cmd_plus_VNH(at_state_t *s, const char *t)
+{
+    /* V.253 9.2.5 - Automatic hangup control */
+    /* TODO: */
+    t += 4;
+    return t;
+}
+/*- End of function --------------------------------------------------------*/
+
+static const char *at_cmd_plus_VPH(at_state_t *s, const char *t)
+{
+    /* V.252 4.11 - Phone hookswitch status */
+    /* TODO: */
+    t += 4;
+    return t;
+}
+/*- End of function --------------------------------------------------------*/
+
+static const char *at_cmd_plus_VPP(at_state_t *s, const char *t)
+{
+    /* V.253 10.4.2 - Voice packet protocol */
+    /* TODO: */
+    t += 4;
+    return t;
+}
+/*- End of function --------------------------------------------------------*/
+
+static const char *at_cmd_plus_VPR(at_state_t *s, const char *t)
+{
+    /* IS-101 10.4.3 - Select DTE/DCE interface rate */
+    /* TODO: */
+    t += 4;
+    return t;
+}
+/*- End of function --------------------------------------------------------*/
+
+static const char *at_cmd_plus_VRA(at_state_t *s, const char *t)
+{
+    /* V.253 10.2.5 - Ringing tone goes away timer */
+    /* TODO: */
+    t += 4;
+    return t;
+}
+/*- End of function --------------------------------------------------------*/
+
+static const char *at_cmd_plus_VRID(at_state_t *s, const char *t)
+{
+    int val;
+
+    /* Extension of V.253 +VCID, Calling number ID report/repeat */
+    t += 5;
+    val = 0;
+    if (!parse_out(s, &t, &val, 1, NULL, "0,1"))
+         return NULL;
+    if (val == 1)
+        at_display_call_info(s);
+    return t;
+}
+/*- End of function --------------------------------------------------------*/
+
+static const char *at_cmd_plus_VRL(at_state_t *s, const char *t)
+{
+    /* V.253 10.1.2 - Ring local phone */
+    /* TODO: */
+    t += 4;
+    return t;
+}
+/*- End of function --------------------------------------------------------*/
+
+static const char *at_cmd_plus_VRN(at_state_t *s, const char *t)
+{
+    /* V.253 10.2.6 - Ringing tone never appeared timer */
+    /* TODO: */
+    t += 4;
+    return t;
+}
+/*- End of function --------------------------------------------------------*/
+
+static const char *at_cmd_plus_VRX(at_state_t *s, const char *t)
+{
+    /* V.253 10.1.3 - Voice receive state */
+    /* TODO: */
+    t += 4;
+    return t;
+}
+/*- End of function --------------------------------------------------------*/
+
+static const char *at_cmd_plus_VSD(at_state_t *s, const char *t)
+{
+    /* V.253 10.2.7 - Silence detection (QUIET and SILENCE) */
+    /* TODO: */
+    t += 4;
+    return t;
+}
+/*- End of function --------------------------------------------------------*/
+
+static const char *at_cmd_plus_VSID(at_state_t *s, const char *t)
+{
+    /* Extension of V.253 +VCID, Set calling number ID */
+    t += 5;
+    if (!parse_string_out(s, &t, &s->local_id, NULL))
+        return NULL;
+    if (at_modem_control(s, AT_MODEM_CONTROL_SETID, s->local_id) < 0)
+        return NULL;
+    return t;
+}
+/*- End of function --------------------------------------------------------*/
+
+static const char *at_cmd_plus_VSM(at_state_t *s, const char *t)
+{
+    /* V.253 10.2.8 - Compression method selection */
+    /* TODO: */
+    t += 4;
+    return t;
+}
+/*- End of function --------------------------------------------------------*/
+
+static const char *at_cmd_plus_VSP(at_state_t *s, const char *t)
+{
+    /* V.253 10.5.1 - Voice speakerphone state */
+    /* TODO: */
+    t += 4;
+    return t;
+}
+/*- End of function --------------------------------------------------------*/
+
+static const char *at_cmd_plus_VTA(at_state_t *s, const char *t)
+{
+    /* V.253 10.5.4 - Train acoustic echo-canceller */ 
+    /* TODO: */
+    t += 4;
+    return t;
+}
+/*- End of function --------------------------------------------------------*/
+
+static const char *at_cmd_plus_VTD(at_state_t *s, const char *t)
+{
+    /* V.253 10.2.9 - Beep tone duration timer */
+    /* TODO: */
+    t += 4;
+    return t;
+}
+/*- End of function --------------------------------------------------------*/
+
+static const char *at_cmd_plus_VTER(at_state_t *s, const char *t)
+{
+    /* V.252 6.4 - Simple telephony event report */
+    /* TODO: */
+    t += 4;
+    return t;
+}
+/*- End of function --------------------------------------------------------*/
+
+static const char *at_cmd_plus_VTH(at_state_t *s, const char *t)
+{
+    /* V.253 10.5.5 - Train line echo-canceller */ 
+    /* TODO: */
+    t += 4;
+    return t;
+}
+/*- End of function --------------------------------------------------------*/
+
+static const char *at_cmd_plus_VTR(at_state_t *s, const char *t)
+{
+    /* V.253 10.1.4 - Voice duplex state */
+    /* TODO: */
+    t += 4;
+    return t;
+}
+/*- End of function --------------------------------------------------------*/
+
+static const char *at_cmd_plus_VTS(at_state_t *s, const char *t)
+{
+    /* V.253 10.1.5 - DTMF and tone generation in voice */
+    /* TODO: */
+    t += 4;
+    return t;
+}
+/*- End of function --------------------------------------------------------*/
+
+static const char *at_cmd_plus_VTX(at_state_t *s, const char *t)
+{
+    /* V.253 10.1.6 - Transmit data state */
+    /* TODO: */
+    t += 4;
+    return t;
+}
+/*- End of function --------------------------------------------------------*/
+
+static const char *at_cmd_plus_VXT(at_state_t *s, const char *t)
+{
+    /* IS-101 10.1.5 - Translate voice data */
+    /* TODO: */
+    t += 4;
+    return t;
+}
+/*- End of function --------------------------------------------------------*/
+
+static const char *at_cmd_plus_W(at_state_t *s, const char *t)
+{
+    /* TIA-678 5.2.4.1 - Compliance indication */
+    /* TODO: */
+    t += 2;
+    return t;
+}
+/*- End of function --------------------------------------------------------*/
+
+static const char *at_cmd_plus_WBAG(at_state_t *s, const char *t)
+{
+    /* TIA-678 C.5.6 Bias Modem Audio Gain */
+    /* TODO: */
+    t += 5;
+    return t;
+}
+/*- End of function --------------------------------------------------------*/
+
+static const char *at_cmd_plus_WCDA(at_state_t *s, const char *t)
+{
+    /* TIA-678 B.3.2.5 Display Data Link Address */
+    /* TODO: */
+    t += 5;
+    return t;
+}
+/*- End of function --------------------------------------------------------*/
+
+static const char *at_cmd_plus_WCHG(at_state_t *s, const char *t)
+{
+    /* TIA-678 B.3.2.4 Display Battery Charging Status */
+    /* TODO: */
+    t += 5;
+    return t;
+}
+/*- End of function --------------------------------------------------------*/
+
+static const char *at_cmd_plus_WCID(at_state_t *s, const char *t)
+{
+    /* TIA-678 B.3.2.1 Display System ID (operator) */
+    /* TODO: */
+    t += 5;
+    return t;
+}
+/*- End of function --------------------------------------------------------*/
+
+static const char *at_cmd_plus_WCLK(at_state_t *s, const char *t)
+{
+    /* TIA-678 B.3.2.3 Lock/Unlock DCE */
+    /* TODO: */
+    t += 5;
+    return t;
+}
+/*- End of function --------------------------------------------------------*/
+
+static const char *at_cmd_plus_WCPN(at_state_t *s, const char *t)
+{
+    /* TIA-678 B.3.2.2 Set Personal Identification Number */
+    /* TODO: */
+    t += 5;
+    return t;
+}
+/*- End of function --------------------------------------------------------*/
+
+static const char *at_cmd_plus_WCXF(at_state_t *s, const char *t)
+{
+    /* TIA-678 B.3.2.6 Display Supported Annex B commands */
+    /* TODO: */
+    t += 5;
+    return t;
+}
+/*- End of function --------------------------------------------------------*/
+
+static const char *at_cmd_plus_WDAC(at_state_t *s, const char *t)
+{
+    /* TIA-678 C.5.1 Data over Analogue Cellular Command Query */
+    /* TODO: */
+    t += 5;
+    return t;
+}
+/*- End of function --------------------------------------------------------*/
+
+static const char *at_cmd_plus_WDIR(at_state_t *s, const char *t)
+{
+    /* TIA-678 C.5.8 Phone Number Directory Selection */
+    /* TODO: */
+    t += 5;
+    return t;
+}
+/*- End of function --------------------------------------------------------*/
+
+static const char *at_cmd_plus_WECR(at_state_t *s, const char *t)
+{
+    /* TIA-678 C.5.3 Enable Cellular Result Codes */
+    /* TODO: */
+    t += 5;
+    return t;
+}
+/*- End of function --------------------------------------------------------*/
+
+static const char *at_cmd_plus_WFON(at_state_t *s, const char *t)
+{
+    /* TIA-678 C.5.5 Phone Specification */
+    /* TODO: */
+    t += 5;
+    return t;
+}
+/*- End of function --------------------------------------------------------*/
+
+static const char *at_cmd_plus_WKPD(at_state_t *s, const char *t)
+{
+    /* TIA-678 C.5.7 Keypad Emulation */
+    /* TODO: */
+    t += 5;
+    return t;
+}
+/*- End of function --------------------------------------------------------*/
+
+static const char *at_cmd_plus_WPBA(at_state_t *s, const char *t)
+{
+    /* TIA-678 C.5.9 Phone Battery Query */
+    /* TODO: */
+    t += 5;
+    return t;
+}
+/*- End of function --------------------------------------------------------*/
+
+static const char *at_cmd_plus_WPTH(at_state_t *s, const char *t)
+{
+    /* TIA-678 C.5.10 Call Path */
+    /* TODO: */
+    t += 5;
+    return t;
+}
+/*- End of function --------------------------------------------------------*/
+
+static const char *at_cmd_plus_WRLK(at_state_t *s, const char *t)
+{
+    /* TIA-678 C.5.4 Roam Lockout */
+    /* TODO: */
+    t += 5;
+    return t;
+}
+/*- End of function --------------------------------------------------------*/
+
+static const char *at_cmd_plus_WS45(at_state_t *s, const char *t)
+{
+    /* TIA-678 5.2.4.2 DTE-side stack selection */
+    /* TODO: */
+    t += 5;
+    return t;
+}
+/*- End of function --------------------------------------------------------*/
+
+static const char *at_cmd_plus_WS46(at_state_t *s, const char *t)
+{
+    /* 3GPP TS 27.007 5.9 - PCCA STD-101 [17] select wireless network */
+    /* TODO: */
+    t += 5;
+    return t;
+}
+/*- End of function --------------------------------------------------------*/
+
+static const char *at_cmd_plus_WS50(at_state_t *s, const char *t)
+{
+    /* TIA-678 B.3.1.1 Normalized Signal Strength */
+    /* TODO: */
+    t += 5;
+    return t;
+}
+/*- End of function --------------------------------------------------------*/
+
+static const char *at_cmd_plus_WS51(at_state_t *s, const char *t)
+{
+    /* TIA-678 B.3.1.2 Carrier Detect Signal Threshold */
+    /* TODO: */
+    t += 5;
+    return t;
+}
+/*- End of function --------------------------------------------------------*/
+
+static const char *at_cmd_plus_WS52(at_state_t *s, const char *t)
+{
+    /* TIA-678 B.3.1.3 Normalized Battery Level */
+    /* TODO: */
+    t += 5;
+    return t;
+}
+/*- End of function --------------------------------------------------------*/
+
+static const char *at_cmd_plus_WS53(at_state_t *s, const char *t)
+{
+    /* TIA-678 B.3.1.4 Normalized Channel Quality */
+    /* TODO: */
+    t += 5;
+    return t;
+}
+/*- End of function --------------------------------------------------------*/
+
+static const char *at_cmd_plus_WS54(at_state_t *s, const char *t)
+{
+    /* TIA-678 B.3.1.5 Carrier Detect Channel Quality Threshold */
+    /* TODO: */
+    t += 5;
+    return t;
+}
+/*- End of function --------------------------------------------------------*/
+
+static const char *at_cmd_plus_WS57(at_state_t *s, const char *t)
+{
+    /* TIA-678 B.3.1.7 Antenna Preference */
+    /* TODO: */
+    t += 5;
+    return t;
+}
+/*- End of function --------------------------------------------------------*/
+
+static const char *at_cmd_plus_WS58(at_state_t *s, const char *t)
+{
+    /* TIA-678 B.3.1.8 Idle Time-out Value */
+    /* TODO: */
+    t += 5;
+    return t;
+}
+/*- End of function --------------------------------------------------------*/
+
+static const char *at_cmd_plus_WSTL(at_state_t *s, const char *t)
+{
+    /* TIA-678 C.5.2 Call Session Time Limit */
+    /* TODO: */
+    t += 5;
+    return t;
+}
+/*- End of function --------------------------------------------------------*/
+
+/*
+    AT command group prefixes:
+
+    +A    Call control (network addressing) issues, common, PSTN, ISDN, Rec. X.25, switched digital
+    +C    Digital cellular extensions
+    +D    Data compression, Rec. V.42bis
+    +E    Error control, Rec. V.42
+    +F    Facsimile, Rec. T.30, etc.
+    +G    Generic issues such as identity and capabilities
+    +I    DTE-DCE interface issues, Rec. V.24, etc.
+    +M    Modulation, Rec. V.32bis, etc.
+    +S    Switched or simultaneous data types
+    +T    Test issues
+    +V    Voice extensions
+    +W    Wireless extensions
+*/
+
+#include "at_interpreter_dictionary.h"
+
+static int command_search(const char *u, int len, int *matched)
+{
+    int i;
+    int index;
+    int first;
+    int last;
+    int entry;
+    int ptr;
+
+    entry = 0;
+    /* Loop over the length of the string to search the trie... */
+    for (i = 0, ptr = 0;  ptr < COMMAND_TRIE_LEN;  i++)
+    {
+        /* The character in u we are processing... */
+        /* V.250 5.4.1 says upper and lower case are equivalent in commands */
+        index = (unsigned char) toupper(u[i]);
+        /* Is there a child node for this character? */
+        /* Note: First and last could have been packed into one uint16_t,
+           but space is not that critical, so the other packing is good
+           enough to make the table reasonable. */
+        first = command_trie[ptr++];
+        last = command_trie[ptr++];
+        entry = command_trie[ptr++];
+        if (index < first  ||  index > last)
+            break;
+        if ((ptr = command_trie[ptr + index - first]) == 0)
+            break;
+        ptr--;
+    }
+    *matched = i;
+    return entry;
+}
+/*- End of function --------------------------------------------------------*/
+
+SPAN_DECLARE(int) at_modem_control(at_state_t *s, int op, const char *num)
+{
+    switch (op)
+    {
+    case AT_MODEM_CONTROL_ANSWER:
+        break;
+    case AT_MODEM_CONTROL_CALL:
+        break;
+    case AT_MODEM_CONTROL_HANGUP:
+        break;
+    case AT_MODEM_CONTROL_OFFHOOK:
+        break;
+    case AT_MODEM_CONTROL_DTR:
+        break;
+    case AT_MODEM_CONTROL_RTS:
+        break;
+    case AT_MODEM_CONTROL_CTS:
+        break;
+    case AT_MODEM_CONTROL_CAR:
+        break;
+    case AT_MODEM_CONTROL_RNG:
+        break;
+    case AT_MODEM_CONTROL_DSR:
+        break;
+    case AT_MODEM_CONTROL_RESTART:
+        break;
+    default:
+        break;
+    }
+    /*endswitch*/
+    return s->modem_control_handler(s, s->modem_control_user_data, op, num);
+}
+/*- End of function --------------------------------------------------------*/
+
+SPAN_DECLARE(void) at_interpreter(at_state_t *s, const char *cmd, int len)
+{
+    int i;
+    int c;
+    int entry;
+    int matched;
+    const char *t;
+
+    if (s->p.echo)
+        s->at_tx_handler(s, s->at_tx_user_data, (uint8_t *) cmd, len);
+
+    for (i = 0;  i < len;  i++)
+    {
+        /* The spec says the top bit should be ignored */
+        c = *cmd++ & 0x7F;
+        /* Handle incoming character */
+        if (s->line_ptr < 2)
+        {
+            /* Look for the initial "at", "AT", "a/" or "A/", and ignore anything before it */
+            /* V.250 5.2.1 only shows "at" and "AT" as command prefixes. "At" and "aT" are
+               not specified, despite 5.4.1 saying upper and lower case are equivalent in
+               commands. Let's be tolerant and accept them. */
+            if (tolower(c) == 'a')
+            {
+                s->line_ptr = 0;
+                s->line[s->line_ptr++] = (char) toupper(c);
+            }
+            else if (s->line_ptr == 1)
+            {
+                if (tolower(c) == 't')
+                {
+                    /* We have an "AT" command */
+                    s->line[s->line_ptr++] = (char) toupper(c);
+                }
+                else if (c == '/')
+                {
+                    /* We have an "A/" command */
+                    /* TODO: implement "A/" command repeat */
+                    s->line[s->line_ptr++] = (char) c;
+                }
+                else
+                {
+                    s->line_ptr = 0;
+                }
+            }
+        }
+        else
+        {
+            /* We are beyond the initial AT */
+            if (c >= 0x20  &&  c <= 0x7E)
+            {
+                /* Add a new char */
+                if (s->line_ptr < (int) (sizeof(s->line) - 1))
+                    s->line[s->line_ptr++] = (char) toupper(c);
+            }
+            else if (c == s->p.s_regs[3])
+            {
+                /* End of command line. Do line validation */
+                s->line[s->line_ptr] = '\0';
+                if (s->line_ptr > 2)
+                {
+                    /* The spec says the commands within a command line are executed in order, until
+                       an error is found, or the end of the command line is reached. */
+                    t = s->line + 2;
+                    while (t  &&  *t)
+                    {
+                        if ((entry = command_search(t, 15, &matched)) <= 0)
+                            break;
+                        if ((t = at_commands[entry - 1](s, t)) == NULL)
+                            break;
+                        if (t == (const char *) -1)
+                            break;
+                    }
+                    if (t != (const char *) -1)
+                    {
+                        if (t == NULL)
+                            at_put_response_code(s, AT_RESPONSE_CODE_ERROR);
+                        else
+                            at_put_response_code(s, AT_RESPONSE_CODE_OK);
+                    }
+                }
+                else if (s->line_ptr == 2)
+                {
+                    /* It's just an empty "AT" command, return OK. */
+                    at_put_response_code(s, AT_RESPONSE_CODE_OK);
+                }
+                s->line_ptr = 0;
+            }
+            else if (c == s->p.s_regs[5])
+            {
+                /* Command line editing character (backspace) */
+                if (s->line_ptr > 0)
+                    s->line_ptr--;
+            }
+            else
+            {
+                /* The spec says control characters, other than those
+                   explicitly handled, should be ignored, and so this
+                   invalid character causes everything buffered
+                   before it to also be ignored. */
+                s->line_ptr = 0;
+            }
+        }
+    }
+}
+/*- End of function --------------------------------------------------------*/
+
+SPAN_DECLARE(void) at_set_class1_handler(at_state_t *s, at_class1_handler_t handler, void *user_data)
+{
+    s->class1_handler = handler;
+    s->class1_user_data = user_data;
+}
+/*- End of function --------------------------------------------------------*/
+
+SPAN_DECLARE(at_state_t *) at_init(at_state_t *s,
+                                   at_tx_handler_t *at_tx_handler,
+                                   void *at_tx_user_data,
+                                   at_modem_control_handler_t *modem_control_handler,
+                                   void *modem_control_user_data)
+{
+    if (s == NULL)
+    {
+        if ((s = (at_state_t *) malloc(sizeof(*s))) == NULL)
+            return NULL;
+    }
+    memset(s, '\0', sizeof(*s));
+    span_log_init(&s->logging, SPAN_LOG_NONE, NULL);
+    span_log_set_protocol(&s->logging, "AT");
+    s->modem_control_handler = modem_control_handler;
+    s->modem_control_user_data = modem_control_user_data;
+    s->at_tx_handler = at_tx_handler;
+    s->at_tx_user_data = at_tx_user_data;
+    s->call_id = NULL;
+    s->local_id = NULL;
+    s->display_call_info = 0;
+    at_set_at_rx_mode(s, AT_MODE_ONHOOK_COMMAND);
+    s->p = profiles[0];
+    return s;
+}
+/*- End of function --------------------------------------------------------*/
+
+SPAN_DECLARE(int) at_release(at_state_t *s)
+{
+    at_reset_call_info(s);
+    if (s->local_id)
+        free(s->local_id);
+    return 0;
+}
+/*- End of function --------------------------------------------------------*/
+
+SPAN_DECLARE(int) at_free(at_state_t *s)
+{
+    int ret;
+
+    ret = at_release(s);
+    free(s);
+    return ret;
+}
+/*- End of function --------------------------------------------------------*/
+/*- End of file ------------------------------------------------------------*/

Repositories maintained by Peter Meerwald, pmeerw@pmeerw.net.