/* libgsmd tool * * (C) 2006-2007 by OpenMoko, Inc. * Written by Harald Welte * All Rights Reserved * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. * */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifndef __GSMD__ #define __GSMD__ #include #undef __GSMD__ #endif #define STDIN_BUF_SIZE 1024 static int pending_responses = 0; /* BTS RF_LOCK */ #define MIN_NO_NET_SECS 60 #define OUR_MCC 901 #define OUR_MNC 99 static time_t last_network_seen; unsigned int rf_lock_active = 0; static void bts_rf_lock(int on) { int fd, rc; static const uint8_t rf_lock[] = { 0x00, 0x23, 0xEE, 0x00, 0x53, 0x45, 0x54, 0x20, 0x31, 0x20, 0x6E, 0x65, 0x74, 0x2E, 0x72, 0x66, 0x5F, 0x6C, 0x6F, 0x63, 0x6B, 0x20, 0x67, 0x73, 0x6D, 0x2C, 0x6C, 0x6F, 0x63, 0x6B, 0x2C, 0x6E, 0x2F, 0x61, 0x2C, 0x6E, 0x2F, 0x61, }; static const uint8_t rf_unlock[] = { 0x00, 0x25, 0xEE, 0x00, 0x53, 0x45, 0x54, 0x20, 0x31, 0x20, 0x6E, 0x65, 0x74, 0x2E, 0x72, 0x66, 0x5F, 0x6C, 0x6F, 0x63, 0x6B, 0x20, 0x67, 0x73, 0x6D, 0x2C, 0x75, 0x6E, 0x6C, 0x6F, 0x63, 0x6B, 0x2C, 0x6E, 0x2F, 0x61, 0x2C, 0x6E, 0x2F, 0x61, }; /* only print message on status change */ if (rf_lock_active != on) syslog(LOG_NOTICE, "RF_LOCK: %sabling lock\n", on ? "En" : "Dis"); fd = socket(AF_INET, SOCK_STREAM, 0); if (fd == -1) { syslog(LOG_ERR, "RF_LOCK: socket creation failed: %d\n", errno); return; } struct sockaddr_in addr; memset(&addr, 0, sizeof(addr)); addr.sin_family = AF_INET; addr.sin_addr.s_addr = htonl(INADDR_LOOPBACK); addr.sin_port = htons(4249); rc = connect(fd, (struct sockaddr *) &addr, sizeof(addr)); if (rc != 0) { syslog(LOG_ERR, "RF_Lock: socket connect failed: %d\n", errno); close(fd); return; } if (on == 1) { rc = write(fd, rf_lock, sizeof(rf_lock)); if (rc != sizeof(rf_lock)) goto error; } else { rc = write(fd, rf_unlock, sizeof(rf_unlock)); if (rc != sizeof(rf_unlock)) goto error; } close(fd); rf_lock_active = on; return; error: close(fd); syslog(LOG_ERR, "RF_Lock: failed to send the message: %d\n", errno); return; } static void we_have_seen_network(unsigned int mcc, unsigned int mnc) { /* ignore our own network */ if (mcc == OUR_MCC && mnc == OUR_MNC) return; /* update timestamp at which we have last seen the network */ last_network_seen = time(NULL); bts_rf_lock(1); } static int network_long_ago() { time_t now = time(NULL); if (now - last_network_seen > MIN_NO_NET_SECS) return 1; return 0; } static void rf_lock_timer_check() { if (network_long_ago()) bts_rf_lock(0); else bts_rf_lock(1); } /* OPERATOR QUEUE */ static LLIST_HEAD(opq); struct opq_entry { struct llist_head list; char opname_num[32]; }; static void *opq_ctx = NULL; static void opq_enqueue(const char *opname_num) { struct opq_entry *e = talloc_zero(opq_ctx, struct opq_entry); unsigned int mcc = 0, mnc = 0; syslog(LOG_DEBUG, "OPQ: Adding operator `%s'\n", opname_num); strncpy(e->opname_num, opname_num, sizeof(e->opname_num)); llist_add_tail(&e->list, &opq); sscanf(opname_num, "%03u%02u", &mcc, &mnc); we_have_seen_network(mcc, mnc); } static void opq_next_operator(struct lgsm_handle *lgsmh) { struct opq_entry *e; if (llist_empty(&opq)) { syslog(LOG_DEBUG, "OPQ: No more operators in queue, re-scanning\n"); lgsm_opers_get(lgsmh); return; } e = llist_entry(opq.next, struct opq_entry, list); syslog(LOG_DEBUG, "OPQ: Selecting Operator %s\n", e->opname_num); llist_del(&e->list); lgsm_netreg_register(lgsmh, e->opname_num); talloc_free(e); } /* this is the handler for receiving passthrough responses */ static int pt_msghandler(struct lgsm_handle *lh, struct gsmd_msg_hdr *gmh) { char *payload = (char *)gmh + sizeof(*gmh); printf("RSTR=`%s'\n", payload); return 0; } /* this is the handler for responses to network/operator commands */ static int net_msghandler(struct lgsm_handle *lh, struct gsmd_msg_hdr *gmh) { const struct gsmd_signal_quality *sq = (struct gsmd_signal_quality *) ((void *) gmh + sizeof(*gmh)); const char *oper = (char *) gmh + sizeof(*gmh); const struct gsmd_msg_oper *opers = (struct gsmd_msg_oper *) ((void *) gmh + sizeof(*gmh)); const struct gsmd_own_number *num = (struct gsmd_own_number *) ((void *) gmh + sizeof(*gmh)); const struct gsmd_voicemail *vmail = (struct gsmd_voicemail *) ((void *) gmh + sizeof(*gmh)); enum gsmd_netreg_state state = *(enum gsmd_netreg_state *) gmh->data; int result = *(int *) gmh->data; static const char *oper_stat[] = { [GSMD_OPER_UNKNOWN] = "of unknown status", [GSMD_OPER_AVAILABLE] = "available", [GSMD_OPER_CURRENT] = "our current operator", [GSMD_OPER_FORBIDDEN] = "forbidden", }; static const char *srvname[] = { [GSMD_SERVICE_ASYNC_MODEM] = "asynchronous modem", [GSMD_SERVICE_SYNC_MODEM] = "synchronous modem", [GSMD_SERVICE_PAD_ACCESS] = "PAD Access (asynchronous)", [GSMD_SERVICE_PACKET_ACCESS] = "Packet Access (synchronous)", [GSMD_SERVICE_VOICE] = "voice", [GSMD_SERVICE_FAX] = "fax", }; switch (gmh->msg_subtype) { case GSMD_NETWORK_SIGQ_GET: if (sq->rssi == 99) printf("Signal undetectable\n"); else printf("Signal quality %i dBm\n", -113 + sq->rssi * 2); if (sq->ber == 99) printf("Error rate undetectable\n"); else printf("Bit error rate %i\n", sq->ber); pending_responses --; break; case GSMD_NETWORK_OPER_GET: case GSMD_NETWORK_OPER_N_GET: if (oper[0]) printf("Our current operator is %s\n", oper); else printf("No current operator\n"); pending_responses --; break; case GSMD_NETWORK_OPER_LIST: for (; !opers->is_last; opers ++) { syslog(LOG_NOTICE, "OPER: %8.*s %16.*s, %.*s for short, is %s\n", sizeof(opers->opname_num), opers->opname_num, sizeof(opers->opname_longalpha), opers->opname_longalpha, sizeof(opers->opname_shortalpha), opers->opname_shortalpha, oper_stat[opers->stat]); opq_enqueue(opers->opname_num); } opq_next_operator(lh); pending_responses --; break; case GSMD_NETWORK_GET_NUMBER: printf("\t%s\t%10s%s%s%s\n", num->addr.number, num->name, (num->service == GSMD_SERVICE_UNKNOWN) ? "" : " related to ", (num->service == GSMD_SERVICE_UNKNOWN) ? "" : srvname[num->service], (num->service == GSMD_SERVICE_UNKNOWN) ? "" : " services"); pending_responses --; break; case GSMD_NETWORK_VMAIL_SET: if (result) printf("Set voicemail error %i\n", result); else printf("Set voicemail OK \n"); pending_responses --; break; case GSMD_NETWORK_VMAIL_GET: if(vmail->addr.number) printf ("voicemail number is %s \n",vmail->addr.number); pending_responses --; break; case GSMD_NETWORK_QUERY_REG: switch (state) { case GSMD_NETREG_UNREG: printf("not searching for network \n"); break; case GSMD_NETREG_REG_HOME: printf("registered (home network) \n"); break; case GSMD_NETREG_UNREG_BUSY: printf("searching for network \n"); break; case GSMD_NETREG_DENIED: printf("registration denied \n"); break; case GSMD_NETREG_REG_ROAMING: printf("registered (roaming) \n"); break; default: break; } pending_responses --; break; default: return -EINVAL; } return 0; } static int phone_msghandler(struct lgsm_handle *lh, struct gsmd_msg_hdr *gmh) { char *payload = (char *)gmh + sizeof(*gmh); int *intresult = (void *)gmh + sizeof(*gmh); const struct gsmd_battery_charge *bc = (struct gsmd_battery_charge *) ((void *) gmh + sizeof(*gmh)); switch (gmh->msg_subtype) { case GSMD_PHONE_GET_IMSI: printf("imsi <%s>\n", payload); break; case GSMD_PHONE_GET_MANUF: printf("manufacturer: %s\n", payload); break; case GSMD_PHONE_GET_MODEL: printf("model: %s\n", payload); break; case GSMD_PHONE_GET_REVISION: printf("revision: %s\n", payload); break; case GSMD_PHONE_GET_SERIAL: printf("serial: %s\n", payload); break; case GSMD_PHONE_POWERUP: if (*intresult) printf("Modem power-up failed: %i\n", *intresult); else printf("Modem powered-up okay\n"); break; case GSMD_PHONE_POWERDOWN: if (*intresult) printf("Modem power-down failed: %i\n", *intresult); else printf("Modem down\n"); break; case GSMD_PHONE_POWER_STATUS: printf("Antenna Status: %s\n", payload); break; case GSMD_PHONE_GET_BATTERY: printf(": %d : %d \n", bc->bcs, bc->bcl); break; case GSMD_PHONE_VIB_ENABLE: if(*intresult) printf("Vibrator enable failed: %i\n", *intresult); else printf("Vibrator enabled\n"); break; case GSMD_PHONE_VIB_DISABLE: if(*intresult) printf("Vibrator disable failed: %i\n", *intresult); else printf("VIbrator disabled\n"); break; default: return -EINVAL; } pending_responses --; return 0; } static const struct msghandler_s { int type; lgsm_msg_handler *fn; } msghandlers[] = { { GSMD_MSG_PASSTHROUGH, pt_msghandler }, { GSMD_MSG_NETWORK, net_msghandler }, { GSMD_MSG_PHONE, phone_msghandler }, { 0, 0 } }; int celllog_main(struct lgsm_handle *lgsmh) { int rc; char buf[STDIN_BUF_SIZE+1]; fd_set readset; char *ptr, *fcomma, *lcomma; int gsm_fd = lgsm_fd(lgsmh); const struct msghandler_s *hndl; for (hndl = msghandlers; hndl->fn; hndl ++) lgsm_register_handler(lgsmh, hndl->type, hndl->fn); fcntl(gsm_fd, F_SETFD, O_NONBLOCK); FD_ZERO(&readset); struct timeval tv = { .tv_sec = 30 }; while (1) { fd_set readset; FD_SET(gsm_fd, &readset); rc = select(gsm_fd+1, &readset, NULL, NULL, &tv); if (rc < 0) break; /* Timeout: Iterate to next operator */ if (rc == 0) { opq_next_operator(lgsmh); tv.tv_sec = 30; rf_lock_timer_check(); } if (FD_ISSET(gsm_fd, &readset)) { /* we've received something on the gsmd socket, pass it * on to the library */ rc = read(gsm_fd, buf, sizeof(buf)); if (rc <= 0) { syslog(LOG_ERR, "ERROR reading from gsm_fd\n"); break; } rc = lgsm_handle_packet(lgsmh, buf, rc); if (rc < 0) syslog(LOG_ERR, "ERROR processing packet: %d(%s)\n", rc, strerror(-rc)); } fflush(stdout); } return 0; } static int cinfo_handler(struct lgsm_handle *lh, int evt, struct gsmd_evt_auxdata *aux) { syslog(LOG_NOTICE, "EVENT: Cell Info: %03u-%03u-%04x-%04x @ %04u (%02u)\n", aux->u.cell_info.mcc, aux->u.cell_info.mnc, aux->u.cell_info.lac, aux->u.cell_info.ci, aux->u.cell_info.arfcn, aux->u.cell_info.rxlev); we_have_seen_network(aux->u.cell_info.mcc, aux->u.cell_info.mnc); return 0; } static int error_handler(struct lgsm_handle *lh, int evt, struct gsmd_evt_auxdata *aux) { int err = aux->u.cme_err.number; switch (err) { case 546: case 547: /* those 'errors' are used by wavecom to indicate if * emergency calls are available or not in case no SIM * is inserted. Not really an error! */ return 0; } syslog(LOG_ERR, "CME ERROR %u from modem\n", err); switch (err) { case 515: /* we have seen the modem get stuck here, so re-set it */ lgsm_phone_power(lh, 1); break; } return 0; } int main(int argc, char **argv) { static struct lgsm_handle *lgsmh; printf("cell_log - (C) 2012-2013 by Harald Welte .\n" "This program is Free Software and has ABSOLUTELY NO WARRANTY\n\n"); openlog("cell_log", 0, LOG_LOCAL0); lgsmh = lgsm_init(LGSMD_DEVICE_GSMD); if (!lgsmh) { fprintf(stderr, "Can't connect to gsmd\n"); exit(1); } lgsm_evt_handler_register(lgsmh, GSMD_EVT_CELLINFO, &cinfo_handler); lgsm_evt_handler_register(lgsmh, GSMD_EVT_IN_ERROR, &error_handler); we_have_seen_network(23, 42); celllog_main(lgsmh); exit(0); }