/*
 * libgsmat: An implementation of GSM 07.07
 *
 * Written by Klaus-Peter Junghanns <support@junghanns.net>
 *
 * Copyright (C) 2005, 2006, 2007 Junghanns.NET GmbH
 * All Rights Reserved.
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 * 
 * 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 General Public License for more details.
 * 
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. 
 *
 */

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
#include <math.h>
#include "libgsmat.h"
#include "gsm_internal.h"
#include "gsm_modem.h"

int gsm_modem_trim(char *data, char *out, int len);

int gsm_modem_transmit(struct gsm_modul *gsm, char *data, int len) {
    int res;

    if (gsm->debug)
	gsm_message(gsm, "> %s\n", data);
    gsm_modem_trim(data, gsm->lastcmd, len);
    res = write(gsm->fd, data, len+2);
    if (res != (len+2)) {
	gsm_error(gsm, "Short write: %d/%d (%s) on span %d\n", res, len+2, strerror(errno), gsm->span);
	return -1;
    }
    return 0;
}

int gsm_modem_send(struct gsm_modul *gsm, char *data) {
    int len = 0;
    
    if (!data) {
	return -1;
    }
    len = strlen(data);
    return gsm_modem_transmit(gsm, data, len);
}

void gsm_modem_send_pin(struct gsm_modul *gsm, char *pin) {
    char buf[256];
    memset(buf, 0x0, sizeof(buf));
    if (gsm->state == GSM_STATE_SIM_PIN_REQ) {
	snprintf(buf, sizeof(buf) - 1, "AT+CPIN=\"%s\"\r", pin);
	gsm_modem_transmit(gsm, buf, strlen(buf)); 
    }
}

void gsm_modem_error_hard(void *data) {
    struct gsm_modul *gsm = (struct gsm_modul *)data;
    gsm->restart_timer = 0;
    gsm->state = GSM_STATE_ERROR;
    gsm->ev.e = GSM_EVENT_ERROR;
    gsm->ev.error.hard = 1;
    gsm->ev.error.cause = 44; /* AST_CAUSE_REQUESTED_CHAN_UNAVAIL */
    gsm->schedev = 1;
}

void gsm_modem_start(struct gsm_modul *gsm) {
    char buf[256];
    memset(buf, 0x0, sizeof(buf));
    snprintf(buf, sizeof(buf) - 1, "ATZ\r");
    gsm_modem_transmit(gsm, buf, strlen(buf));
}

void gsm_modem_restart(void *data) {
    struct gsm_modul *gsm = (struct gsm_modul *)data;
    if (gsm->debug)
        gsm_message(gsm, "restarting\n");
    gsm->dchanup = 0;
    gsm->state = GSM_STATE_POWER_ON;
    gsm->restart_timer = gsm_schedule_event(gsm, 10000, gsm_modem_error_hard, gsm);
    gsm_modem_start(gsm);
}

int gsm_modem_answer(struct gsm_modul *gsm) {
    if (gsm->state == GSM_STATE_RINGING) {
	gsm_modem_transmit(gsm, "ATA\r", 4); 
    }
    return 0; 
}

int gsm_modem_poweroff(struct gsm_modul *gsm) {
    gsm_modem_transmit(gsm, "ATZ\r", 4); 
    gsm_modem_transmit(gsm, "AT+MRST\r", 8); 
    return 0; 
}

int gsm_modem_dial(struct gsm_modul *gsm, int clir, char *called) {
    char buf[256];
    if (gsm->state == GSM_STATE_READY) {
	gsm->hungup = 0;
	if (clir > 0) clir = 1;
	gsm->clir = clir;
	strncpy(gsm->cpn, called, sizeof(gsm->cpn) - 1);
	memset(buf, 0x0, sizeof(buf));
	snprintf(buf, sizeof(buf) - 1, "AT+CLIR=%d\r", clir);
        gsm->state = GSM_STATE_CALL_INIT;    
	gsm_modem_transmit(gsm, buf, strlen(buf)); 
	return 0;
    }
    gsm_error(gsm, "Cannot dial when in state %d\n", gsm->state);
    return -1;
}

int gsm_modem_sms_send_text(struct gsm_modul *gsm, char *destination, char *msg) {
    char buf[1024];
    if (gsm->state == GSM_STATE_READY) {
	snprintf(gsm->msgbuf, sizeof(gsm->msgbuf) - 1, "%s", msg);

	memset(buf, 0x0, sizeof(buf));
	snprintf(buf, sizeof(buf) - 1, "AT+CMGF=1\r");
	gsm_modem_transmit(gsm, buf, strlen(buf)); 
	memset(buf, 0x0, sizeof(buf));
	snprintf(buf, sizeof(buf) - 1, "AT+CMGS=\"%s\"\r", destination);
	gsm_modem_transmit(gsm, buf, strlen(buf)); 

	gsm->state = GSM_STATE_SM_SEND_REQ;    
	return 0;
    }
    gsm_error(gsm, "Cannot send SM when in state %d\n", gsm->state);
    return -1;
}

int gsm_modem_sms_send_pdu(struct gsm_modul *gsm, char *pdu) {
    char buf[1024];
    char smsc[3];
    int smsc_len;
    int len = 0;

    if (!pdu) return 0;
    
    len = strlen(pdu);

    /* SMSC information length */
    strncpy(smsc, pdu, sizeof(smsc) - 1);
    smsc[2] = '\0';
    smsc_len = gsm_hex2int(smsc);
    len = (len / 2) - 1 - smsc_len;

    if (gsm->state == GSM_STATE_READY) {
	snprintf(gsm->msgbuf, sizeof(gsm->msgbuf) - 1, "%s", pdu);

	memset(buf, 0x0, sizeof(buf));
	snprintf(buf, sizeof(buf) - 1, "AT+CMGF=0\r");
	gsm_modem_transmit(gsm, buf, strlen(buf)); 
	memset(buf, 0x0, sizeof(buf));
	snprintf(buf, sizeof(buf) - 1, "AT+CMGS=%d\r", len);
	gsm_modem_transmit(gsm, buf, strlen(buf)); 

	gsm->state = GSM_STATE_SM_SEND_REQ;    
	return 0;
    }
    gsm_error(gsm, "Cannot send SM when in state %d\n", gsm->state);
    return -1;
}

int gsm_modem_request_status(struct gsm_modul *gsm) {
    char buf[256];
    if (gsm->state <= GSM_STATE_READY) {
	memset(buf, 0x0, sizeof(buf));
	snprintf(buf, sizeof(buf) - 1, "AT+CSQ?\r");
	gsm->state = GSM_STATE_STATUS_REQ;    
	gsm_modem_transmit(gsm, buf, strlen(buf));
    } else {
	gsm_error(gsm, "cannot request GSM status, module is in use\n");
    }
    return 0; 
}

int gsm_modem_hangup(struct gsm_modul *gsm) {
    char buf[256];
    gsm->cpn[0] = '\0';
    memset(buf, 0x0, sizeof(buf));
    snprintf(buf, sizeof(buf) - 1, "ATH\r");
    if (!gsm->hungup && ((gsm->state == GSM_STATE_CALL_PRESENT) || (gsm->state == GSM_STATE_CALL_ACTIVE) || (gsm->state == GSM_STATE_RINGING))) {
	gsm->state = GSM_STATE_HANGUP_REQ;
	gsm_modem_transmit(gsm, buf, strlen(buf));
	if (!gsm->restart_timer) {
	    gsm->restart_timer = gsm_schedule_event(gsm, 3000, gsm_modem_restart, gsm);
	}
    }
    return 0; 
}


int gsm_modem_trim(char *data, char *out, int len) {
    int i=0;
    int j=0;

    for (i=0;i<len;i++) {
	if ((data[i] != '\r') && (data[i] != '\n') ) {
	    out[j++] = data[i]; 
	}
    }
    out[j++] = 0x0;
    return j;
}

int gsm_modem_san(struct gsm_modul *gsm, char *in, char *out, int len) {
    int i=0;
    int skip=0;
    char *tmp = NULL;

    if ((len > 0) && ((gsm->sanidx + len) < sizeof(gsm->sanbuf))) {
	memcpy(gsm->sanbuf + gsm->sanidx, in, len);
	gsm->sanidx += len;
	gsm->sanbuf[gsm->sanidx] = 0x0;
    }

    while ((gsm->sanbuf[i] == '\r') || (gsm->sanbuf[i] == '\n')) {
        i++;
    }
    
    skip = i;
//    gsm_error(gsm, "1 sanidx %d\n", gsm->sanidx);

	tmp = strchr(gsm->sanbuf + skip, '\r');
        if (tmp){
    	    i = tmp - (gsm->sanbuf + skip);
    	    memcpy(out, (gsm->sanbuf + skip), i);
	    out[i] = 0x0;
//	gsm_error(gsm, "got %d bytes\n", i);
	    memmove(&gsm->sanbuf[0], &gsm->sanbuf[skip+i], sizeof(gsm->sanbuf) - i - skip);
	    gsm->sanidx -= i + skip;
//	gsm_error(gsm, "2 sanidx %d\n", gsm->sanidx);
	    return i;
	} else {
//	    gsm_error(gsm, "skip %d $%s$\n", skip, gsm->sanbuf);
	    if (gsm->state == GSM_STATE_SM_SEND_REQ) {
		if ((gsm->sanidx - skip == 2) && (gsm->sanbuf[gsm->sanidx - 2] == '>') && (gsm->sanbuf[gsm->sanidx - 1] == ' ')) {
		    out[0] = '>';
		    out[1] = ' ';
		    out[2] = 0x0;
		    gsm->sanidx -= 2 + skip;
//		    gsm_error(gsm, "2 sanidx %d\n", gsm->sanidx);
		    return 2;
	        }
	    }
	}

    return 0;
}


gsm_event *gsm_modem_receive(struct gsm_modul *gsm, char *data, int len) {
    int res;
    char buf[1024];
    char buf2[1024];
    char buf3[1024];
    char callerid[256];
    char loc_str[256];
    char *a = NULL;
    int creg1 = 0;
    int creg2 = 0;
    int creg3 = 0;
    int registered = 0;
    char tmp[256];

    res = gsm_modem_san(gsm, data, buf, len);
    if (res) {
	gsm_modem_trim(buf, buf2, res);

	    if (gsm->debug)
		gsm_message(gsm, "< %s\n",buf2);
	
	    strncpy(buf3, buf2, sizeof(buf3) - 1);
	    if (!strncmp(buf3, "+CREG:", 6)) {
		if (!strncmp(gsm->lastcmd, "AT+CREG?", 8)) {
		    if (sscanf(buf3, "+CREG: %d,%d,%s", &creg1, &creg2, loc_str) == 3) {
	    		// gsm_message(gsm, "creg %d %s\n", creg1, loc_str);
		    }
		    gsm->lastcmd[0] = '\0';
		} else {
		    if (!strncmp(buf3, "+CREG: 002", 10)) {
			creg2 = 2;
		    } else if (!strncmp(buf3, "+CREG: 003", 10)) {
			creg2 = 3;
		    } else if (sscanf(buf3, "+CREG: %d,%s", &creg1, loc_str) == 2) {
	    		/* unsolicited */
			// gsm_message(gsm, "creg %d %d %d\n", creg1, creg2, creg3);
			creg2 = creg1;
		    }
		}
		registered = (creg2 == 1) || (creg2 == 5) || (creg2 == 8);
		if (registered) {
		    if (!gsm->dchanup) {
			gsm->dchanup = 1;
			gsm_modem_send(gsm, "AT+CLIP=1\r");
			gsm->state = GSM_STATE_READY;
			gsm->ev.e = GSM_EVENT_DCHAN_UP;
			return &gsm->ev;
		    }
		} else {
		    if (gsm->dchanup) {
			gsm->dchanup = 0;
			gsm->state = GSM_STATE_READY;
			gsm->ev.e = GSM_EVENT_DCHAN_DOWN;
			return &gsm->ev;
		    }
		}
	    }
	
	switch (gsm->state) {
	    case GSM_STATE_POWER_ON:
		if (!strncmp(buf2, "OK", res)) {
		    if (!strncmp(gsm->lastcmd, "ATZ", 3)) {
			gsm->state = GSM_STATE_INIT_0;
			gsm_modem_send(gsm, "AT+CMEE=2\r");
		    }
		    if (gsm->restart_timer) {
			gsm_schedule_del(gsm, gsm->restart_timer);
			gsm->restart_timer = 0;
		    }
		} else if (!strncmp(buf2, "ATZ", res)) {
		    /* echo */
		    if (gsm->restart_timer) {
			gsm_schedule_del(gsm, gsm->restart_timer);
			gsm->restart_timer = 0;
		    }
		} else {
		    gsm_error(gsm, "!%s!\n", buf2);
		} 
		break;
	    case GSM_STATE_INIT_0:
		if (!strncmp(buf2, "OK", res)) {
		    gsm->state = GSM_STATE_INIT_1;
		    gsm_modem_send(gsm, "AT+MADIGITAL=1\r");
		} else if (!strncmp(buf2, "AT+CMEE=2", res)) {
		    /* echo */
		} else {
		    gsm_error(gsm, "!%s!\n", buf2);
		} 
		break;
	    case GSM_STATE_INIT_1:
		if (!strncmp(buf2, "OK", res)) {
		    gsm->state = GSM_STATE_INIT_2;
		    gsm_modem_send(gsm, "AT+CPIN?\r");
		} else if (!strncmp(buf2, "AT+MADIGITAL=1", res)) {
		    /* echo */
		} else {
		    gsm_error(gsm, "!%s!\n", buf2);
		} 
		break;
	    case GSM_STATE_INIT_2:
		if (!strncmp(buf2, "+CPIN: READY", res)) {
		    gsm_modem_send(gsm, "AT+CREG=2\r");
		    gsm->sim_state = GSM_STATE_SIM_READY;
		} else if (!strncmp(buf2, "+CPIN: SIM PIN", res)) {
		    gsm->sim_state = GSM_STATE_SIM_PIN_REQ;
		} else if (!strncmp(buf2, "OK", res)) {
		    switch(gsm->sim_state) {
			case GSM_STATE_SIM_READY:
			    gsm->state = GSM_STATE_SIM_READY;
			    gsm_modem_send(gsm, "AT+CREG=2\r");
			    break;
			case GSM_STATE_SIM_PIN_REQ:
			    gsm->state = GSM_STATE_SIM_PIN_REQ;
			    gsm->ev.e = GSM_EVENT_PIN_REQUIRED;
		    	    return &gsm->ev;
			    break;
		    }
		} else if (!strncmp(buf2, "AT+CPIN?", res)) {
		    /* echo */
		} else {
		    gsm_error(gsm, "!%s!\n", buf2);
		} 
		break;
	    case GSM_STATE_SIM_PIN_REQ:
		if (!strncmp(buf2, "OK", res)) {
		    //gsm_message(gsm, "LAST>%s<\n",gsm->lastcmd);
		    gsm->state = GSM_STATE_SIM_READY;
		    gsm_modem_send(gsm, "AT+CREG=2\r");
		} else if (!strncmp(buf2, "AT+CREG=2", res)) {
		} else if (!strncmp(buf2, "AT+CPIN", 7)) {
		    /* echo */
		} else {
		    gsm_error(gsm, "!%s!\n", buf2);
		} 
		break;
	    case GSM_STATE_SIM_READY:
		if (!strncmp(buf2, "OK", res)) {
		    gsm_modem_send(gsm, "AT+CREG?\r");
		    gsm->state = GSM_STATE_NET_REQ;
		} else if (!strncmp(buf2, "AT+CREG=2", res)) { 
		    /* echo */
		} else {
		    gsm_error(gsm, "!%s!\n", buf2);
		} 
		break;
	    case GSM_STATE_NET_REQ:
		if (!strncmp(buf2, "+CREG:", 6)) {
		    if (sscanf(buf2, "+CREG: %d,%d,%d", &creg1, &creg2, &creg3) == 3) {
			//gsm_message(gsm, "creg %d %d %d\n", creg1, creg2, creg3);
		    }
		} else {
		//    gsm_error("!%s!\n", buf2);
		} 
		break;
	    case GSM_STATE_READY:
		if (!strncmp(gsm->lastcmd, "AT+CLIP=1", 9)) {
		    gsm_modem_send(gsm, "AT+CLCC=1\r");
		}
		if (!strncmp(gsm->lastcmd, "AT+CLCC=1", 9)) {
		    gsm_modem_send(gsm, "AT+MADIGITAL=1\r");
		}
		if (!strncmp(gsm->lastcmd, "AT+MADIGITAL=1", 14)) {
		    snprintf(tmp, sizeof(tmp) - 1, "AT+MAVOL=8,1,%d\r", GSM_DIGITAL_VOLUME);
		    gsm_modem_send(gsm, tmp);
		}
		if (!strncmp(gsm->lastcmd, "AT+MAVOL=8,1,", 13)) {
		    gsm_modem_send(gsm, "AT+MAMUT=3,1\r");
		}
		if (!strncmp(gsm->lastcmd, "AT+MAMUT=3,1", 8)) {
		    gsm_modem_send(gsm, "ATS7=255\r");
		}
		if (!strncmp(gsm->lastcmd, "ATS7=255", 8)) {
		    gsm_modem_send(gsm, "AT+CLIR=0\r");
		}
		if (!strncmp(gsm->lastcmd, "AT+CLIR=0", 9)) {
		    gsm_modem_send(gsm, "AT+MAFEAT=7,0\r");
		}
		if (!strncmp(gsm->lastcmd, "AT+MAFEAT=7,0", 13)) {
		    gsm_modem_send(gsm, "AT+CMGF=0\r");
		}
		if (!strncmp(gsm->lastcmd, "AT+CMGF=0", 9)) {
		    gsm_modem_send(gsm, "AT+CNMI=0,2,2,0,0\r");
		}
		callerid[0] = '\0';
		gsm->ev.ring.callingnum[0] = '0';
		if (!strncmp(buf2, "+CLIP:", 6)) {
		    gsm->state = GSM_STATE_RINGING;
		    if (sscanf(buf2, "+CLIP: \"%s\",129,,128,\"\",0", callerid) == 1) {
			a = strchr(callerid, '"');
			if (a)
			    *a = '\0';
		    }
		    gsm->hungup = 0;
		    if (!strlen(callerid)) {
			strncpy(gsm->ev.ring.callingnum, "", sizeof(gsm->ev.ring.callingnum) - 1);
		    } else {
			strncpy(gsm->ev.ring.callingnum, callerid, sizeof(gsm->ev.ring.callingnum) - 1);
		    }
		    gsm->ev.e = GSM_EVENT_RING;
		    return &gsm->ev;
		} else if (!strncmp(buf2, "+CMT:", 5)) {
		    gsm->state = GSM_STATE_SM_RECEIVING;
		} else if (!strncmp(buf2, "NO CARRIER", res)) {
    		    if (!strncmp(gsm->lastcmd, "ATD", 3)) {
			gsm->state = GSM_STATE_READY;
			gsm->ev.e = GSM_EVENT_HANGUP;
			gsm->ev.hangup.cause = 16;
			return &gsm->ev;
		    }
		} else if (!strncmp(buf2, "OK", res)) {
    		    if (!strncmp(gsm->lastcmd, "ATD", 3)) {
			gsm->state = GSM_STATE_CALL_PRESENT;
		    }
		    /* sms sent */
		} else if (!strncmp(buf2, "+CREG", 5)) {
		} else if (!strncmp(buf2, "AT+CLIP=1", res)) { 
		} else if (!strncmp(buf2, "AT+CLCC=1", res)) {
		} else if (!strncmp(buf2, "AT+MADIGITAL=1", res)) {
		} else if (!strncmp(buf2, "AT+MAVOL=8,1,", 13)) { 
		} else if (!strncmp(buf2, "AT+MAMUT=3,1", res)) { 
		} else if (!strncmp(buf2, "ATS7=255", res)) { 
		} else if (!strncmp(buf2, "AT+CLIR=0", res)) { 
		} else if (!strncmp(buf2, "AT+MAFEAT=7,0", res)) { 
		} else if (!strncmp(buf2, "AT+CMGF=0", res)) { 
		} else if (!strncmp(buf2, "AT+CNMI=0,2,2,0,0", res)) { 
		} else if (!strncmp(buf2, "+CLCC:", 6)) {
		} else if (!strncmp(buf2, "RING", res)) {
		} else if (!strncmp(buf2, "AT+CHUP", res)) {
		    /* echo */
		} else {
		    gsm_error(gsm, "!%s!\n", buf2);
		} 
		break;
	    case GSM_STATE_RINGING:
		if (!strncmp(buf2, "NO CARRIER", res)) {
		    gsm->state = GSM_STATE_READY;
		    gsm->ev.e = GSM_EVENT_HANGUP;
		    gsm->ev.hangup.cause = 16;
		    return &gsm->ev;
		} else if (!strncmp(buf2, "OK", res)) {
    		    if (!strncmp(gsm->lastcmd, "ATA", 3)) {
			gsm->state = GSM_STATE_CALL_ACTIVE;
		    }
		} else if (!strncmp(buf2, "+CLCC: 1,0,6", 12)) {
		    gsm->state = GSM_STATE_READY;
    		    if (strncmp(gsm->lastcmd, "ATH", 3)) {
			gsm->ev.e = GSM_EVENT_HANGUP;
			return &gsm->ev;
		    } 
		} else if (!strncmp(buf2, "BUSY", res)) {
		    gsm->state = GSM_STATE_READY;
		    gsm->ev.e = GSM_EVENT_HANGUP;
		    gsm->ev.hangup.cause = 17;
		    return &gsm->ev;
		} else if (!strncmp(buf2, "+CLIP:", 6)) {
		} else if (!strncmp(buf2, "RING", res)) {
		} else if (!strncmp(buf2, "ATA", 3)) {
		} else if (!strncmp(buf2, "+CLCC:", 6)) {
		    /* echo */
		} else {
		    gsm_error(gsm, "!%s!\n", buf2);
		} 
		break;
	    case GSM_STATE_CALL_ACTIVE:
		if (!strncmp(buf2, "NO CARRIER", 10)) {
		    gsm->state = GSM_STATE_READY;
		    gsm->ev.e = GSM_EVENT_HANGUP;
		    gsm->ev.hangup.cause = 16;
		    return &gsm->ev;
		} else if ((!strncmp(buf2, "+CLCC: 1,0,6", 12)) || (!strncmp(buf2, "+CLCC: 1,1,6", 12))) {
		    gsm->state = GSM_STATE_READY;
		    gsm->ev.e = GSM_EVENT_HANGUP;
		    gsm->ev.hangup.cause = 16;
		    return &gsm->ev;
		} else if (!strncmp(buf2, "OK", 2)) {
    		    if (!strncmp(gsm->lastcmd, "ATH", 3)) {
			gsm->state = GSM_STATE_READY;
		    }
		} else if (!strncmp(buf2, "+CREG", 5)) {
		} else if (!strncmp(buf2, "+CLCC: 1,0,0", 12)) {
		} else if (!strncmp(buf2, "+CLCC: 1,1,0", 12)) {
		} else if (!strncmp(buf2, "AT+CHUP", 7)) {
		    /* echo */
		    gsm_error(gsm, "!%s!\n", buf2);
		} 
		break;
	    case GSM_STATE_CALL_INIT:
		if (!strncmp(buf2, "AT+CLIR=", 8)) {
		} else if (!strncmp(buf2, "OK", 8)) {
    		    if (!strncmp(gsm->lastcmd, "AT+CLIR=", 8)) {
			gsm->lastcmd[0] = '\0';
			if (strlen(gsm->cpn)) {
			    memset(buf, 0x0, sizeof(buf));
			    snprintf(buf, sizeof(buf) - 1, "ATD%s;\r", gsm->cpn);
			    gsm_modem_transmit(gsm, buf, strlen(buf)); 
		    	    gsm->state = GSM_STATE_CALL_PRESENT;    
			} else {
		    	    gsm->state = GSM_STATE_READY;    
			}
		    }
		}
		break;
	    case GSM_STATE_CALL_PRESENT:
		if (!strncmp(buf2, "+CME ERROR", 9)) {
		    /* modem is in weird state, reboot */
		    gsm_error(gsm, "error while dialing (%s)\n", buf2);
		    gsm->state = GSM_STATE_ERROR;
		    gsm->ev.e = GSM_EVENT_ERROR;
		    gsm->ev.error.hard = 1;
		    gsm->ev.error.cause = 44; /* AST_CAUSE_REQUESTED_CHAN_UNAVAIL */
		    strncpy(gsm->ev.error.err, buf2, sizeof(gsm->ev.error.err) - 1);
		    return &gsm->ev;
		} else if (!strncmp(buf2, "NO CARRIER", 10)) {
		    gsm->state = GSM_STATE_READY;
    		    if (strncmp(gsm->lastcmd, "ATH", 3)) {
			gsm->ev.e = GSM_EVENT_HANGUP;
			gsm->ev.hangup.cause = 16;
			return &gsm->ev;
		    }
		} else if (!strncmp(buf2, "NO ANSWER", 9)) {
		    gsm->state = GSM_STATE_READY;
     		    if (strncmp(gsm->lastcmd, "ATH", 3)) {
			gsm->ev.e = GSM_EVENT_HANGUP;
			gsm->ev.hangup.cause = 16;
			return &gsm->ev;
		    }
		} else if (!strncmp(buf2, "+CLCC: 1,0,6", 12)) {
		    gsm->hungup = 1;
		    /* do not report the hangup event yet. wait for the cause code */
/*		    gsm->state = GSM_STATE_READY;
    		    if (strncmp(gsm->lastcmd, "ATH", 3)) {
			gsm->ev.e = GSM_EVENT_HANGUP;
			gsm->ev.hangup.cause = 16;
			return &gsm->ev;
		    } */
		} else if (!strncmp(buf2, "+CLCC: 1,0,3", 12)) {
		    gsm->ev.e = GSM_EVENT_ALERTING;
		    return &gsm->ev;
		} else if (!strncmp(buf2, "OK", 2)) {
    		    if (!strncmp(gsm->lastcmd, "ATD", 3)) {
			    gsm->lastcmd[0] = '\0';
		    } else {
			gsm->state = GSM_STATE_CALL_ACTIVE;
			gsm->ev.e = GSM_EVENT_ANSWER;
			return &gsm->ev;
		    }
		} else if (!strncmp(buf2, "BUSY", res)) {
		    gsm->state = GSM_STATE_READY;
		    gsm->ev.e = GSM_EVENT_HANGUP;
		    gsm->ev.hangup.cause = 17;
		    return &gsm->ev;
		} else if (!strncmp(buf2, "ATD", 3)) {
		} else if (!strncmp(buf2, "+CLCC: 1,0,2", 12)) {
		} else if (!strncmp(buf2, "+CLCC: 1,0,0", 12)) {
		} else if (!strncmp(buf2, "+CREG", 5)) {
		} else if (!strncmp(buf2, "AT+CHUP", res)) {
		} else {
		    gsm_error(gsm, "!%s!\n", buf2);
		} 
		break;
	    case GSM_STATE_SM_SEND_REQ:
		if (!strncmp(buf2, "> ", 2)) {
		    gsm_modem_transmit(gsm, gsm->msgbuf, strlen(gsm->msgbuf)); 
		    memset(gsm->msgbuf, 0x0, sizeof(gsm->msgbuf));
		    snprintf(gsm->msgbuf, sizeof(gsm->msgbuf) - 1, "%c", 0x1A);
		    gsm_modem_transmit(gsm, gsm->msgbuf, strlen(gsm->msgbuf)); 
		    gsm->state = GSM_STATE_SM_SENDING;
		} else if (!strncmp(buf2, "+CMS ERROR", 10)) {
		    gsm_error(gsm, "error sending SMS on span %d (%s)\n", gsm->span,  buf2);
		    gsm->state = GSM_STATE_READY;
		}
		break;
	    case GSM_STATE_SM_SENDING:
		if (!strncmp(buf2, "+CMGS:", 5)) {
		    gsm_modem_send(gsm, "AT+CMGF=0\r");
		    gsm->state = GSM_STATE_READY;
		    gsm_message(gsm, "SMS sent successfully on span %d.\n", gsm->span);
		} else if (!strncmp(buf2, "+CMS ERROR", 9)) {
		    gsm_error(gsm, "error sending sms (%s)\n", buf2);
		    gsm->state = GSM_STATE_READY;
		}
		break;
	    case GSM_STATE_SM_RECEIVING:
		if (strncmp(gsm->lastcmd, "AT+CNMA", 7)) {
		    strncpy(gsm->msgbuf, buf2, sizeof(gsm->msgbuf) - 1);
		    gsm_modem_send(gsm, "AT+CNMA\r");
		}
		if (!strncmp(buf2, "OK", 2)) {
		    gsm->lastcmd[0] = '\0';
		    gsm->state = GSM_STATE_READY;
		    gsm->ev.e = GSM_EVENT_SM_RECEIVED;
		    if (!gsm_pdu2sm_event(gsm, gsm->msgbuf))
			return &gsm->ev;
		}
		break;
	    case GSM_STATE_HANGUP_REQ:
		if ((!strncmp(buf2, "+CLCC: 1,0,6", 12)) || (!strncmp(buf2, "+CLCC: 1,1,6", 12))){
//		if ((!strncmp(buf2, "NO CARRIER", 10)) || (!strncmp(buf2, "+CLCC: 1,0,6", 12))){
		    gsm->state = GSM_STATE_READY;
		    if (gsm->restart_timer) {
			gsm_schedule_del(gsm, gsm->restart_timer);
			gsm->restart_timer = 0;
		    }
		}
		break;
	    case GSM_STATE_STATUS_REQ:
		if (!strncmp(buf2, "+CSQ:", 5)){
		    if (gsm->restart_timer) {
			gsm_schedule_del(gsm, gsm->restart_timer);
			gsm->restart_timer = 0;
		    }
		    gsm->state = GSM_STATE_READY;
		    gsm_message(gsm, "signal strength on span %d (%s) [min 0, max 31]\n", gsm->span, buf2);
		}
		break;

	}
    }
    return NULL;
}


