xref: /OK3568_Linux_fs/kernel/drivers/net/wireless/rockchip_wlan/rkwifi/bcmdhd/dhd_bitpack.c (revision 4882a59341e53eb6f0b4789bf948001014eff981)
1 /*
2  * Bit packing and Base64 utils for EWP
3  *
4  * Copyright (C) 2020, Broadcom.
5  *
6  *      Unless you and Broadcom execute a separate written software license
7  * agreement governing use of this software, this software is licensed to you
8  * under the terms of the GNU General Public License version 2 (the "GPL"),
9  * available at http://www.broadcom.com/licenses/GPLv2.php, with the
10  * following added to such license:
11  *
12  *      As a special exception, the copyright holders of this software give you
13  * permission to link this software with independent modules, and to copy and
14  * distribute the resulting executable under terms of your choice, provided that
15  * you also meet, for each linked independent module, the terms and conditions of
16  * the license of that module.  An independent module is a module which is not
17  * derived from this software.  The special exception does not apply to any
18  * modifications of the software.
19  *
20  *
21  * <<Broadcom-WL-IPTag/Open:>>
22  *
23  * $Id$
24  */
25 
26 #include <dhd_bitpack.h>
27 
28 #define BIT_PACK_OVERFLOW 0xFFFFFFFFu
29 
30 const char* base64_table = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
31 
32 #define BASE64_MAX_VALUE 63u
33 
34 #define BASE64_UNIT_LEN 6u
35 
36 #define BASE64_OFFSET0 0u
37 #define BASE64_OFFSET1 6u
38 #define BASE64_OFFSET2 12u
39 
40 #define MASK_UPPER_6BIT 0xfc
41 #define MASK_LOWER_6BIT 0x3f
42 
43 #define MASK_UPPER_4BIT 0xf0
44 #define MASK_LOWER_4BIT 0x0f
45 
46 #define MASK_UPPER_2BIT 0xc0
47 #define MASK_LOWER_2BIT 0x03
48 
49 #define SHIFT_2BIT 2u
50 #define SHIFT_4BIT 4u
51 #define SHIFT_6BIT 6u
52 
53 #define BASE64_PADDING_MARGIN 4u
54 
55 /*
56  * Function:	dhd_bit_pack
57  *
58  * Purpose:	bit data packing to given buffer
59  *
60  * Input Parameters:
61  *      buf		buffer to pack bit data
62  *      buf_len		total buffer length
63  *	bit_offset	offset in buffer (bitwise)
64  *	data		data to pack (max 32 bit)
65  *	bit_length	bit length to pack
66  *
67  * Output:
68  *	Updated bit offset in buf
69  */
70 int32
dhd_bit_pack(char * buf,int buf_len,int bit_offset,uint32 data,int32 bit_length)71 dhd_bit_pack(char *buf, int buf_len, int bit_offset, uint32 data, int32 bit_length)
72 {
73 
74 	int32 byte_shift = (bit_offset / 8);
75 	int32 local_bit_offset = bit_offset % 8;
76 	int32 available_bit = 8 - local_bit_offset;
77 	int32 remain_bit = bit_length;
78 	uint32 cropped_data;
79 	int32 idx;
80 	int32 total_byte = BYTE_SIZE(local_bit_offset + bit_length);
81 
82 	if (bit_length > 32) {
83 		/* exceeded max bit length, do nothing */
84 		return bit_offset;
85 	}
86 	if (BYTE_SIZE(bit_offset + bit_length) > buf_len) {
87 		/* can't pack more bits if expected offset is
88 		 * exceeded then buffer size
89 		 */
90 		return bit_offset;
91 	}
92 	if (bit_length < 32 && data >= 1<<bit_length) {
93 		cropped_data = BIT_PACK_OVERFLOW << (32 - bit_length);
94 		cropped_data = cropped_data >> (32 - bit_length);
95 	} else {
96 		cropped_data = data << (32 - bit_length);
97 		cropped_data = cropped_data >> (32 - bit_length);
98 	}
99 
100 	buf += byte_shift;
101 
102 	remain_bit = bit_length;
103 	if (total_byte > 10) {
104 		return bit_offset;
105 	}
106 	for (idx = 0; idx < total_byte; idx++) {
107 		char temp_byte = 0x00;
108 		if (idx == 0) {
109 			local_bit_offset = bit_offset % 8;
110 		} else {
111 			local_bit_offset = 0;
112 		}
113 
114 		available_bit = 8 - local_bit_offset;
115 		remain_bit -= available_bit;
116 		if (remain_bit >= 0) {
117 			temp_byte = cropped_data >> remain_bit;
118 		} else {
119 			temp_byte = cropped_data << (-1*remain_bit);
120 		}
121 		*buf = *buf | temp_byte;
122 		buf ++;
123 	}
124 	bit_offset += bit_length;
125 
126 	return bit_offset;
127 }
128 
129 static char
dhd_base64_get_code(char input)130 dhd_base64_get_code(char input)
131 {
132 	if (input > BASE64_MAX_VALUE) {
133 		return '=';
134 	}
135 	return base64_table[(int)input];
136 }
137 
138 /*
139  * Function:	dhd_base64_encode
140  *
141  * Purpose:	base64 encoding module which converts from 8 bits to
142  *		6 bit based, base64 format using base64_table
143  *		eg:	input:	hex-123456
144  *				bin-0001|0010|0011|0100|0101|0110
145  *			encode every 6 bit :
146  *				bin-000100|100011|010001|010110
147  *			base64 code :
148  *				base64-EjRW
149  *
150  * Input Parameters:
151  *      in_buf		input buffer
152  *      in_buf_len	length of input buffer
153  *	out_buf		output buffer
154  *	out_buf_len	length_ of output buffer
155  *
156  * Output:
157  *	length of encoded base64 string
158  */
159 int32
dhd_base64_encode(char * in_buf,int32 in_buf_len,char * out_buf,int32 out_buf_len)160 dhd_base64_encode(char* in_buf, int32 in_buf_len, char* out_buf, int32 out_buf_len)
161 {
162 	char* input_pos;
163 	char* input_end;
164 	char* base64_out;
165 	char* base64_out_pos;
166 	char* base64_output_end;
167 	char current_byte = 0;
168 	char masked_byte = 0;
169 	int32 estimated_out_len = 0;
170 	int32 offset = 0;
171 
172 	if (!in_buf || !out_buf || in_buf_len == 0 || out_buf_len == 0) {
173 		/* wrong input parameters */
174 		return 0;
175 	}
176 
177 	input_pos = in_buf;
178 	input_end = in_buf + in_buf_len;
179 	base64_out = out_buf;
180 	base64_out_pos = base64_out;
181 	base64_output_end = out_buf + out_buf_len - BASE64_PADDING_MARGIN;
182 	estimated_out_len = in_buf_len / 3 * 4;
183 
184 	if (estimated_out_len > out_buf_len) {
185 		/* estimated output length is
186 		 * larger than output buffer size
187 		 */
188 		return 0;
189 	}
190 
191 	while (input_pos != input_end) {
192 		if (base64_out_pos > base64_output_end) {
193 			/* outbuf buffer size exceeded, finish encoding */
194 			break;
195 		}
196 		if (offset == BASE64_OFFSET0) {
197 			current_byte = *input_pos++;
198 			masked_byte = (current_byte & MASK_UPPER_6BIT) >> SHIFT_2BIT;
199 			*base64_out_pos++ = dhd_base64_get_code(masked_byte);
200 			masked_byte = (current_byte & MASK_LOWER_2BIT) << SHIFT_4BIT;
201 			offset += BASE64_UNIT_LEN;
202 		} else if (offset == BASE64_OFFSET1) {
203 			current_byte = *input_pos++;
204 			masked_byte |= (current_byte & MASK_UPPER_4BIT) >> SHIFT_4BIT;
205 			*base64_out_pos++ = dhd_base64_get_code(masked_byte);
206 			masked_byte = (current_byte & MASK_LOWER_4BIT) << SHIFT_2BIT;
207 			offset += BASE64_UNIT_LEN;
208 		} else if (offset == BASE64_OFFSET2) {
209 			current_byte = *input_pos++;
210 			masked_byte |= (current_byte & MASK_UPPER_2BIT) >> SHIFT_6BIT;
211 			*base64_out_pos++ = dhd_base64_get_code(masked_byte);
212 			offset += BASE64_UNIT_LEN;
213 			masked_byte = (current_byte & MASK_LOWER_6BIT);
214 			*base64_out_pos++ = dhd_base64_get_code(masked_byte);
215 			offset = BASE64_OFFSET0;
216 		}
217 	}
218 	if (offset == BASE64_OFFSET1) {
219 		*base64_out_pos++ = dhd_base64_get_code(masked_byte);
220 		*base64_out_pos++ = '=';
221 		*base64_out_pos++ = '=';
222 	} else if (offset == BASE64_OFFSET2) {
223 		*base64_out_pos++ = dhd_base64_get_code(masked_byte);
224 		*base64_out_pos++ = '=';
225 	}
226 
227 	return base64_out_pos - base64_out;
228 }
229