diff options
author | Philipp <pmaier@sysmocom.de> | 2016-09-02 13:38:01 +0200 |
---|---|---|
committer | Harald Welte <laforge@gnumonks.org> | 2016-09-24 03:17:59 +0000 |
commit | 73f83d533ba8d929a840ab76efb099da4d5b6174 (patch) | |
tree | 760a1631a8f84b2fadaaf3379c077ae2eacc32b6 /openbsc/src/gprs/gprs_sndcp_dcomp.c | |
parent | d8b45778de992ea5dec6d543a7cad10783a3d69b (diff) |
SNDCP: add V.42bis data compression functionality
- Add compression control for V.42bis Add code to handle compression
(gprs_sndcp_dcomp.c/h)
- Add Adjustments in SNDCP
- Add VTY commands
Change-Id: I6d36cbdf2f5c5f83ca9ba57c70452f02b8582e7e
Diffstat (limited to 'openbsc/src/gprs/gprs_sndcp_dcomp.c')
-rw-r--r-- | openbsc/src/gprs/gprs_sndcp_dcomp.c | 357 |
1 files changed, 357 insertions, 0 deletions
diff --git a/openbsc/src/gprs/gprs_sndcp_dcomp.c b/openbsc/src/gprs/gprs_sndcp_dcomp.c new file mode 100644 index 000000000..489106b47 --- /dev/null +++ b/openbsc/src/gprs/gprs_sndcp_dcomp.c @@ -0,0 +1,357 @@ +/* GPRS SNDCP data compression handler */ + +/* (C) 2016 by Sysmocom s.f.m.c. GmbH + * All Rights Reserved + * + * Author: Philipp Maier + * + * 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 Affero 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 <http://www.gnu.org/licenses/>. + * + */ + +#include <stdio.h> +#include <string.h> +#include <stdint.h> +#include <math.h> +#include <errno.h> +#include <stdbool.h> + +#include <osmocom/core/utils.h> +#include <osmocom/core/msgb.h> +#include <osmocom/core/linuxlist.h> +#include <osmocom/core/talloc.h> +#include <osmocom/gsm/tlv.h> + +#include <openbsc/gprs_llc.h> +#include <openbsc/sgsn.h> +#include <openbsc/gprs_sndcp_xid.h> +#include <openbsc/v42bis.h> +#include <openbsc/v42bis_private.h> +#include <openbsc/debug.h> +#include <openbsc/gprs_sndcp_comp.h> +#include <openbsc/gprs_sndcp_dcomp.h> + +/* A struct to capture the output data of compressor and decompressor */ +struct v42bis_output_buffer { + uint8_t *buf; + uint8_t *buf_pointer; + int len; +}; + +/* Handler to capture the output data from the compressor */ +void tx_v42bis_frame_handler(void *user_data, const uint8_t *pkt, int len) +{ + struct v42bis_output_buffer *output_buffer = + (struct v42bis_output_buffer *)user_data; + memcpy(output_buffer->buf_pointer, pkt, len); + output_buffer->buf_pointer += len; + output_buffer->len += len; + return; +} + +/* Handler to capture the output data from the decompressor */ +void rx_v42bis_data_handler(void *user_data, const uint8_t *buf, int len) +{ + struct v42bis_output_buffer *output_buffer = + (struct v42bis_output_buffer *)user_data; + memcpy(output_buffer->buf_pointer, buf, len); + output_buffer->buf_pointer += len; + output_buffer->len += len; + return; +} + +/* Initalize data compression */ +int gprs_sndcp_dcomp_init(const void *ctx, struct gprs_sndcp_comp *comp_entity, + const struct gprs_sndcp_comp_field *comp_field) +{ + /* Note: This function is automatically called from + * gprs_sndcp_comp.c when a new data compression + * entity is created by gprs_sndcp.c */ + + OSMO_ASSERT(comp_entity); + OSMO_ASSERT(comp_field); + + if (comp_entity->compclass == SNDCP_XID_DATA_COMPRESSION + && comp_entity->algo == V42BIS) { + comp_entity->state = + v42bis_init(ctx, NULL, comp_field->v42bis_params->p0, + comp_field->v42bis_params->p1, + comp_field->v42bis_params->p2, + &tx_v42bis_frame_handler, NULL, + V42BIS_MAX_OUTPUT_LENGTH, + &rx_v42bis_data_handler, NULL, + V42BIS_MAX_OUTPUT_LENGTH); + LOGP(DSNDCP, LOGL_INFO, + "V.42bis data compression initalized.\n"); + return 0; + } + + /* Just in case someone tries to initalize an unknown or unsupported + * data compresson. Since everything is checked during the SNDCP + * negotiation process, this should never happen! */ + OSMO_ASSERT(false); +} + +/* Terminate data compression */ +void gprs_sndcp_dcomp_term(struct gprs_sndcp_comp *comp_entity) +{ + /* Note: This function is automatically called from + * gprs_sndcp_comp.c when a data compression + * entity is deleted by gprs_sndcp.c */ + + OSMO_ASSERT(comp_entity); + + if (comp_entity->compclass == SNDCP_XID_DATA_COMPRESSION + && comp_entity->algo == V42BIS) { + if (comp_entity->state) { + v42bis_free((v42bis_state_t *) comp_entity->state); + comp_entity->state = NULL; + } + LOGP(DSNDCP, LOGL_INFO, + "V.42bis data compression terminated.\n"); + return; + } + + /* Just in case someone tries to terminate an unknown or unsupported + * data compresson. Since everything is checked during the SNDCP + * negotiation process, this should never happen! */ + OSMO_ASSERT(false); +} + +/* Perform a full reset of the V.42bis compression state */ +static void v42bis_reset(v42bis_state_t *comp) +{ + /* This function performs a complete reset of the V.42bis compression + * state by reinitalizing the state withe the previously negotiated + * parameters. */ + + int p0, p1, p2; + p0 = comp->decompress.v42bis_parm_p0 | comp->compress.v42bis_parm_p0; + p1 = comp->decompress.v42bis_parm_n2; + p2 = comp->decompress.v42bis_parm_n7; + + DEBUGP(DSNDCP, "Resetting compression state: %p, p0=%d, p1=%d, p2=%d\n", + comp, p0, p1, p2); + + v42bis_init(NULL, comp, p0, p1, p2, &tx_v42bis_frame_handler, NULL, + V42BIS_MAX_OUTPUT_LENGTH, &rx_v42bis_data_handler, NULL, + V42BIS_MAX_OUTPUT_LENGTH); +} + +/* Compress a packet using V.42bis data compression */ +static int v42bis_compress_unitdata(uint8_t *pcomp_index, uint8_t *data, + unsigned int len, v42bis_state_t *comp) +{ + /* Note: This implementation may only be used to compress SN_UNITDATA + * packets, since it resets the compression state for each NPDU. */ + + uint8_t *data_o; + int rc; + int skip = 0; + struct v42bis_output_buffer compressed_data; + + /* Don't bother with short packets */ + if (len < MIN_COMPR_PAYLOAD) + skip = 1; + + /* Skip if compression is not enabled for TX direction */ + if (!comp->compress.v42bis_parm_p0) + skip = 1; + + /* Skip compression */ + if (skip) { + *pcomp_index = 0; + return len; + } + + /* Reset V.42bis compression state */ + v42bis_reset(comp); + + /* Run compressor */ + data_o = talloc_zero_size(comp, len * MAX_DATADECOMPR_FAC); + compressed_data.buf = data_o; + compressed_data.buf_pointer = data_o; + compressed_data.len = 0; + comp->compress.user_data = (&compressed_data); + rc = v42bis_compress(comp, data, len); + if (rc < 0) { + LOGP(DSNDCP, LOGL_ERROR, + "Data compression failed, skipping...\n"); + skip = 1; + } + rc = v42bis_compress_flush(comp); + if (rc < 0) { + LOGP(DSNDCP, LOGL_ERROR, + "Data compression failed, skipping...\n"); + skip = 1; + } + + /* The compressor might yield negative compression gain, in + * this case, we just decide to send the packat as normal, + * uncompressed payload => skip compresssion */ + if (compressed_data.len >= len) { + LOGP(DSNDCP, LOGL_ERROR, + "Data compression ineffective, skipping...\n"); + skip = 1; + } + + /* Skip compression */ + if (skip) { + *pcomp_index = 0; + talloc_free(data_o); + return len; + } + + *pcomp_index = 1; + memcpy(data, data_o, compressed_data.len); + talloc_free(data_o); + + return compressed_data.len; +} + +/* Expand a packet using V.42bis data compression */ +static int v42bis_expand_unitdata(uint8_t *data, unsigned int len, + uint8_t pcomp_index, v42bis_state_t *comp) +{ + /* Note: This implementation may only be used to compress SN_UNITDATA + * packets, since it resets the compression state for each NPDU. */ + + int rc; + struct v42bis_output_buffer uncompressed_data; + uint8_t *data_i; + + /* Skip when the packet is marked as uncompressed */ + if (pcomp_index == 0) { + return len; + } + + /* Reset V.42bis compression state */ + v42bis_reset(comp); + + /* Decompress packet */ + data_i = talloc_zero_size(comp, len); + memcpy(data_i, data, len); + uncompressed_data.buf = data; + uncompressed_data.buf_pointer = data; + uncompressed_data.len = 0; + comp->decompress.user_data = (&uncompressed_data); + rc = v42bis_decompress(comp, data_i, len); + talloc_free(data_i); + if (rc < 0) + return -EINVAL; + rc = v42bis_decompress_flush(comp); + if (rc < 0) + return -EINVAL; + + return uncompressed_data.len; +} + +/* Expand packet */ +int gprs_sndcp_dcomp_expand(uint8_t *data, unsigned int len, uint8_t pcomp, + const struct llist_head *comp_entities) +{ + int rc; + uint8_t pcomp_index = 0; + struct gprs_sndcp_comp *comp_entity; + + OSMO_ASSERT(data); + OSMO_ASSERT(comp_entities); + + LOGP(DSNDCP, LOGL_DEBUG, + "Data compression entity list: comp_entities=%p\n", comp_entities); + + LOGP(DSNDCP, LOGL_DEBUG, "Data compression mode: dcomp=%d\n", pcomp); + + /* Skip on pcomp=0 */ + if (pcomp == 0) { + return len; + } + + /* Find out which compression entity handles the data */ + comp_entity = gprs_sndcp_comp_by_comp(comp_entities, pcomp); + + /* Skip compression if no suitable compression entity can be found */ + if (!comp_entity) { + return len; + } + + /* Note: Only data compression entities may appear in + * data compression context */ + OSMO_ASSERT(comp_entity->compclass == SNDCP_XID_DATA_COMPRESSION); + + /* Note: Currently V42BIS is the only compression method we + * support, so the only allowed algorithm is V42BIS */ + OSMO_ASSERT(comp_entity->algo == V42BIS); + + /* Find pcomp_index */ + pcomp_index = gprs_sndcp_comp_get_idx(comp_entity, pcomp); + + /* Run decompression algo */ + rc = v42bis_expand_unitdata(data, len, pcomp_index, comp_entity->state); + + LOGP(DSNDCP, LOGL_DEBUG, + "Data expansion done, old length=%d, new length=%d, entity=%p\n", + len, rc, comp_entity); + + return rc; +} + +/* Compress packet */ +int gprs_sndcp_dcomp_compress(uint8_t *data, unsigned int len, uint8_t *pcomp, + const struct llist_head *comp_entities, + uint8_t nsapi) +{ + int rc; + uint8_t pcomp_index = 0; + struct gprs_sndcp_comp *comp_entity; + + OSMO_ASSERT(data); + OSMO_ASSERT(pcomp); + OSMO_ASSERT(comp_entities); + + LOGP(DSNDCP, LOGL_DEBUG, + "Data compression entity list: comp_entities=%p\n", comp_entities); + + /* Find out which compression entity handles the data */ + comp_entity = gprs_sndcp_comp_by_nsapi(comp_entities, nsapi); + + /* Skip compression if no suitable compression entity can be found */ + if (!comp_entity) { + *pcomp = 0; + return len; + } + + /* Note: Only data compression entities may appear in + * data compression context */ + OSMO_ASSERT(comp_entity->compclass == SNDCP_XID_DATA_COMPRESSION); + + /* Note: Currently V42BIS is the only compression method we + * support, so the only allowed algorithm is V42BIS */ + OSMO_ASSERT(comp_entity->algo == V42BIS); + + /* Run compression algo */ + rc = v42bis_compress_unitdata(&pcomp_index, data, len, + comp_entity->state); + + /* Find pcomp value */ + *pcomp = gprs_sndcp_comp_get_comp(comp_entity, pcomp_index); + + LOGP(DSNDCP, LOGL_DEBUG, "Data compression mode: dcomp=%d\n", *pcomp); + + LOGP(DSNDCP, LOGL_DEBUG, + "Data compression done, old length=%d, new length=%d, entity=%p\n", + len, rc, comp_entity); + + return rc; +} |