/* Interface handler for Sysmocom L1 (real hardware) */ /* (C) 2011 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 Affero General Public License as published by * the Free Software Foundation; either version 3 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 Affero General Public License * along with this program. If not, see . * */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "gprs_debug.h" #include "femtobts.h" #include "sysmo_l1_if.h" #ifdef HW_SYSMOBTS_V1 #define DEV_SYS_DSP2ARM_NAME "/dev/msgq/femtobts_dsp2arm" #define DEV_SYS_ARM2DSP_NAME "/dev/msgq/femtobts_arm2dsp" #define DEV_L1_DSP2ARM_NAME "/dev/msgq/gsml1_dsp2arm" #define DEV_L1_ARM2DSP_NAME "/dev/msgq/gsml1_arm2dsp" #else #define DEV_SYS_DSP2ARM_NAME "/dev/msgq/superfemto_dsp2arm" #define DEV_SYS_ARM2DSP_NAME "/dev/msgq/superfemto_arm2dsp" #define DEV_L1_DSP2ARM_NAME "/dev/msgq/gsml1_sig_dsp2arm" #define DEV_L1_ARM2DSP_NAME "/dev/msgq/gsml1_sig_arm2dsp" #define DEV_TCH_DSP2ARM_NAME "/dev/msgq/gsml1_tch_dsp2arm" #define DEV_TCH_ARM2DSP_NAME "/dev/msgq/gsml1_tch_arm2dsp" #define DEV_PDTCH_DSP2ARM_NAME "/dev/msgq/gsml1_pdtch_dsp2arm" #define DEV_PDTCH_ARM2DSP_NAME "/dev/msgq/gsml1_pdtch_arm2dsp" #endif static const char *rd_devnames[] = { [MQ_SYS_READ] = DEV_SYS_DSP2ARM_NAME, [MQ_L1_READ] = DEV_L1_DSP2ARM_NAME, #ifndef HW_SYSMOBTS_V1 [MQ_TCH_READ] = DEV_TCH_DSP2ARM_NAME, [MQ_PDTCH_READ] = DEV_PDTCH_DSP2ARM_NAME, #endif }; static const char *wr_devnames[] = { [MQ_SYS_WRITE] = DEV_SYS_ARM2DSP_NAME, [MQ_L1_WRITE] = DEV_L1_ARM2DSP_NAME, #ifndef HW_SYSMOBTS_V1 [MQ_TCH_WRITE] = DEV_TCH_ARM2DSP_NAME, [MQ_PDTCH_WRITE]= DEV_PDTCH_ARM2DSP_NAME, #endif }; /* callback when there's something to read from the l1 msg_queue */ static int l1if_fd_cb(struct osmo_fd *ofd, unsigned int what) { //struct msgb *msg = l1p_msgb_alloc(); struct msgb *msg = msgb_alloc_headroom(sizeof(SuperFemto_Prim_t) + 128, 128, "1l_fd"); struct femtol1_hdl *fl1h = ofd->data; int rc; msg->l1h = msg->data; rc = read(ofd->fd, msg->l1h, msgb_tailroom(msg)); if (rc < 0) { if (rc != -1) LOGP(DL1IF, LOGL_ERROR, "error reading from L1 msg_queue: %s\n", strerror(errno)); msgb_free(msg); return rc; } msgb_put(msg, rc); switch (ofd->priv_nr) { case MQ_SYS_WRITE: if (rc != sizeof(SuperFemto_Prim_t)) LOGP(DL1IF, LOGL_NOTICE, "%u != " "sizeof(SuperFemto_Prim_t)\n", rc); return l1if_handle_sysprim(fl1h, msg); case MQ_L1_WRITE: #ifndef HW_SYSMOBTS_V1 case MQ_TCH_WRITE: case MQ_PDTCH_WRITE: #endif if (rc != sizeof(GsmL1_Prim_t)) LOGP(DL1IF, LOGL_NOTICE, "%u != " "sizeof(GsmL1_Prim_t)\n", rc); return l1if_handle_l1prim(ofd->priv_nr, fl1h, msg); default: /* The compiler can't know that priv_nr is an enum. Assist. */ LOGP(DL1IF, LOGL_FATAL, "writing on a wrong queue: %d\n", ofd->priv_nr); exit(0); break; } }; /* callback when we can write to one of the l1 msg_queue devices */ static int l1fd_write_cb(struct osmo_fd *ofd, struct msgb *msg) { int rc; rc = write(ofd->fd, msg->l1h, msgb_l1len(msg)); if (rc < 0) { LOGP(DL1IF, LOGL_ERROR, "error writing to L1 msg_queue: %s\n", strerror(errno)); return rc; } else if (rc < msg->len) { LOGP(DL1IF, LOGL_ERROR, "short write to L1 msg_queue: " "%u < %u\n", rc, msg->len); return -EIO; } return 0; } int l1if_transport_open(int q, struct femtol1_hdl *hdl) { int rc; /* Step 1: Open all msg_queue file descriptors */ struct osmo_fd *read_ofd = &hdl->read_ofd[q]; struct osmo_wqueue *wq = &hdl->write_q[q]; struct osmo_fd *write_ofd = &hdl->write_q[q].bfd; rc = open(rd_devnames[q], O_RDONLY); if (rc < 0) { LOGP(DL1IF, LOGL_FATAL, "unable to open msg_queue: %s\n", strerror(errno)); return rc; } read_ofd->fd = rc; read_ofd->priv_nr = q; read_ofd->data = hdl; read_ofd->cb = l1if_fd_cb; read_ofd->when = BSC_FD_READ; rc = osmo_fd_register(read_ofd); if (rc < 0) { close(read_ofd->fd); read_ofd->fd = -1; return rc; } rc = open(wr_devnames[q], O_WRONLY); if (rc < 0) { LOGP(DL1IF, LOGL_FATAL, "unable to open msg_queue: %s\n", strerror(errno)); goto out_read; } osmo_wqueue_init(wq, 10); wq->write_cb = l1fd_write_cb; write_ofd->fd = rc; write_ofd->priv_nr = q; write_ofd->data = hdl; write_ofd->when = BSC_FD_WRITE; rc = osmo_fd_register(write_ofd); if (rc < 0) { close(write_ofd->fd); write_ofd->fd = -1; goto out_read; } return 0; out_read: close(hdl->read_ofd[q].fd); osmo_fd_unregister(&hdl->read_ofd[q]); return rc; } int l1if_transport_close(int q, struct femtol1_hdl *hdl) { struct osmo_fd *read_ofd = &hdl->read_ofd[q]; struct osmo_fd *write_ofd = &hdl->write_q[q].bfd; osmo_fd_unregister(read_ofd); close(read_ofd->fd); read_ofd->fd = -1; osmo_fd_unregister(write_ofd); close(write_ofd->fd); write_ofd->fd = -1; return 0; }