You can find attached smsc_at2.c containing "alcatel" as new modem type. But it seems that Alcatel mobiles only understands 7 bits encoding, where can the encoding parameter be modified ?

But even using 7 bits encoding, I haven't seen a wap push come on my phone (whereas alcatel mobiles browser are UP.Phone 4.1 compliant with wap 1.2.1...). If anyone has an idea why it does not work... (are there any requirements at operator side ? )

Olivier



Stipe Tolj wrote:
[EMAIL PROTECTED]">
Olivier Durécu wrote :
PS: for alcatel mobiles, two AT commands are wrong
- AT+CNMI=1,3,0,0 (1,2,0,0 does not work)
- for AT+CMGS, the PDU must begin with extra 00

can you provide us a clean patch of smsc_at2.c for the Aplcatel
support?

Stipe

[EMAIL PROTECTED]
-------------------------------------------------------------------
Wapme Systems AG

Münsterstr. 248
40470 Düsseldorf

Tel: +49-211-74845-0
Fax: +49-211-74845-299

E-Mail: [EMAIL PROTECTED]
Internet: http://www.wapme-systems.de
-------------------------------------------------------------------
wapme.net - wherever you are


-- 
##############################
Olivier Durecu
Alcatel Research & Innovation
Project "Next Generation Radio Solution"

Office B1-132 / Phone +33 (0)1 69 63 16 61

Route de Nozay
F-91460 Marcoussis

/*
 * smsc_at2.c
 * New driver for serial connected AT based
 * devices.
 * 4.9.2001
 * Andreas Fink <[EMAIL PROTECTED]>
 * 
 */

#include <errno.h>
#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
#include <ctype.h>
#include <termios.h>
#include <sys/time.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netdb.h>
#include <sys/ioctl.h>
#include <time.h>

#include "gwlib/gwlib.h"
#include "gwlib/charset.h"
#include "smscconn.h"
#include "smscconn_p.h"
#include "bb_smscconn_cb.h"
#include "msg.h"
#include "sms.h"
#include "dlr.h"

/******************************************************************************
 * Types of GSM modems (as used in kannel.conf: modemtype=xxxx)
 */

struct modem_def
{
    char        *name;
    char        *hwhs;                  /* enable hardware handshake */
    long        speed;
    char        *init1;
    char        *detect_string1;
    char        *detect_string2;
    int         enable_parity;          /* needs to set PARITY */
    int         need_sleep;             /* sleep 1 sec after opening port */
    int         pin_support;
    int         skip_smsc_addr;
    int         prepend_zero_smsc;
    double      send_line_sleep;        /* wait seconds after having sent a line */
};

/* indexes into table below. Has to match ! */
#define AT2_AUTODETECT          0
#define AT2_WAVECOM             1
#define AT2_PREMICELL           2
#define AT2_SIEMENS_TC35        3
#define AT2_SIEMENS             4
#define AT2_NOKIAPHONE          5
#define AT2_FALCOM              6
#define AT2_ERICSSON            7
#define AT_ALCATEL              8

#define MAX_MODEM_TYPES         9
struct modem_def ModemTypes[MAX_MODEM_TYPES] =
{
    { "autodetect",     "AT+IFC=2,2"    , 0     , "AT+CNMI=1,2,0,0,0",  NULL,          
 NULL,   0, 0, 1, 0, 0, 0.1 },
    { "wavecom",        "AT+IFC=2,2"    , 9600  , "AT+CNMI=1,2,0,0,0",  "WAVECOM",     
 NULL,   0, 0, 1, 1, 1, 0.1 },
    { "premicell",      "AT+IFC=2,2"    , 9600  , "AT+CNMI=1,2,0,0,0",  "PREMICEL",    
 NULL,   0, 0, 0, 0, 0, 0.1 },
    { "siemens-tc35",   "AT\\Q3"        , 19200 , "AT+CNMI=1,2,0,0,1",  "SIEMENS",     
 "TC35", 0, 1, 1, 1, 1, 0.1 },
    { "siemens",        "AT\\Q3"        , 19200 , "AT+CNMI=1,2,0,0,0",  "SIEMENS",     
 "M20",  0, 1, 1, 1, 1, 0.1 },
    { "nokiaphone",     "AT+IFC=2,2"    , 9600  , "AT+CNMI=1,2,0,0,0",  "NokiaPhone" , 
 NULL,   0, 1, 1, 1, 1, 0.1 },
    { "falcom",         "AT+IFC=2,2"    , 9600  , "AT+CNMI=1,2,0,0,0",  "Falcom",      
 NULL,   0, 0, 1, 0, 0, 0.1 },
    { "ericsson",       "AT+IFC=2,2"    , 9600  , "AT+CNMI=3,2,0,0",    "R520m",       
 NULL,   0, 0, 1, 1, 1, 0.1 },
    { "alcatel",        "AT+IFC=2,2"    , 9600  , "AT+CNMI=1,3,0,0,0",  "Alcatel",     
 NULL,   0, 0, 1, 1, 1, 0.1 }
};

/* maximum data to attempt to read in one go */
#define MAX_READ        1023

/******************************************************************************
 * Message types defines
 */
#define AT_DELIVER_SM   0
#define AT_SUBMIT_SM    1

/******************************************************************************
 * type of phone number defines
 */
#define PNT_UNKNOWN     0
#define PNT_INTER       1
#define PNT_NATIONAL    2

#define O_DESTROY(a)    { if(a) octstr_destroy(a); a=NULL; }

/* The number of times to attempt to send a message should sending fail */
#define RETRY_SEND 3
 
typedef struct PrivAT2data
{
    List        *outgoing_queue;
    long        device_thread;
    int         shutdown;         /* Internal signal to shut down */
    Octstr      *device;
    int         modemid;
    long        speed;
    int         fd;     /* file descriptor */
    Octstr      *ilb;   /*input line buffer */
    Octstr      *lines; /* the last few lines before OK was seen */
    Octstr      *pin;   /* pincode */
    int         pin_ready;
    SMSCConn    *conn;
    int         phase2plus;
    Octstr      *validityperiod;    
    int         alt_dcs;
} PrivAT2data;


int     at2_open_device(PrivAT2data *privdata);
void    at2_close_device(PrivAT2data *privdata);
void    at2_read_buffer(PrivAT2data *privdata);
Octstr *at2_wait_line(PrivAT2data *privdata, time_t timeout, int gt_flag);
Octstr *at2_read_line(PrivAT2data *privdata, int gt_flag);
int     at2_write(PrivAT2data *privdata, char* line);
int     at2_write_line(PrivAT2data *privdata, char* line);
int     at2_write_ctrlz(PrivAT2data *privdata);
void    at2_flush_buffer(PrivAT2data *privdata);
int     at2_init_device(PrivAT2data *privdata);
int     at2_send_modem_command(PrivAT2data *privdata,char *cmd, time_t timeout, int 
greaterflag);
int     at2_wait_modem_command(PrivAT2data *privdata, time_t timeout, int greaterflag);
void    at2_set_speed(PrivAT2data *privdata, int bps);
void    at2_device_thread(void *arg);
int     at2_shutdown_cb(SMSCConn *conn, int finish_sending);
long    at2_queued_cb(SMSCConn *conn);
void    at2_start_cb(SMSCConn *conn);
int     at2_add_msg_cb(SMSCConn *conn, Msg *sms);
int     smsc_at2_create(SMSCConn *conn, CfgGroup *cfg);
int     at2_pdu_extract(PrivAT2data *privdata, Octstr **pdu, Octstr *buffer);
int     at2_hexchar(int hexc);
Msg     *at2_pdu_decode(Octstr *data);
Msg     *at2_pdu_decode_deliver_sm(Octstr *data);
Octstr  *at2_convertpdu(Octstr *pdutext);
void    at2_decode7bituncompressed(Octstr *input, int len, Octstr *decoded, int 
offset);
void    at2_send_messages(PrivAT2data *privdata);
void    at2_send_one_message(PrivAT2data *privdata, Msg *msg);
int     at2_pdu_encode(Msg *msg, unsigned char *pdu, PrivAT2data *privdata);
int     at2_encode7bituncompressed(Octstr *input, unsigned char *encoded,int offset);
int     at2_encode8bituncompressed(Octstr *input, unsigned char *encoded);
int     at2_numtext(int num);
void    at2_detect_speed(PrivAT2data *privdata);
int     at2_detect_modem_type(PrivAT2data *privdata);
int     at2_modem2id(char *name);

/******************************************************************************
** For debugging purposes octstr_destroy might be a macro,
** so function pointers won't work. at2_octstr_destroy is
** here to make them work again. It is semantically identical
** to octstr_destroy from gwlib/octstr.h
**
*/
static void at2_octstr_destroy(Octstr *ostr)
{
    octstr_destroy(ostr);
}


/******************************************************************************
** at2_open_device
** opens the device port
**
*/

int     at2_open_device1(PrivAT2data *privdata)
{
    info(0,"AT2[%s]: opening device",octstr_get_cstr(privdata->device));
    privdata->fd = open(octstr_get_cstr(privdata->device), O_RDWR|O_NONBLOCK|O_NOCTTY);
    if(privdata->fd == -1)
    {
        error(errno,"AT2[%s]: open failed! 
ERRNO=%d",octstr_get_cstr(privdata->device),errno);
        privdata->fd = -1;
        return -1;
    }
    debug("bb.smsc.at2",0,"AT2[%s]: device opened",octstr_get_cstr(privdata->device));

    return 0;
}


int     at2_open_device(PrivAT2data *privdata)
{
    struct termios tios;
    int ret;
    
    if(0 !=  (ret=at2_open_device1(privdata)))
        return ret;

    at2_set_speed(privdata,ModemTypes[privdata->modemid].speed);

    tcgetattr(privdata->fd, &tios);

    kannel_cfmakeraw(&tios);


    /* ignore break &  parity errors */
    tios.c_iflag |= IGNBRK;
    
    /* INPCK: disable parity check */
    tios.c_iflag &= ~INPCK;
    
     /* hangup on close */
     tios.c_cflag |= HUPCL;
     
     /* enable receiver */
     tios.c_cflag |=  CREAD ;
    
    /* set to 8 bit */
    tios.c_cflag &= ~CSIZE;
    tios.c_cflag |=CS8;
    
    /* no NL to CR-NL mapping outgoing */
    tios.c_oflag &= ~ONLCR;
    
    /* ignore parity */
    tios.c_iflag |= IGNPAR;
    tios.c_iflag &= ~INPCK;

    /* enable hardware flow control */
    tios.c_cflag |= CRTSCTS;
    
    tios.c_cc[VSUSP] = 0; /* otherwhise we can not send CTRL Z */
    /*
    if ( ModemTypes[privdata->modemid].enable_parity )
        tios.c_cflag ^= PARODD;
  */

     ret = tcsetattr(privdata->fd, TCSANOW, &tios); /* apply changes now */
     if(ret == -1)
     {
        error(errno,"at_data_link: fail to set termios attribute");
     }
     tcflush(privdata->fd, TCIOFLUSH);


   /* Nokia 7110 and 6210 need some time between opening
    * the connection and sending the first AT commands */
    if ( ModemTypes[privdata->modemid].need_sleep )
        sleep(1);
    debug("bb.smsc.at2",0,"AT2[%s]: device opened",octstr_get_cstr(privdata->device));
    return 0;
}

/******************************************************************************
** at2_close_device
** closes the device port
**
*/

void    at2_close_device(PrivAT2data *privdata)
{
    info(0,"AT2[%s]: closing device",octstr_get_cstr(privdata->device));
    close(privdata->fd);
    privdata->fd = -1;
}


/******************************************************************************
** at2_read_buffer
** checks if there are any incoming bytes
** and adds them to the line buffer
*/


void    at2_read_buffer(PrivAT2data *privdata)
{
    char buf[MAX_READ+1];
    int s, ret;
    int count;
    fd_set read_fd;
    struct timeval tv;
 
    if(privdata->fd == -1)
    {
        error(errno,"AT2[%s]: at2_read_buffer: fd = -1. Can not read", 
octstr_get_cstr(privdata->device));     
        return;
    }
    count = MAX_READ;

#ifdef  SSIZE_MAX
    if(count > SSIZE_MAX)
        count = SSIZE_MAX;
#endif
        
    tv.tv_sec = 0;
    tv.tv_usec = 1000;

    FD_ZERO(&read_fd);
    FD_SET(privdata->fd, &read_fd);
    ret = select(privdata->fd + 1, &read_fd, NULL, NULL, &tv);
    if (ret == -1) {
        if (! (errno == EINTR || errno == EAGAIN))
            error(errno, "AT2[%s]: error on select",octstr_get_cstr(privdata->device));
        return;
    }

    s = read(privdata->fd, buf,count);
    if(s > 0)
        octstr_append_data(privdata->ilb, buf, s);
}

/******************************************************************************
** at2_wait_line
** looks for a full line to be read from the buffer
** returns the line and removes it from the buffer
** or if no full line is yet received
** waits until the line is there or a timeout occurs
** If gt_flag is set, it is also looking for
** a line containing > even there is no CR yet.
*/

Octstr *at2_wait_line(PrivAT2data *privdata, time_t timeout, int gt_flag)
{
    Octstr *line;
    time_t end_time;
    time_t cur_time;

    time(&end_time);
    if(timeout == 0)
        timeout = 3;
    end_time += timeout;

    if(privdata->lines != NULL)
        octstr_destroy(privdata->lines);
    privdata->lines = octstr_create("");
    while(time(&cur_time) <= end_time)
    {
        line = at2_read_line(privdata, gt_flag);
        if(line)
           return line;
    }
    return NULL;
}


/******************************************************************************
** at2_read_line
** looks for a full line to be read from the buffer
** returns the line and removes it from the buffer
** or if no full line is yet received
** returns NULL. If gt_flag is set, it is also looking for
** a line containing > even there is no CR yet.
*/

Octstr *at2_read_line(PrivAT2data *privdata, int gt_flag)
{
    int  eol;
    int  gtloc;
    int len;
    Octstr *line;
    Octstr *buf2;
    int i;


    at2_read_buffer(privdata);

    len = octstr_len(privdata->ilb);
    if(len == 0)
        return NULL;

    if(gt_flag)
        gtloc = octstr_search_char(privdata->ilb, '>', 0); /* looking for > if needed 
*/
    else 
        gtloc = -1;

 /*   if (gt_flag && (gtloc != -1))
        debug("bb.smsc.at2",0,"in at2_read_line with gt_flag=1, gtloc=%d, 
ilb=%s",gtloc,octstr_get_cstr(privdata->ilb));
*/
    eol = octstr_search_char(privdata->ilb, '\r', 0); /* looking for CR */

    if (  (gtloc != -1) && ( (eol == -1) || (eol > gtloc) ) )
        eol = gtloc;

    if(eol == -1)
        return NULL;

    line = octstr_copy(privdata->ilb, 0, eol);
    buf2 = octstr_copy(privdata->ilb, eol+1, len);
    octstr_destroy(privdata->ilb);
    privdata->ilb = buf2;

    /* remove any non printable chars (including linefeed for example */
    for (i=0; i< octstr_len(line); i++)
    {
        if (octstr_get_char(line, i) < 32)
            octstr_set_char(line, i, ' ');
    }
    octstr_strip_blanks(line);
 
    if ((strcmp(octstr_get_cstr(line),"") == 0) && ( gt_flag == 0)) /* empty line, 
skipping */
    {
        octstr_destroy(line);
        return NULL;
    }
    if((gt_flag) && (gtloc != -1))
    {
        /* debug("bb.smsc.at2", 0, "reappending >"); */
        octstr_append_cstr(line,">"); /* got to re-add it again as the parser needs to 
see it */
    }
    debug("bb.smsc.at2", 0, "AT2[%s]: <-- %s", 
octstr_get_cstr(privdata->device),octstr_get_cstr(line));
    return line;
}

/******************************************************************************
** at_write_line_cstr
** write a line out to the device
** and adds a carriage return/linefeed to it
** returns number of characters sent.
*/

int  at2_write_line(PrivAT2data *privdata, char* line)
{
    int count;
    int s=0;
    Octstr *linestr = NULL;
    
    linestr = octstr_format("%s\r",line);
    
    debug("bb.smsc.at2", 0, "AT2[%s]: --> %s^M", octstr_get_cstr(privdata->device), 
line);
    
    count = octstr_len(linestr);
    s = write(privdata->fd, octstr_get_cstr(linestr), count);
    O_DESTROY(linestr);
    if( s < 0)
    {
        debug("bb.smsc.at2", 0, "AT2[%s]: write failed with errno %d", 
octstr_get_cstr(privdata->device), errno);
        return s;
    }
    tcdrain (privdata->fd);
    gwthread_sleep( ModemTypes[privdata->modemid].send_line_sleep );
    return s;
}

/* #define              at2_write_ctrlz(a)      at2_write(a,"\032") */


int  at2_write_ctrlz(PrivAT2data *privdata)
{
    int s;
    char *ctrlz =  "\032" ;
  
    debug("bb.smsc.at2", 0, "AT2[%s]: --> ^Z", octstr_get_cstr(privdata->device));
    s = write(privdata->fd, ctrlz, 1);
    if( s < 0)
    {
        debug("bb.smsc.at2", 0, "AT2[%s]: write failed with errno %d", 
octstr_get_cstr(privdata->device), errno);
        return s;
    }
    tcdrain (privdata->fd);
    gwthread_sleep( ModemTypes[privdata->modemid].send_line_sleep );
    return s;
}



int  at2_write(PrivAT2data *privdata, char* line)
{
    int count;
    int s;
   
    count = strlen(line);
    debug("bb.smsc.at2", 0, "AT2[%s]: --> %s", octstr_get_cstr(privdata->device), 
line);
    s = write(privdata->fd, line, count);
    tcdrain (privdata->fd);
    return s;
}


/******************************************************************************
** at2_flush_buffer
** clears incoming buffer
*/

void  at2_flush_buffer(PrivAT2data *privdata)
{
    at2_read_buffer(privdata);
    octstr_destroy(privdata->ilb);
    privdata->ilb = octstr_create("");
}


/******************************************************************************
** at2_init_device
** initializes the device after being opened
** detects the modem type, sets speed settings etc.
** on failure returns -1
*/


int     at2_init_device(PrivAT2data *privdata)
{
    int res;
    int ret;
    Octstr *setpin;
    
    info(0,"AT2[%s]: init device",octstr_get_cstr(privdata->device));

    at2_set_speed(privdata,privdata->speed);
    res = at2_send_modem_command(privdata,"AT",0,0);
    if(res == -1)
    {
        /* first try failed, maybe we need another one after just having changed the 
speed */
        res = at2_send_modem_command(privdata,"AT",0,0);
    }
    if(res == -1)
    {
        error(0,"AT2[%s]: no answer from modem",octstr_get_cstr(privdata->device));
        return -1;
    }

    at2_flush_buffer(privdata);

    if(at2_send_modem_command(privdata, "AT&F", 0, 0) == -1)
        return -1;

    if(at2_send_modem_command(privdata, "ATE0", 0, 0) == -1)
        return -1;

    at2_flush_buffer(privdata);
    
    /* enable hardware handshake */
    if(at2_send_modem_command(privdata, ModemTypes[privdata->modemid].hwhs, 0, 0) == 
-1)
        info(0,"AT2[%s]: cannot enable hardware 
handshake",octstr_get_cstr(privdata->device));
 
    /* Check does the modem require a PIN and, if so, send it
     * This is not supported by the Nokia Premicell */
    if(ModemTypes[privdata->modemid].pin_support)
    {
        ret = at2_send_modem_command(privdata, "AT+CPIN?", 0, 0);
        if(ret == -1)
            return -1;
        if(ret == 2)
        {
            if(privdata->pin == NULL)
                return -1;
            setpin = octstr_format("AT+CPIN=%s", octstr_get_cstr(privdata->pin));
            ret = at2_send_modem_command(privdata, octstr_get_cstr(setpin), 0, 0);
            octstr_destroy(setpin);
            if(ret !=0 )
                return -1;
        }
 
        /* we have to wait until +CPIN: READY appears before issuing
        the next command. 10 sec should be suficient */
        if(!privdata->pin_ready)
        {
           ret = at2_wait_modem_command(privdata,10, 0);
           if(ret == -1) /* timeout */
               return -1;
        }
    } 

    /* Set the modem to PDU mode and autodisplay of new messages */
    ret = at2_send_modem_command(privdata, "AT+CMGF=0", 0, 0);
    if(ret !=0 )
        return -1;
   
   /* lets see if it supports GSM SMS 2+ mode */
   ret = at2_send_modem_command(privdata, "AT+CSMS=?",0, 0);
   if(ret !=0)
        privdata->phase2plus = 0; /* if it doesnt even understand the command, I'm 
sure it wont support it */
   else
   {
        /* we have to take a part a string like +CSMS: (0,1,128) */
        Octstr *ts;
        int i;
        List *vals;
 
        ts = privdata->lines;
        privdata->lines = NULL;
        
        i = octstr_search_char(ts,'(',0);
        if (i>0)
        {
           octstr_delete(ts,0,i+1);
        }
        i = octstr_search_char(ts,')',0);
        if (i>0)
        {
           octstr_truncate(ts,i);
        }
        vals = octstr_split(ts, octstr_imm(","));
        octstr_destroy(ts);
        ts = list_search(vals, octstr_imm("1"),(void *) octstr_case_compare);
        if(ts)
            privdata->phase2plus = 1;
        list_destroy(vals,(void *) at2_octstr_destroy);
    }
    if(privdata->phase2plus)
    {
        info(0,"AT2[%s]: Phase 2+ is supported",octstr_get_cstr(privdata->device));
        ret = at2_send_modem_command(privdata, "AT+CSMS=1", 0, 0);
        if(ret != 0)
            return -1;
    }

    /* The Ericsson GM12 modem requires different new message 
     * indication options from the other modems
     */ 
     ret = at2_send_modem_command(privdata, ModemTypes[privdata->modemid].init1, 0, 0);
        if(ret != 0)
            return -1;
    info(0, "AT SMSC successfully opened.");
    return 0;
}
   

/******************************************************************************
** at2_send_modem_command
** sends an AT command to the modem and waits for a reply
** return values:
**  0 = OK
**  1 = ERROR
**  2 = SIM PIN
**  3 = >
**  4 = READY
**  5 = CMGS
** -1 = timeout occurred
*/

int at2_send_modem_command(PrivAT2data *privdata,char *cmd, time_t timeout, int 
gt_flag)
{
    at2_write_line(privdata,cmd);
    return at2_wait_modem_command(privdata, timeout, gt_flag);
}


/******************************************************************************
** at2_wait_modem_command
** waits for the modem to send us something
*/

int at2_wait_modem_command(PrivAT2data *privdata, time_t timeout, int gt_flag)
{
    Octstr *line = NULL;
    Octstr *line2 = NULL;
    Octstr *pdu = NULL;
    int ret;
    time_t end_time;
    time_t cur_time;
    Msg *msg;
    int len;
 
    time(&end_time);
    if(timeout == 0)
        timeout = 3;
    end_time += timeout;

    if(privdata->lines != NULL)
        octstr_destroy(privdata->lines);
    privdata->lines = octstr_create("");
    while(time(&cur_time) <= end_time)
    {
        line = at2_read_line(privdata, gt_flag);
        if(line)
        {
            octstr_append(privdata->lines,line);
            octstr_append_cstr(privdata->lines,"\n");   

           if (-1 != octstr_search(line, octstr_imm("SIM PIN"), 0))
           {
                ret = 2;
                
                goto end;
           }
           
           if (-1 != octstr_search(line, octstr_imm("OK"), 0))
           {
                ret = 0;
                goto end;
           }
           
           if ((gt_flag ) && (-1 != octstr_search(line, octstr_imm(">"), 0)))
           {
                ret = 1;
                goto end;
           }
           
           if (-1 != octstr_search(line, octstr_imm("RING"), 0))
           {
                at2_write_line(privdata,"ATH0");
                continue;
           }

           if (-1 != octstr_search(line, octstr_imm("+CPIN: READY"), 0))
           {
                privdata->pin_ready = 1;
                continue;
           }

           if (-1 != octstr_search(line, octstr_imm("+CMS ERROR"),0))
           {
                error(0,"AT2[%s]: CMS ERROR: %s", octstr_get_cstr(privdata->device), 
octstr_get_cstr(line));
                ret = 1;
                goto end;
           }
           if (-1 != octstr_search(line, octstr_imm("+CMT"), 0))
           {
                line2 = at2_wait_line(privdata,1,0);
 
                if(line2 == NULL)
                {
                    error(0,"AT2[%s]: got +CMT but waiting for next line timed out", 
octstr_get_cstr(privdata->device));
                }
                else
                {
                    octstr_append_cstr(line,"\n");
                    octstr_append(line,line2);
                    O_DESTROY(line2);
                    at2_pdu_extract(privdata, &pdu, line);

                    if(pdu == NULL)
                    {
                        error(0,"AT2[%s]: got +CMT but pdu_extract failed", 
octstr_get_cstr(privdata->device));
                    }
                    else
                    {
                        msg = at2_pdu_decode(pdu);
                        if(msg != NULL)
                        {
                            msg->sms.smsc_id = octstr_duplicate(privdata->conn->id);
                            bb_smscconn_receive(privdata->conn, msg);
                        }
                        if(privdata->phase2plus)
                            at2_write_line(privdata,"AT+CNMA");
                        O_DESTROY(pdu);
                    }
                }
                continue;
           }
 
           if (-1 != octstr_search(line, octstr_imm("ERROR"), 0))
           {
                ret = -1;
                goto end;
           }
        }
    }
    
    len = octstr_len(privdata->ilb);
/*
    error(0,"AT2[%s]: timeout. received \"%s\" until now, buffer size is %d, 
buf=%s",octstr_get_cstr(privdata->device),
         privdata->lines ? octstr_get_cstr(privdata->lines) : "<nothing>", len,
         privdata->ilb ? octstr_get_cstr(privdata->ilb) : "<nothing>");
*/
    O_DESTROY(line);
    O_DESTROY(line2);
    O_DESTROY(pdu);
    return -10; /* timeout */

end:
    octstr_append(privdata->lines,line);
    octstr_append_cstr(privdata->lines,"\n");
    O_DESTROY(line);
    O_DESTROY(line2);
    O_DESTROY(pdu);
    return ret;
}


/******************************************************************************
** at2_set_speed
** sets the serial port speed on the device
*/
void at2_set_speed(PrivAT2data *privdata, int bps)
{
    struct termios tios;
    int ret;
    int speed;
    
    
    tcgetattr(privdata->fd, &tios);
  
    switch(bps)
    {
    case 300:
        speed = B300;
        break;
    case 1200:
        speed = B1200;
        break;
    case 2400:
        speed = B2400;
        break;
    case 4800:
        speed = B4800;
        break;
    case 9600:
        speed = B9600;
        break;
    case 19200:
        speed = B19200;
        break;
    case 38400:
        speed = B38400;
        break;
#ifdef  B57600
    case 57600:
        speed = B57600;
        break;
#endif
    default:
        speed = B9600;
    }     
    cfsetospeed(&tios, speed);
    cfsetispeed(&tios, speed);
    ret = tcsetattr(privdata->fd, TCSANOW, &tios); /* apply changes now */
    if(ret == -1){
        error(errno,"at_data_link: fail to set termios attribute");
    }
    tcflush(privdata->fd, TCIOFLUSH);
  
    info(0,"AT2[%s]: speed set to %d",octstr_get_cstr(privdata->device), bps);
}


/******************************************************************************
** at2_device_thread
** this is the main tread "sitting" on the device
** its task is to initialize the modem
** then wait for messages to arrive or to be sent
**
*/

void at2_device_thread(void *arg)
{
    SMSCConn    *conn = arg;
    PrivAT2data *privdata = conn->data;

    int l;
   
    conn->status = SMSCCONN_CONNECTING;
    
    if(privdata->speed == 0)
        at2_detect_speed(privdata);
        
    if(privdata->modemid == AT2_AUTODETECT)
        at2_detect_modem_type(privdata);
   
    if( at2_open_device(privdata) )
    {
        error(errno, "at2_device_thread: open_at2_device(%s) failed. 
Terminating",octstr_get_cstr(privdata->device));
        return;
    }

    if (at2_init_device(privdata) != 0)
    {
        privdata->shutdown = 1;
        error(0, "AT2[%s]: Opening failed. 
Terminating",octstr_get_cstr(privdata->device));
        return;
    }
    
    conn->status = SMSCCONN_ACTIVE;
    bb_smscconn_connected(conn);
    while (!privdata->shutdown)
    {
         l = list_len(privdata->outgoing_queue);
         if(l>0)
             at2_send_messages(privdata);
         else
             at2_wait_modem_command(privdata,1,0);
   }
    at2_close_device(privdata);
    conn->status = SMSCCONN_DISCONNECTED;
    /* maybe some cleanup here?*/
    gw_free(conn->data);
    conn->data = NULL;
    conn->why_killed = SMSCCONN_KILLED_SHUTDOWN;
    conn->status = SMSCCONN_DEAD;
    bb_smscconn_killed();
}

int at2_shutdown_cb(SMSCConn *conn, int finish_sending)
{
    PrivAT2data *privdata = conn->data;

    debug("bb.sms", 0, "Shutting down SMSCConn AT2, %s",
          finish_sending ? "slow" : "instant");

    /* Documentation claims this would have been done by smscconn.c,
       but isn't when this code is being written. */
    conn->why_killed = SMSCCONN_KILLED_SHUTDOWN;
    privdata->shutdown = 1; /* Separate from why_killed to avoid locking, as
                           why_killed may be changed from outside? */

    if (finish_sending == 0)
    {
        Msg *msg;
        while((msg = list_extract_first(privdata->outgoing_queue)) != NULL) {
            bb_smscconn_send_failed(conn, msg, SMSCCONN_FAILED_SHUTDOWN);
        }
    }
    gwthread_wakeup(privdata->device_thread);
    return 0;

}

long at2_queued_cb(SMSCConn *conn)
{
    PrivAT2data *privdata = conn->data;
    long ret = list_len(privdata->outgoing_queue);

    /* use internal queue as load, maybe something else later */

    conn->load = ret;
    return ret;
}

void at2_start_cb(SMSCConn *conn)
{
    PrivAT2data *privdata = conn->data;

    /* in case there are messages in the buffer already */
    gwthread_wakeup(privdata->device_thread);
    debug("smsc.at2", 0, "smsc_at2: start called");
}

int at2_add_msg_cb(SMSCConn *conn, Msg *sms)
{
    PrivAT2data *privdata = conn->data;
    Msg *copy;

    copy = msg_duplicate(sms);
    list_produce(privdata->outgoing_queue, copy);
    gwthread_wakeup(privdata->device_thread);
    return 0;
}


/**********************************************************************
 * smsc_at2_create
 * starts the whole thing up
 *
 */

int  smsc_at2_create(SMSCConn *conn, CfgGroup *cfg)
{
    PrivAT2data *privdata;
    Octstr *modem_type_string;
    long alt_dcs = 0;
   

    privdata = gw_malloc(sizeof(PrivAT2data));
    privdata->outgoing_queue = list_create();

    privdata->speed = 0;
    cfg_get_integer(&privdata->speed, cfg, octstr_imm("speed"));

    privdata->device = cfg_get(cfg, octstr_imm("device"));
    if (privdata->device == NULL)
    {
        error(0, "'device' missing in at2 configuration.");
        goto error;
    }

    modem_type_string = cfg_get(cfg, octstr_imm("modemtype"));
    if(modem_type_string == NULL)
    {
        info(0,"configuration doesn't show modemtype. will autodetect");
        privdata->modemid = AT2_AUTODETECT;
    }
    else
    {
        info(0,"configuration shows modemtype=%s", octstr_get_cstr(modem_type_string));
        privdata->modemid = at2_modem2id(octstr_get_cstr(modem_type_string));
        if (privdata->speed == 0)
            privdata->speed = ModemTypes[privdata->modemid].speed;
        octstr_destroy(modem_type_string);
    }
    info(0,"configured for modem-id %d", privdata->modemid);
    info(0,"configured for speed %ld", privdata->speed);
    privdata->ilb = octstr_create("");
    privdata->fd = -1;
    privdata->lines = NULL; 
    privdata->pin = cfg_get(cfg, octstr_imm("pin"));
    privdata->pin_ready = 0;
    privdata->conn = conn;
    privdata->phase2plus = 0;
    privdata->validityperiod = cfg_get(cfg, octstr_imm("validityperiod"));

    if (cfg_get_integer(&alt_dcs, cfg, octstr_imm("alt-dcs")) == -1)
        privdata->alt_dcs = 0;
    if (alt_dcs > 1)
        privdata->alt_dcs = 1;
   
    conn->data = privdata;
    conn->name = octstr_format("AT2[%s]", octstr_get_cstr(privdata->device));

    privdata->shutdown = 0;

    conn->status = SMSCCONN_CONNECTING;
    conn->connect_time = time(NULL);

    if((privdata->device_thread = gwthread_create(at2_device_thread, conn)) == -1)
    {
        privdata->shutdown = 1;
        goto error;
    }

    conn->shutdown = at2_shutdown_cb;
    conn->queued = at2_queued_cb;
    conn->start_conn = at2_start_cb;
    conn->send_msg = at2_add_msg_cb;
    return 0;

error:
    error(0, "Failed to create at2 smsc connection");
    if (privdata != NULL)
    {
        list_destroy(privdata->outgoing_queue, NULL);
    }
    gw_free(privdata);
    conn->why_killed = SMSCCONN_KILLED_CANNOT_CONNECT;
    conn->status = SMSCCONN_DEAD;
    info(0, "exiting");
    return -1;
}




/******************************************************************************
 * Extract the first PDU in the string
 */
 
int at2_pdu_extract(PrivAT2data *privdata, Octstr **pdu, Octstr *line)
{
    Octstr *buffer;
    long len = 0;
    int pos = 0;
    int tmp;

    buffer = octstr_duplicate(line);
    /* find the beginning of a message from the modem*/ 
    pos = octstr_search(buffer, octstr_imm("+CMT:"), 0);
    if(pos == -1) 
        goto nomsg;
    pos += 5;
    pos = octstr_search(buffer, octstr_imm(","), pos);
    if(pos == -1)
        goto nomsg;
    pos++;

    /* The message length is after the comma */
    pos = octstr_parse_long(&len, buffer, pos, 10);
    if(pos == -1)
        goto nomsg;

    /* skip the spaces and line return */
    while( isspace(octstr_get_char(buffer, pos)))
        pos++;
    
    /* skip the SMSC address on some modem types */
    if(ModemTypes[privdata->modemid].skip_smsc_addr)
    {
        tmp = at2_hexchar(octstr_get_char(buffer, pos))*16
            + at2_hexchar(octstr_get_char(buffer, pos+1));
        if (tmp < 0)
            goto nomsg;
        tmp = 2 + tmp * 2;
        pos += tmp;
    }
    
    /* check if the buffer is long enough to contain the full message */
    if( octstr_len(buffer) < len * 2 + pos)
        goto nomsg;
    
    /* copy the PDU then remove it from the input buffer*/
    *pdu = octstr_copy(buffer, pos, len*2);

    octstr_destroy(buffer);
    return 1;

 nomsg:
    octstr_destroy(buffer);
    return 0;
}

/**********************************************************************
 * Get the numeric value of the text hex
 */
int at2_hexchar(int hexc)
{
    hexc = toupper(hexc) - 48;
    return (hexc>9) ? hexc-7 : hexc;
}



/******************************************************************************
 * Decode a raw PDU into a Msg
 */
Msg *at2_pdu_decode(Octstr *data)
{
        int type;
        Msg *msg = NULL;

        /* Get the PDU type */
        type = octstr_get_char(data, 1) & 3;
        
        switch(type) {

        case AT_DELIVER_SM:
                msg = at2_pdu_decode_deliver_sm(data);
                break;

                /* Add other message types here: */
        
        }

        return msg;
}

/******************************************************************************
 * Decode a DELIVER PDU
 */
Msg *at2_pdu_decode_deliver_sm(Octstr *data)
{
        int len, pos, i;
        char origaddr[21];
        int udhi, dcs, udhlen;
        Octstr *origin = NULL;
        Octstr *udh = NULL;
        Octstr *text = NULL, *tmpstr;
        Octstr *pdu = NULL;
        Msg *message = NULL;
        struct universaltime mtime;     /* time structure */
        long stime;             /* time in seconds */

        /* Note: some parts of the PDU are not decoded because they are
         * not needed for the Msg type. */

        /* convert the pdu to binary format for ease of processing */
        pdu = at2_convertpdu(data);
        
        /* UDH Indicator */
        udhi = (octstr_get_char(pdu, 0) & 64) >> 6;

        /* originating address */
        len = octstr_get_char(pdu, 1);
        pos = 3;
        for(i=0; i<len; i+=2, pos++) {
                origaddr[i] = (octstr_get_char(pdu, pos) & 15) + 48 ;
                origaddr[i+1] = (octstr_get_char(pdu, pos) >> 4) + 48;
        }
        origaddr[i] = '\0';
        origin = octstr_create_from_data(origaddr, len);

        /* skip the PID for now */
        pos++;
        
        /* DCS */
        dcs = octstr_get_char(pdu, pos); 
        pos++;
        
        /* get the timestamp */
        mtime.year   = octstr_get_char(pdu, pos) + 1900; pos++;
        mtime.month  = octstr_get_char(pdu, pos); pos++;
        mtime.day    = octstr_get_char(pdu, pos); pos++;
        mtime.hour   = octstr_get_char(pdu, pos); pos++;
        mtime.minute = octstr_get_char(pdu, pos); pos++;
        mtime.second = octstr_get_char(pdu, pos); pos++;
        /* time zone: */
        /* XXX handle negative time zones */
        mtime.hour  += octstr_get_char(pdu, pos); pos++;
        stime = date_convert_universal(&mtime);
        
        /* get data length */
        len = octstr_get_char(pdu, pos);
        pos++;

        /* if there is a UDH */
        udhlen = 0;
        if(udhi) {
                udhlen = octstr_get_char(pdu, pos);
                pos++;
                udh = octstr_copy(pdu, pos, udhlen);
                pos += udhlen;
                len -= udhlen +1;
        }

        /* build the message */         
        message = msg_create(sms);
        if (!dcs_to_fields(&message, dcs)) {
            /* XXX Should reject this message ? */
            debug("bb.smsc.at2", 0, "Invalid DCS");
            dcs_to_fields(&message, 0);
        }

        /* deal with the user data -- 7 or 8 bit encoded */     
        tmpstr = octstr_copy(pdu,pos,len);
        if(message->sms.coding == DC_8BIT || message->sms.coding == DC_UCS2) {
                text = octstr_duplicate(tmpstr);
        } else {
                int offset=0;
                text = octstr_create("");
                if (udhi && message->sms.coding == DC_7BIT) {
                        int nbits;
                        nbits = (udhlen + 1)*8;
                        offset = (((nbits/7)+1)*7-nbits)%7;     /* Fill bits for UDH 
to septet boundary */
                }
                at2_decode7bituncompressed(tmpstr, len, text, offset);
        }

        message->sms.sender = origin;
        /* Put a dummy address in the receiver for now (SMSC requires one) */
        message->sms.receiver = octstr_create_from_data("1234", 4);
        /*message->sms.receiver = destination;*/
        if (udhi) {
                message->sms.udhdata = udh;
        }
        message->sms.msgdata = text;
        message->sms.time = stime;

        /* cleanup */
        octstr_destroy(pdu);
        octstr_destroy(tmpstr);
        
        return message;
}

/******************************************************************************
 * Converts the text representation of hexa to binary
 */
Octstr *at2_convertpdu(Octstr *pdutext)
{
    Octstr *pdu;
    int i;
    int len = octstr_len(pdutext);

    pdu = octstr_create("");
    for (i=0; i<len; i+=2)
    {
        octstr_append_char(pdu, at2_hexchar(octstr_get_char(pdutext,i))*16 
                              + at2_hexchar(octstr_get_char(pdutext,i+1)));
    }
    return pdu;
}


/**********************************************************************
 * Decode 7bit uncompressed user data
 */
int at2_rmask[8] = { 0, 1, 3, 7, 15, 31, 63, 127 };
int at2_lmask[8] = { 0, 128, 192, 224, 240, 248, 252, 254 };

void at2_decode7bituncompressed(Octstr *input, int len, Octstr *decoded, int offset)
{
        unsigned char septet, octet, prevoctet;
        int i;
        int r = 1;
        int c = 7;
        int pos = 0;
        
        /* Shift the buffer offset bits to the left */
        if (offset > 0) {
                unsigned char *ip;
                for (i = 0, ip = octstr_get_cstr(input); i < octstr_len(input); i++) {
                        if (i == octstr_len(input) - 1)
                                *ip = *ip >> offset;
                        else
                                *ip = (*ip >> offset) | (*(ip + 1) << (8 - offset));
                        ip++;
                }
        }
        octet = octstr_get_char(input, pos);
        prevoctet = 0;
        for(i=0; i<len; i++) {
                septet = ((octet & at2_rmask[c]) << (r-1)) + prevoctet;
                octstr_append_char(decoded, septet);

                prevoctet = (octet & at2_lmask[r]) >> c;
        
                /* When r=7 we have a full character in prevoctet*/
                if((r==7) && (i<len-1)){
                        i++;
                        octstr_append_char(decoded, prevoctet);
                        prevoctet = 0;
                }

                r = (r>6)? 1 : r+1;
                c = (c<2)? 7 : c-1;

                pos++;
                octet = octstr_get_char(input, pos);
        }
        charset_gsm_to_latin1(decoded);
}



/******************************************************************************
 * Sends messages from the queue
 */

void at2_send_messages(PrivAT2data *privdata)
{
    Msg *msg;
    
    do
    {
        if( ( msg = list_extract_first(privdata->outgoing_queue )))
            at2_send_one_message(privdata, msg);
    } while (msg);
}

/******************************************************************************
** sends a single message
** after having it sent, the msg is no longe belonging to us
*/
void at2_send_one_message(PrivAT2data *privdata, Msg *msg)
{
    unsigned char command[500], pdu[500];
    int ret = -1; 
    char sc[3];
    int retries = RETRY_SEND;
   
   /* the standard says you should be prepending the PDU with 00 to indicate to use 
the default SC.
      some older modems dont expect this so it can be disabled 
     * NB: This extra padding is not counted in the CMGS byte count */

    sc[0] = '\0';

    if(ModemTypes[privdata->modemid].prepend_zero_smsc)
        strcpy(sc, "00");

    if(msg_type(msg)==sms)
    {
        at2_pdu_encode(msg, &pdu[0], privdata);
        
        ret = -99;
        retries = RETRY_SEND;
        while ((ret != 0) && (retries-- > 0))
        {
            /* send the initial command and then wait for > */
            sprintf(command, "AT+CMGS=%d", strlen(pdu)/2);
            ret = at2_send_modem_command(privdata, command, 5, 1);
            debug("bb.at", 0, "send command status: %d", ret);
            if(ret != 1) /* > only! */
                continue;
            /* ok the > has been see now so we can send the PDU now and a control Z 
but no CR or LF */
            sprintf(command, "%s%s", sc, pdu);
            at2_write(privdata,command);
            at2_write_ctrlz(privdata);
        /* wait 20 secs for modem command */
            ret = at2_wait_modem_command(privdata, 20, 0);
            debug("bb.at", 0, "send command status: %d", ret);
            if(ret != 0) /* OK only */
                continue;
            counter_increase(privdata->conn->sent);
            bb_smscconn_sent(privdata->conn, msg);
        }
        if(ret != 0)
        {
                /* 
         * no need to do counter_increase(privdata->conn->failed) here, 
         * since bb_smscconn_send_failed() will inc the counter on 
         * SMSCCONN_FAILED_MALFORMED
         */
            bb_smscconn_send_failed(privdata->conn,msg,SMSCCONN_FAILED_MALFORMED);
        }
    }
}


/******************************************************************************
 * Encode a Msg into a PDU
 */
int at2_pdu_encode(Msg *msg, unsigned char *pdu, PrivAT2data *privdata)
{
    int pos = 0, i,len, setvalidity=0;
    int ntype = PNT_UNKNOWN; /* number type default */
    int nstartpos = 0;   /* offset for the phone number */
    int dcs;  /* data coding scheme (GSM 03.38) */
    
    /* The message is encoded directly in the text representation of 
     * the hex values that will be sent to the modem.
     * Each octet is coded with two characters. */
    
    /* message type SUBMIT
     *    01010001 = 0x51 indicating add. UDH, TP-VP(Rel) & MSG_SUBMIT
     * or 00010001 = 0x11 for just TP-VP(Rel) & MSG_SUBMIT */

    pdu[pos] = octstr_len(msg->sms.udhdata) ? at2_numtext(5) : at2_numtext(1);
    pos++;
    pdu[pos] = at2_numtext(AT_SUBMIT_SM);
    pos++;
    
    /* message reference (0 for now) */
    pdu[pos] = at2_numtext(0);
    pos++;
    pdu[pos] = at2_numtext(0);
    pos++;
    
    /* destination address */
    octstr_strip_blanks(msg->sms.receiver); /* strip blanks before length calculation 
*/
    len = octstr_len(msg->sms.receiver);

    /* Check for international numbers
     * number starting with '+' or '00' are international,
     * others are national. */
    if (strncmp(octstr_get_cstr(msg->sms.receiver), "+", 1) == 0) {
        debug("bb.smsc.at2", 0, "international starting with + 
(%s)",octstr_get_cstr(msg->sms.receiver) );
        nstartpos++;
        ntype = PNT_INTER; /* international */
    } else if (strncmp(octstr_get_cstr(msg->sms.receiver), "00", 2) == 0) {
        debug("bb.smsc.at2", 0, "international starting with 00 
(%s)",octstr_get_cstr(msg->sms.receiver) );
        nstartpos += 2;
        ntype = PNT_INTER; /* international */
    }
    
    /* address length */
    pdu[pos] = at2_numtext(((len - nstartpos) & 240) >> 4);
    pos++;
    pdu[pos] = at2_numtext((len - nstartpos) & 15);
    pos++;
            
    /* Type of number */
    pdu[pos] = at2_numtext(8 + ntype);
    pos++;
    /* numbering plan: ISDN/Telephone numbering plan */
    pdu[pos] = at2_numtext(1);
    pos++;
    
    /* make sure there is no blank in the phone number and encode
     * an even number of digits */
    octstr_strip_blanks(msg->sms.receiver);
    for(i=nstartpos; i<len; i+=2) {
        if (i+1 < len) {
            pdu[pos] = octstr_get_char(msg->sms.receiver, i+1);
        } else {
            pdu[pos] = at2_numtext (15);
        }
        pos++;
        pdu[pos] = octstr_get_char(msg->sms.receiver, i);
        pos++;
    }
    
    /* protocol identifier */
    /* 0x00 implicit */
    pdu[pos] = at2_numtext(0);
    pos++;
    pdu[pos] = at2_numtext(0);
    pos++;
    
    /* data coding scheme */

    dcs = fields_to_dcs(msg, privdata->alt_dcs);

    pdu[pos] = at2_numtext(dcs >> 4);
    pos++;
    pdu[pos] = at2_numtext(dcs % 16);
    pos++;

    /* Validity-Period (TP-VP)
     * see GSM 03.40 section 9.2.3.12
     * defaults to 24 hours = 167 if not set */
    if ( msg->sms.validity) {
        if (msg->sms.validity > 635040)
        setvalidity = 255;
        if (msg->sms.validity >= 50400 && msg->sms.validity <= 635040)
        setvalidity = (msg->sms.validity - 1) / 7 / 24 / 60 + 192 + 1;
        if (msg->sms.validity > 43200 && msg->sms.validity < 50400)
        setvalidity = 197;
        if (msg->sms.validity >= 2880 && msg->sms.validity <= 43200)
        setvalidity = (msg->sms.validity - 1) / 24 / 60 + 166 + 1;
        if (msg->sms.validity > 1440 && msg->sms.validity < 2880)
        setvalidity = 168;
        if (msg->sms.validity >= 750 && msg->sms.validity <= 1440)
        setvalidity = (msg->sms.validity - 720 - 1) / 30 + 143 + 1;
        if (msg->sms.validity > 720 && msg->sms.validity < 750)
        setvalidity = 144;
        if (msg->sms.validity >= 5 && msg->sms.validity <= 720)
        setvalidity = (msg->sms.validity - 1) / 5 - 1 + 1;
        if (msg->sms.validity < 5)
        setvalidity = 0;
    } else 
        setvalidity = (privdata->validityperiod != NULL ? 
atoi(octstr_get_cstr(privdata->validityperiod)) : 167);
    
    if (setvalidity >= 0 && setvalidity <= 143)
        debug("bb.smsc.at2", 0, "TP-Validity-Period: %d minutes", 
              (setvalidity+1)*5);
    else if (setvalidity >= 144 && setvalidity <= 167)
        debug("bb.smsc.at2", 0, "TP-Validity-Period: %3.1f hours", 
              ((float)(setvalidity-143)/2)+12);
    else if (setvalidity >= 168 && setvalidity <= 196)
        debug("bb.smsc.at2", 0, "TP-Validity-Period: %d days", 
              (setvalidity-166));
    else 
        debug("bb.smsc.at2", 0, "TP-Validity-Period: %d weeks", 
              (setvalidity-192));
    pdu[pos] = at2_numtext((setvalidity & 240) >> 4);
    pos++;
    pdu[pos] = at2_numtext(setvalidity & 15);
    pos++;

    /* user data length - include length of UDH if it exists */
        len = sms_msgdata_len(msg);

    if(octstr_len(msg->sms.udhdata)) {
        if (msg->sms.coding == DC_8BIT || msg->sms.coding == DC_UCS2) {
            len += octstr_len(msg->sms.udhdata);
        } else {
            /*
             * The reason we branch here is because UDH data length is determined
             * in septets if we are in GSM coding, otherwise it's in octets. Adding 6
             * will ensure that for an octet length of 0, we get septet length 0,
             * and for octet length 1 we get septet length 2. 
             */
            len += (((8*octstr_len(msg->sms.udhdata)) + 6)/7);        
    }
    }

    pdu[pos] = at2_numtext((len & 240) >> 4);
    pos++;
    pdu[pos] = at2_numtext(len & 15);
    pos++;
    
    /* udh */
    if(octstr_len(msg->sms.udhdata)) {
            pos += at2_encode8bituncompressed(msg->sms.udhdata, &pdu[pos]);
    }

    /* user data */
    /* if the data is too long, it is cut */
    if(msg->sms.coding == DC_8BIT || msg->sms.coding == DC_UCS2) {
        pos += at2_encode8bituncompressed(msg->sms.msgdata, &pdu[pos]);
    } else {
        int offset=0;

        if (octstr_len(msg->sms.udhdata)) {                   /* Have UDH */
            int nbits = octstr_len(msg->sms.udhdata)*8; /* Includes UDH length byte */
            offset = (((nbits/7)+1)*7-nbits)%7;         /* Fill bits */
        }
        pos += at2_encode7bituncompressed(msg->sms.msgdata, &pdu[pos],offset);
    }
    pdu[pos] = 0;

    return 0;
}



/**********************************************************************
 * Encode 7bit uncompressed user data
 */
int at2_encode7bituncompressed(Octstr *input, unsigned char *encoded,int offset)
{

        unsigned char prevoctet, tmpenc;
        int i;
        int c = 1;
        int r = 7;
        int pos = 0;
        int len;
        unsigned char enc7bit[256];
        int j,encpos = 0;
        int ermask[8] = { 0, 1, 3, 7, 15, 31, 63, 127 };
        int elmask[8] = { 0, 64, 96, 112, 120, 124, 126, 127 };

        charset_latin1_to_gsm(input);
        len = octstr_len(input);

        /* prevoctet is set to the first character and we'll start the loop
         * at the following char. */
        prevoctet = octstr_get_char(input ,0);
        for(i=1; i<octstr_len(input); i++) {
                /* a byte is encoded with what is left of the previous character
                 * and filled with as much as possible of the current one. */
                tmpenc = prevoctet + ((octstr_get_char(input,i) & ermask[c]) << r);
                enc7bit[encpos] = tmpenc; encpos++;
                c = (c>6)? 1 : c+1;
                r = (r<2)? 7 : r-1;

                /* prevoctet becomes the part of the current octet that hasn't
                 * been copied to 'encoded' or the next char if the current has
                 * been completely copied already. */
                prevoctet = (octstr_get_char(input,i) & elmask[r]) >> (c-1);
                if(r == 7) {
                        i++;
                        prevoctet = octstr_get_char(input, i);
                }
        }

        /* if the length of the message is a multiple of 8 then we
         * are finished. Otherwise prevoctet still contains part of a 
         * character so we add it. */
        if((len/8)*8 != len) {
                enc7bit[encpos] = prevoctet;encpos++;
        }

        /* Now shift the buffer by the offset */
        if (offset > 0) {
                unsigned char nextdrop, lastdrop;

                nextdrop = lastdrop = 0;
                for (i = 0; i < encpos; i++) {
                        nextdrop = enc7bit[i] >> (8 - offset);          /* This drops 
off by shifting */
                        if (i == 0)
                                enc7bit[i] = enc7bit[i] << offset;              /* 
This drops off by shifting */
                        else
                                enc7bit[i] = (enc7bit[i] << offset) | lastdrop;
                        lastdrop = nextdrop;
                }

                if (offset > ((len*7) % 8)) {
                        enc7bit [i] = nextdrop;
                        i++;
                }
        }
        else
                i = encpos;

        for (j = 0; j < i; j++) {
                encoded[pos] = at2_numtext((enc7bit [j] & 240) >> 4); pos++;
                encoded[pos] = at2_numtext(enc7bit [j] & 15); pos++;
        }
        return pos;
                
}


/**********************************************************************
 * Encode 8bit uncompressed user data
 */

int at2_encode8bituncompressed(Octstr *input, unsigned char *encoded)
{
    int len, i;

    len = octstr_len(input);
        
    for(i=0; i<len; i++) {
           /* each character is encoded in its hex representation (2 chars) */
           encoded[i*2] = at2_numtext((octstr_get_char(input, i) & 240) >> 4);
          encoded[i*2+1] = at2_numtext(octstr_get_char(input, i) & 15);
    }
    return len*2;
}


/**********************************************************************
 * Code a half-byte to its text hexa representation
 */
int at2_numtext(int num)
{
        return (num > 9) ? (num+55) : (num+48);
}





/**********************************************************************
 * at2_detect_speed
 * try to detect modem speeds
 */

void at2_detect_speed(PrivAT2data *privdata)
{
    int autospeeds[] = { 38400, 19200, 9600 };
    int i;
    int res;
    
    debug("bb.smsc.at2",0,"AT2[%s]: detecting modem speed. 
",octstr_get_cstr(privdata->device));

    for(i=0;i< (sizeof(autospeeds) / sizeof(int));i++)
    {
        at2_open_device1(privdata);
        at2_set_speed(privdata,autospeeds[i]);
        res = at2_send_modem_command(privdata,"",1,0); /* send a return so the modem 
can detect the speed */

        res = at2_send_modem_command(privdata,"AT",0,0);
        if(res !=0)
            res = at2_send_modem_command(privdata,"AT",0,0);
        if(res != 0)
            res = at2_send_modem_command(privdata,"AT",0,0);
        if(res == 0)
        {
            privdata->speed = autospeeds[i];
            i = 99; /* skip out of loop */
        }
        at2_close_device(privdata);
    }
    info(0, "AT2[%s]: detect speed is 
%ld",octstr_get_cstr(privdata->device),privdata->speed);
}

/**********************************************************************
 * at2_detect_modem_type
 * try to detect speed
 */

int at2_detect_modem_type(PrivAT2data *privdata)
{
    int res;
    
    debug("bb.smsc.at2",0,"AT2[%s]: detecting modem 
type",octstr_get_cstr(privdata->device));
 
    at2_open_device1(privdata);
    at2_set_speed(privdata,privdata->speed);
    
    res = at2_send_modem_command(privdata,"",1,0); /* send a return so the modem can 
detect the speed */

    res = at2_send_modem_command(privdata,"AT",0,0);

    if(at2_send_modem_command(privdata, "AT&F", 0, 0) == -1)
        return -1;

    if(at2_send_modem_command(privdata, "ATE0", 0, 0) == -1)
        return -1;

    at2_flush_buffer(privdata);
    
    if(at2_send_modem_command(privdata, "ATI", 0, 0) == -1)
        return -1;

    /* we try to detect the modem automatically */

    if (-1 != octstr_search(privdata->lines, octstr_imm("SIEMENS"), 0))
    {
        debug("bb.smsc.at2",0,"AT2[%s]: its some kind of 
SIEMENS",octstr_get_cstr(privdata->device));
        if (-1 != octstr_search(privdata->lines, octstr_imm("TC35"), 0))
        {
            info(0,"AT2[%s]: Modemtype set to SIEMENS 
TC35",octstr_get_cstr(privdata->device));
            privdata->modemid = AT2_SIEMENS_TC35;
        }
        else if (-1 != octstr_search(privdata->lines, octstr_imm("M20"), 0))
        {
            info(0,"AT2[%s]: Modemtype set to SIEMENS 
M20",octstr_get_cstr(privdata->device));
            privdata->modemid = AT2_SIEMENS;
        }
        else
        {
            info(0,"AT2[%s]: Modemtype set to 
SIEMENS",octstr_get_cstr(privdata->device));
            privdata->modemid = AT2_SIEMENS;
        }
    }
    else if (-1 != octstr_search(privdata->lines, octstr_imm("WAVECOM"), 0))
    {
        debug("bb.smsc.at2",0,"AT2[%s]: its a 
WAVECOM",octstr_get_cstr(privdata->device));
        info(0,"AT2[%s]: Modemtype set to WAVECOM",octstr_get_cstr(privdata->device));
        privdata->modemid = AT2_WAVECOM;
    }
    else if (-1 != octstr_search(privdata->lines, octstr_imm("ERICSSON"), 0))
    {
        debug("bb.smsc.at2",0,"AT2[%s]: its a 
ERICSSON",octstr_get_cstr(privdata->device));
        info(0,"AT2[%s]: Modemtype set to ERICSSON",octstr_get_cstr(privdata->device));
        privdata->modemid = AT2_ERICSSON;
    }
    else if (-1 != octstr_search(privdata->lines, octstr_imm("PREMICELL"), 0))
    {
        debug("bb.smsc.at2",0,"AT2[%s]: its a 
PREMICELL",octstr_get_cstr(privdata->device));
        info(0,"AT2[%s]: Modemtype set to 
PREMICELL",octstr_get_cstr(privdata->device));
        privdata->modemid = AT2_PREMICELL;
    }
    else if (-1 != octstr_search(privdata->lines, octstr_imm("Nokia Mobile Phones"), 
0))
    {
        debug("bb.smsc.at2",0,"AT2[%s]: its a 
NOKIAPHONE",octstr_get_cstr(privdata->device));
        info(0,"AT2[%s]: Modemtype set to 
NOKIAPHONE",octstr_get_cstr(privdata->device));
        privdata->modemid = AT2_NOKIAPHONE;
    }
    else if (-1 != octstr_search(privdata->lines, octstr_imm("Falcom"), 0))
    {
        debug("bb.smsc.at2",0,"AT2[%s]: its a 
FALCOM",octstr_get_cstr(privdata->device));
        info(0,"AT2[%s]: Modemtype set to FALCOM",octstr_get_cstr(privdata->device));
        privdata->modemid = AT2_FALCOM;
    }

    /* lets see if it supports GSM SMS 2+ mode */
   res = at2_send_modem_command(privdata, "AT+CSMS=?",0, 0);
   if(res !=0)
        privdata->phase2plus = 0; /* if it doesnt even understand the command, I'm 
sure it wont support it */
   else
   {
        /* we have to take a part a string like +CSMS: (0,1,128) */
        Octstr *ts;
        int i;
        List *vals;
 
        ts = privdata->lines;
        privdata->lines = NULL;
        
        i = octstr_search_char(ts,'(',0);
        if (i>0)
        {
           octstr_delete(ts,0,i+1);
        }
        i = octstr_search_char(ts,')',0);
        if (i>0)
        {
           octstr_truncate(ts,i);
        }
        vals = octstr_split(ts, octstr_imm(","));
        octstr_destroy(ts);
        ts = list_search(vals, octstr_imm("1"),(void *) octstr_case_compare);
        if(ts)
            privdata->phase2plus = 1;
        list_destroy(vals,(void *) at2_octstr_destroy);
    }
    if(privdata->phase2plus)
        info(0,"AT2[%s]: Phase 2+ is supported",octstr_get_cstr(privdata->device));
    info(0,"AT2[%s]: Modemtype set to %s",octstr_get_cstr(privdata->device), 
ModemTypes[privdata->modemid].name);
    return 0;
}

int     at2_modem2id(char *name1)
{
    int i;
    for(i=0;i< MAX_MODEM_TYPES; i++)
    {
        if (strcmp(ModemTypes[i].name,name1)==0)
        {
            return i;
         }
    }
    return 0;
}


Reply via email to