xref: /btstack/chipset/realtek/btstack_chipset_realtek.c (revision 3366b4c19dd4436c7a49303095c5cd7d1045585d)
18bb555aaSBjoern Hartmann /*
274f83314SMatthias Ringwald  * Copyright (C) 2022 BlueKitchen GmbH
38bb555aaSBjoern Hartmann  *
48bb555aaSBjoern Hartmann  * Redistribution and use in source and binary forms, with or without
58bb555aaSBjoern Hartmann  * modification, are permitted provided that the following conditions
68bb555aaSBjoern Hartmann  * are met:
78bb555aaSBjoern Hartmann  *
88bb555aaSBjoern Hartmann  * 1. Redistributions of source code must retain the above copyright
98bb555aaSBjoern Hartmann  *    notice, this list of conditions and the following disclaimer.
108bb555aaSBjoern Hartmann  * 2. Redistributions in binary form must reproduce the above copyright
118bb555aaSBjoern Hartmann  *    notice, this list of conditions and the following disclaimer in the
128bb555aaSBjoern Hartmann  *    documentation and/or other materials provided with the distribution.
138bb555aaSBjoern Hartmann  * 3. Neither the name of the copyright holders nor the names of
148bb555aaSBjoern Hartmann  *    contributors may be used to endorse or promote products derived
158bb555aaSBjoern Hartmann  *    from this software without specific prior written permission.
168bb555aaSBjoern Hartmann  * 4. Any redistribution, use, or modification is done solely for
178bb555aaSBjoern Hartmann  *    personal benefit and not for any commercial purpose or for
188bb555aaSBjoern Hartmann  *    monetary gain.
198bb555aaSBjoern Hartmann  *
208bb555aaSBjoern Hartmann  * THIS SOFTWARE IS PROVIDED BY BLUEKITCHEN GMBH AND CONTRIBUTORS
218bb555aaSBjoern Hartmann  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
228bb555aaSBjoern Hartmann  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
238bb555aaSBjoern Hartmann  * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL BLUEKITCHEN
248bb555aaSBjoern Hartmann  * GMBH OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
258bb555aaSBjoern Hartmann  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
268bb555aaSBjoern Hartmann  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
278bb555aaSBjoern Hartmann  * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
288bb555aaSBjoern Hartmann  * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
298bb555aaSBjoern Hartmann  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
308bb555aaSBjoern Hartmann  * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
318bb555aaSBjoern Hartmann  * SUCH DAMAGE.
328bb555aaSBjoern Hartmann  *
338bb555aaSBjoern Hartmann  * Please inquire about commercial licensing options at
348bb555aaSBjoern Hartmann  * [email protected]
358bb555aaSBjoern Hartmann  *
368bb555aaSBjoern Hartmann  */
378bb555aaSBjoern Hartmann 
388bb555aaSBjoern Hartmann #define BTSTACK_FILE__ "btstack_chipset_realtek.c"
398bb555aaSBjoern Hartmann 
408bb555aaSBjoern Hartmann /*
418bb555aaSBjoern Hartmann  *  btstack_chipset_realtek.c
428bb555aaSBjoern Hartmann  *
438bb555aaSBjoern Hartmann  *  Adapter to use REALTEK-based chipsets with BTstack
448bb555aaSBjoern Hartmann  */
458bb555aaSBjoern Hartmann 
468bb555aaSBjoern Hartmann #include "btstack_chipset_realtek.h"
478bb555aaSBjoern Hartmann 
488bb555aaSBjoern Hartmann #include <stddef.h> /* NULL */
498bb555aaSBjoern Hartmann #include <stdio.h>
508bb555aaSBjoern Hartmann #include <string.h> /* memcpy */
518bb555aaSBjoern Hartmann 
528bb555aaSBjoern Hartmann #include "btstack_control.h"
538bb555aaSBjoern Hartmann #include "btstack_debug.h"
548bb555aaSBjoern Hartmann #include "btstack_event.h"
558bb555aaSBjoern Hartmann #include "btstack_util.h"
568bb555aaSBjoern Hartmann #include "hci.h"
578bb555aaSBjoern Hartmann #include "hci_transport.h"
588bb555aaSBjoern Hartmann 
5974f83314SMatthias Ringwald #ifdef _MSC_VER
6074f83314SMatthias Ringwald // ignore deprecated warning for fopen
6174f83314SMatthias Ringwald #pragma warning(disable : 4996)
6274f83314SMatthias Ringwald #endif
6374f83314SMatthias Ringwald 
648bb555aaSBjoern Hartmann #define ROM_LMP_NONE 0x0000
658bb555aaSBjoern Hartmann #define ROM_LMP_8723a 0x1200
668bb555aaSBjoern Hartmann #define ROM_LMP_8723b 0x8723
678bb555aaSBjoern Hartmann #define ROM_LMP_8821a 0X8821
688bb555aaSBjoern Hartmann #define ROM_LMP_8761a 0X8761
698bb555aaSBjoern Hartmann #define ROM_LMP_8822b 0X8822
708bb555aaSBjoern Hartmann 
718bb555aaSBjoern Hartmann #define HCI_DOWNLOAD_FW 0xFC20
728bb555aaSBjoern Hartmann #define HCI_READ_ROM_VERSION 0xFC6D
738bb555aaSBjoern Hartmann #define HCI_READ_LMP_VERSION 0x1001
748bb555aaSBjoern Hartmann #define HCI_RESET 0x0C03
758bb555aaSBjoern Hartmann 
768bb555aaSBjoern Hartmann #define FILL_COMMAND(buf, command) ((int16_t *)buf)[0] = command
778bb555aaSBjoern Hartmann #define FILL_LENGTH(buf, length) buf[2] = length
788bb555aaSBjoern Hartmann #define FILL_INDEX(buf, index) buf[3] = index
798bb555aaSBjoern Hartmann #define FILL_FW_DATA(buf, firmware, ptr, len) memcpy(buf + 4, firmware + ptr, len)
808bb555aaSBjoern Hartmann 
818bb555aaSBjoern Hartmann enum {
828bb555aaSBjoern Hartmann     STATE_READ_ROM_VERSION,
838bb555aaSBjoern Hartmann     STATE_LOAD_FIRMWARE,
848bb555aaSBjoern Hartmann     STATE_RESET,
858bb555aaSBjoern Hartmann     STATE_DONE,
868bb555aaSBjoern Hartmann };
878bb555aaSBjoern Hartmann 
888bb555aaSBjoern Hartmann enum { FW_DONE, FW_MORE_TO_DO };
898bb555aaSBjoern Hartmann 
908bb555aaSBjoern Hartmann typedef struct {
918bb555aaSBjoern Hartmann     uint16_t prod_id;
928bb555aaSBjoern Hartmann     uint16_t lmp_sub;
938bb555aaSBjoern Hartmann     char *   mp_patch_name;
948bb555aaSBjoern Hartmann     char *   patch_name;
958bb555aaSBjoern Hartmann     char *   config_name;
968bb555aaSBjoern Hartmann 
978bb555aaSBjoern Hartmann     uint8_t *fw_cache1;
988bb555aaSBjoern Hartmann     int      fw_len1;
998bb555aaSBjoern Hartmann } patch_info;
1008bb555aaSBjoern Hartmann 
1018bb555aaSBjoern Hartmann static patch_info fw_patch_table[] = {
1028bb555aaSBjoern Hartmann     /* { pid, lmp_sub, mp_fw_name, fw_name, config_name, fw_cache, fw_len } */
1038bb555aaSBjoern Hartmann     {0x1724, 0x1200, "mp_rtl8723a_fw", "rtl8723a_fw", "rtl8723a_config", NULL, 0},	/* RTL8723A */
1048bb555aaSBjoern Hartmann     {0x8723, 0x1200, "mp_rtl8723a_fw", "rtl8723a_fw", "rtl8723a_config", NULL, 0},	/* 8723AE */
1058bb555aaSBjoern Hartmann     {0xA723, 0x1200, "mp_rtl8723a_fw", "rtl8723a_fw", "rtl8723a_config", NULL, 0},	/* 8723AE for LI */
1068bb555aaSBjoern Hartmann     {0x0723, 0x1200, "mp_rtl8723a_fw", "rtl8723a_fw", "rtl8723a_config", NULL, 0},	/* 8723AE */
1078bb555aaSBjoern Hartmann     {0x3394, 0x1200, "mp_rtl8723a_fw", "rtl8723a_fw", "rtl8723a_config", NULL, 0},	/* 8723AE for Azurewave */
1088bb555aaSBjoern Hartmann 
1098bb555aaSBjoern Hartmann     {0x0724, 0x1200, "mp_rtl8723a_fw", "rtl8723a_fw", "rtl8723a_config", NULL, 0},	/* 8723AU */
1108bb555aaSBjoern Hartmann     {0x8725, 0x1200, "mp_rtl8723a_fw", "rtl8723a_fw", "rtl8723a_config", NULL, 0},	/* 8723AU */
1118bb555aaSBjoern Hartmann     {0x872A, 0x1200, "mp_rtl8723a_fw", "rtl8723a_fw", "rtl8723a_config", NULL, 0},	/* 8723AU */
1128bb555aaSBjoern Hartmann     {0x872B, 0x1200, "mp_rtl8723a_fw", "rtl8723a_fw", "rtl8723a_config", NULL, 0},	/* 8723AU */
1138bb555aaSBjoern Hartmann 
114*3366b4c1SMatthias Ringwald     {0xb720, 0x8723, "mp_rtl8723b_fw", "rtl8723b_fw", "rtl8723b_config", NULL, 0},	/* RTL8723BU */
115*3366b4c1SMatthias Ringwald     {0xb72A, 0x8723, "mp_rtl8723b_fw", "rtl8723b_fw", "rtl8723b_config", NULL, 0},	/* RTL8723BU */
116*3366b4c1SMatthias Ringwald 
1178bb555aaSBjoern Hartmann     {0xb728, 0x8723, "mp_rtl8723b_fw", "rtl8723b_fw", "rtl8723b_config", NULL, 0},	/* RTL8723BE for LC */
1188bb555aaSBjoern Hartmann     {0xb723, 0x8723, "mp_rtl8723b_fw", "rtl8723b_fw", "rtl8723b_config", NULL, 0},	/* RTL8723BE */
1198bb555aaSBjoern Hartmann     {0xb72B, 0x8723, "mp_rtl8723b_fw", "rtl8723b_fw", "rtl8723b_config", NULL, 0},	/* RTL8723BE */
1208bb555aaSBjoern Hartmann     {0xb001, 0x8723, "mp_rtl8723b_fw", "rtl8723b_fw", "rtl8723b_config", NULL, 0},	/* RTL8723BE for HP */
1218bb555aaSBjoern Hartmann     {0xb002, 0x8723, "mp_rtl8723b_fw", "rtl8723b_fw", "rtl8723b_config", NULL, 0},	/* RTL8723BE */
1228bb555aaSBjoern Hartmann     {0xb003, 0x8723, "mp_rtl8723b_fw", "rtl8723b_fw", "rtl8723b_config", NULL, 0},	/* RTL8723BE */
1238bb555aaSBjoern Hartmann     {0xb004, 0x8723, "mp_rtl8723b_fw", "rtl8723b_fw", "rtl8723b_config", NULL, 0},	/* RTL8723BE */
1248bb555aaSBjoern Hartmann     {0xb005, 0x8723, "mp_rtl8723b_fw", "rtl8723b_fw", "rtl8723b_config", NULL, 0},	/* RTL8723BE */
1258bb555aaSBjoern Hartmann     {0x3410, 0x8723, "mp_rtl8723b_fw", "rtl8723b_fw", "rtl8723b_config", NULL, 0},	/* RTL8723BE for Azurewave */
1268bb555aaSBjoern Hartmann     {0x3416, 0x8723, "mp_rtl8723b_fw", "rtl8723b_fw", "rtl8723b_config", NULL, 0},	/* RTL8723BE for Azurewave */
1278bb555aaSBjoern Hartmann     {0x3459, 0x8723, "mp_rtl8723b_fw", "rtl8723b_fw", "rtl8723b_config", NULL, 0},	/* RTL8723BE for Azurewave */
1288bb555aaSBjoern Hartmann     {0xE085, 0x8723, "mp_rtl8723b_fw", "rtl8723b_fw", "rtl8723b_config", NULL, 0},	/* RTL8723BE for Foxconn */
1298bb555aaSBjoern Hartmann     {0xE08B, 0x8723, "mp_rtl8723b_fw", "rtl8723b_fw", "rtl8723b_config", NULL, 0},	/* RTL8723BE for Foxconn */
1308bb555aaSBjoern Hartmann     {0xE09E, 0x8723, "mp_rtl8723b_fw", "rtl8723b_fw", "rtl8723b_config", NULL, 0},	/* RTL8723BE for Foxconn */
1318bb555aaSBjoern Hartmann 
132*3366b4c1SMatthias Ringwald     {0xd720, 0x8723, "mp_rtl8723du_fw", "rtl8723du_fw", "rtl8723du_config", NULL, 0}, /* RTL8723DU */
133*3366b4c1SMatthias Ringwald     {0xd723, 0x8723, "mp_rtl8723du_fw", "rtl8723du_fw", "rtl8723du_config", NULL, 0}, /* RTL8723DU */
134*3366b4c1SMatthias Ringwald     {0xd739, 0x8723, "mp_rtl8723du_fw", "rtl8723du_fw", "rtl8723du_config", NULL, 0}, /* RTL8723DU */
135*3366b4c1SMatthias Ringwald     {0xb009, 0x8723, "mp_rtl8723du_fw", "rtl8723du_fw", "rtl8723du_config", NULL, 0}, /* RTL8723DU */
136*3366b4c1SMatthias Ringwald     {0x0231, 0x8723, "mp_rtl8723du_fw", "rtl8723du_fw", "rtl8723du_config", NULL, 0}, /* RTL8723DU for LiteOn */
137*3366b4c1SMatthias Ringwald 
138*3366b4c1SMatthias Ringwald     {0xb733, 0x8723, "mp_rtl8723fu_fw", "rtl8723fu_fw", "rtl8723fu_config", NULL, 0}, /* RTL8723FU */
139*3366b4c1SMatthias Ringwald     {0xb73a, 0x8723, "mp_rtl8723fu_fw", "rtl8723fu_fw", "rtl8723fu_config", NULL, 0}, /* RTL8723FU */
140*3366b4c1SMatthias Ringwald     {0xf72b, 0x8723, "mp_rtl8723fu_fw", "rtl8723fu_fw", "rtl8723fu_config", NULL, 0}, /* RTL8723FU */
141*3366b4c1SMatthias Ringwald 
1428bb555aaSBjoern Hartmann     {0xA761, 0x8761, "mp_rtl8761a_fw", "rtl8761au_fw", "rtl8761a_config", NULL, 0},	/* RTL8761AU only */
1438bb555aaSBjoern Hartmann     {0x818B, 0x8761, "mp_rtl8761a_fw", "rtl8761aw_fw", "rtl8761aw_config", NULL, 0},	/* RTL8761AW + 8192EU */
1448bb555aaSBjoern Hartmann     {0x818C, 0x8761, "mp_rtl8761a_fw", "rtl8761aw_fw", "rtl8761aw_config", NULL, 0},	/* RTL8761AW + 8192EU */
145*3366b4c1SMatthias Ringwald     {0x8760, 0x8761, "mp_rtl8761a_fw", "rtl8761au8192ee_fw", "rtl8761a_config", NULL, 0},	/* RTL8761AU + 8192EE */
146*3366b4c1SMatthias Ringwald     {0xB761, 0x8761, "mp_rtl8761a_fw", "rtl8761au_fw", "rtl8761a_config", NULL, 0},	/* RTL8761AUV only */
147*3366b4c1SMatthias Ringwald     {0x8761, 0x8761, "mp_rtl8761a_fw", "rtl8761au8192ee_fw", "rtl8761a_config", NULL, 0},	/* RTL8761AU + 8192EE for LI */
148*3366b4c1SMatthias Ringwald     {0x8A60, 0x8761, "mp_rtl8761a_fw", "rtl8761au8812ae_fw", "rtl8761a_config", NULL, 0},	/* RTL8761AU + 8812AE */
149*3366b4c1SMatthias Ringwald     {0x3527, 0x8761, "mp_rtl8761a_fw", "rtl8761au8192ee_fw", "rtl8761a_config", NULL, 0},	/* RTL8761AU + 8814AE */
150*3366b4c1SMatthias Ringwald 
151*3366b4c1SMatthias Ringwald     {0x8771, 0x8761, "mp_rtl8761b_fw", "rtl8761bu_fw", "rtl8761bu_config", NULL, 0}, /* RTL8761BU only */
152*3366b4c1SMatthias Ringwald     {0xa725, 0x8761, "mp_rtl8761b_fw", "rtl8725au_fw", "rtl8725au_config", NULL, 0}, /* RTL8725AU */
153*3366b4c1SMatthias Ringwald     {0xa72A, 0x8761, "mp_rtl8761b_fw", "rtl8725au_fw", "rtl8725au_config", NULL, 0}, /* RTL8725AU BT only */
1548bb555aaSBjoern Hartmann 
1558bb555aaSBjoern Hartmann     {0x8821, 0x8821, "mp_rtl8821a_fw", "rtl8821a_fw", "rtl8821a_config", NULL, 0},	/* RTL8821AE */
1568bb555aaSBjoern Hartmann     {0x0821, 0x8821, "mp_rtl8821a_fw", "rtl8821a_fw", "rtl8821a_config", NULL, 0},	/* RTL8821AE */
1578bb555aaSBjoern Hartmann     {0x0823, 0x8821, "mp_rtl8821a_fw", "rtl8821a_fw", "rtl8821a_config", NULL, 0},	/* RTL8821AU */
1588bb555aaSBjoern Hartmann     {0x3414, 0x8821, "mp_rtl8821a_fw", "rtl8821a_fw", "rtl8821a_config", NULL, 0},	/* RTL8821AE */
1598bb555aaSBjoern Hartmann     {0x3458, 0x8821, "mp_rtl8821a_fw", "rtl8821a_fw", "rtl8821a_config", NULL, 0},	/* RTL8821AE */
1608bb555aaSBjoern Hartmann     {0x3461, 0x8821, "mp_rtl8821a_fw", "rtl8821a_fw", "rtl8821a_config", NULL, 0},	/* RTL8821AE */
1618bb555aaSBjoern Hartmann     {0x3462, 0x8821, "mp_rtl8821a_fw", "rtl8821a_fw", "rtl8821a_config", NULL, 0},	/* RTL8821AE */
1628bb555aaSBjoern Hartmann 
1638bb555aaSBjoern Hartmann     {0xb820, 0x8821, "mp_rtl8821cu_fw", "rtl8821cu_fw", "rtl8821cu_config", NULL, 0}, /* RTL8821CU */
1648bb555aaSBjoern Hartmann     {0xc820, 0x8821, "mp_rtl8821cu_fw", "rtl8821cu_fw", "rtl8821cu_config", NULL, 0}, /* RTL8821CU */
165*3366b4c1SMatthias Ringwald     {0xc821, 0x8821, "mp_rtl8821cu_fw", "rtl8821cu_fw", "rtl8821cu_config", NULL, 0}, /* RTL8821CE */
166*3366b4c1SMatthias Ringwald     {0xc823, 0x8821, "mp_rtl8821cu_fw", "rtl8821cu_fw", "rtl8821cu_config", NULL, 0}, /* RTL8821CE */
167*3366b4c1SMatthias Ringwald     {0xc824, 0x8821, "mp_rtl8821cu_fw", "rtl8821cu_fw", "rtl8821cu_config", NULL, 0}, /* RTL8821CE */
168*3366b4c1SMatthias Ringwald     {0xc825, 0x8821, "mp_rtl8821cu_fw", "rtl8821cu_fw", "rtl8821cu_config", NULL, 0}, /* RTL8821CE */
169*3366b4c1SMatthias Ringwald     {0xc827, 0x8821, "mp_rtl8821cu_fw", "rtl8821cu_fw", "rtl8821cu_config", NULL, 0}, /* RTL8821CE */
170*3366b4c1SMatthias Ringwald     {0xc025, 0x8821, "mp_rtl8821cu_fw", "rtl8821cu_fw", "rtl8821cu_config", NULL, 0}, /* RTL8821CE */
171*3366b4c1SMatthias Ringwald     {0xc024, 0x8821, "mp_rtl8821cu_fw", "rtl8821cu_fw", "rtl8821cu_config", NULL, 0}, /* RTL8821CE */
172*3366b4c1SMatthias Ringwald     {0xc030, 0x8821, "mp_rtl8821cu_fw", "rtl8821cu_fw", "rtl8821cu_config", NULL, 0}, /* RTL8821CE */
173*3366b4c1SMatthias Ringwald     {0xb00a, 0x8821, "mp_rtl8821cu_fw", "rtl8821cu_fw", "rtl8821cu_config", NULL, 0}, /* RTL8821CE */
174*3366b4c1SMatthias Ringwald     {0xb00e, 0x8821, "mp_rtl8821cu_fw", "rtl8821cu_fw", "rtl8821cu_config", NULL, 0}, /* RTL8821CE */
175*3366b4c1SMatthias Ringwald     {0xc032, 0x8821, "mp_rtl8821cu_fw", "rtl8821cu_fw", "rtl8821cu_config", NULL, 0}, /* RTL8821CE */
176*3366b4c1SMatthias Ringwald     {0x4000, 0x8821, "mp_rtl8821cu_fw", "rtl8821cu_fw", "rtl8821cu_config", NULL, 0}, /* RTL8821CE for LiteOn */
177*3366b4c1SMatthias Ringwald     {0x4001, 0x8821, "mp_rtl8821cu_fw", "rtl8821cu_fw", "rtl8821cu_config", NULL, 0}, /* RTL8821CE for LiteOn */
178*3366b4c1SMatthias Ringwald     {0x3529, 0x8821, "mp_rtl8821cu_fw", "rtl8821cu_fw", "rtl8821cu_config", NULL, 0}, /* RTL8821CE for Azurewave */
179*3366b4c1SMatthias Ringwald     {0x3530, 0x8821, "mp_rtl8821cu_fw", "rtl8821cu_fw", "rtl8821cu_config", NULL, 0}, /* RTL8821CE for Azurewave */
180*3366b4c1SMatthias Ringwald     {0x3532, 0x8821, "mp_rtl8821cu_fw", "rtl8821cu_fw", "rtl8821cu_config", NULL, 0}, /* RTL8821CE for Azurewave */
181*3366b4c1SMatthias Ringwald     {0x3533, 0x8821, "mp_rtl8821cu_fw", "rtl8821cu_fw", "rtl8821cu_config", NULL, 0}, /* RTL8821CE for Azurewave */
182*3366b4c1SMatthias Ringwald     {0x3538, 0x8821, "mp_rtl8821cu_fw", "rtl8821cu_fw", "rtl8821cu_config", NULL, 0}, /* RTL8821CE for Azurewave */
183*3366b4c1SMatthias Ringwald     {0x3539, 0x8821, "mp_rtl8821cu_fw", "rtl8821cu_fw", "rtl8821cu_config", NULL, 0}, /* RTL8821CE for Azurewave */
184*3366b4c1SMatthias Ringwald     {0x3558, 0x8821, "mp_rtl8821cu_fw", "rtl8821cu_fw", "rtl8821cu_config", NULL, 0}, /* RTL8821CE for Azurewave */
185*3366b4c1SMatthias Ringwald     {0x3559, 0x8821, "mp_rtl8821cu_fw", "rtl8821cu_fw", "rtl8821cu_config", NULL, 0}, /* RTL8821CE for Azurewave */
186*3366b4c1SMatthias Ringwald     {0x3581, 0x8821, "mp_rtl8821cu_fw", "rtl8821cu_fw", "rtl8821cu_config", NULL, 0}, /* RTL8821CE for Azurewave */
187*3366b4c1SMatthias Ringwald     {0x3540, 0x8821, "mp_rtl8821cu_fw", "rtl8821cu_fw", "rtl8821cu_config", NULL, 0}, /* RTL8821CE */
188*3366b4c1SMatthias Ringwald     {0x3541, 0x8821, "mp_rtl8821cu_fw", "rtl8821cu_fw", "rtl8821cu_config", NULL, 0}, /* RTL8821CE for GSD */
189*3366b4c1SMatthias Ringwald     {0x3543, 0x8821, "mp_rtl8821cu_fw", "rtl8821cu_fw", "rtl8821cu_config", NULL, 0}, /* RTL8821CE for GSD */
190*3366b4c1SMatthias Ringwald     {0xc80c, 0x8821, "mp_rtl8821cu_fw", "rtl8821cu_fw", "rtl8821cu_config", NULL, 0}, /* RTL8821CUH */
191*3366b4c1SMatthias Ringwald 
192*3366b4c1SMatthias Ringwald     {0xd820, 0x8822, "mp_rtl8821du_fw", "rtl8821du_fw", "rtl8821du_config", NULL, 0}, /* RTL8821DU */
193*3366b4c1SMatthias Ringwald 
194*3366b4c1SMatthias Ringwald     {0xb82c, 0x8822, "mp_rtl8822bu_fw", "rtl8822bu_fw", "rtl8822bu_config", NULL, 0}, /* RTL8822BU */
1958bb555aaSBjoern Hartmann 
1968bb555aaSBjoern Hartmann     {0xc82c, 0x8822, "mp_rtl8822cu_fw", "rtl8822cu_fw", "rtl8822cu_config", NULL, 0}, /* RTL8822CU */
197*3366b4c1SMatthias Ringwald     {0xc82e, 0x8822, "mp_rtl8822cu_fw", "rtl8822cu_fw", "rtl8822cu_config", NULL, 0}, /* RTL8822CU */
198*3366b4c1SMatthias Ringwald     {0xc81d, 0x8822, "mp_rtl8822cu_fw", "rtl8822cu_fw", "rtl8822cu_config", NULL, 0}, /* RTL8822CU */
1998bb555aaSBjoern Hartmann 
200*3366b4c1SMatthias Ringwald     {0xc822, 0x8822, "mp_rtl8822cu_fw", "rtl8822cu_fw", "rtl8822cu_config", NULL, 0}, /* RTL8822CE */
201*3366b4c1SMatthias Ringwald     {0xc82b, 0x8822, "mp_rtl8822cu_fw", "rtl8822cu_fw", "rtl8822cu_config", NULL, 0}, /* RTL8822CE */
202*3366b4c1SMatthias Ringwald     {0xb00c, 0x8822, "mp_rtl8822cu_fw", "rtl8822cu_fw", "rtl8822cu_config", NULL, 0}, /* RTL8822CE */
203*3366b4c1SMatthias Ringwald     {0xb00d, 0x8822, "mp_rtl8822cu_fw", "rtl8822cu_fw", "rtl8822cu_config", NULL, 0}, /* RTL8822CE */
204*3366b4c1SMatthias Ringwald     {0xc123, 0x8822, "mp_rtl8822cu_fw", "rtl8822cu_fw", "rtl8822cu_config", NULL, 0}, /* RTL8822CE */
205*3366b4c1SMatthias Ringwald     {0xc126, 0x8822, "mp_rtl8822cu_fw", "rtl8822cu_fw", "rtl8822cu_config", NULL, 0}, /* RTL8822CE */
206*3366b4c1SMatthias Ringwald     {0xc127, 0x8822, "mp_rtl8822cu_fw", "rtl8822cu_fw", "rtl8822cu_config", NULL, 0}, /* RTL8822CE */
207*3366b4c1SMatthias Ringwald     {0xc128, 0x8822, "mp_rtl8822cu_fw", "rtl8822cu_fw", "rtl8822cu_config", NULL, 0}, /* RTL8822CE */
208*3366b4c1SMatthias Ringwald     {0xc129, 0x8822, "mp_rtl8822cu_fw", "rtl8822cu_fw", "rtl8822cu_config", NULL, 0}, /* RTL8822CE */
209*3366b4c1SMatthias Ringwald     {0xc131, 0x8822, "mp_rtl8822cu_fw", "rtl8822cu_fw", "rtl8822cu_config", NULL, 0}, /* RTL8822CE */
210*3366b4c1SMatthias Ringwald     {0xc136, 0x8822, "mp_rtl8822cu_fw", "rtl8822cu_fw", "rtl8822cu_config", NULL, 0}, /* RTL8822CE */
211*3366b4c1SMatthias Ringwald     {0x3549, 0x8822, "mp_rtl8822cu_fw", "rtl8822cu_fw", "rtl8822cu_config", NULL, 0}, /* RTL8822CE for Azurewave */
212*3366b4c1SMatthias Ringwald     {0x3548, 0x8822, "mp_rtl8822cu_fw", "rtl8822cu_fw", "rtl8822cu_config", NULL, 0}, /* RTL8822CE for Azurewave */
213*3366b4c1SMatthias Ringwald     {0xc125, 0x8822, "mp_rtl8822cu_fw", "rtl8822cu_fw", "rtl8822cu_config", NULL, 0}, /* RTL8822CE */
214*3366b4c1SMatthias Ringwald     {0x4005, 0x8822, "mp_rtl8822cu_fw", "rtl8822cu_fw", "rtl8822cu_config", NULL, 0}, /* RTL8822CE for LiteOn */
215*3366b4c1SMatthias Ringwald     {0x3051, 0x8822, "mp_rtl8822cu_fw", "rtl8822cu_fw", "rtl8822cu_config", NULL, 0}, /* RTL8822CE for LiteOn */
216*3366b4c1SMatthias Ringwald     {0x18ef, 0x8822, "mp_rtl8822cu_fw", "rtl8822cu_fw", "rtl8822cu_config", NULL, 0}, /* RTL8822CE */
217*3366b4c1SMatthias Ringwald     {0x161f, 0x8822, "mp_rtl8822cu_fw", "rtl8822cu_fw", "rtl8822cu_config", NULL, 0}, /* RTL8822CE */
218*3366b4c1SMatthias Ringwald     {0x3053, 0x8822, "mp_rtl8822cu_fw", "rtl8822cu_fw", "rtl8822cu_config", NULL, 0}, /* RTL8822CE */
219*3366b4c1SMatthias Ringwald     {0xc547, 0x8822, "mp_rtl8822cu_fw", "rtl8822cu_fw", "rtl8822cu_config", NULL, 0}, /* RTL8822CE */
220*3366b4c1SMatthias Ringwald     {0x3553, 0x8822, "mp_rtl8822cu_fw", "rtl8822cu_fw", "rtl8822cu_config", NULL, 0}, /* RTL8822CE */
221*3366b4c1SMatthias Ringwald     {0x3555, 0x8822, "mp_rtl8822cu_fw", "rtl8822cu_fw", "rtl8822cu_config", NULL, 0}, /* RTL8822CE */
222*3366b4c1SMatthias Ringwald     {0xc82f, 0x8822, "mp_rtl8822cu_fw", "rtl8822cu_fw", "rtl8822cu_config", NULL, 0}, /* RTL8822CE-VS */
223*3366b4c1SMatthias Ringwald     {0xc02f, 0x8822, "mp_rtl8822cu_fw", "rtl8822cu_fw", "rtl8822cu_config", NULL, 0}, /* RTL8822CE-VS */
224*3366b4c1SMatthias Ringwald     {0xc03f, 0x8822, "mp_rtl8822cu_fw", "rtl8822cu_fw", "rtl8822cu_config", NULL, 0}, /* RTL8822CE-VS */
225*3366b4c1SMatthias Ringwald 
226*3366b4c1SMatthias Ringwald     {0x8851, 0x8852, "mp_rtl8851au_fw", "rtl8851au_fw", "rtl8851au_config", NULL, 0}, /* RTL8851AU */
227*3366b4c1SMatthias Ringwald 
228*3366b4c1SMatthias Ringwald     {0xb851, 0x8851, "mp_rtl8851bu_fw", "rtl8851bu_fw", "rtl8851bu_config", NULL, 0}, /* RTL8851BU */
229*3366b4c1SMatthias Ringwald 
230*3366b4c1SMatthias Ringwald     {0x885a, 0x8852, "mp_rtl8852au_fw", "rtl8852au_fw", "rtl8852au_config", NULL, 0}, /* RTL8852AU */
231*3366b4c1SMatthias Ringwald     {0x8852, 0x8852, "mp_rtl8852au_fw", "rtl8852au_fw", "rtl8852au_config", NULL, 0}, /* RTL8852AE */
232*3366b4c1SMatthias Ringwald     {0xa852, 0x8852, "mp_rtl8852au_fw", "rtl8852au_fw", "rtl8852au_config", NULL, 0}, /* RTL8852AE */
233*3366b4c1SMatthias Ringwald     {0x2852, 0x8852, "mp_rtl8852au_fw", "rtl8852au_fw", "rtl8852au_config", NULL, 0}, /* RTL8852AE */
234*3366b4c1SMatthias Ringwald     {0x385a, 0x8852, "mp_rtl8852au_fw", "rtl8852au_fw", "rtl8852au_config", NULL, 0}, /* RTL8852AE */
235*3366b4c1SMatthias Ringwald     {0x3852, 0x8852, "mp_rtl8852au_fw", "rtl8852au_fw", "rtl8852au_config", NULL, 0}, /* RTL8852AE */
236*3366b4c1SMatthias Ringwald     {0x1852, 0x8852, "mp_rtl8852au_fw", "rtl8852au_fw", "rtl8852au_config", NULL, 0}, /* RTL8852AE */
237*3366b4c1SMatthias Ringwald     {0x4852, 0x8852, "mp_rtl8852au_fw", "rtl8852au_fw", "rtl8852au_config", NULL, 0}, /* RTL8852AE */
238*3366b4c1SMatthias Ringwald     {0x4006, 0x8852, "mp_rtl8852au_fw", "rtl8852au_fw", "rtl8852au_config", NULL, 0}, /* RTL8852AE */
239*3366b4c1SMatthias Ringwald     {0x3561, 0x8852, "mp_rtl8852au_fw", "rtl8852au_fw", "rtl8852au_config", NULL, 0}, /* RTL8852AE */
240*3366b4c1SMatthias Ringwald     {0x3562, 0x8852, "mp_rtl8852au_fw", "rtl8852au_fw", "rtl8852au_config", NULL, 0}, /* RTL8852AE */
241*3366b4c1SMatthias Ringwald     {0x588a, 0x8852, "mp_rtl8852au_fw", "rtl8852au_fw", "rtl8852au_config", NULL, 0}, /* RTL8852AE */
242*3366b4c1SMatthias Ringwald     {0x589a, 0x8852, "mp_rtl8852au_fw", "rtl8852au_fw", "rtl8852au_config", NULL, 0}, /* RTL8852AE */
243*3366b4c1SMatthias Ringwald     {0x590a, 0x8852, "mp_rtl8852au_fw", "rtl8852au_fw", "rtl8852au_config", NULL, 0}, /* RTL8852AE */
244*3366b4c1SMatthias Ringwald     {0xc125, 0x8852, "mp_rtl8852au_fw", "rtl8852au_fw", "rtl8852au_config", NULL, 0}, /* RTL8852AE */
245*3366b4c1SMatthias Ringwald     {0xe852, 0x8852, "mp_rtl8852au_fw", "rtl8852au_fw", "rtl8852au_config", NULL, 0}, /* RTL8852AE */
246*3366b4c1SMatthias Ringwald     {0xb852, 0x8852, "mp_rtl8852au_fw", "rtl8852au_fw", "rtl8852au_config", NULL, 0}, /* RTL8852AE */
247*3366b4c1SMatthias Ringwald     {0xc852, 0x8852, "mp_rtl8852au_fw", "rtl8852au_fw", "rtl8852au_config", NULL, 0}, /* RTL8852AE */
248*3366b4c1SMatthias Ringwald     {0xc549, 0x8852, "mp_rtl8852au_fw", "rtl8852au_fw", "rtl8852au_config", NULL, 0}, /* RTL8852AE */
249*3366b4c1SMatthias Ringwald     {0xc127, 0x8852, "mp_rtl8852au_fw", "rtl8852au_fw", "rtl8852au_config", NULL, 0}, /* RTL8852AE */
250*3366b4c1SMatthias Ringwald     {0x3565, 0x8852, "mp_rtl8852au_fw", "rtl8852au_fw", "rtl8852au_config", NULL, 0}, /* RTL8852AE */
251*3366b4c1SMatthias Ringwald 
252*3366b4c1SMatthias Ringwald     {0xa85b, 0x8852, "mp_rtl8852bu_fw", "rtl8852bu_fw", "rtl8852bu_config", NULL, 0}, /* RTL8852BU */
253*3366b4c1SMatthias Ringwald     {0xb85b, 0x8852, "mp_rtl8852bu_fw", "rtl8852bu_fw", "rtl8852bu_config", NULL, 0}, /* RTL8852BE */
254*3366b4c1SMatthias Ringwald     {0xb85c, 0x8852, "mp_rtl8852bu_fw", "rtl8852bu_fw", "rtl8852bu_config", NULL, 0}, /* RTL8852BE */
255*3366b4c1SMatthias Ringwald     {0x3571, 0x8852, "mp_rtl8852bu_fw", "rtl8852bu_fw", "rtl8852bu_config", NULL, 0}, /* RTL8852BE */
256*3366b4c1SMatthias Ringwald     {0x3570, 0x8852, "mp_rtl8852bu_fw", "rtl8852bu_fw", "rtl8852bu_config", NULL, 0}, /* RTL8852BE */
257*3366b4c1SMatthias Ringwald     {0x3572, 0x8852, "mp_rtl8852bu_fw", "rtl8852bu_fw", "rtl8852bu_config", NULL, 0}, /* RTL8852BE */
258*3366b4c1SMatthias Ringwald     {0x4b06, 0x8852, "mp_rtl8852bu_fw", "rtl8852bu_fw", "rtl8852bu_config", NULL, 0}, /* RTL8852BE */
259*3366b4c1SMatthias Ringwald     {0x885b, 0x8852, "mp_rtl8852bu_fw", "rtl8852bu_fw", "rtl8852bu_config", NULL, 0}, /* RTL8852BE */
260*3366b4c1SMatthias Ringwald     {0x886b, 0x8852, "mp_rtl8852bu_fw", "rtl8852bu_fw", "rtl8852bu_config", NULL, 0}, /* RTL8852BE */
261*3366b4c1SMatthias Ringwald     {0x887b, 0x8852, "mp_rtl8852bu_fw", "rtl8852bu_fw", "rtl8852bu_config", NULL, 0}, /* RTL8852BE */
262*3366b4c1SMatthias Ringwald     {0xc559, 0x8852, "mp_rtl8852bu_fw", "rtl8852bu_fw", "rtl8852bu_config", NULL, 0}, /* RTL8852BE */
263*3366b4c1SMatthias Ringwald     {0xb052, 0x8852, "mp_rtl8852bu_fw", "rtl8852bu_fw", "rtl8852bu_config", NULL, 0}, /* RTL8852BE */
264*3366b4c1SMatthias Ringwald     {0xb152, 0x8852, "mp_rtl8852bu_fw", "rtl8852bu_fw", "rtl8852bu_config", NULL, 0}, /* RTL8852BE */
265*3366b4c1SMatthias Ringwald     {0xb252, 0x8852, "mp_rtl8852bu_fw", "rtl8852bu_fw", "rtl8852bu_config", NULL, 0}, /* RTL8852BE */
266*3366b4c1SMatthias Ringwald     {0x4853, 0x8852, "mp_rtl8852bu_fw", "rtl8852bu_fw", "rtl8852bu_config", NULL, 0}, /* RTL8852BE */
267*3366b4c1SMatthias Ringwald     {0x1670, 0x8852, "mp_rtl8852bu_fw", "rtl8852bu_fw", "rtl8852bu_config", NULL, 0}, /* RTL8852BE */
268*3366b4c1SMatthias Ringwald 
269*3366b4c1SMatthias Ringwald     {0xc85a, 0x8852, "mp_rtl8852cu_fw", "rtl8852cu_fw", "rtl8852cu_config", NULL, 0}, /* RTL8852CU */
270*3366b4c1SMatthias Ringwald     {0x0852, 0x8852, "mp_rtl8852cu_fw", "rtl8852cu_fw", "rtl8852cu_config", NULL, 0}, /* RTL8852CE */
271*3366b4c1SMatthias Ringwald     {0x5852, 0x8852, "mp_rtl8852cu_fw", "rtl8852cu_fw", "rtl8852cu_config", NULL, 0}, /* RTL8852CE */
272*3366b4c1SMatthias Ringwald     {0xc85c, 0x8852, "mp_rtl8852cu_fw", "rtl8852cu_fw", "rtl8852cu_config", NULL, 0}, /* RTL8852CE */
273*3366b4c1SMatthias Ringwald     {0x885c, 0x8852, "mp_rtl8852cu_fw", "rtl8852cu_fw", "rtl8852cu_config", NULL, 0}, /* RTL8852CE */
274*3366b4c1SMatthias Ringwald     {0x886c, 0x8852, "mp_rtl8852cu_fw", "rtl8852cu_fw", "rtl8852cu_config", NULL, 0}, /* RTL8852CE */
275*3366b4c1SMatthias Ringwald     {0x887c, 0x8852, "mp_rtl8852cu_fw", "rtl8852cu_fw", "rtl8852cu_config", NULL, 0}, /* RTL8852CE */
276*3366b4c1SMatthias Ringwald     {0x4007, 0x8852, "mp_rtl8852cu_fw", "rtl8852cu_fw", "rtl8852cu_config", NULL, 0}, /* RTL8852CE */
277*3366b4c1SMatthias Ringwald 
278*3366b4c1SMatthias Ringwald     {0xe822, 0x8822, "mp_rtl8822eu_fw", "rtl8822eu_fw", "rtl8822eu_config", NULL, 0}, /* RTL8822EU */
279*3366b4c1SMatthias Ringwald     {0xa82a, 0x8822, "mp_rtl8822eu_fw", "rtl8822eu_fw", "rtl8822eu_config", NULL, 0}, /* RTL8822EU */
2808bb555aaSBjoern Hartmann 
2818bb555aaSBjoern Hartmann     /* NOTE: must append patch entries above the null entry */
2828bb555aaSBjoern Hartmann     {0, 0, NULL, NULL, NULL, NULL, 0}};
2838bb555aaSBjoern Hartmann 
2848bb555aaSBjoern Hartmann uint16_t project_id[] = {
2858bb555aaSBjoern Hartmann     ROM_LMP_8723a, ROM_LMP_8723b, ROM_LMP_8821a, ROM_LMP_8761a, ROM_LMP_NONE,
2868bb555aaSBjoern Hartmann     ROM_LMP_NONE,  ROM_LMP_NONE,  ROM_LMP_NONE,  ROM_LMP_8822b, ROM_LMP_8723b, /* RTL8723DU */
2878bb555aaSBjoern Hartmann     ROM_LMP_8821a,                                                             /* RTL8821CU */
2888bb555aaSBjoern Hartmann     ROM_LMP_NONE,  ROM_LMP_NONE,  ROM_LMP_8822b,                               /* RTL8822CU */
2898bb555aaSBjoern Hartmann     ROM_LMP_8761a,                                                             /* index 14 for 8761BU */
2908bb555aaSBjoern Hartmann };
2918bb555aaSBjoern Hartmann 
2928bb555aaSBjoern Hartmann static btstack_packet_callback_registration_t hci_event_callback_registration;
2938bb555aaSBjoern Hartmann static uint8_t                                state = STATE_READ_ROM_VERSION;
2948bb555aaSBjoern Hartmann static uint8_t                                rom_version;
2958bb555aaSBjoern Hartmann static uint16_t                               lmp_subversion;
2968bb555aaSBjoern Hartmann static uint16_t                               product_id;
2978bb555aaSBjoern Hartmann static patch_info *                           patch;
2988bb555aaSBjoern Hartmann 
2998bb555aaSBjoern Hartmann #ifdef HAVE_POSIX_FILE_IO
3008bb555aaSBjoern Hartmann static const char *firmware_folder_path = ".";
3018bb555aaSBjoern Hartmann static const char *firmware_file_path;
3028bb555aaSBjoern Hartmann static const char *config_folder_path = ".";
3038bb555aaSBjoern Hartmann static const char *config_file_path;
3048bb555aaSBjoern Hartmann static char        firmware_file[1000];
3058bb555aaSBjoern Hartmann static char        config_file[1000];
3068bb555aaSBjoern Hartmann #endif
3078bb555aaSBjoern Hartmann 
3088bb555aaSBjoern Hartmann const uint8_t FW_SIGNATURE[8]        = {0x52, 0x65, 0x61, 0x6C, 0x74, 0x65, 0x63, 0x68};
3098bb555aaSBjoern Hartmann const uint8_t EXTENSION_SIGNATURE[4] = {0x51, 0x04, 0xFD, 0x77};
3108bb555aaSBjoern Hartmann 
3118bb555aaSBjoern Hartmann static void hci_packet_handler(uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size) {
3128bb555aaSBjoern Hartmann     UNUSED(channel);
3138bb555aaSBjoern Hartmann     UNUSED(size);
3148bb555aaSBjoern Hartmann     if (packet_type != HCI_EVENT_PACKET || hci_event_packet_get_type(packet) != HCI_EVENT_COMMAND_COMPLETE) {
3158bb555aaSBjoern Hartmann         return;
3168bb555aaSBjoern Hartmann     }
3178bb555aaSBjoern Hartmann     uint16_t opcode = hci_event_command_complete_get_command_opcode(packet);
3188bb555aaSBjoern Hartmann 
3198bb555aaSBjoern Hartmann     switch (opcode) {
3208bb555aaSBjoern Hartmann     case HCI_READ_ROM_VERSION:
3218bb555aaSBjoern Hartmann         rom_version = hci_event_command_complete_get_return_parameters(packet)[1];
3228bb555aaSBjoern Hartmann         log_info("Received ROM version 0x%02x", rom_version);
3238bb555aaSBjoern Hartmann         break;
3248bb555aaSBjoern Hartmann     default:
3258bb555aaSBjoern Hartmann         break;
3268bb555aaSBjoern Hartmann     }
3278bb555aaSBjoern Hartmann }
3288bb555aaSBjoern Hartmann 
3298bb555aaSBjoern Hartmann static void chipset_init(const void *config) {
3308bb555aaSBjoern Hartmann     UNUSED(config);
3318bb555aaSBjoern Hartmann #ifdef HAVE_POSIX_FILE_IO
3328bb555aaSBjoern Hartmann     // determine file path
3338bb555aaSBjoern Hartmann     if (firmware_file_path == NULL || config_file_path == NULL) {
3348bb555aaSBjoern Hartmann         log_info("firmware or config file path is empty. Using product id 0x%04x!", product_id);
3358bb555aaSBjoern Hartmann         patch = NULL;
33692728706SMatthias Ringwald         for (uint16_t i = 0; i < sizeof(fw_patch_table) / sizeof(patch_info); i++) {
3378bb555aaSBjoern Hartmann             if (fw_patch_table[i].prod_id == product_id) {
3388bb555aaSBjoern Hartmann                 patch = &fw_patch_table[i];
3398bb555aaSBjoern Hartmann                 break;
3408bb555aaSBjoern Hartmann             }
3418bb555aaSBjoern Hartmann         }
3428bb555aaSBjoern Hartmann         if (patch == NULL) {
3438bb555aaSBjoern Hartmann             log_info("Product id 0x%04x is unknown", product_id);
3448bb555aaSBjoern Hartmann             state = STATE_DONE;
3458bb555aaSBjoern Hartmann             return;
3468bb555aaSBjoern Hartmann         }
34774f83314SMatthias Ringwald         snprintf(firmware_file, sizeof(firmware_file), "%s/%s", firmware_folder_path, patch->patch_name);
34874f83314SMatthias Ringwald         snprintf(config_file, sizeof(config_file), "%s/%s", config_folder_path, patch->config_name);
3498bb555aaSBjoern Hartmann         firmware_file_path = &firmware_file[0];
3508bb555aaSBjoern Hartmann         config_file_path   = &config_file[0];
3518bb555aaSBjoern Hartmann         lmp_subversion     = patch->lmp_sub;
3528bb555aaSBjoern Hartmann     }
3538bb555aaSBjoern Hartmann     log_info("Using firmware '%s' and config '%s'", firmware_file_path, config_file_path);
3548bb555aaSBjoern Hartmann 
3558bb555aaSBjoern Hartmann     // activate hci callback
3568bb555aaSBjoern Hartmann     hci_event_callback_registration.callback = &hci_packet_handler;
3578bb555aaSBjoern Hartmann     hci_add_event_handler(&hci_event_callback_registration);
3588bb555aaSBjoern Hartmann     state = STATE_READ_ROM_VERSION;
3598bb555aaSBjoern Hartmann #endif
3608bb555aaSBjoern Hartmann }
3618bb555aaSBjoern Hartmann 
3628bb555aaSBjoern Hartmann #ifdef HAVE_POSIX_FILE_IO
3638bb555aaSBjoern Hartmann 
3648bb555aaSBjoern Hartmann /**
3658bb555aaSBjoern Hartmann  * @brief Opens the specified file and stores content to an allocated buffer
3668bb555aaSBjoern Hartmann  *
3678bb555aaSBjoern Hartmann  * @param file
3688bb555aaSBjoern Hartmann  * @param buf
3698bb555aaSBjoern Hartmann  * @param name
3708bb555aaSBjoern Hartmann  * @return uint32_t Length of file
3718bb555aaSBjoern Hartmann  */
3728bb555aaSBjoern Hartmann static uint32_t read_file(FILE **file, uint8_t **buf, const char *name) {
3738bb555aaSBjoern Hartmann     uint32_t size;
3748bb555aaSBjoern Hartmann 
3758bb555aaSBjoern Hartmann     // open file
3768bb555aaSBjoern Hartmann     *file = fopen(name, "rb");
3778bb555aaSBjoern Hartmann     if (*file == NULL) {
3788bb555aaSBjoern Hartmann         log_info("Failed to open file %s", name);
3798bb555aaSBjoern Hartmann         return 0;
3808bb555aaSBjoern Hartmann     }
3818bb555aaSBjoern Hartmann 
3828bb555aaSBjoern Hartmann     // determine length of file
3838bb555aaSBjoern Hartmann     fseek(*file, 0, SEEK_END);
3848bb555aaSBjoern Hartmann     size = ftell(*file);
3858bb555aaSBjoern Hartmann     fseek(*file, 0, SEEK_SET);
3868bb555aaSBjoern Hartmann     if (size <= 0) {
3878bb555aaSBjoern Hartmann         return 0;
3888bb555aaSBjoern Hartmann     }
3898bb555aaSBjoern Hartmann 
3908bb555aaSBjoern Hartmann     // allocate memory
3918bb555aaSBjoern Hartmann     *buf = malloc(size);
3928bb555aaSBjoern Hartmann     if (*buf == NULL) {
3938bb555aaSBjoern Hartmann         fclose(*file);
3948bb555aaSBjoern Hartmann         *file = NULL;
3958bb555aaSBjoern Hartmann         log_info("Failed to allocate %u bytes for file %s", size, name);
3968bb555aaSBjoern Hartmann         return 0;
3978bb555aaSBjoern Hartmann     }
3988bb555aaSBjoern Hartmann 
3998bb555aaSBjoern Hartmann     // read file
40058080ea6SMatthias Ringwald     size_t ret = fread(*buf, size, 1, *file);
4018bb555aaSBjoern Hartmann     if (ret != 1) {
40209fffc16SMatthias Ringwald         log_info("Failed to read %u bytes from file %s (ret = %d)", size, name, (int) ret);
4038bb555aaSBjoern Hartmann         fclose(*file);
4048bb555aaSBjoern Hartmann         free(*buf);
4058bb555aaSBjoern Hartmann         *file = NULL;
4068bb555aaSBjoern Hartmann         *buf  = NULL;
4078bb555aaSBjoern Hartmann         return 0;
4088bb555aaSBjoern Hartmann     }
4098bb555aaSBjoern Hartmann 
4108bb555aaSBjoern Hartmann     log_info("Opened file %s and read %u bytes", name, size);
4118bb555aaSBjoern Hartmann     return size;
4128bb555aaSBjoern Hartmann }
4138bb555aaSBjoern Hartmann 
4148bb555aaSBjoern Hartmann static void finalize_file_and_buffer(FILE **file, uint8_t **buffer) {
4158bb555aaSBjoern Hartmann     fclose(*file);
4168bb555aaSBjoern Hartmann     free(*buffer);
4178bb555aaSBjoern Hartmann     *buffer = NULL;
4188bb555aaSBjoern Hartmann     *file   = NULL;
4198bb555aaSBjoern Hartmann }
4208bb555aaSBjoern Hartmann 
4218bb555aaSBjoern Hartmann static uint8_t update_firmware(const char *firmware, const char *config, uint8_t *hci_cmd_buffer) {
4228bb555aaSBjoern Hartmann     static uint8_t *patch_buf = NULL;
4238bb555aaSBjoern Hartmann     static uint32_t fw_total_len;
4248bb555aaSBjoern Hartmann     static uint32_t fw_ptr;
4258bb555aaSBjoern Hartmann     static uint8_t  index;
4268bb555aaSBjoern Hartmann 
4278bb555aaSBjoern Hartmann     // read firmware and config
4288bb555aaSBjoern Hartmann     if (patch_buf == NULL) {
4298bb555aaSBjoern Hartmann         uint16_t patch_length = 0;
4308bb555aaSBjoern Hartmann         uint32_t offset;
4318bb555aaSBjoern Hartmann         FILE *   fw = NULL;
4328bb555aaSBjoern Hartmann         uint32_t fw_size;
4338bb555aaSBjoern Hartmann         uint8_t *fw_buf = NULL;
4348bb555aaSBjoern Hartmann 
4358bb555aaSBjoern Hartmann         FILE *   conf = NULL;
4368bb555aaSBjoern Hartmann         uint32_t conf_size;
4378bb555aaSBjoern Hartmann         uint8_t *conf_buf = NULL;
4388bb555aaSBjoern Hartmann 
4398bb555aaSBjoern Hartmann         if (firmware == NULL || config == NULL) {
4408bb555aaSBjoern Hartmann             log_info("Please specify realtek firmware and config file paths");
4418bb555aaSBjoern Hartmann             return FW_DONE;
4428bb555aaSBjoern Hartmann         }
4438bb555aaSBjoern Hartmann 
4448bb555aaSBjoern Hartmann         // read firmware
4458bb555aaSBjoern Hartmann         fw_size = read_file(&fw, &fw_buf, firmware);
4468bb555aaSBjoern Hartmann         if (fw_size == 0) {
4478bb555aaSBjoern Hartmann             log_info("Firmware size is 0. Quit!");
4488bb555aaSBjoern Hartmann             return FW_DONE;
4498bb555aaSBjoern Hartmann         }
4508bb555aaSBjoern Hartmann 
4518bb555aaSBjoern Hartmann         // read config
4528bb555aaSBjoern Hartmann         conf_size = read_file(&conf, &conf_buf, config);
4538bb555aaSBjoern Hartmann         if (conf_size == 0) {
4548bb555aaSBjoern Hartmann             log_info("Config size is 0. Quit!");
4558bb555aaSBjoern Hartmann             fclose(fw);
4568bb555aaSBjoern Hartmann             free(fw_buf);
4578bb555aaSBjoern Hartmann             fw_buf = NULL;
4588bb555aaSBjoern Hartmann             fw     = NULL;
4598bb555aaSBjoern Hartmann             return FW_DONE;
4608bb555aaSBjoern Hartmann             finalize_file_and_buffer(&fw, &fw_buf);
4618bb555aaSBjoern Hartmann         }
4628bb555aaSBjoern Hartmann 
4638bb555aaSBjoern Hartmann         // check signature
4648bb555aaSBjoern Hartmann         if (memcmp(fw_buf, FW_SIGNATURE, 8) != 0 || memcmp(fw_buf + fw_size - 4, EXTENSION_SIGNATURE, 4) != 0) {
4658bb555aaSBjoern Hartmann             log_info("Wrong signature. Quit!");
4668bb555aaSBjoern Hartmann             finalize_file_and_buffer(&fw, &fw_buf);
4678bb555aaSBjoern Hartmann             finalize_file_and_buffer(&conf, &conf_buf);
4688bb555aaSBjoern Hartmann             return FW_DONE;
4698bb555aaSBjoern Hartmann         }
4708bb555aaSBjoern Hartmann 
4718bb555aaSBjoern Hartmann         // check project id
4728bb555aaSBjoern Hartmann         if (lmp_subversion != project_id[*(fw_buf + fw_size - 7)]) {
4738bb555aaSBjoern Hartmann             log_info("Wrong project id. Quit!");
4748bb555aaSBjoern Hartmann             finalize_file_and_buffer(&fw, &fw_buf);
4758bb555aaSBjoern Hartmann             finalize_file_and_buffer(&conf, &conf_buf);
4768bb555aaSBjoern Hartmann             return FW_DONE;
4778bb555aaSBjoern Hartmann         }
4788bb555aaSBjoern Hartmann 
4798bb555aaSBjoern Hartmann         // read firmware version
4808bb555aaSBjoern Hartmann         uint32_t fw_version = little_endian_read_32(fw_buf, 8);
4818bb555aaSBjoern Hartmann         log_info("Firmware version: 0x%x", fw_version);
4828bb555aaSBjoern Hartmann 
4838bb555aaSBjoern Hartmann         // read number of patches
4848bb555aaSBjoern Hartmann         uint16_t fw_num_patches = little_endian_read_16(fw_buf, 12);
4858bb555aaSBjoern Hartmann         log_info("Number of patches: %d", fw_num_patches);
4868bb555aaSBjoern Hartmann 
4878bb555aaSBjoern Hartmann         // find correct entry
4888bb555aaSBjoern Hartmann         for (uint16_t i = 0; i < fw_num_patches; i++) {
4898bb555aaSBjoern Hartmann             if (little_endian_read_16(fw_buf, 14 + 2 * i) == rom_version + 1) {
4908bb555aaSBjoern Hartmann                 patch_length = little_endian_read_16(fw_buf, 14 + 2 * fw_num_patches + 2 * i);
4918bb555aaSBjoern Hartmann                 offset       = little_endian_read_32(fw_buf, 14 + 4 * fw_num_patches + 4 * i);
4928bb555aaSBjoern Hartmann                 log_info("patch_length %u, offset %u", patch_length, offset);
4938bb555aaSBjoern Hartmann                 break;
4948bb555aaSBjoern Hartmann             }
4958bb555aaSBjoern Hartmann         }
4968bb555aaSBjoern Hartmann         if (patch_length == 0) {
4978bb555aaSBjoern Hartmann             log_debug("Failed to find valid patch");
4988bb555aaSBjoern Hartmann             finalize_file_and_buffer(&fw, &fw_buf);
4998bb555aaSBjoern Hartmann             finalize_file_and_buffer(&conf, &conf_buf);
5008bb555aaSBjoern Hartmann             return FW_DONE;
5018bb555aaSBjoern Hartmann         }
5028bb555aaSBjoern Hartmann 
5038bb555aaSBjoern Hartmann         // allocate patch buffer
5048bb555aaSBjoern Hartmann         fw_total_len = patch_length + conf_size;
5058bb555aaSBjoern Hartmann         patch_buf    = malloc(fw_total_len);
5068bb555aaSBjoern Hartmann         if (patch_buf == NULL) {
5078bb555aaSBjoern Hartmann             log_debug("Failed to allocate %u bytes for patch buffer", fw_total_len);
5088bb555aaSBjoern Hartmann             finalize_file_and_buffer(&fw, &fw_buf);
5098bb555aaSBjoern Hartmann             finalize_file_and_buffer(&conf, &conf_buf);
5108bb555aaSBjoern Hartmann             return FW_DONE;
5118bb555aaSBjoern Hartmann         }
5128bb555aaSBjoern Hartmann 
5138bb555aaSBjoern Hartmann         // copy patch
5148bb555aaSBjoern Hartmann         memcpy(patch_buf, fw_buf + offset, patch_length);
5158bb555aaSBjoern Hartmann         memcpy(patch_buf + patch_length - 4, &fw_version, 4);
5168bb555aaSBjoern Hartmann         memcpy(patch_buf + patch_length, conf_buf, conf_size);
5178bb555aaSBjoern Hartmann         fw_ptr = 0;
5188bb555aaSBjoern Hartmann         index  = 0;
5198bb555aaSBjoern Hartmann 
5208bb555aaSBjoern Hartmann         // close files
5218bb555aaSBjoern Hartmann         finalize_file_and_buffer(&fw, &fw_buf);
5228bb555aaSBjoern Hartmann         finalize_file_and_buffer(&conf, &conf_buf);
5238bb555aaSBjoern Hartmann     }
5248bb555aaSBjoern Hartmann 
5258bb555aaSBjoern Hartmann     uint8_t len;
5268bb555aaSBjoern Hartmann     if (fw_total_len - fw_ptr > 252) {
5278bb555aaSBjoern Hartmann         len = 252;
5288bb555aaSBjoern Hartmann     } else {
5298bb555aaSBjoern Hartmann         len = fw_total_len - fw_ptr;
5308bb555aaSBjoern Hartmann         index |= 0x80;  // end
5318bb555aaSBjoern Hartmann     }
5328bb555aaSBjoern Hartmann 
5338bb555aaSBjoern Hartmann     if (len) {
5348bb555aaSBjoern Hartmann         FILL_COMMAND(hci_cmd_buffer, HCI_DOWNLOAD_FW);
5358bb555aaSBjoern Hartmann         FILL_LENGTH(hci_cmd_buffer, len + 1);
5368bb555aaSBjoern Hartmann         FILL_INDEX(hci_cmd_buffer, index);
5378bb555aaSBjoern Hartmann         FILL_FW_DATA(hci_cmd_buffer, patch_buf, fw_ptr, len);
5388bb555aaSBjoern Hartmann         index++;
5398bb555aaSBjoern Hartmann         fw_ptr += len;
5408bb555aaSBjoern Hartmann         return FW_MORE_TO_DO;
5418bb555aaSBjoern Hartmann     }
5428bb555aaSBjoern Hartmann 
5438bb555aaSBjoern Hartmann     // cleanup and return
5448bb555aaSBjoern Hartmann     free(patch_buf);
5458bb555aaSBjoern Hartmann     patch_buf = NULL;
5468bb555aaSBjoern Hartmann     return FW_DONE;
5478bb555aaSBjoern Hartmann }
5488bb555aaSBjoern Hartmann 
5498bb555aaSBjoern Hartmann #endif  // HAVE_POSIX_FILE_IO
5508bb555aaSBjoern Hartmann 
5518bb555aaSBjoern Hartmann static btstack_chipset_result_t chipset_next_command(uint8_t *hci_cmd_buffer) {
5528bb555aaSBjoern Hartmann #ifdef HAVE_POSIX_FILE_IO
5538bb555aaSBjoern Hartmann     uint8_t ret;
5548bb555aaSBjoern Hartmann     while (true) {
5558bb555aaSBjoern Hartmann         switch (state) {
5568bb555aaSBjoern Hartmann         case STATE_READ_ROM_VERSION:
5578bb555aaSBjoern Hartmann             FILL_COMMAND(hci_cmd_buffer, HCI_READ_ROM_VERSION);
5588bb555aaSBjoern Hartmann             FILL_LENGTH(hci_cmd_buffer, 0);
5598bb555aaSBjoern Hartmann             state = STATE_LOAD_FIRMWARE;
5608bb555aaSBjoern Hartmann             break;
5618bb555aaSBjoern Hartmann         case STATE_LOAD_FIRMWARE:
5628bb555aaSBjoern Hartmann             if (lmp_subversion != ROM_LMP_8723a) {
5638bb555aaSBjoern Hartmann                 ret = update_firmware(firmware_file_path, config_file_path, hci_cmd_buffer);
5648bb555aaSBjoern Hartmann             } else {
5658bb555aaSBjoern Hartmann                 log_info("Realtek firmware for old patch style not implemented");
5668bb555aaSBjoern Hartmann                 ret = FW_DONE;
5678bb555aaSBjoern Hartmann             }
5688bb555aaSBjoern Hartmann             if (ret != FW_DONE) {
5698bb555aaSBjoern Hartmann                 break;
5708bb555aaSBjoern Hartmann             }
5718bb555aaSBjoern Hartmann             // we are done fall through
5728bb555aaSBjoern Hartmann             state = STATE_RESET;
5738bb555aaSBjoern Hartmann         case STATE_RESET:
5748bb555aaSBjoern Hartmann             FILL_COMMAND(hci_cmd_buffer, HCI_RESET);
5758bb555aaSBjoern Hartmann             FILL_LENGTH(hci_cmd_buffer, 0);
5768bb555aaSBjoern Hartmann             state = STATE_DONE;
5778bb555aaSBjoern Hartmann             break;
5788bb555aaSBjoern Hartmann         case STATE_DONE:
5798bb555aaSBjoern Hartmann             hci_remove_event_handler(&hci_event_callback_registration);
5808bb555aaSBjoern Hartmann             return BTSTACK_CHIPSET_DONE;
5818bb555aaSBjoern Hartmann             break;
5828bb555aaSBjoern Hartmann         default:
5838bb555aaSBjoern Hartmann             log_info("Invalid state %d", state);
5848bb555aaSBjoern Hartmann             return BTSTACK_CHIPSET_DONE;
5858bb555aaSBjoern Hartmann             break;
5868bb555aaSBjoern Hartmann         }
5878bb555aaSBjoern Hartmann         return BTSTACK_CHIPSET_VALID_COMMAND;
5888bb555aaSBjoern Hartmann     }
5898bb555aaSBjoern Hartmann #else   // HAVE_POSIX_FILE_IO
5908bb555aaSBjoern Hartmann     log_info("Realtek without File IO is not implemented yet");
5918bb555aaSBjoern Hartmann     return BTSTACK_CHIPSET_NO_INIT_SCRIPT;
5928bb555aaSBjoern Hartmann #endif  // HAVE_POSIX_FILE_IO
5938bb555aaSBjoern Hartmann }
5948bb555aaSBjoern Hartmann 
5958bb555aaSBjoern Hartmann void btstack_chipset_realtek_set_firmware_file_path(const char *path) {
5968bb555aaSBjoern Hartmann #ifdef HAVE_POSIX_FILE_IO
5978bb555aaSBjoern Hartmann     firmware_file_path = path;
5988bb555aaSBjoern Hartmann #endif
5998bb555aaSBjoern Hartmann }
6008bb555aaSBjoern Hartmann 
6018bb555aaSBjoern Hartmann void btstack_chipset_realtek_set_firmware_folder_path(const char *path) {
6028bb555aaSBjoern Hartmann #ifdef HAVE_POSIX_FILE_IO
6038bb555aaSBjoern Hartmann     firmware_folder_path = path;
6048bb555aaSBjoern Hartmann #endif
6058bb555aaSBjoern Hartmann }
6068bb555aaSBjoern Hartmann 
6078bb555aaSBjoern Hartmann void btstack_chipset_realtek_set_config_file_path(const char *path) {
6088bb555aaSBjoern Hartmann #ifdef HAVE_POSIX_FILE_IO
6098bb555aaSBjoern Hartmann     config_file_path = path;
6108bb555aaSBjoern Hartmann #endif
6118bb555aaSBjoern Hartmann }
6128bb555aaSBjoern Hartmann 
6138bb555aaSBjoern Hartmann void btstack_chipset_realtek_set_config_folder_path(const char *path) {
6148bb555aaSBjoern Hartmann #ifdef HAVE_POSIX_FILE_IO
6158bb555aaSBjoern Hartmann     config_folder_path = path;
6168bb555aaSBjoern Hartmann #endif
6178bb555aaSBjoern Hartmann }
6188bb555aaSBjoern Hartmann 
6195a4c0fdaSMatthias Ringwald void btstack_chipset_realtek_set_lmp_subversion(uint16_t version) {
6205a4c0fdaSMatthias Ringwald     lmp_subversion = version;
6215a4c0fdaSMatthias Ringwald }
6228bb555aaSBjoern Hartmann 
6235a4c0fdaSMatthias Ringwald void btstack_chipset_realtek_set_product_id(uint16_t id) {
6245a4c0fdaSMatthias Ringwald     product_id = id;
6255a4c0fdaSMatthias Ringwald }
6265a4c0fdaSMatthias Ringwald 
6275a4c0fdaSMatthias Ringwald uint16_t btstack_chipset_realtek_get_num_usb_controllers(void){
6285a4c0fdaSMatthias Ringwald     return (sizeof(fw_patch_table) / sizeof(patch_info)) - 1; // sentinel
6295a4c0fdaSMatthias Ringwald }
6305a4c0fdaSMatthias Ringwald 
6315a4c0fdaSMatthias Ringwald void btstack_chipset_realtek_get_vendor_product_id(uint16_t index, uint16_t * out_vendor_id, uint16_t * out_product_id){
6325a4c0fdaSMatthias Ringwald     btstack_assert(index < ((sizeof(fw_patch_table) / sizeof(patch_info)) - 1));
6335a4c0fdaSMatthias Ringwald     *out_vendor_id = 0xbda;
6345a4c0fdaSMatthias Ringwald     *out_product_id = fw_patch_table[index].prod_id;
6355a4c0fdaSMatthias Ringwald }
6368bb555aaSBjoern Hartmann 
6378bb555aaSBjoern Hartmann static const btstack_chipset_t btstack_chipset_realtek = {
6388bb555aaSBjoern Hartmann     "REALTEK", chipset_init, chipset_next_command,
6398bb555aaSBjoern Hartmann     NULL,  // chipset_set_baudrate_command,
6408bb555aaSBjoern Hartmann     NULL,  // chipset_set_bd_addr_command not supported or implemented
6418bb555aaSBjoern Hartmann };
6428bb555aaSBjoern Hartmann 
6438bb555aaSBjoern Hartmann // MARK: public API
6448bb555aaSBjoern Hartmann const btstack_chipset_t *btstack_chipset_realtek_instance(void) { return &btstack_chipset_realtek; }
645