/* RF Ctl handling socket */ /* (C) 2010 by Harald Welte * (C) 2010 by Holger Hans Peter Freyther * (C) 2010 by On-Waves * 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 #define RF_CMD_QUERY '?' #define RF_CMD_OFF '0' #define RF_CMD_ON '1' static int lock_each_trx(struct gsm_network *net, int lock) { struct gsm_bts *bts; llist_for_each_entry(bts, &net->bts_list, list) { struct gsm_bts_trx *trx; llist_for_each_entry(trx, &bts->trx_list, list) { gsm_trx_lock_rf(trx, lock); } } return 0; } /* * Send a '1' when one TRX is online, otherwise send 0 */ static void handle_query(struct bsc_msc_rf_conn *conn) { struct msgb *msg; struct gsm_bts *bts; char send = '0'; llist_for_each_entry(bts, &conn->gsm_network->bts_list, list) { struct gsm_bts_trx *trx; llist_for_each_entry(trx, &bts->trx_list, list) { if (trx->nm_state.availability == NM_AVSTATE_OK && trx->nm_state.operational != NM_STATE_LOCKED) { send = '1'; break; } } } msg = msgb_alloc(10, "RF Query"); if (!msg) { LOGP(DINP, LOGL_ERROR, "Failed to allocate response msg.\n"); return; } msg->l2h = msgb_put(msg, 1); msg->l2h[0] = send; if (write_queue_enqueue(&conn->queue, msg) != 0) { LOGP(DINP, LOGL_ERROR, "Failed to enqueue the answer.\n"); msgb_free(msg); return; } return; } static int rf_read_cmd(struct bsc_fd *fd) { struct bsc_msc_rf_conn *conn = fd->data; char buf[1]; int rc; rc = read(fd->fd, buf, sizeof(buf)); if (rc != sizeof(buf)) { LOGP(DINP, LOGL_ERROR, "Short read %d/%s\n", errno, strerror(errno)); bsc_unregister_fd(fd); close(fd->fd); write_queue_clear(&conn->queue); talloc_free(conn); return -1; } switch (buf[0]) { case RF_CMD_QUERY: handle_query(conn); break; case RF_CMD_OFF: lock_each_trx(conn->gsm_network, 1); break; case RF_CMD_ON: lock_each_trx(conn->gsm_network, 0); break; default: LOGP(DINP, LOGL_ERROR, "Unknown command %d\n", buf[0]); break; } return 0; } static int rf_write_cmd(struct bsc_fd *fd, struct msgb *msg) { int rc; rc = write(fd->fd, msg->data, msg->len); if (rc != msg->len) { LOGP(DINP, LOGL_ERROR, "Short write %d/%s\n", errno, strerror(errno)); return -1; } return 0; } static int rf_ctl_accept(struct bsc_fd *bfd, unsigned int what) { struct bsc_msc_rf_conn *conn; struct bsc_msc_rf *rf = bfd->data; struct sockaddr_un addr; socklen_t len = sizeof(addr); int fd; fd = accept(bfd->fd, (struct sockaddr *) &addr, &len); if (fd < 0) { LOGP(DINP, LOGL_ERROR, "Failed to accept. errno: %d/%s\n", errno, strerror(errno)); return -1; } conn = talloc_zero(rf, struct bsc_msc_rf_conn); if (!conn) { LOGP(DINP, LOGL_ERROR, "Failed to allocate mem.\n"); close(fd); return -1; } write_queue_init(&conn->queue, 10); conn->queue.bfd.data = conn; conn->queue.bfd.fd = fd; conn->queue.bfd.when = BSC_FD_READ | BSC_FD_WRITE; conn->queue.read_cb = rf_read_cmd; conn->queue.write_cb = rf_write_cmd; conn->gsm_network = rf->gsm_network; if (bsc_register_fd(&conn->queue.bfd) != 0) { close(fd); talloc_free(conn); return -1; } return 0; } struct bsc_msc_rf *bsc_msc_rf_create(const char *path, struct gsm_network *net) { unsigned int namelen; struct sockaddr_un local; struct bsc_fd *bfd; struct bsc_msc_rf *rf; int rc; rf = talloc_zero(NULL, struct bsc_msc_rf); if (!rf) { LOGP(DINP, LOGL_ERROR, "Failed to create bsc_msc_rf.\n"); return NULL; } bfd = &rf->listen; bfd->fd = socket(AF_UNIX, SOCK_STREAM, 0); if (bfd->fd < 0) { LOGP(DINP, LOGL_ERROR, "Can not create socket. %d/%s\n", errno, strerror(errno)); return NULL; } local.sun_family = AF_UNIX; strncpy(local.sun_path, path, sizeof(local.sun_path)); local.sun_path[sizeof(local.sun_path) - 1] = '\0'; unlink(local.sun_path); /* we use the same magic that X11 uses in Xtranssock.c for * calculating the proper length of the sockaddr */ #if defined(BSD44SOCKETS) || defined(__UNIXWARE__) local.sun_len = strlen(local.sun_path); #endif #if defined(BSD44SOCKETS) || defined(SUN_LEN) namelen = SUN_LEN(&local); #else namelen = strlen(local.sun_path) + offsetof(struct sockaddr_un, sun_path); #endif rc = bind(bfd->fd, (struct sockaddr *) &local, namelen); if (rc != 0) { LOGP(DINP, LOGL_ERROR, "Failed to bind '%s' errno: %d/%s\n", local.sun_path, errno, strerror(errno)); close(bfd->fd); talloc_free(rf); return NULL; } if (listen(bfd->fd, 0) != 0) { LOGP(DINP, LOGL_ERROR, "Failed to listen: %d/%s\n", errno, strerror(errno)); close(bfd->fd); talloc_free(rf); return NULL; } bfd->when = BSC_FD_READ; bfd->cb = rf_ctl_accept; bfd->data = rf; if (bsc_register_fd(bfd) != 0) { LOGP(DINP, LOGL_ERROR, "Failed to register bfd.\n"); close(bfd->fd); talloc_free(rf); return NULL; } rf->gsm_network = net; return rf; }