| // 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); |