/* * This file is part of Generic-gsm. * * Generic-gsm is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * Generic-gsm is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with Generic-gsm; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * * Peter Harper * * $Id: generic-gsm.c,v 1.1 2003/12/02 06:17:38 rmello Exp $ */ static char rcsid[] = "@(#) $"; #include "ns.h" #include "../qcluster/message.h" #include "../qcluster/cluster.h" #include "../qcluster/locks.h" #include "../smsq/smsq.h" #include "generic-gsm.h" #include // #define CFG_DIGICOM_HACK int Ns_ModuleVersion = 1; static struct q_message_type *g_sms_deliver_type; static Ns_RWLock g_generic_gsm_links_mutex; static Tcl_HashTable g_generic_gsm_links; /* * Static functions */ static int generic_gsm_tcl_init(ClientData data, Tcl_Interp *interp, int argc, char *argv[]); static int generic_gsm_tcl_delete(ClientData data, Tcl_Interp *interp, int argc, char *argv[]); static int generic_gsm_tcl_set_status(ClientData data, Tcl_Interp *interp, int argc, char *argv[]); static int generic_gsm_tcl_update(ClientData data, Tcl_Interp *interp, int argc, char *argv[]); static int generic_gsm_tcl_connected_p(ClientData data, Tcl_Interp *interp, int argc, char *argv[]); static int generic_gsm_add_commands(Tcl_Interp *interp, ClientData data); static struct generic_gsm_link * generic_gsm_get_link_ptr(char *link_id); void generic_gsm_send_logic(struct generic_gsm_link *link_ptr); void generic_gsm_read_logic(struct generic_gsm_link *link_ptr, char *text); void generic_gsm_fail_current_message(struct generic_gsm_link *link_ptr); int blocked_recv(int sock, void *msg_buf, int msg_size, int flags); int blocked_send(int sock, const void *msg_buf, int msg_size, int flags); int blocked_read(int fd, void *msg_buf, int msg_size); int blocked_write(int fd, const void *msg_buf, int msg_size); /* *---------------------------------------------------------------------- * * Ns_ModuleInit -- * * Register the Generic GSM functions with the Tcl interpreter * * Results: * None. * * Side effects: * *---------------------------------------------------------------------- */ NS_EXPORT int Ns_ModuleInit(char *server, char *module) { /* * Initialise the hash table for storage of pointers to link structures. * Also, initialise the a Read/Write mutex to control access. */ Ns_RWLockInit(&g_generic_gsm_links_mutex); Tcl_InitHashTable(&g_generic_gsm_links, TCL_STRING_KEYS); Ns_Log(Notice, "generic_gsm: initialisation complete"); return Ns_TclInitInterps(server, generic_gsm_add_commands, NULL); } /* *---------------------------------------------------------------------- * * generic_gsm_init -- * * Creates a new link strucure, adds it to the links hash table, * and then enters the Generic GSM link execution routine. This * Function doesn't exit until the link is taken deleted, or server * shutdown. * * Results: * *---------------------------------------------------------------------- */ static int generic_gsm_init(char *link_id, struct q_group *in_group_ptr, struct q_group *out_group_ptr, int status, char *device, int device_timeout_secs, char *service_centre, char *sim_pin, int poll_time_secs, int post_send_delay_usecs, int use_pdu_p, int max_retries, char *msisdn) { struct generic_gsm_link *link_ptr; Tcl_HashEntry *entry; int new_flag; /* * Create the new link structure, and add it to the hash table of * current links. */ link_ptr = ns_malloc(sizeof(struct generic_gsm_link)); strcpy(link_ptr->link_id, link_id); strcpy(link_ptr->device, device); link_ptr->device_timeout_secs = device_timeout_secs; strcpy(link_ptr->service_centre, service_centre); strcpy(link_ptr->sim_pin, sim_pin); ns_sockpair(link_ptr->msg_pipe); link_ptr->in_group_ptr = in_group_ptr; link_ptr->out_group_ptr = out_group_ptr; link_ptr->sock = -1; link_ptr->sock = poll_time_secs; link_ptr->non_read_count = 0; link_ptr->link_status = status; link_ptr->status = GSMSTATE_IDLE; link_ptr->old_status = GSMSTATE_IDLE; Tcl_InitHashTable(&link_ptr->cmgd_ids, TCL_STRING_KEYS); link_ptr->poll_time_secs = poll_time_secs; link_ptr->post_send_delay_usecs = post_send_delay_usecs; link_ptr->use_pdu_p = use_pdu_p; link_ptr->max_retries = max_retries; link_ptr->msg_ptr = NULL; strcpy(link_ptr->msisdn, msisdn); Ns_RWLockWrLock(&g_generic_gsm_links_mutex); entry = Tcl_CreateHashEntry(&g_generic_gsm_links, link_id, &new_flag); Tcl_SetHashValue(entry, link_ptr); Ns_RWLockUnlock(&g_generic_gsm_links_mutex); /* * Execute the Generic GSM link processing loop code. */ generic_gsm_link_execute(link_ptr); /* * Remove the link from the hashtable, and free the link structure * memory. */ Ns_RWLockWrLock(&g_generic_gsm_links_mutex); Tcl_DeleteHashEntry(entry); Ns_RWLockUnlock(&g_generic_gsm_links_mutex); ns_free(link_ptr); Ns_Log(Notice,"generic_gsm_init: Exiting"); return NS_TRUE; } /* *---------------------------------------------------------------------- * * generic_gsm_link_execute -- * * Code that actually processes the link. * * Results: * *---------------------------------------------------------------------- */ static int generic_gsm_link_execute(struct generic_gsm_link *link_ptr) { SOCKET max; fd_set set; int finished; int connected; unsigned char msg_buf; int sock_save; struct timeval tv; int retval; struct termios *tty; /* * Kick off the reader thread. */ Ns_ThreadCreate(generic_gsm_link_reader, (void *) link_ptr, 0, NULL); Ns_ThreadCreate(generic_gsm_link_event_handler, (void *) link_ptr, 0, NULL); finished = 0; while (!finished) { /* * Loop until we're online, and connected. */ connected = 0; while (!connected && !finished) { FD_ZERO(&set); FD_SET(link_ptr->msg_pipe[0], &set); max = link_ptr->msg_pipe[0]; tv.tv_sec = 1; tv.tv_usec = 0; retval = select(max+1, &set, NULL, NULL, &tv); if (retval && FD_ISSET(link_ptr->msg_pipe[0], &set)) { /* * Recieved a message, so lets process it. */ if (blocked_recv(link_ptr->msg_pipe[0], &msg_buf, 1, 0) <= 0) { Ns_Log(Error,"generic_gsm_link_execute: Pipe read error, %s", ns_sockstrerror(ns_sockerrno)); finished = 1; } else { switch (msg_buf) { case GENERICGSM_ONLINE: case GENERICGSM_OFFLINE: link_ptr->link_status = msg_buf; break; case GENERICGSM_DELETE: Ns_Log(Notice, "generic_gsm_link_execute: Link delete message, setting finished flag"); finished = 1; break; case GENERICGSM_GROUP_ACTIVITY: break; default: Ns_Log(Error,"generic_gsm_link_execute: Unexpected pipe message %d whilst in unconnected loop", msg_buf); } } } else { /* * Select timed out, so lets attempt a connection to the * device (if we're online). */ if (link_ptr->link_status == GENERICGSM_ONLINE) { link_ptr->sock = open(link_ptr->device, O_RDWR | O_NONBLOCK | O_NOCTTY); // link_ptr->sock = open(link_ptr->device, O_RDWR); if (link_ptr->sock < 0) { Ns_Log(Error, "generic_gsm_link_execute: (Link %s) Unable to open device %s", link_ptr->link_id, link_ptr->device); sleep(1); } else { // if (ioctl(link_ptr->sock, TIOCSCTTY, 0) < 0) { // Ns_Log(Error, // "generic_gsm_link_execute: ioctl() failed, continuing anyway"); // } /* Try and get tty settings */ tcdrain(link_ptr->sock); tcflush(link_ptr->sock, TCIOFLUSH); if (tcgetattr(link_ptr->sock, &link_ptr->tty) < 0) { Ns_Log(Error, "generic_gsm_link_execute: togetattr() failed"); close(link_ptr->sock); } else { connected = 1; tty = &link_ptr->tty; tty->c_cc[VMIN] - 0; /* nonblocking */ tty->c_cc[VTIME] = 0; tty->c_oflag = 0; tty->c_lflag = 0; tty->c_cflag &= ~(CSIZE | CSTOPB | PARENB); tty->c_cflag |= CS8 | CREAD; tty->c_cflag |= CLOCAL; /* ignore modem status lines */ tty->c_lflag = IGNBRK | IGNPAR; tty->c_lflag &= ~ICANON; /* non-canonical mode */ tty->c_lflag &= ~(ECHO|ECHOE|ECHOK|ECHOKE); tty->c_cflag |= CRTSCTS ; /* flow control */ cfsetospeed(tty, B9600); tcdrain(link_ptr->sock); if (tcsetattr(link_ptr->sock, TCSANOW, tty) < 0) { Ns_Log(Error, "generic_gsm_link_execute: tcsetattr() failed"); close(link_ptr->sock); } else { connected = 1; } } } } } } /* * We're now connected. Send message to Reader thread, and loop * whilst we're still connected. * */ if (connected && !finished) { Ns_Log(Notice, "generic_gsm_link_execute: (Link %s), Connected to device %s (%d)", link_ptr->link_id, link_ptr->device, link_ptr->sock); msg_buf = GENERICGSM_CONNECTED; blocked_send(link_ptr->msg_pipe[0], &msg_buf, 1, 0); } link_ptr->old_status = GSMSTATE_IDLE; link_ptr->status = GSMSTATE_ATE0; generic_gsm_send_logic(link_ptr); while (connected && !finished) { /* * Create select to wait for any messages. */ FD_ZERO(&set); FD_SET(link_ptr->msg_pipe[0], &set); max = link_ptr->msg_pipe[0]; tv.tv_sec = link_ptr->poll_time_secs; tv.tv_usec = 0; select(max+1, &set, NULL, NULL, &tv); if (FD_ISSET(link_ptr->msg_pipe[0], &set)) { /* * Another thread sent message. * Read the signal and process. */ if (blocked_recv(link_ptr->msg_pipe[0], &msg_buf, 1, 0) <= 0) { Ns_Log(Error,"generic_gsm_link_execute: Pipe read error, %s", ns_sockstrerror(ns_sockerrno)); } else { /* * If the message is a number less than 100, it is deemed a * signalling message rather than a state change. */ if (msg_buf < 100) { switch (msg_buf) { case GENERICGSM_ONLINE: break; /* * Offline message, clear the connected flag so we drop out * the 'connected' loop. */ case GENERICGSM_OFFLINE: case GENERICGSM_DISCONNECTED: msg_buf = GENERICGSM_OFFLINE; sock_save = link_ptr->sock; link_ptr->sock = -1; link_ptr->link_status = GENERICGSM_OFFLINE; link_ptr->status = GSMSTATE_IDLE; blocked_send(link_ptr->msg_pipe[0], &msg_buf, 1, 0); close(sock_save); connected = 0; break; /* * Delete message. Exit the thread. */ case GENERICGSM_DELETE: msg_buf = GENERICGSM_OFFLINE; sock_save = link_ptr->sock; link_ptr->sock = -1; link_ptr->link_status = GENERICGSM_OFFLINE; link_ptr->status = GSMSTATE_IDLE; blocked_send(link_ptr->msg_pipe[0], &msg_buf, 1, 0); close(sock_save); connected = 0; finished = 1; break; case GENERICGSM_GROUP_ACTIVITY: if (link_ptr->status == GSMSTATE_IDLE && link_ptr->non_read_count < GENERICGSM_MAX_BEFORE_READ) { link_ptr->status = GSMSTATE_CMGS; } generic_gsm_send_logic(link_ptr); break; /* * Default case. Warn against any unexpected messages. */ default: Ns_Log(Error,"generic_gsm_link_reader: Unexpected pipe message %d whilst in connected loop", msg_buf); break; } } else { /* * This is a status change message. If this is an IDLE * message: * If there are messages to process, and we haven't reached * the maximum 'messages per read', signal a message send to * happen. * Otherwise, signal a message read query to happen. */ if (link_ptr->status == GSMSTATE_IDLE && !smsq_group_empty_p(link_ptr->out_group_ptr) && link_ptr->non_read_count < GENERICGSM_MAX_BEFORE_READ) { link_ptr->status = GSMSTATE_CMGS; } else if (link_ptr->status == GSMSTATE_IDLE && link_ptr->old_status != GSMSTATE_CMGL_DATA) { link_ptr->status = GSMSTATE_CMGL; } generic_gsm_send_logic(link_ptr); } } } else { /* * We've timed out on the select, so lets do a quick check for * messages on the queue. If any exist, kick off a send, otherwise * kick off a read message query. */ if (link_ptr->status == GSMSTATE_IDLE && !smsq_group_empty_p(link_ptr->out_group_ptr) && link_ptr->non_read_count < GENERICGSM_MAX_BEFORE_READ) { link_ptr->status = GSMSTATE_CMGS; } else if (link_ptr->status == GSMSTATE_IDLE) { link_ptr->status = GSMSTATE_CMGL; } generic_gsm_send_logic(link_ptr); } } if (link_ptr->msg_ptr != NULL) { generic_gsm_fail_current_message(link_ptr); } /* * Inform the Reader thread that the connection to the device has * closed. */ msg_buf = GENERICGSM_DISCONNECTED; blocked_send(link_ptr->msg_pipe[0], &msg_buf, 1, 0); } /* * Inform the Reader thread that this link is shutting down. * Wait for a second to make sure threads have time to clean up. */ msg_buf = GENERICGSM_DELETE; blocked_send(link_ptr->msg_pipe[0], &msg_buf, 1, 0); return NS_TRUE; } void generic_gsm_send_logic(struct generic_gsm_link *link_ptr) { struct q_message *msg_ptr; char bits8_msg[170]; /* Should get away with 160, but just to be safe */ char send_buffer[1024]; Tcl_HashSearch cmgd_search; unsigned char msg_buf; switch (link_ptr->status) { case GSMSTATE_IDLE: break; case GSMSTATE_CMGL: link_ptr->non_read_count = 0; /* Send CMGL */ link_ptr->cmgl_status = 0; if (link_ptr->use_pdu_p) { sprintf(send_buffer, "AT+CMGL=4\r"); } else { sprintf(send_buffer, "AT+CMGL=\"ALL\"\r"); } link_ptr->sent_wait_count = 0; link_ptr->status = GSMSTATE_CMGL_DATA; blocked_write(link_ptr->sock, send_buffer, strlen(send_buffer)); break; case GSMSTATE_CMGS: /* * Try and find another message on the queue. If one exists, * change state to send it. */ msg_ptr = smsq_get_msg_and_set_status(link_ptr->out_group_ptr, Q_MSG_STATUS_READY, Q_MSG_STATUS_PROCESSING); if (msg_ptr != NULL) { link_ptr->msg_ptr = msg_ptr; link_ptr->payload_ptr = msg_ptr->msg_ptr; link_ptr->status = GSMSTATE_CMGS_ARROW; link_ptr->sent_wait_count = 0; /* Send CMGS */ if (link_ptr->use_pdu_p) { generic_gsm_encode_pdu(link_ptr->pdu_string, &link_ptr->pdu_length, link_ptr); sprintf(send_buffer, "AT+CMGS=%d\r", link_ptr->pdu_length); } else { sprintf(send_buffer, "AT+CMGS=\"%s\"\r", link_ptr->payload_ptr->dest_address); } blocked_write(link_ptr->sock, send_buffer, strlen(send_buffer)); } else { link_ptr->status = GSMSTATE_IDLE; } break; case GSMSTATE_CMGS_MESSAGE: /* Send CMGS data */ if (link_ptr->use_pdu_p) { /* Don't forget the (26) */ sprintf(send_buffer, "%s%c", link_ptr->pdu_string, 26); link_ptr->sent_wait_count = 0; link_ptr->status = GSMSTATE_CMGS_RESP; blocked_write(link_ptr->sock, send_buffer, strlen(send_buffer)); } else { gsm0338_unpack_7bit(bits8_msg, link_ptr->payload_ptr->user_data, link_ptr->payload_ptr->user_data_len, link_ptr->payload_ptr->unpacked_data_len); /* Don't forget the (26) */ sprintf(send_buffer, "%s%c", bits8_msg, 26); link_ptr->sent_wait_count = 0; link_ptr->status = GSMSTATE_CMGS_RESP; blocked_write(link_ptr->sock, send_buffer, strlen(send_buffer)); } break; case GSMSTATE_ATE0: /* Send ATE0 */ sprintf(send_buffer, "ATE0\r\n"); link_ptr->sent_wait_count = 0; link_ptr->status = GSMSTATE_ATE0_ATE0; blocked_write(link_ptr->sock, send_buffer, strlen(send_buffer)); break; case GSMSTATE_CPIN: /* Send CPIN */ sprintf(send_buffer, "AT+CPIN=\"%s\"\r\r", link_ptr->sim_pin); link_ptr->sent_wait_count = 0; link_ptr->status = GSMSTATE_CPIN_OK; blocked_write(link_ptr->sock, send_buffer, strlen(send_buffer)); break; case GSMSTATE_CSCA: /* Send CSCA */ sprintf(send_buffer, "AT+CSCA=\"%s\"\r\r", link_ptr->service_centre); link_ptr->sent_wait_count = 0; link_ptr->status = GSMSTATE_CSCA_OK; blocked_write(link_ptr->sock, send_buffer, strlen(send_buffer)); break; case GSMSTATE_CMGF: /* Send CMGF */ if (link_ptr->use_pdu_p) { sprintf(send_buffer, "AT+CMGF=0\r\r"); } else { sprintf(send_buffer, "AT+CMGF=1\r\r"); } link_ptr->sent_wait_count = 0; link_ptr->status = GSMSTATE_CMGF_OK; blocked_write(link_ptr->sock, send_buffer, strlen(send_buffer)); break; case GSMSTATE_CNMI: /* Send SCRT */ sprintf(send_buffer, "AT+CNMI=1,1\r\r"); link_ptr->sent_wait_count = 0; link_ptr->status = GSMSTATE_CNMI_OK; blocked_write(link_ptr->sock, send_buffer, strlen(send_buffer)); break; case GSMSTATE_CMGD: /* Send CMGD */ link_ptr->cmgd_search_ptr = Tcl_FirstHashEntry( &link_ptr->cmgd_ids, &cmgd_search); if (link_ptr->cmgd_search_ptr != NULL) { sprintf(send_buffer, "AT+CMGD=%s\r\r", Tcl_GetHashKey( &link_ptr->cmgd_ids, link_ptr->cmgd_search_ptr)); link_ptr->sent_wait_count = 0; link_ptr->status = GSMSTATE_CMGD_OK; blocked_write(link_ptr->sock, send_buffer, strlen(send_buffer)); } else { if (!smsq_group_empty_p(link_ptr->out_group_ptr)) { link_ptr->status = GSMSTATE_IDLE; msg_buf = GENERICGSM_GROUP_ACTIVITY; blocked_send(link_ptr->msg_pipe[1], &msg_buf, 1, 0); } else { link_ptr->status = GSMSTATE_IDLE; } } break; default: break; } } /* *---------------------------------------------------------------------- * * generic_gsm_link_reader -- * * Reader thread. * * Results: * *---------------------------------------------------------------------- */ static void generic_gsm_link_reader(void *arg) { SOCKET max; fd_set set; unsigned char msg_buf; int finished; int connected; int n; struct generic_gsm_link *link_ptr; char buffered[1024]; int buffered_size; int count; char *brk_pos; unsigned char old_status; struct timeval tv; link_ptr = (struct generic_gsm_link *) arg; finished = 0; while (!finished) { /* * Loop until we're connected. */ connected = 0; while (!connected && !finished) { FD_ZERO(&set); FD_SET(link_ptr->msg_pipe[1], &set); max = link_ptr->msg_pipe[1]; select(max+1, &set, NULL, NULL, NULL); if (FD_ISSET(link_ptr->msg_pipe[1], &set)) { if (blocked_recv(link_ptr->msg_pipe[1], &msg_buf, 1, 0) <= 0) { Ns_Log(Error,"generic_gsm_link_reader: Pipe read error, %s", ns_sockstrerror(ns_sockerrno)); } else { switch (msg_buf) { case GENERICGSM_CONNECTED: connected = 1; break; case GENERICGSM_DELETE: finished = 1; break; case GENERICGSM_DISCONNECTED: case GENERICGSM_OFFLINE: break; default: Ns_Log(Error,"generic_gsm_link_reader: Unexpected pipe message %d whilst in unconnected loop", msg_buf); } } } } /* * We're now connected. Loop while we're still connected. */ while (connected && !finished) { /* * Create select to wait for data. */ FD_ZERO(&set); FD_SET(link_ptr->sock, &set); FD_SET(link_ptr->msg_pipe[1], &set); max = link_ptr->sock; if (link_ptr->msg_pipe[1] > max) { max = link_ptr->msg_pipe[1]; } tv.tv_sec = 1; tv.tv_usec = 0; select(max+1, &set, NULL, NULL, &tv); if (FD_ISSET(link_ptr->msg_pipe[1], &set)) { /* * Another thread sent message. * Read the signal and process. */ if (blocked_recv(link_ptr->msg_pipe[1], &msg_buf, 1, 0) <= 0) { Ns_Log(Error,"generic_gsm_link_reader: Pipe read error, %s", ns_sockstrerror(ns_sockerrno)); } else { switch (msg_buf) { /* * Offline message, clear the connected flag so we drop out * the 'connected' loop. */ case GENERICGSM_OFFLINE: case GENERICGSM_DISCONNECTED: connected = 0; break; /* * Delete message. Exit the thread. */ case GENERICGSM_DELETE: finished = 1; break; /* * Default case. Warn against any unexpected messages. */ default: Ns_Log(Error,"generic_gsm_link_reader: Unexpected pipe message %d whilst in connected loop", msg_buf); break; } } } else if (FD_ISSET(link_ptr->sock, &set)) { /* * Read data from the modem. */ n = read(link_ptr->sock, buffered + buffered_size, sizeof(buffered) - buffered_size); /* * Check for a read error */ if (n < 0) { msg_buf = GENERICGSM_DISCONNECTED; blocked_send(link_ptr->msg_pipe[1], &msg_buf, 1, 0); connected = 0; } else { // for (count = 0; count < n; count++) { // Ns_Log(Notice, ">>>> 0x%x", buffered[count + buffered_size]); // } buffered_size += n; buffered[buffered_size] = '\0'; while (buffered_size > 0 && ((brk_pos = strpbrk(buffered, "\r\n\f")) != NULL || !strcmp(buffered, "> "))) { if (brk_pos == NULL) { brk_pos = buffered + 2; buffered_size++; } /* * Ignore empty lines */ if (brk_pos > buffered) { *brk_pos = '\0'; old_status = link_ptr->status; link_ptr->old_status = link_ptr->status; generic_gsm_read_logic(link_ptr, buffered); Ns_Log(Notice, "> \"%s\" (%d -> %d)", buffered, old_status, link_ptr->status); /* * For all state changes that require activity from the * Writer thread, construct and send the pipe message. */ switch (link_ptr->status) { case GSMSTATE_CMGS_ARROW: case GSMSTATE_CMGS_MESSAGE: case GSMSTATE_ATE0: case GSMSTATE_CPIN: case GSMSTATE_CSCA: case GSMSTATE_CMGF: case GSMSTATE_CNMI: case GSMSTATE_CMGD: case GSMSTATE_CMGL: case GSMSTATE_IDLE: msg_buf = link_ptr->status; blocked_send(link_ptr->msg_pipe[1], &msg_buf, 1, 0); break; } } if ((buffered_size - (brk_pos - buffered) - 1) > 0) { memmove(buffered, brk_pos + 1, buffered_size - (brk_pos - buffered) - 1); } buffered_size -= ((brk_pos - buffered) + 1); buffered[buffered_size] = '\0'; } } } } } Ns_Log(Notice,"generic_gsm_link_reader: Exiting"); Ns_ThreadExit (0); return; } void generic_gsm_read_logic(struct generic_gsm_link *link_ptr, char *text) { unsigned char old_status; char msg_buf; char cmgl_text[1024]; char *cur_pos; char *cmgl_msg_num; char *cmgl_msisdn; char msg_id[128]; Tcl_HashEntry *entry; int new_flag; char msisdn[128]; unsigned char msg_data[256]; int msg_data_length; int unpacked_size; int data_coding_scheme; struct smsc_tm smsc_timestamp; old_status = link_ptr->status; switch (link_ptr->status) { case GSMSTATE_FAILED_SYNC: if (!strcmp(text, "OK") || (!strcmp(text, "> ") && link_ptr->old_status != GSMSTATE_CMGS_ARROW) || !strcmp(text, "ERROR") || !memcmp(text, "+CMS ERROR:", strlen("+CMS ERROR:"))) { link_ptr->status = link_ptr->failed_sync_status; link_ptr->non_read_count++; } break; case GSMSTATE_ATE0_ATE0: case GSMSTATE_ATE0_OK: if (!strcmp(text, "ATE0")) { link_ptr->status = GSMSTATE_ATE0_OK; } else if (!strcmp(text, "OK")) { /* * ATE0 command complete. If a service centre address was * specified for this link, initiate a CSCA command. * Otherwise, initiate a CMGF command. */ if (strcmp(link_ptr->sim_pin, "")) { link_ptr->status = GSMSTATE_CPIN; } else if (strcmp(link_ptr->service_centre, "")) { link_ptr->status = GSMSTATE_CSCA; } else { link_ptr->status = GSMSTATE_CMGF; } } else { /* * Unexpected data, so set status to failed_sync, which should * continue reading until an OK/ERROR/+CMS ERROR occurs. */ if (strcmp(link_ptr->sim_pin, "")) { link_ptr->failed_sync_status = GSMSTATE_CPIN; } else if (strcmp(link_ptr->service_centre, "")) { link_ptr->failed_sync_status = GSMSTATE_CSCA; } else { link_ptr->failed_sync_status = GSMSTATE_CMGF; } link_ptr->status = GSMSTATE_FAILED_SYNC; generic_gsm_read_logic(link_ptr, text); } break; case GSMSTATE_CPIN_OK: if (!strcmp(text, "OK")) { /* * CPIN command complete. */ if (strcmp(link_ptr->service_centre, "")) { link_ptr->status = GSMSTATE_CSCA; } else { link_ptr->status = GSMSTATE_CMGF; } } else { if (strcmp(link_ptr->service_centre, "")) { link_ptr->failed_sync_status = GSMSTATE_CSCA; } else { link_ptr->failed_sync_status = GSMSTATE_CMGF; } link_ptr->status = GSMSTATE_FAILED_SYNC; generic_gsm_read_logic(link_ptr, text); } break; case GSMSTATE_CSCA_OK: if (!strcmp(text, "OK")) { /* * CSCA command complete. */ link_ptr->status = GSMSTATE_CMGF; } else { link_ptr->failed_sync_status = GSMSTATE_CMGF; link_ptr->status = GSMSTATE_FAILED_SYNC; generic_gsm_read_logic(link_ptr, text); } break; case GSMSTATE_CMGF_OK: if (!strcmp(text, "OK")) { /* * CMGF command complete. */ link_ptr->status = GSMSTATE_CNMI; } else { /* * If the CMGF message failed, then it's likely the gsm modem * hasn't found the network yet, so wait for 2 seconds, and then * reattempt. */ Ns_Log(Notice, "generic_gsm_read_logic: Failed CMGF message, its likely the modem either doesn't have a SIM card, or the modem is still registering with the network."); sleep(2); link_ptr->failed_sync_status = GSMSTATE_CMGF; link_ptr->status = GSMSTATE_FAILED_SYNC; generic_gsm_read_logic(link_ptr, text); } break; case GSMSTATE_CNMI_OK: if (!strcmp(text, "OK")) { /* * CNMI command complete. */ link_ptr->status = GSMSTATE_IDLE; } else { link_ptr->failed_sync_status = GSMSTATE_IDLE; link_ptr->status = GSMSTATE_FAILED_SYNC; generic_gsm_read_logic(link_ptr, text); } break; case GSMSTATE_CMGS_ARROW: if (strcmp(text, "> ")) { generic_gsm_fail_current_message(link_ptr); link_ptr->failed_sync_status = GSMSTATE_IDLE; link_ptr->status = GSMSTATE_FAILED_SYNC; generic_gsm_read_logic(link_ptr, text); } else { link_ptr->status = GSMSTATE_CMGS_MESSAGE; } break; case GSMSTATE_CMGS_MESSAGE: /* * We should never get any data whilst in this state, so its always * an error. Some GSM modems (notably, the Wavecom 02) return an * error indicator "ERROR" straight after the ">" if there is no * SIM card (or the SIM card isn't initialised) in the modem. */ generic_gsm_fail_current_message(link_ptr); link_ptr->failed_sync_status = GSMSTATE_IDLE; link_ptr->status = GSMSTATE_FAILED_SYNC; generic_gsm_read_logic(link_ptr, text); break; case GSMSTATE_CMGS_RESP: if (!memcmp(text, "+CMGS:", strlen("+CMGS:"))) { link_ptr->status = GSMSTATE_CMGS_OK; } else { generic_gsm_fail_current_message(link_ptr); link_ptr->failed_sync_status = GSMSTATE_IDLE; link_ptr->status = GSMSTATE_FAILED_SYNC; generic_gsm_read_logic(link_ptr, text); } break; case GSMSTATE_CMGS_OK: if (!strcmp(text, "OK")) { /* * CMGS command complete. */ /* * Sleep, as the generic_gsm can't seem to keep up (that, or * the network. */ smsq_set_msg_status(link_ptr->msg_ptr->msg_id, link_ptr->out_group_ptr, Q_MSG_STATUS_COMPLETE); link_ptr->msg_ptr = NULL; usleep(link_ptr->post_send_delay_usecs); link_ptr->status = GSMSTATE_IDLE; link_ptr->non_read_count++; } else { generic_gsm_fail_current_message(link_ptr); usleep(link_ptr->post_send_delay_usecs); link_ptr->failed_sync_status = GSMSTATE_IDLE; link_ptr->status = GSMSTATE_FAILED_SYNC; generic_gsm_read_logic(link_ptr, text); } break; case GSMSTATE_CMGL_DATA: /* * PDU mode receive */ if (link_ptr->use_pdu_p) { if (link_ptr->cmgl_status == 1) { /* * We're expecting the message content here, so decode the pdu * string, and submit the message onto the queue. * Add the message number into the "message numbers to delete" * hashtable. */ link_ptr->cmgl_status = 0; generic_gsm_decode_pdu(text, msisdn, msg_data, &msg_data_length, &unpacked_size, &data_coding_scheme, &smsc_timestamp); smsq_submit_8bit(link_ptr->in_group_ptr, msg_id, msisdn, NULL, msg_data, msg_data_length, unpacked_size, data_coding_scheme, DCS_DEFAULT, &smsc_timestamp, NULL, NULL); entry = Tcl_CreateHashEntry(&link_ptr->cmgd_ids, link_ptr->cmgl_msg_num, &new_flag); Tcl_SetHashValue(entry, NULL); } else if (!memcmp(text, "+CMGL:", strlen("+CMGL:"))) { /* * Lets extract the message number and msisdn. */ strcpy(cmgl_text, text); cur_pos = cmgl_text; cur_pos = strchr(cur_pos, ','); if (cur_pos != NULL) { *cur_pos = '\0'; cmgl_msg_num = &cmgl_text[7]; cur_pos = strchr(cur_pos+1, ','); } /* Skip the next two commas */ if (cur_pos != NULL) { cur_pos = strchr(cur_pos+1, ','); } if (cur_pos != NULL) { link_ptr->pdu_length = (int)strtol(cur_pos+1, NULL, 10); strcpy(link_ptr->cmgl_msg_num, cmgl_msg_num); link_ptr->cmgl_status = 1; } else { Ns_Log(Error,"generic_gsm_link_reader: Unable to parse CMGL string \"%s\"", text); } } else if (!strcmp(text, "OK")) { /* * CMGL command complete. */ link_ptr->status = GSMSTATE_CMGD; } else { link_ptr->failed_sync_status = GSMSTATE_CMGD; link_ptr->status = GSMSTATE_FAILED_SYNC; generic_gsm_read_logic(link_ptr, text); } } else { /* * Text mode receive */ if (link_ptr->cmgl_status == 1) { #ifdef CFG_DIGICOM_HACK /************************************************************** * Digicom wierdity fix. If the incoming message content is OK, * either a blank message has been sent in (and this is the modems * "OK" response), or the user has sent in OK. In the latter case, * we'll probably get some unsolicited data from the modem. * We also have to cope with an unexpected "+CMGL:" message, which * might occur if there are more messages in this CMGL read series, * and this message is a blank one. I.e: * +CMGL: ...... * +CMGL: ...... * Content of the second message. * OK * **************************************************************/ if (!strcmp(text, "OK") || !memcmp(text, "+CMGL:", strlen("+CMGL:"))) { link_ptr->cmgl_status = 0; smsq_submit_ascii(link_ptr->in_group_ptr, msg_id, link_ptr->cmgl_msisdn, link_ptr->msisdn, "OK", NULL, NULL, NULL); entry = Tcl_CreateHashEntry(&link_ptr->cmgd_ids, link_ptr->cmgl_msg_num, &new_flag); Tcl_SetHashValue(entry, NULL); /* * If the message content was OK, this was probably the last * message in the CMGL series, so lets change states (this is a * bit dodgy, as if * we've received more than one message, and the first message was * an explicit "OK" message, we'll be ignoring further messages. * I believe these will get picked up the next time round). * Otherwise, it was another CMGL: message, so lets process this * text as though it was another message (by calling this function * again with the same text). */ if (!strcmp(text, "OK")) { link_ptr->status = GSMSTATE_CMGD; } else { /* * */ generic_gsm_read_logic(link_ptr, text); } } else { #endif /* * We're expecting the message content here, so submit the * message and data to the queue. Add the message number into the * "message numbers to delete" hashtable. * * NB: Currently not filling in timestamp for this message. */ link_ptr->cmgl_status = 0; smsq_submit_ascii(link_ptr->in_group_ptr, msg_id, link_ptr->cmgl_msisdn, link_ptr->msisdn, text, NULL, NULL, NULL); entry = Tcl_CreateHashEntry(&link_ptr->cmgd_ids, link_ptr->cmgl_msg_num, &new_flag); Tcl_SetHashValue(entry, NULL); #ifdef CFG_DIGICOM_HACK } #endif } else if (!memcmp(text, "+CMGL:", strlen("+CMGL:"))) { strcpy(cmgl_text, text); cur_pos = cmgl_text; /* * Lets extract the message number and msisdn. */ cur_pos = strchr(cur_pos, ','); if (cur_pos != NULL) { *cur_pos = '\0'; cmgl_msg_num = &cmgl_text[7]; cur_pos = strchr(cur_pos+1, ','); } if (cur_pos != NULL) { cmgl_msisdn = cur_pos + 2; } if (cur_pos != NULL) { cur_pos = strchr(cmgl_msisdn, '"'); *cur_pos = '\0'; link_ptr->cmgl_status = 1; strcpy(link_ptr->cmgl_msg_num, cmgl_msg_num); strcpy(link_ptr->cmgl_msisdn, cmgl_msisdn); } else { Ns_Log(Error,"generic_gsm_link_reader: Unable to parse CMGL string \"%s\"", text); } } else if (!strcmp(text, "OK")) { /* * CMGL command complete. */ link_ptr->status = GSMSTATE_CMGD; } else { link_ptr->failed_sync_status = GSMSTATE_CMGD; link_ptr->status = GSMSTATE_FAILED_SYNC; generic_gsm_read_logic(link_ptr, text); } } break; case GSMSTATE_CMGD_OK: if (!strcmp(text, "OK")) { /* * CMGD command complete, schedule the next one. */ link_ptr->status = GSMSTATE_CMGD; Tcl_DeleteHashEntry(link_ptr->cmgd_search_ptr); } else { link_ptr->failed_sync_status = GSMSTATE_CMGD; link_ptr->status = GSMSTATE_FAILED_SYNC; generic_gsm_read_logic(link_ptr, text); } break; default: if (!strncmp(text, "+CMTI:", 6)) { link_ptr->status = GSMSTATE_CMGL; } else { Ns_Log(Error,"generic_gsm_link_reader: Unsolicited modem data \"%s\"", text); } break; } } void generic_gsm_fail_current_message(struct generic_gsm_link *link_ptr) { link_ptr->payload_ptr->del_attempt_count++; if (link_ptr->max_retries != -1 && link_ptr->payload_ptr->del_attempt_count >= link_ptr->max_retries) { Ns_Log(Error,"generic-gsm: Link %s: Message id %s failed after %d delivery attempt(s)", link_ptr->link_id, link_ptr->msg_ptr->msg_id, link_ptr->payload_ptr->del_attempt_count); smsq_set_msg_status(link_ptr->msg_ptr->msg_id, link_ptr->out_group_ptr, Q_MSG_STATUS_FAILED); } else { smsq_set_msg_status(link_ptr->msg_ptr->msg_id, link_ptr->out_group_ptr, Q_MSG_STATUS_READY); } link_ptr->msg_ptr = NULL; } /* *---------------------------------------------------------------------- * * generic_gsm_link_event_handler -- * * Converts thread events into * * Results: * *---------------------------------------------------------------------- */ static void generic_gsm_link_event_handler(void *arg) { struct generic_gsm_link *link_ptr; struct q_group *group_ptr; char link_id[20]; Ns_Time waittime; unsigned char msg_buf; int finished; int result; SOCKET msg_pipe[2]; link_ptr = (struct generic_gsm_link *) arg; group_ptr = link_ptr->out_group_ptr; strcpy(link_id, link_ptr->link_id); msg_pipe[0] = link_ptr->msg_pipe[0]; msg_pipe[1] = link_ptr->msg_pipe[1]; finished = 0; while (!finished) { Ns_GetTime(&waittime); Ns_IncrTime(&waittime, 1, 0); // One second, beware changing this, as // this will affect the device timeout // algorithm. result = Ns_CondTimedWait(&group_ptr->event, &group_ptr->event_lock, &waittime); if (generic_gsm_get_link_ptr(link_id) != NULL) { switch (result) { case NS_OK: if (link_ptr->link_status == GENERICGSM_ONLINE) { msg_buf = GENERICGSM_GROUP_ACTIVITY; blocked_send(msg_pipe[1], &msg_buf, 1, 0); } break; case NS_TIMEOUT: /* * Timeout, so do nothing. */ break; default: Ns_Log(Error, "generic_gsm_event_handler: Error from CondTimedWait: %d", result); break; } if (link_ptr->status != GSMSTATE_IDLE) { if (link_ptr->sent_wait_count > link_ptr->device_timeout_secs) { link_ptr->sent_wait_count = 0; Ns_Log(Error, "generic_gsm_event_handler: %d second response timeout, bouncing generic_gsm link %s", link_ptr->device_timeout_secs, link_ptr->link_id); generic_gsm_set_status(link_ptr, GENERICGSM_OFFLINE); generic_gsm_set_status(link_ptr, GENERICGSM_ONLINE); } else { link_ptr->sent_wait_count++; } } } else { /* * Link has been deleted so set the finish flag. */ finished = 1; } } sleep(1); ns_sockclose(msg_pipe[0]); ns_sockclose(msg_pipe[1]); Ns_Log(Notice,"generic_gsm_link_event_handler: Exiting"); Ns_ThreadExit (0); } int blocked_recv(int sock, void *msg_buf, int msg_size, int flags) { int read_len; int read_val; read_len = 0; read_val = 0; while (read_len < msg_size) { if ((read_val = recv(sock, msg_buf + read_len, msg_size - read_len, flags)) <= 0) { read_len = -1; break; } else { read_len += read_val; } } // Ns_Log(Notice, "(2) recv %d, %d, %d", sock, msg_size, *((char *) msg_buf)); return read_len; } int blocked_send(int sock, const void *msg_buf, int msg_size, int flags) { int write_len; int write_val; write_len = 0; write_val = 0; // Ns_Log(Notice, "send %d, %d, %d", sock, msg_size, *((char *) msg_buf)); while (write_len < msg_size) { if ((write_val = send(sock, msg_buf + write_len, msg_size - write_len,flags)) <= 0) { write_len = -1; break; } else { write_len += write_val; } } return write_len; } int blocked_read(int fd, void *msg_buf, int msg_size) { int read_len; int read_val; read_len = 0; read_val = 0; while (read_len < msg_size) { if ((read_val = read(fd, msg_buf + read_len, msg_size - read_len)) <= 0) { read_len = -1; break; } else { read_len += read_val; } } return read_len; } int blocked_write(int fd, const void *msg_buf, int msg_size) { int write_len; int write_val; write_len = 0; write_val = 0; Ns_Log(Notice, "Blocked write to %d, of %d bytes: %s", fd, msg_size, msg_buf); while (write_len < msg_size) { if ((write_val = write(fd, msg_buf + write_len, msg_size - write_len)) <= 0) { write_len = -1; break; } else { write_len += write_val; } } return write_len; } /* *---------------------------------------------------------------------- * * generic_gsm_get_link_ptr -- * * Returns the pointer to the link structure (lookup in the hash table) * specified via the link_id. * * Results: * *---------------------------------------------------------------------- */ static struct generic_gsm_link * generic_gsm_get_link_ptr(char *link_id) { Tcl_HashEntry *entry; Ns_RWLockRdLock(&g_generic_gsm_links_mutex); entry = Tcl_FindHashEntry(&g_generic_gsm_links, link_id); if (entry == NULL) { Ns_RWLockUnlock(&g_generic_gsm_links_mutex); return NULL; } Ns_RWLockUnlock(&g_generic_gsm_links_mutex); return Tcl_GetHashValue(entry); } static int generic_gsm_delete(struct generic_gsm_link *link_ptr) { unsigned char msg_buf; msg_buf = GENERICGSM_DELETE; blocked_send(link_ptr->msg_pipe[1], &msg_buf, 1, 0); return NS_TRUE; } static int generic_gsm_set_status(struct generic_gsm_link *link_ptr, int status) { unsigned char msg_buf; msg_buf = (char) status; blocked_send(link_ptr->msg_pipe[1], &msg_buf, 1, 0); return NS_TRUE; } static int generic_gsm_update(struct generic_gsm_link *link_ptr, char *device, int device_timeout_secs, char *service_centre, char *sim_pin, int poll_time_secs, int post_send_delay_usecs, int use_pdu_p, int max_retries, char *msisdn) { unsigned char msg_buf; strcpy(link_ptr->device, device); link_ptr->device_timeout_secs = device_timeout_secs; strcpy(link_ptr->service_centre, service_centre); strcpy(link_ptr->sim_pin, sim_pin); link_ptr->poll_time_secs = poll_time_secs; link_ptr->post_send_delay_usecs = post_send_delay_usecs; link_ptr->use_pdu_p = use_pdu_p; link_ptr->max_retries = max_retries; strcpy(link_ptr->msisdn, msisdn); msg_buf = GENERICGSM_UPDATE; blocked_send(link_ptr->msg_pipe[1], &msg_buf, 1, 0); return NS_TRUE; } /* *---------------------------------------------------------------------- * * generic_gsm_add_commands -- * * Adds the generic_gsm API functions to the Tcl interpreter. * * Results: * *---------------------------------------------------------------------- */ static int generic_gsm_add_commands(Tcl_Interp *interp, ClientData data) { Tcl_CreateCommand(interp, "generic_gsm_init", generic_gsm_tcl_init, NULL, NULL); Tcl_CreateCommand(interp, "generic_gsm_delete", generic_gsm_tcl_delete, NULL, NULL); Tcl_CreateCommand(interp, "generic_gsm_set_status", generic_gsm_tcl_set_status, NULL, NULL); Tcl_CreateCommand(interp, "generic_gsm_update", generic_gsm_tcl_update, NULL, NULL); return NS_OK; } /* *---------------------------------------------------------------------- * * generic_gsm_tcl_init -- * * Tcl API function to initialise a new/starting link. * * Results: * *---------------------------------------------------------------------- */ static int generic_gsm_tcl_init(ClientData data, Tcl_Interp *interp, int argc, char *argv[]) { struct q_group *in_group_ptr; struct q_group *out_group_ptr; char *link_id; char *device; char *service_centre; char *sim_pin; char *status_ptr; char *msisdn; int status; int device_timeout_secs; int poll_time_secs; int post_send_delay_usecs; int use_pdu_p; int max_retries; if (argc < 13 || argc > 14) { Tcl_AppendResult(interp, "Usage: ", argv[0], " link_id in_group out_group status device device_timeout_secs service_centre sim_pin poll_time_secs post_send_delay_usecs use_pdu_p max_retries [msisdn]", NULL); return TCL_ERROR; } link_id = argv[1]; in_group_ptr = q_get_group_ptr(g_server, argv[2]); if (in_group_ptr == NULL) { Tcl_AppendResult(interp, "Unknown group name: ", argv[1], NULL); return TCL_ERROR; } out_group_ptr = q_get_group_ptr(g_server, argv[3]); if (out_group_ptr == NULL) { Tcl_AppendResult(interp, "Unknown group name: ", argv[1], NULL); return TCL_ERROR; } status_ptr = argv[4]; if (!strcmp(status_ptr,"online")) { status = GENERICGSM_ONLINE; } else if (!strcmp(status_ptr,"offline")) { status = GENERICGSM_OFFLINE; } else { Tcl_AppendResult(interp, "Unknown status : ", status_ptr, ", expecting online|offline", NULL); return TCL_ERROR; } device = argv[5]; if (Tcl_GetInt(interp, argv[6], &device_timeout_secs) != TCL_OK) { return TCL_ERROR; } service_centre = argv[7]; sim_pin = argv[8]; if (Tcl_GetInt(interp, argv[9], &poll_time_secs) != TCL_OK) { return TCL_ERROR; } if (Tcl_GetInt(interp, argv[10], &post_send_delay_usecs) != TCL_OK) { return TCL_ERROR; } if (Tcl_GetInt(interp, argv[11], &use_pdu_p) != TCL_OK) { return TCL_ERROR; } if (Tcl_GetInt(interp, argv[12], &max_retries) != TCL_OK) { return TCL_ERROR; } if (argc == 14) { msisdn = argv[13]; } else { msisdn = ""; } generic_gsm_init(link_id, in_group_ptr, out_group_ptr, status, device, device_timeout_secs, service_centre, sim_pin, poll_time_secs, post_send_delay_usecs, use_pdu_p, max_retries, msisdn); Tcl_AppendResult(interp, "1", NULL); return TCL_OK; } /* *---------------------------------------------------------------------- * * generic_gsm_tcl_delete -- * * Tcl API function to delete a running link. * * Results: * *---------------------------------------------------------------------- */ static int generic_gsm_tcl_delete(ClientData data, Tcl_Interp *interp, int argc, char *argv[]) { struct generic_gsm_link *link_ptr; char *link_id; if (argc != 2) { Tcl_AppendResult(interp, "Usage: ", argv[0], " link_id", NULL); return TCL_ERROR; } link_id = argv[1]; link_ptr = generic_gsm_get_link_ptr(link_id); if (link_ptr == NULL) { Tcl_AppendResult(interp, "Unknown link_id: ", link_id, NULL); return TCL_ERROR; } generic_gsm_delete(link_ptr); Tcl_AppendResult(interp, "1", NULL); return TCL_OK; } /* *---------------------------------------------------------------------- * * generic_gsm_tcl_connect_p -- * * Tcl API function to check whether a link is connected * * Results: * *---------------------------------------------------------------------- */ static int generic_gsm_tcl_connect_p(ClientData data, Tcl_Interp *interp, int argc, char *argv[]) { struct generic_gsm_link *link_ptr; char *link_id; if (argc != 2) { Tcl_AppendResult(interp, "Usage: ", argv[0], " link_id", NULL); return TCL_ERROR; } link_id = argv[1]; link_ptr = generic_gsm_get_link_ptr(link_id); if (link_ptr == NULL) { Tcl_AppendResult(interp, "Unknown link_id: ", link_id, NULL); return TCL_ERROR; } if (link_ptr->sock != -1) { Tcl_AppendResult(interp, "1", NULL); return TCL_OK; } else { Tcl_AppendResult(interp, "0", NULL); return TCL_OK; } } /* *---------------------------------------------------------------------- * * generic_gsm_tcl_set_status -- * * Tcl API function to set the status of a running link. * * Results: * *---------------------------------------------------------------------- */ static int generic_gsm_tcl_set_status(ClientData data, Tcl_Interp *interp, int argc, char *argv[]) { struct generic_gsm_link *link_ptr; char *link_id; char *status_ptr; int status; if (argc != 3) { Tcl_AppendResult(interp, "Usage: ", argv[0], " link_id online|offline", NULL); return TCL_ERROR; } link_id = argv[1]; link_ptr = generic_gsm_get_link_ptr(link_id); if (link_ptr == NULL) { Tcl_AppendResult(interp, "Unknown link_id: ", link_id, NULL); return TCL_ERROR; } status_ptr = argv[2]; if (!strcmp(status_ptr,"online")) { status = GENERICGSM_ONLINE; } else if (!strcmp(status_ptr,"offline")) { status = GENERICGSM_OFFLINE; } else { Tcl_AppendResult(interp, "Unknown status : ", status_ptr, ", expecting online|offline", NULL); return TCL_ERROR; } generic_gsm_set_status(link_ptr, status); Tcl_AppendResult(interp, "1", NULL); return TCL_OK; } /* *---------------------------------------------------------------------- * * generic_gsm_tcl_update -- * * Tcl API function to update parameters for a running link. * * Results: * *---------------------------------------------------------------------- */ static int generic_gsm_tcl_update(ClientData data, Tcl_Interp *interp, int argc, char *argv[]) { struct generic_gsm_link *link_ptr; char *link_id; char *device; char *service_centre; char *sim_pin; char *msisdn; int device_timeout_secs; int poll_time_secs; int post_send_delay_usecs; int use_pdu_p; int max_retries; if (argc < 10 || argc > 11) { Tcl_AppendResult(interp, "Usage: ", argv[0], " link_id device device_timeout_secs service_centre sim_pin poll_time_secs post_send_delay_usecs use_pdu_p max_retries [msisdn]", NULL); return TCL_ERROR; } link_id = argv[1]; link_ptr = generic_gsm_get_link_ptr(link_id); if (link_ptr == NULL) { Tcl_AppendResult(interp, "Unknown link_id: ", link_id, NULL); return TCL_ERROR; } device = argv[2]; if (Tcl_GetInt(interp, argv[3], &device_timeout_secs) != TCL_OK) { return TCL_ERROR; } service_centre = argv[4]; sim_pin = argv[5]; if (Tcl_GetInt(interp, argv[6], &poll_time_secs) != TCL_OK) { return TCL_ERROR; } if (Tcl_GetInt(interp, argv[7], &post_send_delay_usecs) != TCL_OK) { return TCL_ERROR; } if (Tcl_GetInt(interp, argv[8], &use_pdu_p) != TCL_OK) { return TCL_ERROR; } if (Tcl_GetInt(interp, argv[9], &max_retries) != TCL_OK) { return TCL_ERROR; } if (argc == 11) { msisdn = argv[10]; } else { msisdn = ""; } generic_gsm_update(link_ptr, device, device_timeout_secs, service_centre, sim_pin, poll_time_secs, post_send_delay_usecs, use_pdu_p, max_retries, msisdn); Tcl_AppendResult(interp, "1", NULL); return TCL_OK; } /* *---------------------------------------------------------------------- * * generic_gsm_encode_pdu -- * * Function to encode the pdu string for transmission of an SMS whilst * the GSM modem is in PDU mode. * * Results: * *---------------------------------------------------------------------- */ static void generic_gsm_encode_pdu(unsigned char *pdu_string_ptr, int *pdu_length_ptr, struct generic_gsm_link *link_ptr) { char tmp_buf[256]; char *msisdn_ptr; int international_p; int i; int pdu_length; pdu_string_ptr[0] = '\0'; pdu_length = 0; /* * Calculate the service centre number length in bytes (excluding the * "+", which gets removed, and including the number format byte). * Append the byteswapped service centre number, remembering to skip * the preceeding "+". */ sprintf(tmp_buf, "%.2X91", ((strlen(link_ptr->service_centre) - 1) / 2) + 1); strcat(pdu_string_ptr, tmp_buf); generic_gsm_byteswap(&link_ptr->service_centre[1], tmp_buf); strcat(pdu_string_ptr, tmp_buf); /* * TP flags */ sprintf(tmp_buf, "%.2X", link_ptr->payload_ptr->tp_mask); strcat(pdu_string_ptr, tmp_buf); pdu_length++; /* * Message reference */ strcat(pdu_string_ptr, "00"); pdu_length++; /* * Add the MSISDN. */ if (link_ptr->payload_ptr->dest_address[0] == '+') { international_p = 1; msisdn_ptr = &link_ptr->payload_ptr->dest_address[1]; } else if (link_ptr->payload_ptr->dest_address[0] == '0') { international_p = 0; msisdn_ptr = &link_ptr->payload_ptr->dest_address[1]; } else { international_p = 1; msisdn_ptr = &link_ptr->payload_ptr->dest_address[0]; } sprintf(tmp_buf, "%.2X", strlen(msisdn_ptr)); strcat(pdu_string_ptr, tmp_buf); pdu_length++; if (international_p) { strcat(pdu_string_ptr, "91"); } else { strcat(pdu_string_ptr, "81"); } pdu_length++; generic_gsm_byteswap(msisdn_ptr, tmp_buf); strcat(pdu_string_ptr, tmp_buf); pdu_length += strlen(tmp_buf) / 2; /* * Protocol id. */ sprintf(tmp_buf, "%.2X", link_ptr->payload_ptr->protocol_id); strcat(pdu_string_ptr, tmp_buf); pdu_length++; /* * Data coding scheme */ sprintf(tmp_buf, "%.2X", link_ptr->payload_ptr->data_coding_scheme); strcat(pdu_string_ptr, tmp_buf); pdu_length++; /* * Validity period. */ if (link_ptr->payload_ptr->tp_mask & (TP_VAL_PER_REL | TP_VAL_PER_ABS)) { sprintf(tmp_buf, "%.2X", (int)link_ptr->payload_ptr->validity_period); strcat(pdu_string_ptr, tmp_buf); pdu_length++; } sprintf(tmp_buf, "%.2X", link_ptr->payload_ptr->unpacked_data_len); strcat(pdu_string_ptr, tmp_buf); pdu_length++; for (i = 0; i < link_ptr->payload_ptr->user_data_len; i++) { sprintf(tmp_buf, "%.2X", link_ptr->payload_ptr->user_data[i]); strcat(pdu_string_ptr, tmp_buf); } pdu_length += link_ptr->payload_ptr->user_data_len; *pdu_length_ptr = pdu_length; } static void generic_gsm_decode_pdu(unsigned char *pdu_string_ptr, char *msisdn_ptr, unsigned char *msg_data_ptr, int *msg_data_size_ptr, int *unpacked_size_ptr, int *data_coding_scheme_ptr, struct smsc_tm *smsc_timestamp_ptr) { int smsc_len; int msisdn_len; int type_of_address; int msisdn_char_num; int data_coding_scheme; int i; unsigned char *cur_ptr; unsigned char tmp_buf[3]; unsigned char msisdn[128]; unsigned char unswapped_timestamp[16]; unsigned char timestamp[16]; unsigned int gmt_byte; tmp_buf[2] = '\0'; cur_ptr = pdu_string_ptr; // 0791449737019037040C914477578790050000108092110462000441E19008 memcpy(tmp_buf, cur_ptr, 2); smsc_len = strtol(tmp_buf,NULL,16); // Skip the length octet, the smsc address, and the inital 04 DELIVER byte cur_ptr += smsc_len * 2 + 4; memcpy(tmp_buf, cur_ptr, 2); msisdn_len = strtol(tmp_buf,NULL,16); // Skip the msisdn length octet cur_ptr += 2; memcpy(tmp_buf, cur_ptr, 2); type_of_address = strtol(tmp_buf,NULL,16); // Skip the type of address octet cur_ptr += 2; if (msisdn_len % 2 == 1) { msisdn_char_num = msisdn_len + 1; } else { msisdn_char_num = msisdn_len; } memcpy(msisdn, cur_ptr, msisdn_char_num); msisdn[msisdn_char_num] = '\0'; if (type_of_address == 0x91) { msisdn_ptr[0] = '+'; generic_gsm_byteswap(msisdn, msisdn_ptr + 1); msisdn_ptr[msisdn_len+1] = '\0'; } else { generic_gsm_byteswap(msisdn, msisdn_ptr); msisdn_ptr[msisdn_len] = '\0'; } // Skip the msisdn cur_ptr += msisdn_char_num; // Skip the protocol id cur_ptr += 2; memcpy(tmp_buf, cur_ptr, 2); data_coding_scheme = strtol(tmp_buf,NULL,16); // Skip the data coding scheme cur_ptr += 2; /* * Extract the timestamp. */ memcpy(unswapped_timestamp, cur_ptr, 14); unswapped_timestamp[14] = '\0'; generic_gsm_byteswap(unswapped_timestamp, timestamp); // year memcpy(tmp_buf, timestamp, 2); smsc_timestamp_ptr->tm_year = strtol(tmp_buf,NULL,10) + 2000; // month memcpy(tmp_buf, timestamp + 2, 2); smsc_timestamp_ptr->tm_mon = strtol(tmp_buf,NULL,10) - 1; // day memcpy(tmp_buf, timestamp + 4, 2); smsc_timestamp_ptr->tm_mday = strtol(tmp_buf,NULL,10); // hour memcpy(tmp_buf, timestamp + 6, 2); smsc_timestamp_ptr->tm_hour = strtol(tmp_buf,NULL,10); // minute memcpy(tmp_buf, timestamp + 8, 2); smsc_timestamp_ptr->tm_min = strtol(tmp_buf,NULL,10); // sec memcpy(tmp_buf, timestamp + 10, 2); smsc_timestamp_ptr->tm_sec = strtol(tmp_buf,NULL,10); // timezone (difference from GMT */ memcpy(tmp_buf, timestamp + 12, 2); gmt_byte = strtol(tmp_buf, NULL, 10); if (gmt_byte & 128) { smsc_timestamp_ptr->tm_tz = -(gmt_byte & 127); } else { smsc_timestamp_ptr->tm_tz = gmt_byte & 127; } cur_ptr += 14; memcpy(tmp_buf, cur_ptr, 2); *unpacked_size_ptr = strtol(tmp_buf,NULL,16); cur_ptr += 2; *msg_data_size_ptr = strlen(cur_ptr) / 2; for (i = 0; i < (*msg_data_size_ptr); i++) { memcpy(tmp_buf, cur_ptr, 2); msg_data_ptr[i] = (unsigned char) strtol(tmp_buf,NULL,16); cur_ptr += 2; } } static void generic_gsm_byteswap(char *in_string_ptr, char *out_string_ptr) { int len; int i; len = strlen(in_string_ptr); for (i = 0; i < len; i += 2) { out_string_ptr[i + 1] = in_string_ptr[i]; if ((i + 1) < len) { out_string_ptr[i] = in_string_ptr[i + 1]; } } out_string_ptr[i] = '\0'; out_string_ptr[i+1] = '\0'; if ((len % 2) == 1) { out_string_ptr[i] = 'F'; } }