void lapd_rx_cb(struct osmo_dlsap_prim *dp, uint8_t tei, uint8_t sapi,
		void *rx_cbdata)
{
	struct msgb *msg = dp->oph.msg;

	switch (dp->oph.primitive) {
	case PRIM_DL_EST:
		DEBUGP(DLAPDTEST, "DL_EST: sapi(%d) tei(%d)\n", sapi, tei);
		break;
	case PRIM_DL_REL:
		DEBUGP(DLAPDTEST, "DL_REL: sapi(%d) tei(%d)\n", sapi, tei);
		break;
	case PRIM_DL_DATA:
	case PRIM_DL_UNIT_DATA:
		if (dp->oph.operation == PRIM_OP_INDICATION) {
			struct msgb *nmsg;
			char *ptr;
			int x;

			msg->l2h = msg->l3h;

			DEBUGP(DLAPDTEST, "RX: %s sapi=%d tei=%d\n",
				osmo_hexdump(msgb_l2(msg), msgb_l2len(msg)),
				sapi, tei);

			LOGP(DLAPDTEST, LOGL_DEBUG, "forwarding message\n");

                        nmsg = msgb_alloc(1024, "LAPD/test");
                        if (nmsg == NULL) {
                                LOGP(DLAPDTEST, LOGL_ERROR, "cannot alloc msg\n");
                                return;
                        }
                        ptr = (char *)msgb_put(nmsg, sizeof(int));

                        x = *((int *)msg->data);
                        memcpy(ptr, &x, sizeof(int));

			/* send the message back to client over LAPD */
			lapd_transmit(lapd, tei, sapi, msg);
			return;
		}
		break;
	case PRIM_MDL_ERROR:
		DEBUGP(DLMI, "MDL_EERROR: cause(%d)\n", dp->u.error_ind.cause);
		break;
	default:
		printf("ERROR: unknown prim\n");
		break;
	}
}
static int rcv_rll(struct osmocom_ms *ms, struct msgb *msg)
{
	struct abis_rsl_rll_hdr *rllh = msgb_l2(msg);
	int msg_type = rllh->c.msg_type;

	if (msg_type == RSL_MT_UNIT_DATA_IND) {
		unit_data_ind(ms, msg);
	} else
		LOGP(DRSL, LOGL_NOTICE, "RSLms message unhandled\n");

	msgb_free(msg);

	return 0;
}
Ejemplo n.º 3
0
/* FIXME: read from a B channel TS */
static int handle_tsX_read(struct osmo_fd *bfd)
{
	struct e1inp_line *line = bfd->data;
	unsigned int ts_nr = bfd->priv_nr;
	struct e1inp_ts *e1i_ts = &line->ts[ts_nr-1];
	struct msgb *msg = msgb_alloc(TSX_ALLOC_SIZE, "mISDN TSx");
	struct mISDNhead *hh;
	int ret;

	if (!msg)
		return -ENOMEM;

	hh = (struct mISDNhead *) msg->data;

	ret = recv(bfd->fd, msg->data, TSX_ALLOC_SIZE, 0);
	if (ret < 0) {
		fprintf(stderr, "recvfrom error  %s\n", strerror(errno));
		return ret;
	}

	msgb_put(msg, ret);

	if (hh->prim != PH_CONTROL_IND)
		DEBUGP(DLMIB, "<= BCHAN len = %d, prim(0x%x) id(0x%x): %s\n",
			ret, hh->prim, hh->id,
			get_value_string(prim_names, hh->prim));

	switch (hh->prim) {
	case PH_DATA_IND:
		msg->l2h = msg->data + MISDN_HEADER_LEN;
		DEBUGP(DLMIB, "BCHAN RX: %s\n",
			osmo_hexdump(msgb_l2(msg), ret - MISDN_HEADER_LEN));
		/* the number of bytes received indicates that data to send */
		handle_tsX_write(bfd, msgb_l2len(msg));
		return e1inp_rx_ts(e1i_ts, msg, 0, 0);
	case PH_ACTIVATE_IND:
	case PH_DATA_CNF:
		break;
	default:
		break;
	}
	/* FIXME: why do we free signalling msgs in the caller, and trau not? */
	msgb_free(msg);

	return ret;
}
Ejemplo n.º 4
0
int chan_conf(struct osmocom_ms *ms, struct msgb *msg)
{
	struct abis_rsl_cchan_hdr *ch = msgb_l2(msg);
	struct gsm48_req_ref *ref = (struct gsm48_req_ref *) (ch->data + 1);

	if (msgb_l2len(msg) < sizeof(*ch) + sizeof(*ref)) {
		LOGP(DRR, LOGL_ERROR, "CHAN_CNF too slort\n");
		return -EINVAL;
	}

	rach_ref.valid = 1;
	rach_ref.t1 = ref->t1;
	rach_ref.t2 = ref->t2;
	rach_ref.t3 = ref->t3_low | (ref->t3_high << 3);

	return 0;
}
Ejemplo n.º 5
0
static int rcv_cch(struct osmocom_ms *ms, struct msgb *msg)
{
	struct abis_rsl_cchan_hdr *ch = msgb_l2(msg);
	int msg_type = ch->c.msg_type;
	int rc;

	LOGP(DRSL, LOGL_INFO, "Received '%s' from layer1\n",
		rsl_msg_name(msg_type));

	if (state == SCAN_STATE_RACH && msg_type == RSL_MT_CHAN_CONF) {
	 	rc = chan_conf(ms, msg);
		msgb_free(msg);
		return rc;
	}

	LOGP(DRSL, LOGL_NOTICE, "RSLms message unhandled\n");
	msgb_free(msg);
	return 0;
}
static int rcv_rsl(struct msgb *msg, struct lapdm_entity *le, void *l3ctx)
{
	struct osmocom_ms *ms = l3ctx;
	struct abis_rsl_common_hdr *rslh = msgb_l2(msg);
	int rc = 0;

	switch (rslh->msg_discr & 0xfe) {
	case ABIS_RSL_MDISC_RLL:
		rc = rcv_rll(ms, msg);
		break;
	default:
		LOGP(DRSL, LOGL_NOTICE, "unknown RSLms msg_discr 0x%02x\n",
			rslh->msg_discr);
		msgb_free(msg);
		rc = -EINVAL;
		break;
	}

	return rc;
}
Ejemplo n.º 7
0
/* Take a B4 format message from L1 and create RSLms UNIT DATA IND */
static int send_rslms_rll_l3_ui(struct lapdm_msg_ctx *mctx, struct msgb *msg)
{
	uint8_t l3_len = msg->tail - (uint8_t *)msgb_l3(msg);
	struct abis_rsl_rll_hdr *rllh;

	/* Add the RSL + RLL header */
	msgb_tv16_push(msg, RSL_IE_L3_INFO, l3_len);
	msgb_push(msg, 2 + 2);
	rsl_rll_push_hdr(msg, RSL_MT_UNIT_DATA_IND, mctx->chan_nr,
		mctx->link_id, 1);
	rllh = (struct abis_rsl_rll_hdr *)msgb_l2(msg);

	rllh->data[0] = RSL_IE_TIMING_ADVANCE;
	rllh->data[1] = mctx->ta_ind;

	rllh->data[2] = RSL_IE_MS_POWER;
	rllh->data[3] = mctx->tx_power_ind;
	
	return rslms_sendmsg(msg, mctx->dl->entity);
}
Ejemplo n.º 8
0
/* input function that L2 calls when sending messages up to L3 */
static int layer3_from_layer2(struct msgb *msg, struct osmocom_ms *ms)
{
	struct abis_rsl_common_hdr *rslh = msgb_l2(msg);
	int rc = 0;

	switch (rslh->msg_discr & 0xfe) {
	case ABIS_RSL_MDISC_RLL:
		rc = rslms_rx_rll(msg, ms);
		break;
	default:
		/* FIXME: implement this */
		LOGP(DRSL, LOGL_NOTICE, "unknown RSLms msg_discr 0x%02x\n",
			rslh->msg_discr);
		msgb_free(msg);
		rc = -EINVAL;
		break;
	}

	return rc;
}
Ejemplo n.º 9
0
static int unit_data_ind(struct osmocom_ms *ms, struct msgb *msg)
{
	struct abis_rsl_rll_hdr *rllh = msgb_l2(msg);
	struct tlv_parsed tv;
	uint8_t ch_type, ch_subch, ch_ts;
	
	DEBUGP(DRSL, "RSLms UNIT DATA IND chan_nr=0x%02x link_id=0x%02x\n",
		rllh->chan_nr, rllh->link_id);

	rsl_tlv_parse(&tv, rllh->data, msgb_l2len(msg)-sizeof(*rllh));
	if (!TLVP_PRESENT(&tv, RSL_IE_L3_INFO)) {
		DEBUGP(DRSL, "UNIT_DATA_IND without L3 INFO ?!?\n");
		return -EIO;
	}
	msg->l3h = (uint8_t *) TLVP_VAL(&tv, RSL_IE_L3_INFO);

	if (state != SCAN_STATE_READ && state != SCAN_STATE_RACH) {
	 	return -EINVAL;
	}

	rsl_dec_chan_nr(rllh->chan_nr, &ch_type, &ch_subch, &ch_ts);
	switch (ch_type) {
	case RSL_CHAN_PCH_AGCH:
		return pch_agch(ms, msg);
	case RSL_CHAN_BCCH:
		return bcch(ms, msg);
#if 0
	case RSL_CHAN_Bm_ACCHs:
	case RSL_CHAN_Lm_ACCHs:
	case RSL_CHAN_SDCCH4_ACCH:
	case RSL_CHAN_SDCCH8_ACCH:
		return rx_acch(ms, msg);
#endif
	default:
		LOGP(DRSL, LOGL_NOTICE, "RSL with chan_nr 0x%02x unknown.\n",
			rllh->chan_nr);
		return -EINVAL;
	}
}
Ejemplo n.º 10
0
static int rslms_rx_rll(struct msgb *msg, struct osmocom_ms *ms)
{
	struct abis_rsl_rll_hdr *rllh = msgb_l2(msg);
	int rc = 0;

	switch (rllh->c.msg_type) {
	case RSL_MT_DATA_IND:
		DEBUGP(DRSL, "RSLms DATA IND\n");
		/* FIXME: implement this */
		break;
	case RSL_MT_UNIT_DATA_IND:
		rc = rslms_rx_udata_ind(msg, ms);
		break;
	case RSL_MT_EST_IND:
		DEBUGP(DRSL, "RSLms EST IND\n");
		/* FIXME: implement this */
		break;
	case RSL_MT_EST_CONF:
		DEBUGP(DRSL, "RSLms EST CONF\n");
		/* FIXME: implement this */
		break;
	case RSL_MT_REL_CONF:
		DEBUGP(DRSL, "RSLms REL CONF\n");
		/* FIXME: implement this */
		break;
	case RSL_MT_ERROR_IND:
		DEBUGP(DRSL, "RSLms ERR IND\n");
		/* FIXME: implement this */
		break;
	default:
		LOGP(DRSL, LOGL_NOTICE, "unknown RSLms message type "
			"0x%02x\n", rllh->c.msg_type);
		rc = -EINVAL;
		break;
	}
	msgb_free(msg);
	return rc;
}
static int unit_data_ind(struct osmocom_ms *ms, struct msgb *msg)
{
	struct abis_rsl_rll_hdr *rllh = msgb_l2(msg);
	struct tlv_parsed tv;
	uint8_t ch_type, ch_subch, ch_ts;

	DEBUGP(DRSL, "RSLms UNIT DATA IND chan_nr=0x%02x link_id=0x%02x\n",
		rllh->chan_nr, rllh->link_id);

	rsl_tlv_parse(&tv, rllh->data, msgb_l2len(msg)-sizeof(*rllh));
	if (!TLVP_PRESENT(&tv, RSL_IE_L3_INFO)) {
		DEBUGP(DRSL, "UNIT_DATA_IND without L3 INFO ?!?\n");
		return -EIO;
	}
	msg->l3h = (uint8_t *) TLVP_VAL(&tv, RSL_IE_L3_INFO);

	rsl_dec_chan_nr(rllh->chan_nr, &ch_type, &ch_subch, &ch_ts);
	switch (ch_type) {
	case RSL_CHAN_BCCH:
		return bcch(ms, msg);
	default:
		return 0;
	}
}
Ejemplo n.º 12
0
static int rslms_rx_udata_ind(struct msgb *msg, struct osmocom_ms *ms)
{
	struct abis_rsl_rll_hdr *rllh = msgb_l2(msg);
	struct tlv_parsed tv;
	int rc = 0;
	
	DEBUGP(DRSL, "RSLms UNIT DATA IND chan_nr=0x%02x link_id=0x%02x\n",
		rllh->chan_nr, rllh->link_id);

	rsl_tlv_parse(&tv, rllh->data, msgb_l2len(msg)-sizeof(*rllh));
	if (!TLVP_PRESENT(&tv, RSL_IE_L3_INFO)) {
		DEBUGP(DRSL, "UNIT_DATA_IND without L3 INFO ?!?\n");
		return -EIO;
	}
	msg->l3h = (uint8_t *) TLVP_VAL(&tv, RSL_IE_L3_INFO);

	if (rllh->chan_nr == RSL_CHAN_PCH_AGCH) {
		rc = gsm48_rx_ccch(msg, ms);
	} else if (rllh->chan_nr == RSL_CHAN_BCCH) {
		rc = gsm48_rx_bcch(msg, ms);
	}

	return rc;
}
Ejemplo n.º 13
0
int oap_client_handle(struct oap_client_state *state,
		      const struct msgb *msg_rx, struct msgb **msg_tx)
{
	uint8_t *data = msgb_l2(msg_rx);
	size_t data_len = msgb_l2len(msg_rx);
	struct osmo_oap_message oap_msg = {0};
	int rc = 0;

	*msg_tx = NULL;

	OSMO_ASSERT(data);

	rc = osmo_oap_decode(&oap_msg, data, data_len);
	if (rc < 0) {
		LOGP(DLOAP, LOGL_ERROR,
		     "Decoding OAP message failed with error '%s' (%d)\n",
		     get_value_string(gsm48_gmm_cause_names, -rc), -rc);
		return -10;
	}

	switch (state->state) {
	case OAP_UNINITIALIZED:
		LOGP(DLOAP, LOGL_ERROR,
		     "Received OAP message %d, but the OAP client is"
		     " not initialized\n", oap_msg.message_type);
		return -ENOTCONN;
	case OAP_DISABLED:
		LOGP(DLOAP, LOGL_ERROR,
		     "Received OAP message %d, but the OAP client is"
		     " disabled\n", oap_msg.message_type);
		return -ENOTCONN;
	default:
		break;
	}

	switch (oap_msg.message_type) {
	case OAP_MSGT_CHALLENGE_REQUEST:
		return handle_challenge(state, &oap_msg, msg_tx);

	case OAP_MSGT_REGISTER_RESULT:
		/* successfully registered */
		state->state = OAP_REGISTERED;
		break;

	case OAP_MSGT_REGISTER_ERROR:
		LOGP(DLOAP, LOGL_ERROR,
		     "OAP registration failed\n");
		state->state = OAP_INITIALIZED;
		if (state->registration_failures < 3) {
			state->registration_failures ++;
			return oap_client_register(state, msg_tx);
		}
		return -11;

	case OAP_MSGT_REGISTER_REQUEST:
	case OAP_MSGT_CHALLENGE_RESULT:
		LOGP(DLOAP, LOGL_ERROR,
		     "Received invalid OAP message type for OAP client side: %d\n",
		     (int)oap_msg.message_type);
		return -12;

	default:
		LOGP(DLOAP, LOGL_ERROR,
		     "Unknown OAP message type: %d\n",
		     (int)oap_msg.message_type);
		return -13;
	}

	return 0;
}
Ejemplo n.º 14
0
static int handle_ts1_read(struct osmo_fd *bfd)
{
	struct e1inp_line *line = bfd->data;
	struct misdn_line *mline = line->driver_data;
	unsigned int ts_nr = bfd->priv_nr;
	struct e1inp_ts *e1i_ts = &line->ts[ts_nr-1];
	struct e1inp_sign_link *link;
	struct msgb *msg = msgb_alloc(TS1_ALLOC_SIZE, "mISDN TS1");
	struct sockaddr_mISDN l2addr;
	struct mISDNhead *hh;
	socklen_t alen;
	int ret;

	if (!msg)
		return -ENOMEM;

	hh = (struct mISDNhead *) msg->data;

	alen = sizeof(l2addr);
	ret = recvfrom(bfd->fd, msg->data, 300, 0,
		       (struct sockaddr *) &l2addr, &alen);
	if (ret < 0) {
		fprintf(stderr, "recvfrom error  %s\n", strerror(errno));
		msgb_free(msg);
		return ret;
	}

	if (alen != sizeof(l2addr)) {
		fprintf(stderr, "%s error len\n", __func__);
		msgb_free(msg);
		return -EINVAL;
	}

	msgb_put(msg, ret);

	DEBUGP(DLMI, "alen =%d, dev(%d) channel(%d) sapi(%d) tei(%d)\n",
		alen, l2addr.dev, l2addr.channel, l2addr.sapi, l2addr.tei);

	DEBUGP(DLMI, "<= len = %d, prim(0x%x) id(0x%x): %s\n",
		ret, hh->prim, hh->id, get_value_string(prim_names, hh->prim));

	switch (hh->prim) {
	case DL_INFORMATION_IND:
		/* mISDN tells us which channel number is allocated for this
		 * tuple of (SAPI, TEI). */
		DEBUGP(DLMI, "DL_INFORMATION_IND: use channel(%d) sapi(%d) tei(%d) for now\n",
			l2addr.channel, l2addr.sapi, l2addr.tei);
		link = e1inp_lookup_sign_link(e1i_ts, l2addr.tei, l2addr.sapi);
		if (!link) {
			DEBUGPC(DLMI, "mISDN message for unknown sign_link\n");
			msgb_free(msg);
			return -EINVAL;
		}
		/* save the channel number in the driver private struct */
		link->driver.misdn.channel = l2addr.channel;
		msgb_free(msg);
		break;
	case DL_ESTABLISH_IND:
		DEBUGP(DLMI, "DL_ESTABLISH_IND: channel(%d) sapi(%d) tei(%d)\n",
			l2addr.channel, l2addr.sapi, l2addr.tei);
		/* For some strange reason, sometimes the DL_INFORMATION_IND tells
		 * us the wrong channel, and we only get the real channel number
		 * during the DL_ESTABLISH_IND */
		link = e1inp_lookup_sign_link(e1i_ts, l2addr.tei, l2addr.sapi);
		if (!link) {
			DEBUGPC(DLMI, "mISDN message for unknown sign_link\n");
			msgb_free(msg);
			return -EINVAL;
		}
		/* save the channel number in the driver private struct */
		link->driver.misdn.channel = l2addr.channel;
		ret = e1inp_event(e1i_ts, S_L_INP_TEI_UP, l2addr.tei, l2addr.sapi);
		msgb_free(msg);
		break;
	case DL_RELEASE_IND:
		DEBUGP(DLMI, "DL_RELEASE_IND: channel(%d) sapi(%d) tei(%d)\n",
		l2addr.channel, l2addr.sapi, l2addr.tei);
		ret = e1inp_event(e1i_ts, S_L_INP_TEI_DN, l2addr.tei, l2addr.sapi);
		msgb_free(msg);
		break;
	case DL_DATA_IND:
	case DL_UNITDATA_IND:
		msg->l2h = msg->data + MISDN_HEADER_LEN;
		DEBUGP(DLMI, "RX: %s\n", osmo_hexdump(msgb_l2(msg), ret - MISDN_HEADER_LEN));
		if (mline->use_userspace_lapd) {
			LOGP(DLMI, LOGL_ERROR, "DL_DATA_IND but userspace LAPD ?!?\n");
			msgb_free(msg);
			return -EIO;
		}
		ret = e1inp_rx_ts(e1i_ts, msg, l2addr.tei, l2addr.sapi);
		break;
	case PH_ACTIVATE_IND:
		DEBUGP(DLMI, "PH_ACTIVATE_IND: channel(%d) sapi(%d) tei(%d)\n",
		l2addr.channel, l2addr.sapi, l2addr.tei);
		msgb_free(msg);
		break;
	case PH_DEACTIVATE_IND:
		DEBUGP(DLMI, "PH_DEACTIVATE_IND: channel(%d) sapi(%d) tei(%d)\n",
		l2addr.channel, l2addr.sapi, l2addr.tei);
		msgb_free(msg);
		break;
	case PH_DATA_IND:
		if (!mline->use_userspace_lapd) {
			LOGP(DLMI, LOGL_ERROR, "PH_DATA_IND but kernel LAPD ?!?\n");
			return -EIO;
		}
		/* remove the Misdn Header */
		msgb_pull(msg, MISDN_HEADER_LEN);
		/* hand into the LAPD code */
		DEBUGP(DLMI, "RX: %s\n", osmo_hexdump(msg->data, msg->len));
		ret = e1inp_rx_ts_lapd(e1i_ts, msg);
		break;
	default:
		msgb_free(msg);
		break;
	}
	return ret;
}
Ejemplo n.º 15
0
static int handle_ts1_read(struct bsc_fd *bfd)
{
	struct e1inp_line *line = bfd->data;
	unsigned int ts_nr = bfd->priv_nr;
	struct e1inp_ts *e1i_ts = &line->ts[ts_nr-1];
	struct e1inp_sign_link *link;
	struct msgb *msg = msgb_alloc(TS1_ALLOC_SIZE, "mISDN TS1");
	struct sockaddr_mISDN l2addr;
	struct mISDNhead *hh;
	socklen_t alen;
	int ret;

	if (!msg)
		return -ENOMEM;

	hh = (struct mISDNhead *) msg->data;

	alen = sizeof(l2addr);
	ret = recvfrom(bfd->fd, msg->data, 300, 0,
		       (struct sockaddr *) &l2addr, &alen);
	if (ret < 0) {
		fprintf(stderr, "recvfrom error  %s\n", strerror(errno));
		return ret;
	}

	if (alen != sizeof(l2addr)) {
		fprintf(stderr, "%s error len\n", __func__);
		return -EINVAL;
	}

	msgb_put(msg, ret);

	DEBUGP(DMI, "alen =%d, dev(%d) channel(%d) sapi(%d) tei(%d)\n",
		alen, l2addr.dev, l2addr.channel, l2addr.sapi, l2addr.tei);

	DEBUGP(DMI, "<= len = %d, prim(0x%x) id(0x%x): %s\n",
		ret, hh->prim, hh->id, get_prim_name(hh->prim));

	switch (hh->prim) {
	case DL_INFORMATION_IND:
		/* mISDN tells us which channel number is allocated for this
		 * tuple of (SAPI, TEI). */
		DEBUGP(DMI, "DL_INFORMATION_IND: use channel(%d) sapi(%d) tei(%d) for now\n",
			l2addr.channel, l2addr.sapi, l2addr.tei);
		link = e1inp_lookup_sign_link(e1i_ts, l2addr.tei, l2addr.sapi);
		if (!link) {
			DEBUGPC(DMI, "mISDN message for unknown sign_link\n");
			msgb_free(msg);
			return -EINVAL;
		}
		/* save the channel number in the driver private struct */
		link->driver.misdn.channel = l2addr.channel;
		break;
	case DL_ESTABLISH_IND:
		DEBUGP(DMI, "DL_ESTABLISH_IND: channel(%d) sapi(%d) tei(%d)\n",
		l2addr.channel, l2addr.sapi, l2addr.tei);
		ret = e1inp_event(e1i_ts, EVT_E1_TEI_UP, l2addr.tei, l2addr.sapi);
		break;
	case DL_RELEASE_IND:
		DEBUGP(DMI, "DL_RELEASE_IND: channel(%d) sapi(%d) tei(%d)\n",
		l2addr.channel, l2addr.sapi, l2addr.tei);
		ret = e1inp_event(e1i_ts, EVT_E1_TEI_DN, l2addr.tei, l2addr.sapi);
		break;
	case DL_DATA_IND:
	case DL_UNITDATA_IND:
		msg->l2h = msg->data + MISDN_HEADER_LEN;
		DEBUGP(DMI, "RX: %s\n", hexdump(msgb_l2(msg), ret - MISDN_HEADER_LEN));
		ret = e1inp_rx_ts(e1i_ts, msg, l2addr.tei, l2addr.sapi);
		break;
	case PH_ACTIVATE_IND:
		DEBUGP(DMI, "PH_ACTIVATE_IND: channel(%d) sapi(%d) tei(%d)\n",
		l2addr.channel, l2addr.sapi, l2addr.tei);
		break;
	case PH_DEACTIVATE_IND:
		DEBUGP(DMI, "PH_DEACTIVATE_IND: channel(%d) sapi(%d) tei(%d)\n",
		l2addr.channel, l2addr.sapi, l2addr.tei);
		break;
	default:
		break;
	}
	return ret;
}
Ejemplo n.º 16
0
static int handle_ts1_read(struct osmo_fd *bfd)
{
    struct e1inp_line *line = bfd->data;
    unsigned int ts_nr = bfd->priv_nr;
    struct e1inp_ts *e1i_ts = &line->ts[ts_nr-1];
    struct e1inp_sign_link *link;
    struct ipaccess_head *hh;
    struct msgb *msg;
    int ret = 0, error;

    error = ipa_msg_recv(bfd->fd, &msg);
    if (error < 0)
        return error;
    else if (error == 0) {
        hsl_drop(e1i_ts->line, bfd);
        LOGP(DLINP, LOGL_NOTICE, "Sign link vanished, dead socket\n");
        return error;
    }
    DEBUGP(DLMI, "RX %u: %s\n", ts_nr, osmo_hexdump(msgb_l2(msg), msgb_l2len(msg)));

    hh = (struct ipaccess_head *) msg->data;
    if (hh->proto == HSL_PROTO_DEBUG) {
        LOGP(DLINP, LOGL_NOTICE, "HSL debug: %s\n", msg->data + sizeof(*hh));
        msgb_free(msg);
        return ret;
    }

    /* HSL proprietary RSL extension */
    if (hh->proto == 0 && (msg->l2h[0] == 0x81 || msg->l2h[0] == 0x80)) {
        ret = process_hsl_rsl(msg, line, bfd);
        if (ret < 0) {
            hsl_drop(e1i_ts->line, bfd);
            return ret;
        } else if (ret == 1)
            return 0;
        /* else: continue... */
    }

#ifdef HSL_SR_1_0
    /* HSL for whatever reason chose to use 0x81 instead of 0x80 for FOM */
    if (hh->proto == 255 && msg->l2h[0] == (ABIS_OM_MDISC_FOM | 0x01))
        msg->l2h[0] = ABIS_OM_MDISC_FOM;
#endif
    link = e1inp_lookup_sign_link(e1i_ts, hh->proto, 0);
    if (!link) {
        LOGP(DLINP, LOGL_ERROR, "no matching signalling link for "
             "hh->proto=0x%02x\n", hh->proto);
        msgb_free(msg);
        return -EIO;
    }
    msg->dst = link;

    /* XXX: better use e1inp_ts_rx? */
    if (!e1i_ts->line->ops->sign_link) {
        LOGP(DLINP, LOGL_ERROR, "Fix your application, "
             "no action set for signalling messages.\n");
        return -ENOENT;
    }
    e1i_ts->line->ops->sign_link(msg);

    return ret;
}