/* get next frame from the tx queue. because the ms has multiple datalinks, * each datalink's queue is read round-robin. */ static int l2_ph_data_conf(struct msgb *msg, struct lapdm_entity *le) { struct osmo_phsap_prim pp; /* we may send again */ le->tx_pending = 0; /* free confirm message */ if (msg) msgb_free(msg); if (lapdm_phsap_dequeue_prim(le, &pp) < 0) { /* no message in all queues */ /* If user didn't request PH-EMPTY_FRAME.req, abort */ if (!(le->flags & LAPDM_ENT_F_EMPTY_FRAME)) return 0; /* otherwise, send PH-EMPTY_FRAME.req */ osmo_prim_init(&pp.oph, SAP_GSM_PH, PRIM_PH_EMPTY_FRAME, PRIM_OP_REQUEST, NULL); } else { le->tx_pending = 1; } return le->l1_prim_cb(&pp.oph, le->l1_ctx); }
/*! \brief dequeue a msg that's pending transmission via L1 and wrap it into * a osmo_phsap_prim */ int lapdm_phsap_dequeue_prim(struct lapdm_entity *le, struct osmo_phsap_prim *pp) { struct msgb *msg; uint8_t pad; msg = tx_dequeue_msgb(le); if (!msg) return -ENODEV; /* if we have a message, send PH-DATA.req */ osmo_prim_init(&pp->oph, SAP_GSM_PH, PRIM_PH_DATA, PRIM_OP_REQUEST, msg); /* Pull chan_nr and link_id */ pp->u.data.chan_nr = *msg->data; msgb_pull(msg, 1); pp->u.data.link_id = *msg->data; msgb_pull(msg, 1); pad = *msg->data; msgb_pull(msg, 1); /* Pad the frame, we can transmit now */ lapdm_pad_msgb(msg, pad); return 0; }
/* write a frame into the tx queue */ static int tx_ph_data_enqueue(struct lapdm_datalink *dl, struct msgb *msg, uint8_t chan_nr, uint8_t link_id, uint8_t pad) { struct lapdm_entity *le = dl->entity; struct osmo_phsap_prim pp; /* if there is a pending message, queue it */ if (le->tx_pending || le->flags & LAPDM_ENT_F_POLLING_ONLY) { *msgb_push(msg, 1) = pad; *msgb_push(msg, 1) = link_id; *msgb_push(msg, 1) = chan_nr; msgb_enqueue(&dl->dl.tx_queue, msg); return -EBUSY; } osmo_prim_init(&pp.oph, SAP_GSM_PH, PRIM_PH_DATA, PRIM_OP_REQUEST, msg); pp.u.data.chan_nr = chan_nr; pp.u.data.link_id = link_id; /* send the frame now */ le->tx_pending = 0; /* disabled flow control */ lapdm_pad_msgb(msg, pad); return le->l1_prim_cb(&pp.oph, le->l1_ctx); }
/* forward a RUA message to the SCCP User API to SCCP/SUA */ static int rua_to_scu(struct hnb_context *hnb, struct hnbgw_cnlink *cn, enum osmo_scu_prim_type type, uint32_t context_id, uint32_t cause, const uint8_t *data, unsigned int len) { struct msgb *msg = msgb_alloc(1500, "rua_to_sua"); struct osmo_scu_prim *prim; struct hnbgw_context_map *map; int rc; if (!cn) { DEBUGP(DRUA, "CN=NULL, discarding message\n"); return 0; } prim = (struct osmo_scu_prim *) msgb_put(msg, sizeof(*prim)); osmo_prim_init(&prim->oph, SCCP_SAP_USER, type, PRIM_OP_REQUEST, msg); map = context_map_alloc_by_hnb(hnb, context_id, cn); /* add primitive header */ switch (type) { case OSMO_SCU_PRIM_N_CONNECT: prim->u.connect.called_addr; prim->u.connect.calling_addr; prim->u.connect.sccp_class = 2; prim->u.connect.conn_id = map->scu_conn_id; break; case OSMO_SCU_PRIM_N_DATA: prim->u.data.conn_id = map->scu_conn_id; break; case OSMO_SCU_PRIM_N_DISCONNECT: prim->u.disconnect.conn_id = map->scu_conn_id; prim->u.disconnect.cause = cause; break; case OSMO_SCU_PRIM_N_UNITDATA: prim->u.unitdata.called_addr; prim->u.unitdata.calling_addr; break; default: return -EINVAL; } /* add optional data section, if needed */ if (data && len) { msg->l2h = msgb_put(msg, len); memcpy(msg->l2h, data, len); } rc = osmo_sua_user_link_down(cn->sua_link, &prim->oph); return rc; }
static int mph_info_chan_confirm(struct gsm_lchan *lchan, enum osmo_mph_info_type type, uint8_t cause) { struct osmo_phsap_prim l1sap; memset(&l1sap, 0, sizeof(l1sap)); osmo_prim_init(&l1sap.oph, SAP_GSM_PH, PRIM_MPH_INFO, PRIM_OP_CONFIRM, NULL); l1sap.u.info.type = type; l1sap.u.info.u.act_cnf.chan_nr = gsm_lchan2chan_nr(lchan); l1sap.u.info.u.act_cnf.cause = cause; return l1sap_up(lchan->ts->trx, &l1sap); }
static int send(struct msgb *in_msg, struct lapdm_channel *chan) { struct osmo_phsap_prim pp; struct msgb *msg; int rc; msg = msgb_alloc_headroom(128, 64, "PH-DATA.ind"); osmo_prim_init(&pp.oph, SAP_GSM_PH, PRIM_PH_DATA, PRIM_OP_INDICATION, msg); /* copy over actual MAC block */ msg->l2h = msgb_put(msg, msgb_l2len(in_msg)); memcpy(msg->l2h, in_msg->l2h, msgb_l2len(in_msg)); /* LAPDm requires those... */ pp.u.data.chan_nr = 0; pp.u.data.link_id = 0; /* feed into the LAPDm code of libosmogsm */ rc = lapdm_phsap_up(&pp.oph, &chan->lapdm_dcch); ASSERT(rc == 0 || rc == -EBUSY); return 0; }