xref: /btstack/chipset/realtek/btstack_chipset_realtek.c (revision e3ba22907f903f11cd12321c31e7936b8dd1157e)
1 /*
2  * Copyright (C) 2022 BlueKitchen GmbH
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions
6  * are met:
7  *
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright
11  *    notice, this list of conditions and the following disclaimer in the
12  *    documentation and/or other materials provided with the distribution.
13  * 3. Neither the name of the copyright holders nor the names of
14  *    contributors may be used to endorse or promote products derived
15  *    from this software without specific prior written permission.
16  * 4. Any redistribution, use, or modification is done solely for
17  *    personal benefit and not for any commercial purpose or for
18  *    monetary gain.
19  *
20  * THIS SOFTWARE IS PROVIDED BY BLUEKITCHEN GMBH AND CONTRIBUTORS
21  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
22  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
23  * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL BLUEKITCHEN
24  * GMBH OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
25  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
26  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
27  * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
28  * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
29  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
30  * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
31  * SUCH DAMAGE.
32  *
33  * Please inquire about commercial licensing options at
34  * [email protected]
35  *
36  */
37 
38 #define BTSTACK_FILE__ "btstack_chipset_realtek.c"
39 
40 /*
41  *  btstack_chipset_realtek.c
42  *
43  *  Adapter to use REALTEK-based chipsets with BTstack
44  */
45 
46 #include "btstack_chipset_realtek.h"
47 
48 #include <stddef.h> /* NULL */
49 #include <stdio.h>
50 #include <string.h> /* memcpy */
51 
52 #include "btstack_control.h"
53 #include "btstack_debug.h"
54 #include "btstack_event.h"
55 #include "btstack_util.h"
56 #include "hci.h"
57 #include "hci_transport.h"
58 
59 #ifdef _MSC_VER
60 // ignore deprecated warning for fopen
61 #pragma warning(disable : 4996)
62 #endif
63 
64 #define ROM_LMP_NONE 0x0000
65 #define ROM_LMP_8723a 0x1200
66 #define ROM_LMP_8723b 0x8723
67 #define ROM_LMP_8821a 0X8821
68 #define ROM_LMP_8761a 0X8761
69 #define ROM_LMP_8822b 0X8822
70 
71 #define HCI_DOWNLOAD_FW 0xFC20
72 #define HCI_READ_ROM_VERSION 0xFC6D
73 #define HCI_READ_LMP_VERSION 0x1001
74 #define HCI_RESET 0x0C03
75 
76 #define FILL_COMMAND(buf, command) ((int16_t *)buf)[0] = command
77 #define FILL_LENGTH(buf, length) buf[2] = length
78 #define FILL_INDEX(buf, index) buf[3] = index
79 #define FILL_FW_DATA(buf, firmware, ptr, len) memcpy(buf + 4, firmware + ptr, len)
80 
81 enum {
82     STATE_READ_ROM_VERSION,
83     STATE_LOAD_FIRMWARE,
84     STATE_RESET,
85     STATE_DONE,
86 };
87 
88 enum { FW_DONE, FW_MORE_TO_DO };
89 
90 typedef struct {
91     uint16_t prod_id;
92     uint16_t lmp_sub;
93     char *   mp_patch_name;
94     char *   patch_name;
95     char *   config_name;
96 
97     uint8_t *fw_cache1;
98     int      fw_len1;
99 } patch_info;
100 
101 static patch_info fw_patch_table[] = {
102     /* { pid, lmp_sub, mp_fw_name, fw_name, config_name, fw_cache, fw_len } */
103     {0x1724, 0x1200, "mp_rtl8723a_fw", "rtl8723a_fw", "rtl8723a_config", NULL, 0},	/* RTL8723A */
104     {0x8723, 0x1200, "mp_rtl8723a_fw", "rtl8723a_fw", "rtl8723a_config", NULL, 0},	/* 8723AE */
105     {0xA723, 0x1200, "mp_rtl8723a_fw", "rtl8723a_fw", "rtl8723a_config", NULL, 0},	/* 8723AE for LI */
106     {0x0723, 0x1200, "mp_rtl8723a_fw", "rtl8723a_fw", "rtl8723a_config", NULL, 0},	/* 8723AE */
107     {0x3394, 0x1200, "mp_rtl8723a_fw", "rtl8723a_fw", "rtl8723a_config", NULL, 0},	/* 8723AE for Azurewave */
108 
109     {0x0724, 0x1200, "mp_rtl8723a_fw", "rtl8723a_fw", "rtl8723a_config", NULL, 0},	/* 8723AU */
110     {0x8725, 0x1200, "mp_rtl8723a_fw", "rtl8723a_fw", "rtl8723a_config", NULL, 0},	/* 8723AU */
111     {0x872A, 0x1200, "mp_rtl8723a_fw", "rtl8723a_fw", "rtl8723a_config", NULL, 0},	/* 8723AU */
112     {0x872B, 0x1200, "mp_rtl8723a_fw", "rtl8723a_fw", "rtl8723a_config", NULL, 0},	/* 8723AU */
113 
114     {0xb720, 0x8723, "mp_rtl8723b_fw", "rtl8723b_fw", "rtl8723b_config", NULL, 0},	/* RTL8723BU */
115     {0xb72A, 0x8723, "mp_rtl8723b_fw", "rtl8723b_fw", "rtl8723b_config", NULL, 0},	/* RTL8723BU */
116 
117     {0xb728, 0x8723, "mp_rtl8723b_fw", "rtl8723b_fw", "rtl8723b_config", NULL, 0},	/* RTL8723BE for LC */
118     {0xb723, 0x8723, "mp_rtl8723b_fw", "rtl8723b_fw", "rtl8723b_config", NULL, 0},	/* RTL8723BE */
119     {0xb72B, 0x8723, "mp_rtl8723b_fw", "rtl8723b_fw", "rtl8723b_config", NULL, 0},	/* RTL8723BE */
120     {0xb001, 0x8723, "mp_rtl8723b_fw", "rtl8723b_fw", "rtl8723b_config", NULL, 0},	/* RTL8723BE for HP */
121     {0xb002, 0x8723, "mp_rtl8723b_fw", "rtl8723b_fw", "rtl8723b_config", NULL, 0},	/* RTL8723BE */
122     {0xb003, 0x8723, "mp_rtl8723b_fw", "rtl8723b_fw", "rtl8723b_config", NULL, 0},	/* RTL8723BE */
123     {0xb004, 0x8723, "mp_rtl8723b_fw", "rtl8723b_fw", "rtl8723b_config", NULL, 0},	/* RTL8723BE */
124     {0xb005, 0x8723, "mp_rtl8723b_fw", "rtl8723b_fw", "rtl8723b_config", NULL, 0},	/* RTL8723BE */
125     {0x3410, 0x8723, "mp_rtl8723b_fw", "rtl8723b_fw", "rtl8723b_config", NULL, 0},	/* RTL8723BE for Azurewave */
126     {0x3416, 0x8723, "mp_rtl8723b_fw", "rtl8723b_fw", "rtl8723b_config", NULL, 0},	/* RTL8723BE for Azurewave */
127     {0x3459, 0x8723, "mp_rtl8723b_fw", "rtl8723b_fw", "rtl8723b_config", NULL, 0},	/* RTL8723BE for Azurewave */
128     {0xE085, 0x8723, "mp_rtl8723b_fw", "rtl8723b_fw", "rtl8723b_config", NULL, 0},	/* RTL8723BE for Foxconn */
129     {0xE08B, 0x8723, "mp_rtl8723b_fw", "rtl8723b_fw", "rtl8723b_config", NULL, 0},	/* RTL8723BE for Foxconn */
130     {0xE09E, 0x8723, "mp_rtl8723b_fw", "rtl8723b_fw", "rtl8723b_config", NULL, 0},	/* RTL8723BE for Foxconn */
131 
132     {0xd720, 0x8723, "mp_rtl8723du_fw", "rtl8723du_fw", "rtl8723du_config", NULL, 0}, /* RTL8723DU */
133     {0xd723, 0x8723, "mp_rtl8723du_fw", "rtl8723du_fw", "rtl8723du_config", NULL, 0}, /* RTL8723DU */
134     {0xd739, 0x8723, "mp_rtl8723du_fw", "rtl8723du_fw", "rtl8723du_config", NULL, 0}, /* RTL8723DU */
135     {0xb009, 0x8723, "mp_rtl8723du_fw", "rtl8723du_fw", "rtl8723du_config", NULL, 0}, /* RTL8723DU */
136     {0x0231, 0x8723, "mp_rtl8723du_fw", "rtl8723du_fw", "rtl8723du_config", NULL, 0}, /* RTL8723DU for LiteOn */
137 
138     {0xb733, 0x8723, "mp_rtl8723fu_fw", "rtl8723fu_fw", "rtl8723fu_config", NULL, 0}, /* RTL8723FU */
139     {0xb73a, 0x8723, "mp_rtl8723fu_fw", "rtl8723fu_fw", "rtl8723fu_config", NULL, 0}, /* RTL8723FU */
140     {0xf72b, 0x8723, "mp_rtl8723fu_fw", "rtl8723fu_fw", "rtl8723fu_config", NULL, 0}, /* RTL8723FU */
141 
142     {0xA761, 0x8761, "mp_rtl8761a_fw", "rtl8761au_fw", "rtl8761a_config", NULL, 0},	/* RTL8761AU only */
143     {0x818B, 0x8761, "mp_rtl8761a_fw", "rtl8761aw_fw", "rtl8761aw_config", NULL, 0},	/* RTL8761AW + 8192EU */
144     {0x818C, 0x8761, "mp_rtl8761a_fw", "rtl8761aw_fw", "rtl8761aw_config", NULL, 0},	/* RTL8761AW + 8192EU */
145     {0x8760, 0x8761, "mp_rtl8761a_fw", "rtl8761au8192ee_fw", "rtl8761a_config", NULL, 0},	/* RTL8761AU + 8192EE */
146     {0xB761, 0x8761, "mp_rtl8761a_fw", "rtl8761au_fw", "rtl8761a_config", NULL, 0},	/* RTL8761AUV only */
147     {0x8761, 0x8761, "mp_rtl8761a_fw", "rtl8761au8192ee_fw", "rtl8761a_config", NULL, 0},	/* RTL8761AU + 8192EE for LI */
148     {0x8A60, 0x8761, "mp_rtl8761a_fw", "rtl8761au8812ae_fw", "rtl8761a_config", NULL, 0},	/* RTL8761AU + 8812AE */
149     {0x3527, 0x8761, "mp_rtl8761a_fw", "rtl8761au8192ee_fw", "rtl8761a_config", NULL, 0},	/* RTL8761AU + 8814AE */
150 
151     {0x8771, 0x8761, "mp_rtl8761b_fw", "rtl8761bu_fw", "rtl8761bu_config", NULL, 0}, /* RTL8761BU only */
152     {0xa725, 0x8761, "mp_rtl8761b_fw", "rtl8725au_fw", "rtl8725au_config", NULL, 0}, /* RTL8725AU */
153     {0xa72A, 0x8761, "mp_rtl8761b_fw", "rtl8725au_fw", "rtl8725au_config", NULL, 0}, /* RTL8725AU BT only */
154 
155     {0x8821, 0x8821, "mp_rtl8821a_fw", "rtl8821a_fw", "rtl8821a_config", NULL, 0},	/* RTL8821AE */
156     {0x0821, 0x8821, "mp_rtl8821a_fw", "rtl8821a_fw", "rtl8821a_config", NULL, 0},	/* RTL8821AE */
157     {0x0823, 0x8821, "mp_rtl8821a_fw", "rtl8821a_fw", "rtl8821a_config", NULL, 0},	/* RTL8821AU */
158     {0x3414, 0x8821, "mp_rtl8821a_fw", "rtl8821a_fw", "rtl8821a_config", NULL, 0},	/* RTL8821AE */
159     {0x3458, 0x8821, "mp_rtl8821a_fw", "rtl8821a_fw", "rtl8821a_config", NULL, 0},	/* RTL8821AE */
160     {0x3461, 0x8821, "mp_rtl8821a_fw", "rtl8821a_fw", "rtl8821a_config", NULL, 0},	/* RTL8821AE */
161     {0x3462, 0x8821, "mp_rtl8821a_fw", "rtl8821a_fw", "rtl8821a_config", NULL, 0},	/* RTL8821AE */
162 
163     {0xb820, 0x8821, "mp_rtl8821cu_fw", "rtl8821cu_fw", "rtl8821cu_config", NULL, 0}, /* RTL8821CU */
164     {0xc820, 0x8821, "mp_rtl8821cu_fw", "rtl8821cu_fw", "rtl8821cu_config", NULL, 0}, /* RTL8821CU */
165     {0xc821, 0x8821, "mp_rtl8821cu_fw", "rtl8821cu_fw", "rtl8821cu_config", NULL, 0}, /* RTL8821CE */
166     {0xc823, 0x8821, "mp_rtl8821cu_fw", "rtl8821cu_fw", "rtl8821cu_config", NULL, 0}, /* RTL8821CE */
167     {0xc824, 0x8821, "mp_rtl8821cu_fw", "rtl8821cu_fw", "rtl8821cu_config", NULL, 0}, /* RTL8821CE */
168     {0xc825, 0x8821, "mp_rtl8821cu_fw", "rtl8821cu_fw", "rtl8821cu_config", NULL, 0}, /* RTL8821CE */
169     {0xc827, 0x8821, "mp_rtl8821cu_fw", "rtl8821cu_fw", "rtl8821cu_config", NULL, 0}, /* RTL8821CE */
170     {0xc025, 0x8821, "mp_rtl8821cu_fw", "rtl8821cu_fw", "rtl8821cu_config", NULL, 0}, /* RTL8821CE */
171     {0xc024, 0x8821, "mp_rtl8821cu_fw", "rtl8821cu_fw", "rtl8821cu_config", NULL, 0}, /* RTL8821CE */
172     {0xc030, 0x8821, "mp_rtl8821cu_fw", "rtl8821cu_fw", "rtl8821cu_config", NULL, 0}, /* RTL8821CE */
173     {0xb00a, 0x8821, "mp_rtl8821cu_fw", "rtl8821cu_fw", "rtl8821cu_config", NULL, 0}, /* RTL8821CE */
174     {0xb00e, 0x8821, "mp_rtl8821cu_fw", "rtl8821cu_fw", "rtl8821cu_config", NULL, 0}, /* RTL8821CE */
175     {0xc032, 0x8821, "mp_rtl8821cu_fw", "rtl8821cu_fw", "rtl8821cu_config", NULL, 0}, /* RTL8821CE */
176     {0x4000, 0x8821, "mp_rtl8821cu_fw", "rtl8821cu_fw", "rtl8821cu_config", NULL, 0}, /* RTL8821CE for LiteOn */
177     {0x4001, 0x8821, "mp_rtl8821cu_fw", "rtl8821cu_fw", "rtl8821cu_config", NULL, 0}, /* RTL8821CE for LiteOn */
178     {0x3529, 0x8821, "mp_rtl8821cu_fw", "rtl8821cu_fw", "rtl8821cu_config", NULL, 0}, /* RTL8821CE for Azurewave */
179     {0x3530, 0x8821, "mp_rtl8821cu_fw", "rtl8821cu_fw", "rtl8821cu_config", NULL, 0}, /* RTL8821CE for Azurewave */
180     {0x3532, 0x8821, "mp_rtl8821cu_fw", "rtl8821cu_fw", "rtl8821cu_config", NULL, 0}, /* RTL8821CE for Azurewave */
181     {0x3533, 0x8821, "mp_rtl8821cu_fw", "rtl8821cu_fw", "rtl8821cu_config", NULL, 0}, /* RTL8821CE for Azurewave */
182     {0x3538, 0x8821, "mp_rtl8821cu_fw", "rtl8821cu_fw", "rtl8821cu_config", NULL, 0}, /* RTL8821CE for Azurewave */
183     {0x3539, 0x8821, "mp_rtl8821cu_fw", "rtl8821cu_fw", "rtl8821cu_config", NULL, 0}, /* RTL8821CE for Azurewave */
184     {0x3558, 0x8821, "mp_rtl8821cu_fw", "rtl8821cu_fw", "rtl8821cu_config", NULL, 0}, /* RTL8821CE for Azurewave */
185     {0x3559, 0x8821, "mp_rtl8821cu_fw", "rtl8821cu_fw", "rtl8821cu_config", NULL, 0}, /* RTL8821CE for Azurewave */
186     {0x3581, 0x8821, "mp_rtl8821cu_fw", "rtl8821cu_fw", "rtl8821cu_config", NULL, 0}, /* RTL8821CE for Azurewave */
187     {0x3540, 0x8821, "mp_rtl8821cu_fw", "rtl8821cu_fw", "rtl8821cu_config", NULL, 0}, /* RTL8821CE */
188     {0x3541, 0x8821, "mp_rtl8821cu_fw", "rtl8821cu_fw", "rtl8821cu_config", NULL, 0}, /* RTL8821CE for GSD */
189     {0x3543, 0x8821, "mp_rtl8821cu_fw", "rtl8821cu_fw", "rtl8821cu_config", NULL, 0}, /* RTL8821CE for GSD */
190     {0xc80c, 0x8821, "mp_rtl8821cu_fw", "rtl8821cu_fw", "rtl8821cu_config", NULL, 0}, /* RTL8821CUH */
191 
192     {0xd820, 0x8822, "mp_rtl8821du_fw", "rtl8821du_fw", "rtl8821du_config", NULL, 0}, /* RTL8821DU */
193 
194     {0xb82c, 0x8822, "mp_rtl8822bu_fw", "rtl8822bu_fw", "rtl8822bu_config", NULL, 0}, /* RTL8822BU */
195 
196     {0xc82c, 0x8822, "mp_rtl8822cu_fw", "rtl8822cu_fw", "rtl8822cu_config", NULL, 0}, /* RTL8822CU */
197     {0xc82e, 0x8822, "mp_rtl8822cu_fw", "rtl8822cu_fw", "rtl8822cu_config", NULL, 0}, /* RTL8822CU */
198     {0xc81d, 0x8822, "mp_rtl8822cu_fw", "rtl8822cu_fw", "rtl8822cu_config", NULL, 0}, /* RTL8822CU */
199 
200     {0xc822, 0x8822, "mp_rtl8822cu_fw", "rtl8822cu_fw", "rtl8822cu_config", NULL, 0}, /* RTL8822CE */
201     {0xc82b, 0x8822, "mp_rtl8822cu_fw", "rtl8822cu_fw", "rtl8822cu_config", NULL, 0}, /* RTL8822CE */
202     {0xb00c, 0x8822, "mp_rtl8822cu_fw", "rtl8822cu_fw", "rtl8822cu_config", NULL, 0}, /* RTL8822CE */
203     {0xb00d, 0x8822, "mp_rtl8822cu_fw", "rtl8822cu_fw", "rtl8822cu_config", NULL, 0}, /* RTL8822CE */
204     {0xc123, 0x8822, "mp_rtl8822cu_fw", "rtl8822cu_fw", "rtl8822cu_config", NULL, 0}, /* RTL8822CE */
205     {0xc126, 0x8822, "mp_rtl8822cu_fw", "rtl8822cu_fw", "rtl8822cu_config", NULL, 0}, /* RTL8822CE */
206     {0xc127, 0x8822, "mp_rtl8822cu_fw", "rtl8822cu_fw", "rtl8822cu_config", NULL, 0}, /* RTL8822CE */
207     {0xc128, 0x8822, "mp_rtl8822cu_fw", "rtl8822cu_fw", "rtl8822cu_config", NULL, 0}, /* RTL8822CE */
208     {0xc129, 0x8822, "mp_rtl8822cu_fw", "rtl8822cu_fw", "rtl8822cu_config", NULL, 0}, /* RTL8822CE */
209     {0xc131, 0x8822, "mp_rtl8822cu_fw", "rtl8822cu_fw", "rtl8822cu_config", NULL, 0}, /* RTL8822CE */
210     {0xc136, 0x8822, "mp_rtl8822cu_fw", "rtl8822cu_fw", "rtl8822cu_config", NULL, 0}, /* RTL8822CE */
211     {0x3549, 0x8822, "mp_rtl8822cu_fw", "rtl8822cu_fw", "rtl8822cu_config", NULL, 0}, /* RTL8822CE for Azurewave */
212     {0x3548, 0x8822, "mp_rtl8822cu_fw", "rtl8822cu_fw", "rtl8822cu_config", NULL, 0}, /* RTL8822CE for Azurewave */
213     {0xc125, 0x8822, "mp_rtl8822cu_fw", "rtl8822cu_fw", "rtl8822cu_config", NULL, 0}, /* RTL8822CE */
214     {0x4005, 0x8822, "mp_rtl8822cu_fw", "rtl8822cu_fw", "rtl8822cu_config", NULL, 0}, /* RTL8822CE for LiteOn */
215     {0x3051, 0x8822, "mp_rtl8822cu_fw", "rtl8822cu_fw", "rtl8822cu_config", NULL, 0}, /* RTL8822CE for LiteOn */
216     {0x18ef, 0x8822, "mp_rtl8822cu_fw", "rtl8822cu_fw", "rtl8822cu_config", NULL, 0}, /* RTL8822CE */
217     {0x161f, 0x8822, "mp_rtl8822cu_fw", "rtl8822cu_fw", "rtl8822cu_config", NULL, 0}, /* RTL8822CE */
218     {0x3053, 0x8822, "mp_rtl8822cu_fw", "rtl8822cu_fw", "rtl8822cu_config", NULL, 0}, /* RTL8822CE */
219     {0xc547, 0x8822, "mp_rtl8822cu_fw", "rtl8822cu_fw", "rtl8822cu_config", NULL, 0}, /* RTL8822CE */
220     {0x3553, 0x8822, "mp_rtl8822cu_fw", "rtl8822cu_fw", "rtl8822cu_config", NULL, 0}, /* RTL8822CE */
221     {0x3555, 0x8822, "mp_rtl8822cu_fw", "rtl8822cu_fw", "rtl8822cu_config", NULL, 0}, /* RTL8822CE */
222     {0xc82f, 0x8822, "mp_rtl8822cu_fw", "rtl8822cu_fw", "rtl8822cu_config", NULL, 0}, /* RTL8822CE-VS */
223     {0xc02f, 0x8822, "mp_rtl8822cu_fw", "rtl8822cu_fw", "rtl8822cu_config", NULL, 0}, /* RTL8822CE-VS */
224     {0xc03f, 0x8822, "mp_rtl8822cu_fw", "rtl8822cu_fw", "rtl8822cu_config", NULL, 0}, /* RTL8822CE-VS */
225 
226     {0x8851, 0x8852, "mp_rtl8851au_fw", "rtl8851au_fw", "rtl8851au_config", NULL, 0}, /* RTL8851AU */
227 
228     {0xb851, 0x8851, "mp_rtl8851bu_fw", "rtl8851bu_fw", "rtl8851bu_config", NULL, 0}, /* RTL8851BU */
229 
230     {0x885a, 0x8852, "mp_rtl8852au_fw", "rtl8852au_fw", "rtl8852au_config", NULL, 0}, /* RTL8852AU */
231     {0x8852, 0x8852, "mp_rtl8852au_fw", "rtl8852au_fw", "rtl8852au_config", NULL, 0}, /* RTL8852AE */
232     {0xa852, 0x8852, "mp_rtl8852au_fw", "rtl8852au_fw", "rtl8852au_config", NULL, 0}, /* RTL8852AE */
233     {0x2852, 0x8852, "mp_rtl8852au_fw", "rtl8852au_fw", "rtl8852au_config", NULL, 0}, /* RTL8852AE */
234     {0x385a, 0x8852, "mp_rtl8852au_fw", "rtl8852au_fw", "rtl8852au_config", NULL, 0}, /* RTL8852AE */
235     {0x3852, 0x8852, "mp_rtl8852au_fw", "rtl8852au_fw", "rtl8852au_config", NULL, 0}, /* RTL8852AE */
236     {0x1852, 0x8852, "mp_rtl8852au_fw", "rtl8852au_fw", "rtl8852au_config", NULL, 0}, /* RTL8852AE */
237     {0x4852, 0x8852, "mp_rtl8852au_fw", "rtl8852au_fw", "rtl8852au_config", NULL, 0}, /* RTL8852AE */
238     {0x4006, 0x8852, "mp_rtl8852au_fw", "rtl8852au_fw", "rtl8852au_config", NULL, 0}, /* RTL8852AE */
239     {0x3561, 0x8852, "mp_rtl8852au_fw", "rtl8852au_fw", "rtl8852au_config", NULL, 0}, /* RTL8852AE */
240     {0x3562, 0x8852, "mp_rtl8852au_fw", "rtl8852au_fw", "rtl8852au_config", NULL, 0}, /* RTL8852AE */
241     {0x588a, 0x8852, "mp_rtl8852au_fw", "rtl8852au_fw", "rtl8852au_config", NULL, 0}, /* RTL8852AE */
242     {0x589a, 0x8852, "mp_rtl8852au_fw", "rtl8852au_fw", "rtl8852au_config", NULL, 0}, /* RTL8852AE */
243     {0x590a, 0x8852, "mp_rtl8852au_fw", "rtl8852au_fw", "rtl8852au_config", NULL, 0}, /* RTL8852AE */
244     {0xc125, 0x8852, "mp_rtl8852au_fw", "rtl8852au_fw", "rtl8852au_config", NULL, 0}, /* RTL8852AE */
245     {0xe852, 0x8852, "mp_rtl8852au_fw", "rtl8852au_fw", "rtl8852au_config", NULL, 0}, /* RTL8852AE */
246     {0xb852, 0x8852, "mp_rtl8852au_fw", "rtl8852au_fw", "rtl8852au_config", NULL, 0}, /* RTL8852AE */
247     {0xc852, 0x8852, "mp_rtl8852au_fw", "rtl8852au_fw", "rtl8852au_config", NULL, 0}, /* RTL8852AE */
248     {0xc549, 0x8852, "mp_rtl8852au_fw", "rtl8852au_fw", "rtl8852au_config", NULL, 0}, /* RTL8852AE */
249     {0xc127, 0x8852, "mp_rtl8852au_fw", "rtl8852au_fw", "rtl8852au_config", NULL, 0}, /* RTL8852AE */
250     {0x3565, 0x8852, "mp_rtl8852au_fw", "rtl8852au_fw", "rtl8852au_config", NULL, 0}, /* RTL8852AE */
251 
252     {0xa85b, 0x8852, "mp_rtl8852bu_fw", "rtl8852bu_fw", "rtl8852bu_config", NULL, 0}, /* RTL8852BU */
253     {0xb85b, 0x8852, "mp_rtl8852bu_fw", "rtl8852bu_fw", "rtl8852bu_config", NULL, 0}, /* RTL8852BE */
254     {0xb85c, 0x8852, "mp_rtl8852bu_fw", "rtl8852bu_fw", "rtl8852bu_config", NULL, 0}, /* RTL8852BE */
255     {0x3571, 0x8852, "mp_rtl8852bu_fw", "rtl8852bu_fw", "rtl8852bu_config", NULL, 0}, /* RTL8852BE */
256     {0x3570, 0x8852, "mp_rtl8852bu_fw", "rtl8852bu_fw", "rtl8852bu_config", NULL, 0}, /* RTL8852BE */
257     {0x3572, 0x8852, "mp_rtl8852bu_fw", "rtl8852bu_fw", "rtl8852bu_config", NULL, 0}, /* RTL8852BE */
258     {0x4b06, 0x8852, "mp_rtl8852bu_fw", "rtl8852bu_fw", "rtl8852bu_config", NULL, 0}, /* RTL8852BE */
259     {0x885b, 0x8852, "mp_rtl8852bu_fw", "rtl8852bu_fw", "rtl8852bu_config", NULL, 0}, /* RTL8852BE */
260     {0x886b, 0x8852, "mp_rtl8852bu_fw", "rtl8852bu_fw", "rtl8852bu_config", NULL, 0}, /* RTL8852BE */
261     {0x887b, 0x8852, "mp_rtl8852bu_fw", "rtl8852bu_fw", "rtl8852bu_config", NULL, 0}, /* RTL8852BE */
262     {0xc559, 0x8852, "mp_rtl8852bu_fw", "rtl8852bu_fw", "rtl8852bu_config", NULL, 0}, /* RTL8852BE */
263     {0xb052, 0x8852, "mp_rtl8852bu_fw", "rtl8852bu_fw", "rtl8852bu_config", NULL, 0}, /* RTL8852BE */
264     {0xb152, 0x8852, "mp_rtl8852bu_fw", "rtl8852bu_fw", "rtl8852bu_config", NULL, 0}, /* RTL8852BE */
265     {0xb252, 0x8852, "mp_rtl8852bu_fw", "rtl8852bu_fw", "rtl8852bu_config", NULL, 0}, /* RTL8852BE */
266     {0x4853, 0x8852, "mp_rtl8852bu_fw", "rtl8852bu_fw", "rtl8852bu_config", NULL, 0}, /* RTL8852BE */
267     {0x1670, 0x8852, "mp_rtl8852bu_fw", "rtl8852bu_fw", "rtl8852bu_config", NULL, 0}, /* RTL8852BE */
268 
269     {0xc85a, 0x8852, "mp_rtl8852cu_fw", "rtl8852cu_fw", "rtl8852cu_config", NULL, 0}, /* RTL8852CU */
270     {0x0852, 0x8852, "mp_rtl8852cu_fw", "rtl8852cu_fw", "rtl8852cu_config", NULL, 0}, /* RTL8852CE */
271     {0x5852, 0x8852, "mp_rtl8852cu_fw", "rtl8852cu_fw", "rtl8852cu_config", NULL, 0}, /* RTL8852CE */
272     {0xc85c, 0x8852, "mp_rtl8852cu_fw", "rtl8852cu_fw", "rtl8852cu_config", NULL, 0}, /* RTL8852CE */
273     {0x885c, 0x8852, "mp_rtl8852cu_fw", "rtl8852cu_fw", "rtl8852cu_config", NULL, 0}, /* RTL8852CE */
274     {0x886c, 0x8852, "mp_rtl8852cu_fw", "rtl8852cu_fw", "rtl8852cu_config", NULL, 0}, /* RTL8852CE */
275     {0x887c, 0x8852, "mp_rtl8852cu_fw", "rtl8852cu_fw", "rtl8852cu_config", NULL, 0}, /* RTL8852CE */
276     {0x4007, 0x8852, "mp_rtl8852cu_fw", "rtl8852cu_fw", "rtl8852cu_config", NULL, 0}, /* RTL8852CE */
277 
278     {0xe822, 0x8822, "mp_rtl8822eu_fw", "rtl8822eu_fw", "rtl8822eu_config", NULL, 0}, /* RTL8822EU */
279     {0xa82a, 0x8822, "mp_rtl8822eu_fw", "rtl8822eu_fw", "rtl8822eu_config", NULL, 0}, /* RTL8822EU */
280 
281     /* NOTE: must append patch entries above the null entry */
282     {0, 0, NULL, NULL, NULL, NULL, 0}};
283 
284 uint16_t project_id[] = {
285     ROM_LMP_8723a, ROM_LMP_8723b, ROM_LMP_8821a, ROM_LMP_8761a, ROM_LMP_NONE,
286     ROM_LMP_NONE,  ROM_LMP_NONE,  ROM_LMP_NONE,  ROM_LMP_8822b, ROM_LMP_8723b, /* RTL8723DU */
287     ROM_LMP_8821a,                                                             /* RTL8821CU */
288     ROM_LMP_NONE,  ROM_LMP_NONE,  ROM_LMP_8822b,                               /* RTL8822CU */
289     ROM_LMP_8761a,                                                             /* index 14 for 8761BU */
290 };
291 
292 static btstack_packet_callback_registration_t hci_event_callback_registration;
293 static uint8_t                                state = STATE_READ_ROM_VERSION;
294 static uint8_t                                rom_version;
295 static uint16_t                               lmp_subversion;
296 static uint16_t                               product_id;
297 static patch_info *                           patch;
298 
299 #ifdef HAVE_POSIX_FILE_IO
300 static const char *firmware_folder_path = ".";
301 static const char *firmware_file_path;
302 static const char *config_folder_path = ".";
303 static const char *config_file_path;
304 static char        firmware_file[1000];
305 static char        config_file[1000];
306 #endif
307 
308 const uint8_t FW_SIGNATURE[8]        = {0x52, 0x65, 0x61, 0x6C, 0x74, 0x65, 0x63, 0x68};
309 const uint8_t EXTENSION_SIGNATURE[4] = {0x51, 0x04, 0xFD, 0x77};
310 
311 static void hci_packet_handler(uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size) {
312     UNUSED(channel);
313     UNUSED(size);
314     if (packet_type != HCI_EVENT_PACKET || hci_event_packet_get_type(packet) != HCI_EVENT_COMMAND_COMPLETE) {
315         return;
316     }
317     uint16_t opcode = hci_event_command_complete_get_command_opcode(packet);
318 
319     switch (opcode) {
320     case HCI_READ_ROM_VERSION:
321         rom_version = hci_event_command_complete_get_return_parameters(packet)[1];
322         log_info("Received ROM version 0x%02x", rom_version);
323         break;
324     default:
325         break;
326     }
327 }
328 
329 static void chipset_init(const void *config) {
330     UNUSED(config);
331 #ifdef HAVE_POSIX_FILE_IO
332     // determine file path
333     if (firmware_file_path == NULL || config_file_path == NULL) {
334         log_info("firmware or config file path is empty. Using product id 0x%04x!", product_id);
335         patch = NULL;
336         for (uint16_t i = 0; i < sizeof(fw_patch_table) / sizeof(patch_info); i++) {
337             if (fw_patch_table[i].prod_id == product_id) {
338                 patch = &fw_patch_table[i];
339                 break;
340             }
341         }
342         if (patch == NULL) {
343             log_info("Product id 0x%04x is unknown", product_id);
344             state = STATE_DONE;
345             return;
346         }
347         snprintf(firmware_file, sizeof(firmware_file), "%s/%s", firmware_folder_path, patch->patch_name);
348         snprintf(config_file, sizeof(config_file), "%s/%s", config_folder_path, patch->config_name);
349         firmware_file_path = &firmware_file[0];
350         config_file_path   = &config_file[0];
351         lmp_subversion     = patch->lmp_sub;
352     }
353     log_info("Using firmware '%s' and config '%s'", firmware_file_path, config_file_path);
354 
355     // activate hci callback
356     hci_event_callback_registration.callback = &hci_packet_handler;
357     hci_add_event_handler(&hci_event_callback_registration);
358     state = STATE_READ_ROM_VERSION;
359 #endif
360 }
361 
362 #ifdef HAVE_POSIX_FILE_IO
363 
364 /**
365  * @brief Opens the specified file and stores content to an allocated buffer
366  *
367  * @param file
368  * @param buf
369  * @param name
370  * @return uint32_t Length of file
371  */
372 static uint32_t read_file(FILE **file, uint8_t **buf, const char *name) {
373     uint32_t size;
374 
375     // open file
376     *file = fopen(name, "rb");
377     if (*file == NULL) {
378         log_info("Failed to open file %s", name);
379         return 0;
380     }
381 
382     // determine length of file
383     fseek(*file, 0, SEEK_END);
384     size = ftell(*file);
385     fseek(*file, 0, SEEK_SET);
386     if (size <= 0) {
387         return 0;
388     }
389 
390     // allocate memory
391     *buf = malloc(size);
392     if (*buf == NULL) {
393         fclose(*file);
394         *file = NULL;
395         log_info("Failed to allocate %u bytes for file %s", size, name);
396         return 0;
397     }
398 
399     // read file
400     size_t ret = fread(*buf, size, 1, *file);
401     if (ret != 1) {
402         log_info("Failed to read %u bytes from file %s (ret = %d)", size, name, (int) ret);
403         fclose(*file);
404         free(*buf);
405         *file = NULL;
406         *buf  = NULL;
407         return 0;
408     }
409 
410     log_info("Opened file %s and read %u bytes", name, size);
411     return size;
412 }
413 
414 static void finalize_file_and_buffer(FILE **file, uint8_t **buffer) {
415     fclose(*file);
416     free(*buffer);
417     *buffer = NULL;
418     *file   = NULL;
419 }
420 
421 static uint8_t update_firmware(const char *firmware, const char *config, uint8_t *hci_cmd_buffer) {
422     static uint8_t *patch_buf = NULL;
423     static uint32_t fw_total_len;
424     static uint32_t fw_ptr;
425     static uint8_t  index;
426 
427     // read firmware and config
428     if (patch_buf == NULL) {
429         uint16_t patch_length = 0;
430         uint32_t offset;
431         FILE *   fw = NULL;
432         uint32_t fw_size;
433         uint8_t *fw_buf = NULL;
434 
435         FILE *   conf = NULL;
436         uint32_t conf_size;
437         uint8_t *conf_buf = NULL;
438 
439         if (firmware == NULL || config == NULL) {
440             log_info("Please specify realtek firmware and config file paths");
441             return FW_DONE;
442         }
443 
444         // read firmware
445         fw_size = read_file(&fw, &fw_buf, firmware);
446         if (fw_size == 0) {
447             log_info("Firmware size is 0. Quit!");
448             return FW_DONE;
449         }
450 
451         // read config
452         conf_size = read_file(&conf, &conf_buf, config);
453         if (conf_size == 0) {
454             log_info("Config size is 0. Quit!");
455             fclose(fw);
456             free(fw_buf);
457             fw_buf = NULL;
458             fw     = NULL;
459             return FW_DONE;
460             finalize_file_and_buffer(&fw, &fw_buf);
461         }
462 
463         // check signature
464         if (memcmp(fw_buf, FW_SIGNATURE, 8) != 0 || memcmp(fw_buf + fw_size - 4, EXTENSION_SIGNATURE, 4) != 0) {
465             log_info("Wrong signature. Quit!");
466             finalize_file_and_buffer(&fw, &fw_buf);
467             finalize_file_and_buffer(&conf, &conf_buf);
468             return FW_DONE;
469         }
470 
471         // check project id
472         if (lmp_subversion != project_id[*(fw_buf + fw_size - 7)]) {
473             log_info("Wrong project id. Quit!");
474             finalize_file_and_buffer(&fw, &fw_buf);
475             finalize_file_and_buffer(&conf, &conf_buf);
476             return FW_DONE;
477         }
478 
479         // read firmware version
480         uint32_t fw_version = little_endian_read_32(fw_buf, 8);
481         log_info("Firmware version: 0x%x", fw_version);
482 
483         // read number of patches
484         uint16_t fw_num_patches = little_endian_read_16(fw_buf, 12);
485         log_info("Number of patches: %d", fw_num_patches);
486 
487         // find correct entry
488         for (uint16_t i = 0; i < fw_num_patches; i++) {
489             if (little_endian_read_16(fw_buf, 14 + 2 * i) == rom_version + 1) {
490                 patch_length = little_endian_read_16(fw_buf, 14 + 2 * fw_num_patches + 2 * i);
491                 offset       = little_endian_read_32(fw_buf, 14 + 4 * fw_num_patches + 4 * i);
492                 log_info("patch_length %u, offset %u", patch_length, offset);
493                 break;
494             }
495         }
496         if (patch_length == 0) {
497             log_debug("Failed to find valid patch");
498             finalize_file_and_buffer(&fw, &fw_buf);
499             finalize_file_and_buffer(&conf, &conf_buf);
500             return FW_DONE;
501         }
502 
503         // allocate patch buffer
504         fw_total_len = patch_length + conf_size;
505         patch_buf    = malloc(fw_total_len);
506         if (patch_buf == NULL) {
507             log_debug("Failed to allocate %u bytes for patch buffer", fw_total_len);
508             finalize_file_and_buffer(&fw, &fw_buf);
509             finalize_file_and_buffer(&conf, &conf_buf);
510             return FW_DONE;
511         }
512 
513         // copy patch
514         memcpy(patch_buf, fw_buf + offset, patch_length);
515         memcpy(patch_buf + patch_length - 4, &fw_version, 4);
516         memcpy(patch_buf + patch_length, conf_buf, conf_size);
517         fw_ptr = 0;
518         index  = 0;
519 
520         // close files
521         finalize_file_and_buffer(&fw, &fw_buf);
522         finalize_file_and_buffer(&conf, &conf_buf);
523     }
524 
525     uint8_t len;
526     if (fw_total_len - fw_ptr > 252) {
527         len = 252;
528     } else {
529         len = fw_total_len - fw_ptr;
530         index |= 0x80;  // end
531     }
532 
533     if (len) {
534         FILL_COMMAND(hci_cmd_buffer, HCI_DOWNLOAD_FW);
535         FILL_LENGTH(hci_cmd_buffer, len + 1);
536         FILL_INDEX(hci_cmd_buffer, index);
537         FILL_FW_DATA(hci_cmd_buffer, patch_buf, fw_ptr, len);
538         index++;
539         fw_ptr += len;
540         return FW_MORE_TO_DO;
541     }
542 
543     // cleanup and return
544     free(patch_buf);
545     patch_buf = NULL;
546     return FW_DONE;
547 }
548 
549 #endif  // HAVE_POSIX_FILE_IO
550 
551 static btstack_chipset_result_t chipset_next_command(uint8_t *hci_cmd_buffer) {
552 #ifdef HAVE_POSIX_FILE_IO
553     uint8_t ret;
554     while (true) {
555         switch (state) {
556         case STATE_READ_ROM_VERSION:
557             FILL_COMMAND(hci_cmd_buffer, HCI_READ_ROM_VERSION);
558             FILL_LENGTH(hci_cmd_buffer, 0);
559             state = STATE_LOAD_FIRMWARE;
560             break;
561         case STATE_LOAD_FIRMWARE:
562             if (lmp_subversion != ROM_LMP_8723a) {
563                 ret = update_firmware(firmware_file_path, config_file_path, hci_cmd_buffer);
564             } else {
565                 log_info("Realtek firmware for old patch style not implemented");
566                 ret = FW_DONE;
567             }
568             if (ret != FW_DONE) {
569                 break;
570             }
571             // we are done fall through
572             state = STATE_RESET;
573         case STATE_RESET:
574             FILL_COMMAND(hci_cmd_buffer, HCI_RESET);
575             FILL_LENGTH(hci_cmd_buffer, 0);
576             state = STATE_DONE;
577             break;
578         case STATE_DONE:
579             hci_remove_event_handler(&hci_event_callback_registration);
580             return BTSTACK_CHIPSET_DONE;
581             break;
582         default:
583             log_info("Invalid state %d", state);
584             return BTSTACK_CHIPSET_DONE;
585             break;
586         }
587         return BTSTACK_CHIPSET_VALID_COMMAND;
588     }
589 #else   // HAVE_POSIX_FILE_IO
590     log_info("Realtek without File IO is not implemented yet");
591     return BTSTACK_CHIPSET_NO_INIT_SCRIPT;
592 #endif  // HAVE_POSIX_FILE_IO
593 }
594 
595 void btstack_chipset_realtek_set_firmware_file_path(const char *path) {
596 #ifdef HAVE_POSIX_FILE_IO
597     firmware_file_path = path;
598 #endif
599 }
600 
601 void btstack_chipset_realtek_set_firmware_folder_path(const char *path) {
602 #ifdef HAVE_POSIX_FILE_IO
603     firmware_folder_path = path;
604 #endif
605 }
606 
607 void btstack_chipset_realtek_set_config_file_path(const char *path) {
608 #ifdef HAVE_POSIX_FILE_IO
609     config_file_path = path;
610 #endif
611 }
612 
613 void btstack_chipset_realtek_set_config_folder_path(const char *path) {
614 #ifdef HAVE_POSIX_FILE_IO
615     config_folder_path = path;
616 #endif
617 }
618 
619 void btstack_chipset_realtek_set_lmp_subversion(uint16_t version) {
620     lmp_subversion = version;
621 }
622 
623 void btstack_chipset_realtek_set_product_id(uint16_t id) {
624     product_id = id;
625 }
626 
627 uint16_t btstack_chipset_realtek_get_num_usb_controllers(void){
628     return (sizeof(fw_patch_table) / sizeof(patch_info)) - 1; // sentinel
629 }
630 
631 void btstack_chipset_realtek_get_vendor_product_id(uint16_t index, uint16_t * out_vendor_id, uint16_t * out_product_id){
632     btstack_assert(index < ((sizeof(fw_patch_table) / sizeof(patch_info)) - 1));
633     *out_vendor_id = 0xbda;
634     *out_product_id = fw_patch_table[index].prod_id;
635 }
636 
637 static const btstack_chipset_t btstack_chipset_realtek = {
638     "REALTEK", chipset_init, chipset_next_command,
639     NULL,  // chipset_set_baudrate_command,
640     NULL,  // chipset_set_bd_addr_command not supported or implemented
641 };
642 
643 // MARK: public API
644 const btstack_chipset_t *btstack_chipset_realtek_instance(void) { return &btstack_chipset_realtek; }
645