| // SPDX-License-Identifier: Zlib | |
| #include "../zlib_inflate/inflate.h" | |
| #include "dfltcc_util.h" | |
| #include "dfltcc_inflate.h" | |
| #include <asm/setup.h> | |
| #include <linux/export.h> | |
| #include <linux/zutil.h> | |
| /* | |
| * Expand. | |
| */ | |
| int dfltcc_can_inflate( | |
| z_streamp strm | |
| ) | |
| { | |
| struct inflate_state *state = (struct inflate_state *)strm->state; | |
| struct dfltcc_state *dfltcc_state = GET_DFLTCC_STATE(state); | |
| /* Check for kernel dfltcc command line parameter */ | |
| if (zlib_dfltcc_support == ZLIB_DFLTCC_DISABLED || | |
| zlib_dfltcc_support == ZLIB_DFLTCC_DEFLATE_ONLY) | |
| return 0; | |
| /* Unsupported hardware */ | |
| return is_bit_set(dfltcc_state->af.fns, DFLTCC_XPND) && | |
| is_bit_set(dfltcc_state->af.fmts, DFLTCC_FMT0); | |
| } | |
| EXPORT_SYMBOL(dfltcc_can_inflate); | |
| void dfltcc_reset_inflate_state(z_streamp strm) { | |
| struct inflate_state *state = (struct inflate_state *)strm->state; | |
| struct dfltcc_state *dfltcc_state = GET_DFLTCC_STATE(state); | |
| dfltcc_reset_state(dfltcc_state); | |
| } | |
| EXPORT_SYMBOL(dfltcc_reset_inflate_state); | |
| static int dfltcc_was_inflate_used( | |
| z_streamp strm | |
| ) | |
| { | |
| struct inflate_state *state = (struct inflate_state *)strm->state; | |
| struct dfltcc_param_v0 *param = &GET_DFLTCC_STATE(state)->param; | |
| return !param->nt; | |
| } | |
| static int dfltcc_inflate_disable( | |
| z_streamp strm | |
| ) | |
| { | |
| struct inflate_state *state = (struct inflate_state *)strm->state; | |
| struct dfltcc_state *dfltcc_state = GET_DFLTCC_STATE(state); | |
| if (!dfltcc_can_inflate(strm)) | |
| return 0; | |
| if (dfltcc_was_inflate_used(strm)) | |
| /* DFLTCC has already decompressed some data. Since there is not | |
| * enough information to resume decompression in software, the call | |
| * must fail. | |
| */ | |
| return 1; | |
| /* DFLTCC was not used yet - decompress in software */ | |
| memset(&dfltcc_state->af, 0, sizeof(dfltcc_state->af)); | |
| return 0; | |
| } | |
| static dfltcc_cc dfltcc_xpnd( | |
| z_streamp strm | |
| ) | |
| { | |
| struct inflate_state *state = (struct inflate_state *)strm->state; | |
| struct dfltcc_param_v0 *param = &GET_DFLTCC_STATE(state)->param; | |
| size_t avail_in = strm->avail_in; | |
| size_t avail_out = strm->avail_out; | |
| dfltcc_cc cc; | |
| cc = dfltcc(DFLTCC_XPND | HBT_CIRCULAR, | |
| param, &strm->next_out, &avail_out, | |
| &strm->next_in, &avail_in, state->window); | |
| strm->avail_in = avail_in; | |
| strm->avail_out = avail_out; | |
| return cc; | |
| } | |
| dfltcc_inflate_action dfltcc_inflate( | |
| z_streamp strm, | |
| int flush, | |
| int *ret | |
| ) | |
| { | |
| struct inflate_state *state = (struct inflate_state *)strm->state; | |
| struct dfltcc_state *dfltcc_state = GET_DFLTCC_STATE(state); | |
| struct dfltcc_param_v0 *param = &dfltcc_state->param; | |
| dfltcc_cc cc; | |
| if (flush == Z_BLOCK || flush == Z_PACKET_FLUSH) { | |
| /* DFLTCC does not support stopping on block boundaries (Z_BLOCK flush option) | |
| * as well as the use of Z_PACKET_FLUSH option (used exclusively by PPP driver) | |
| */ | |
| if (dfltcc_inflate_disable(strm)) { | |
| *ret = Z_STREAM_ERROR; | |
| return DFLTCC_INFLATE_BREAK; | |
| } else | |
| return DFLTCC_INFLATE_SOFTWARE; | |
| } | |
| if (state->last) { | |
| if (state->bits != 0) { | |
| strm->next_in++; | |
| strm->avail_in--; | |
| state->bits = 0; | |
| } | |
| state->mode = CHECK; | |
| return DFLTCC_INFLATE_CONTINUE; | |
| } | |
| if (strm->avail_in == 0 && !param->cf) | |
| return DFLTCC_INFLATE_BREAK; | |
| if (!state->window || state->wsize == 0) { | |
| state->mode = MEM; | |
| return DFLTCC_INFLATE_CONTINUE; | |
| } | |
| /* Translate stream to parameter block */ | |
| param->cvt = CVT_ADLER32; | |
| param->sbb = state->bits; | |
| if (param->hl) | |
| param->nt = 0; /* Honor history for the first block */ | |
| param->cv = state->check; | |
| /* Inflate */ | |
| do { | |
| cc = dfltcc_xpnd(strm); | |
| } while (cc == DFLTCC_CC_AGAIN); | |
| /* Translate parameter block to stream */ | |
| strm->msg = oesc_msg(dfltcc_state->msg, param->oesc); | |
| state->last = cc == DFLTCC_CC_OK; | |
| state->bits = param->sbb; | |
| state->check = param->cv; | |
| if (cc == DFLTCC_CC_OP2_CORRUPT && param->oesc != 0) { | |
| /* Report an error if stream is corrupted */ | |
| state->mode = BAD; | |
| return DFLTCC_INFLATE_CONTINUE; | |
| } | |
| state->mode = TYPEDO; | |
| /* Break if operands are exhausted, otherwise continue looping */ | |
| return (cc == DFLTCC_CC_OP1_TOO_SHORT || cc == DFLTCC_CC_OP2_TOO_SHORT) ? | |
| DFLTCC_INFLATE_BREAK : DFLTCC_INFLATE_CONTINUE; | |
| } | |
| EXPORT_SYMBOL(dfltcc_inflate); |