diff options
author | Harald Welte <laforge@gnumonks.org> | 2010-06-01 18:28:10 +0200 |
---|---|---|
committer | Harald Welte <laforge@gnumonks.org> | 2010-06-01 18:28:10 +0200 |
commit | ebabdea0a6df604d4b08cc532e04e845bc456705 (patch) | |
tree | a6eca7eb06f38531a3c4d2fefa3ed44856bba0da /openbsc/src/gprs/gprs_sndcp.c | |
parent | 84488245e6210abb25b4c8eefec8712d8e02d513 (diff) |
[GPRS] hand SNDCP N-PDUs to the GTP to the GGSN
This so far only works for UNIT-DATA and only if the N-PDU is not fragmented at
the SNDCP layer.
Diffstat (limited to 'openbsc/src/gprs/gprs_sndcp.c')
-rw-r--r-- | openbsc/src/gprs/gprs_sndcp.c | 156 |
1 files changed, 144 insertions, 12 deletions
diff --git a/openbsc/src/gprs/gprs_sndcp.c b/openbsc/src/gprs/gprs_sndcp.c index 99a7638be..ec704e0d0 100644 --- a/openbsc/src/gprs/gprs_sndcp.c +++ b/openbsc/src/gprs/gprs_sndcp.c @@ -32,6 +32,7 @@ #include <openbsc/debug.h> #include <openbsc/gprs_bssgp.h> #include <openbsc/gprs_llc.h> +#include <openbsc/sgsn.h> /* Chapter 7.2: SN-PDU Formats */ struct sndcp_common_hdr { @@ -44,7 +45,7 @@ struct sndcp_common_hdr { /* octet 2 */ uint8_t pcomp; uint8_t dcomp; -}; +} __attribute__((packed)); struct sndcp_udata_hdr { /* octet 3 */ @@ -52,42 +53,172 @@ struct sndcp_udata_hdr { uint8_t seg_nr:4; /* octet 4 */ uint8_t npdu_low; +} __attribute__((packed)); + +/* See 6.7.1.2 Reassembly */ +enum sndcp_rx_state { + SNDCP_RX_S_FIRST, + SNDCP_RX_S_SUBSEQ, + SNDCP_RX_S_DISCARD, +}; + + +static void *tall_sndcp_ctx; + +/* A fragment queue entry, containing one framgent of a N-PDU */ +struct frag_queue_entry { + struct llist_head list; + uint8_t seg_nr; + uint32_t data_len; + uint8_t data[0]; +}; + +/* A fragment queue header, maintaining list of fragments for one N-PDU */ +struct frag_queue_head { + uint16_t npdu; + + /* linked list of frag_queue_entry: one for each fragment */ + struct llist_head frag_list; + + struct timer_list timer; }; struct sndcp_entity { + struct llist_head list; + + struct gprs_llc_lle *lle; + uint8_t nsapi; + + enum sndcp_rx_state rx_state; + struct frag_queue_head fqueue; }; -/* Entry point for the LL-UNITDATA.indication */ -int sndcp_unitdata_ind(struct msgb *msg, uint8_t sapi, uint8_t *hdr, uint8_t len) +LLIST_HEAD(sndcp_entities); + +#if 0 +static struct frag_queue_entry _find_fqe(struct freg_queue_head *fqh, uint8_t seg_nr) +{ + +} + +static struct frag_queue_head _find_fqh(struct sndcp_entity *sne, uint16_t npdu) +{ + +} + +static int ul_enqueue_fragment(struct sndcp_entity *sne, uint16_t npdu, + uint8_t seg_nr, uint32_t data_len, uint8_t *data) +{ + +} +#endif + +static struct sndcp_entity *sndcp_entity_by_lle(const struct gprs_llc_lle *lle, + uint8_t nsapi) +{ + struct sndcp_entity *sne; + + llist_for_each_entry(sne, &sndcp_entities, list) { + if (sne->lle == lle && sne->nsapi == nsapi) + return sne; + } + return NULL; +} + +static struct sndcp_entity *sndcp_entity_alloc(struct gprs_llc_lle *lle, + uint8_t nsapi) +{ + struct sndcp_entity *sne; + + sne = talloc_zero(tall_sndcp_ctx, struct sndcp_entity); + if (!sne) + return NULL; + + sne->lle = lle; + sne->nsapi = nsapi; + sne->fqueue.timer.data = sne; + //sne->fqueue.timer.cb = FIXME; + sne->rx_state = SNDCP_RX_S_FIRST; + + return sne; +} + +/* Entry point for the SNSM-ACTIVATE.indication */ +int sndcp_sm_activate_ind(struct gprs_llc_lle *lle, uint8_t nsapi) +{ + if (sndcp_entity_by_lle(lle, nsapi)) + return -EEXIST; + + if (!sndcp_entity_alloc(lle, nsapi)) + return -ENOMEM; + + return 0; +} + +/* Section 5.1.2.17 LL-UNITDATA.ind */ +int sndcp_llunitdata_ind(struct msgb *msg, struct gprs_llc_lle *lle, uint8_t *hdr, uint8_t len) { + struct sndcp_entity *sne; + struct sndcp_common_hdr *sch = (struct sndcp_common_hdr *)hdr; struct sndcp_udata_hdr *suh; - uint16_t npdu; + uint8_t *comp, *npdu; + uint16_t npdu_num; + int npdu_len; - if (suh->type == 0) { + if (sch->type == 0) { LOGP(DGPRS, LOGL_ERROR, "SN-DATA PDU at unitdata_ind() function\n"); return -EINVAL; } - npdu = (suh->npdu_high << 8) | suh->npdu_low; + if (len < sizeof(*sch) + sizeof(*comp) + sizeof(*suh)) { + LOGP(DGPRS, LOGL_ERROR, "SN-UNITDATA PDU too short (%u)\n", len); + return -EIO; + } + + sne = sndcp_entity_by_lle(lle, sch->nsapi); + if (!sne) { + LOGP(DGPRS, LOGL_ERROR, "Message for non-existing SNDCP Entity\n"); + return -EIO; + } + + if (!sch->first || sch->more) { + /* FIXME: implement fragment re-assembly */ + LOGP(DGPRS, LOGL_ERROR, "We don't support reassembly yet\n"); + return -EIO; + } + + comp = (hdr + sizeof(struct sndcp_common_hdr)); + if (comp) { + LOGP(DGPRS, LOGL_ERROR, "We don't support compression yet\n"); + return -EIO; + } + suh = (struct sndcp_udata_hdr *) (comp + sizeof(*comp)); + npdu_num = (suh->npdu_high << 8) | suh->npdu_low; + + npdu = (uint8_t *)suh + sizeof(*suh); + npdu_len = (msg->data + msg->len) - npdu; + if (npdu_len) { + LOGP(DGPRS, LOGL_ERROR, "Short SNDCP N-PDU: %d\n", npdu_len); + return -EIO; + } + /* actually send the N-PDU to the SGSN core code, which then + * hands it off to the correct GTP tunnel + GGSN via gtp_data_req() */ + return sgsn_rx_sndcp_ud_ind(lle->llme->tlli, sne->nsapi, msg, npdu_len, npdu); } /* Section 5.1.2.1 LL-RESET.ind */ -static int sndcp_ll_reset_ind(struct sndcp_entity *se,) +static int sndcp_ll_reset_ind(struct sndcp_entity *se) { /* treat all outstanding SNDCP-LLC request type primitives as not sent */ /* reset all SNDCP XID parameters to default values */ } -/* Section 5.1.2.17 LL-UNITDATA.ind */ -static int sndcp_ll_unitdata_ind() -{ -} - static int sndcp_ll_status_ind() { /* inform the SM sub-layer by means of SNSM-STATUS.req */ } +#if 0 static struct sndcp_state_list {{ uint32_t states; unsigned int type; @@ -127,3 +258,4 @@ static int sndcp_rx_llc_prim() case LL_UNITDATA_IND: case LL_STATUS_IND: } +#endif |