Re: Wap Push problems
Olivier Durécu <[email protected]>
| Newsgroups | gmane.comp.mobile.kannel.devel |
|---|---|
| Organization | Alcatel CIT / Research & Innovation |
| Message-ID | <[email protected]> |
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: >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
(text/plain, 52.6 KB)
/* * 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; }