1 | // SPDX-License-Identifier: Zlib |
2 | |
3 | #include "../zlib_inflate/inflate.h" |
4 | #include "dfltcc_util.h" |
5 | #include "dfltcc_inflate.h" |
6 | #include <asm/setup.h> |
7 | #include <linux/export.h> |
8 | #include <linux/zutil.h> |
9 | |
10 | /* |
11 | * Expand. |
12 | */ |
13 | int dfltcc_can_inflate( |
14 | z_streamp strm |
15 | ) |
16 | { |
17 | struct inflate_state *state = (struct inflate_state *)strm->state; |
18 | struct dfltcc_state *dfltcc_state = GET_DFLTCC_STATE(state); |
19 | |
20 | /* Check for kernel dfltcc command line parameter */ |
21 | if (zlib_dfltcc_support == ZLIB_DFLTCC_DISABLED || |
22 | zlib_dfltcc_support == ZLIB_DFLTCC_DEFLATE_ONLY) |
23 | return 0; |
24 | |
25 | /* Unsupported hardware */ |
26 | return is_bit_set(bits: dfltcc_state->af.fns, DFLTCC_XPND) && |
27 | is_bit_set(bits: dfltcc_state->af.fmts, DFLTCC_FMT0); |
28 | } |
29 | EXPORT_SYMBOL(dfltcc_can_inflate); |
30 | |
31 | void dfltcc_reset_inflate_state(z_streamp strm) { |
32 | struct inflate_state *state = (struct inflate_state *)strm->state; |
33 | struct dfltcc_state *dfltcc_state = GET_DFLTCC_STATE(state); |
34 | |
35 | dfltcc_reset_state(dfltcc_state); |
36 | } |
37 | EXPORT_SYMBOL(dfltcc_reset_inflate_state); |
38 | |
39 | static int dfltcc_was_inflate_used( |
40 | z_streamp strm |
41 | ) |
42 | { |
43 | struct inflate_state *state = (struct inflate_state *)strm->state; |
44 | struct dfltcc_param_v0 *param = &GET_DFLTCC_STATE(state)->param; |
45 | |
46 | return !param->nt; |
47 | } |
48 | |
49 | static int dfltcc_inflate_disable( |
50 | z_streamp strm |
51 | ) |
52 | { |
53 | struct inflate_state *state = (struct inflate_state *)strm->state; |
54 | struct dfltcc_state *dfltcc_state = GET_DFLTCC_STATE(state); |
55 | |
56 | if (!dfltcc_can_inflate(strm)) |
57 | return 0; |
58 | if (dfltcc_was_inflate_used(strm)) |
59 | /* DFLTCC has already decompressed some data. Since there is not |
60 | * enough information to resume decompression in software, the call |
61 | * must fail. |
62 | */ |
63 | return 1; |
64 | /* DFLTCC was not used yet - decompress in software */ |
65 | memset(&dfltcc_state->af, 0, sizeof(dfltcc_state->af)); |
66 | return 0; |
67 | } |
68 | |
69 | static dfltcc_cc dfltcc_xpnd( |
70 | z_streamp strm |
71 | ) |
72 | { |
73 | struct inflate_state *state = (struct inflate_state *)strm->state; |
74 | struct dfltcc_param_v0 *param = &GET_DFLTCC_STATE(state)->param; |
75 | size_t avail_in = strm->avail_in; |
76 | size_t avail_out = strm->avail_out; |
77 | dfltcc_cc cc; |
78 | |
79 | cc = dfltcc(DFLTCC_XPND | HBT_CIRCULAR, |
80 | param, op1: &strm->next_out, len1: &avail_out, |
81 | op2: &strm->next_in, len2: &avail_in, hist: state->window); |
82 | strm->avail_in = avail_in; |
83 | strm->avail_out = avail_out; |
84 | return cc; |
85 | } |
86 | |
87 | dfltcc_inflate_action dfltcc_inflate( |
88 | z_streamp strm, |
89 | int flush, |
90 | int *ret |
91 | ) |
92 | { |
93 | struct inflate_state *state = (struct inflate_state *)strm->state; |
94 | struct dfltcc_state *dfltcc_state = GET_DFLTCC_STATE(state); |
95 | struct dfltcc_param_v0 *param = &dfltcc_state->param; |
96 | dfltcc_cc cc; |
97 | |
98 | if (flush == Z_BLOCK || flush == Z_PACKET_FLUSH) { |
99 | /* DFLTCC does not support stopping on block boundaries (Z_BLOCK flush option) |
100 | * as well as the use of Z_PACKET_FLUSH option (used exclusively by PPP driver) |
101 | */ |
102 | if (dfltcc_inflate_disable(strm)) { |
103 | *ret = Z_STREAM_ERROR; |
104 | return DFLTCC_INFLATE_BREAK; |
105 | } else |
106 | return DFLTCC_INFLATE_SOFTWARE; |
107 | } |
108 | |
109 | if (state->last) { |
110 | if (state->bits != 0) { |
111 | strm->next_in++; |
112 | strm->avail_in--; |
113 | state->bits = 0; |
114 | } |
115 | state->mode = CHECK; |
116 | return DFLTCC_INFLATE_CONTINUE; |
117 | } |
118 | |
119 | if (strm->avail_in == 0 && !param->cf) |
120 | return DFLTCC_INFLATE_BREAK; |
121 | |
122 | if (!state->window || state->wsize == 0) { |
123 | state->mode = MEM; |
124 | return DFLTCC_INFLATE_CONTINUE; |
125 | } |
126 | |
127 | /* Translate stream to parameter block */ |
128 | param->cvt = CVT_ADLER32; |
129 | param->sbb = state->bits; |
130 | if (param->hl) |
131 | param->nt = 0; /* Honor history for the first block */ |
132 | param->cv = state->check; |
133 | |
134 | /* Inflate */ |
135 | do { |
136 | cc = dfltcc_xpnd(strm); |
137 | } while (cc == DFLTCC_CC_AGAIN); |
138 | |
139 | /* Translate parameter block to stream */ |
140 | strm->msg = oesc_msg(buf: dfltcc_state->msg, oesc: param->oesc); |
141 | state->last = cc == DFLTCC_CC_OK; |
142 | state->bits = param->sbb; |
143 | state->check = param->cv; |
144 | if (cc == DFLTCC_CC_OP2_CORRUPT && param->oesc != 0) { |
145 | /* Report an error if stream is corrupted */ |
146 | state->mode = BAD; |
147 | return DFLTCC_INFLATE_CONTINUE; |
148 | } |
149 | state->mode = TYPEDO; |
150 | /* Break if operands are exhausted, otherwise continue looping */ |
151 | return (cc == DFLTCC_CC_OP1_TOO_SHORT || cc == DFLTCC_CC_OP2_TOO_SHORT) ? |
152 | DFLTCC_INFLATE_BREAK : DFLTCC_INFLATE_CONTINUE; |
153 | } |
154 | EXPORT_SYMBOL(dfltcc_inflate); |
155 | |