xref: /btstack/chipset/realtek/btstack_chipset_realtek.c (revision 8bb555aa6b09e3f91bb7caaba1c7b6db6ac22e10)
1*8bb555aaSBjoern Hartmann /*
2*8bb555aaSBjoern Hartmann  * Copyright (C) 2014 BlueKitchen GmbH
3*8bb555aaSBjoern Hartmann  *
4*8bb555aaSBjoern Hartmann  * Redistribution and use in source and binary forms, with or without
5*8bb555aaSBjoern Hartmann  * modification, are permitted provided that the following conditions
6*8bb555aaSBjoern Hartmann  * are met:
7*8bb555aaSBjoern Hartmann  *
8*8bb555aaSBjoern Hartmann  * 1. Redistributions of source code must retain the above copyright
9*8bb555aaSBjoern Hartmann  *    notice, this list of conditions and the following disclaimer.
10*8bb555aaSBjoern Hartmann  * 2. Redistributions in binary form must reproduce the above copyright
11*8bb555aaSBjoern Hartmann  *    notice, this list of conditions and the following disclaimer in the
12*8bb555aaSBjoern Hartmann  *    documentation and/or other materials provided with the distribution.
13*8bb555aaSBjoern Hartmann  * 3. Neither the name of the copyright holders nor the names of
14*8bb555aaSBjoern Hartmann  *    contributors may be used to endorse or promote products derived
15*8bb555aaSBjoern Hartmann  *    from this software without specific prior written permission.
16*8bb555aaSBjoern Hartmann  * 4. Any redistribution, use, or modification is done solely for
17*8bb555aaSBjoern Hartmann  *    personal benefit and not for any commercial purpose or for
18*8bb555aaSBjoern Hartmann  *    monetary gain.
19*8bb555aaSBjoern Hartmann  *
20*8bb555aaSBjoern Hartmann  * THIS SOFTWARE IS PROVIDED BY BLUEKITCHEN GMBH AND CONTRIBUTORS
21*8bb555aaSBjoern Hartmann  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
22*8bb555aaSBjoern Hartmann  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
23*8bb555aaSBjoern Hartmann  * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL BLUEKITCHEN
24*8bb555aaSBjoern Hartmann  * GMBH OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
25*8bb555aaSBjoern Hartmann  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
26*8bb555aaSBjoern Hartmann  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
27*8bb555aaSBjoern Hartmann  * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
28*8bb555aaSBjoern Hartmann  * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
29*8bb555aaSBjoern Hartmann  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
30*8bb555aaSBjoern Hartmann  * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
31*8bb555aaSBjoern Hartmann  * SUCH DAMAGE.
32*8bb555aaSBjoern Hartmann  *
33*8bb555aaSBjoern Hartmann  * Please inquire about commercial licensing options at
34*8bb555aaSBjoern Hartmann  * [email protected]
35*8bb555aaSBjoern Hartmann  *
36*8bb555aaSBjoern Hartmann  */
37*8bb555aaSBjoern Hartmann 
38*8bb555aaSBjoern Hartmann #define BTSTACK_FILE__ "btstack_chipset_realtek.c"
39*8bb555aaSBjoern Hartmann 
40*8bb555aaSBjoern Hartmann /*
41*8bb555aaSBjoern Hartmann  *  btstack_chipset_realtek.c
42*8bb555aaSBjoern Hartmann  *
43*8bb555aaSBjoern Hartmann  *  Adapter to use REALTEK-based chipsets with BTstack
44*8bb555aaSBjoern Hartmann  */
45*8bb555aaSBjoern Hartmann 
46*8bb555aaSBjoern Hartmann #include "btstack_chipset_realtek.h"
47*8bb555aaSBjoern Hartmann 
48*8bb555aaSBjoern Hartmann #include <stddef.h> /* NULL */
49*8bb555aaSBjoern Hartmann #include <stdio.h>
50*8bb555aaSBjoern Hartmann #include <string.h> /* memcpy */
51*8bb555aaSBjoern Hartmann 
52*8bb555aaSBjoern Hartmann #include "btstack_control.h"
53*8bb555aaSBjoern Hartmann #include "btstack_debug.h"
54*8bb555aaSBjoern Hartmann #include "btstack_event.h"
55*8bb555aaSBjoern Hartmann #include "btstack_util.h"
56*8bb555aaSBjoern Hartmann #include "hci.h"
57*8bb555aaSBjoern Hartmann #include "hci_transport.h"
58*8bb555aaSBjoern Hartmann 
59*8bb555aaSBjoern Hartmann #define ROM_LMP_NONE 0x0000
60*8bb555aaSBjoern Hartmann #define ROM_LMP_8723a 0x1200
61*8bb555aaSBjoern Hartmann #define ROM_LMP_8723b 0x8723
62*8bb555aaSBjoern Hartmann #define ROM_LMP_8821a 0X8821
63*8bb555aaSBjoern Hartmann #define ROM_LMP_8761a 0X8761
64*8bb555aaSBjoern Hartmann #define ROM_LMP_8822b 0X8822
65*8bb555aaSBjoern Hartmann 
66*8bb555aaSBjoern Hartmann #define HCI_DOWNLOAD_FW 0xFC20
67*8bb555aaSBjoern Hartmann #define HCI_READ_ROM_VERSION 0xFC6D
68*8bb555aaSBjoern Hartmann #define HCI_READ_LMP_VERSION 0x1001
69*8bb555aaSBjoern Hartmann #define HCI_RESET 0x0C03
70*8bb555aaSBjoern Hartmann 
71*8bb555aaSBjoern Hartmann #define FILL_COMMAND(buf, command) ((int16_t *)buf)[0] = command
72*8bb555aaSBjoern Hartmann #define FILL_LENGTH(buf, length) buf[2] = length
73*8bb555aaSBjoern Hartmann #define FILL_INDEX(buf, index) buf[3] = index
74*8bb555aaSBjoern Hartmann #define FILL_FW_DATA(buf, firmware, ptr, len) memcpy(buf + 4, firmware + ptr, len)
75*8bb555aaSBjoern Hartmann 
76*8bb555aaSBjoern Hartmann enum {
77*8bb555aaSBjoern Hartmann     STATE_READ_ROM_VERSION,
78*8bb555aaSBjoern Hartmann     STATE_LOAD_FIRMWARE,
79*8bb555aaSBjoern Hartmann     STATE_RESET,
80*8bb555aaSBjoern Hartmann     STATE_DONE,
81*8bb555aaSBjoern Hartmann };
82*8bb555aaSBjoern Hartmann 
83*8bb555aaSBjoern Hartmann enum { FW_DONE, FW_MORE_TO_DO };
84*8bb555aaSBjoern Hartmann 
85*8bb555aaSBjoern Hartmann typedef struct {
86*8bb555aaSBjoern Hartmann     uint16_t prod_id;
87*8bb555aaSBjoern Hartmann     uint16_t lmp_sub;
88*8bb555aaSBjoern Hartmann     char *   mp_patch_name;
89*8bb555aaSBjoern Hartmann     char *   patch_name;
90*8bb555aaSBjoern Hartmann     char *   config_name;
91*8bb555aaSBjoern Hartmann 
92*8bb555aaSBjoern Hartmann     uint8_t *fw_cache1;
93*8bb555aaSBjoern Hartmann     int      fw_len1;
94*8bb555aaSBjoern Hartmann } patch_info;
95*8bb555aaSBjoern Hartmann 
96*8bb555aaSBjoern Hartmann static patch_info fw_patch_table[] = {
97*8bb555aaSBjoern Hartmann     /* { pid, lmp_sub, mp_fw_name, fw_name, config_name, fw_cache, fw_len } */
98*8bb555aaSBjoern Hartmann     {0x1724, 0x1200, "mp_rtl8723a_fw", "rtl8723a_fw", "rtl8723a_config", NULL, 0}, /* RTL8723A */
99*8bb555aaSBjoern Hartmann     {0x8723, 0x1200, "mp_rtl8723a_fw", "rtl8723a_fw", "rtl8723a_config", NULL, 0}, /* 8723AE */
100*8bb555aaSBjoern Hartmann     {0xA723, 0x1200, "mp_rtl8723a_fw", "rtl8723a_fw", "rtl8723a_config", NULL, 0}, /* 8723AE for LI */
101*8bb555aaSBjoern Hartmann     {0x0723, 0x1200, "mp_rtl8723a_fw", "rtl8723a_fw", "rtl8723a_config", NULL, 0}, /* 8723AE */
102*8bb555aaSBjoern Hartmann     {0x3394, 0x1200, "mp_rtl8723a_fw", "rtl8723a_fw", "rtl8723a_config", NULL, 0}, /* 8723AE for Azurewave */
103*8bb555aaSBjoern Hartmann 
104*8bb555aaSBjoern Hartmann     {0x0724, 0x1200, "mp_rtl8723a_fw", "rtl8723a_fw", "rtl8723a_config", NULL, 0}, /* 8723AU */
105*8bb555aaSBjoern Hartmann     {0x8725, 0x1200, "mp_rtl8723a_fw", "rtl8723a_fw", "rtl8723a_config", NULL, 0}, /* 8723AU */
106*8bb555aaSBjoern Hartmann     {0x872A, 0x1200, "mp_rtl8723a_fw", "rtl8723a_fw", "rtl8723a_config", NULL, 0}, /* 8723AU */
107*8bb555aaSBjoern Hartmann     {0x872B, 0x1200, "mp_rtl8723a_fw", "rtl8723a_fw", "rtl8723a_config", NULL, 0}, /* 8723AU */
108*8bb555aaSBjoern Hartmann 
109*8bb555aaSBjoern Hartmann     {0xb720, 0x8723, "mp_rtl8723b_fw", "rtl8723b_fw", "rtl8723bu_config", NULL, 0}, /* RTL8723BU */
110*8bb555aaSBjoern Hartmann     {0xb72A, 0x8723, "mp_rtl8723b_fw", "rtl8723b_fw", "rtl8723bu_config", NULL, 0}, /* RTL8723BU */
111*8bb555aaSBjoern Hartmann     {0xb728, 0x8723, "mp_rtl8723b_fw", "rtl8723b_fw", "rtl8723b_config", NULL, 0},  /* RTL8723BE for LC */
112*8bb555aaSBjoern Hartmann     {0xb723, 0x8723, "mp_rtl8723b_fw", "rtl8723b_fw", "rtl8723b_config", NULL, 0},  /* RTL8723BE */
113*8bb555aaSBjoern Hartmann     {0xb72B, 0x8723, "mp_rtl8723b_fw", "rtl8723b_fw", "rtl8723b_config", NULL, 0},  /* RTL8723BE */
114*8bb555aaSBjoern Hartmann     {0xb001, 0x8723, "mp_rtl8723b_fw", "rtl8723b_fw", "rtl8723b_config", NULL, 0},  /* RTL8723BE for HP */
115*8bb555aaSBjoern Hartmann     {0xb002, 0x8723, "mp_rtl8723b_fw", "rtl8723b_fw", "rtl8723b_config", NULL, 0},  /* RTL8723BE */
116*8bb555aaSBjoern Hartmann     {0xb003, 0x8723, "mp_rtl8723b_fw", "rtl8723b_fw", "rtl8723b_config", NULL, 0},  /* RTL8723BE */
117*8bb555aaSBjoern Hartmann     {0xb004, 0x8723, "mp_rtl8723b_fw", "rtl8723b_fw", "rtl8723b_config", NULL, 0},  /* RTL8723BE */
118*8bb555aaSBjoern Hartmann     {0xb005, 0x8723, "mp_rtl8723b_fw", "rtl8723b_fw", "rtl8723b_config", NULL, 0},  /* RTL8723BE */
119*8bb555aaSBjoern Hartmann 
120*8bb555aaSBjoern Hartmann     {0x3410, 0x8723, "mp_rtl8723b_fw", "rtl8723b_fw", "rtl8723b_config", NULL, 0}, /* RTL8723BE for Azurewave */
121*8bb555aaSBjoern Hartmann     {0x3416, 0x8723, "mp_rtl8723b_fw", "rtl8723b_fw", "rtl8723b_config", NULL, 0}, /* RTL8723BE for Azurewave */
122*8bb555aaSBjoern Hartmann     {0x3459, 0x8723, "mp_rtl8723b_fw", "rtl8723b_fw", "rtl8723b_config", NULL, 0}, /* RTL8723BE for Azurewave */
123*8bb555aaSBjoern Hartmann     {0xE085, 0x8723, "mp_rtl8723b_fw", "rtl8723b_fw", "rtl8723b_config", NULL, 0}, /* RTL8723BE for Foxconn */
124*8bb555aaSBjoern Hartmann     {0xE08B, 0x8723, "mp_rtl8723b_fw", "rtl8723b_fw", "rtl8723b_config", NULL, 0}, /* RTL8723BE for Foxconn */
125*8bb555aaSBjoern Hartmann     {0xE09E, 0x8723, "mp_rtl8723b_fw", "rtl8723b_fw", "rtl8723b_config", NULL, 0}, /* RTL8723BE for Foxconn */
126*8bb555aaSBjoern Hartmann 
127*8bb555aaSBjoern Hartmann     {0xA761, 0x8761, "mp_rtl8761a_fw", "rtl8761au_fw", "rtl8761a_config", NULL, 0},  /* RTL8761AU only */
128*8bb555aaSBjoern Hartmann     {0x818B, 0x8761, "mp_rtl8761a_fw", "rtl8761aw_fw", "rtl8761aw_config", NULL, 0}, /* RTL8761AW + 8192EU */
129*8bb555aaSBjoern Hartmann     {0x818C, 0x8761, "mp_rtl8761a_fw", "rtl8761aw_fw", "rtl8761aw_config", NULL, 0}, /* RTL8761AW + 8192EU */
130*8bb555aaSBjoern Hartmann     {0x8760, 0x8761, "mp_rtl8761a_fw", "rtl8761au_fw", "rtl8761a_config", NULL, 0},  /* RTL8761AU + 8192EE */
131*8bb555aaSBjoern Hartmann     {0xB761, 0x8761, "mp_rtl8761a_fw", "rtl8761au_fw", "rtl8761a_config", NULL, 0},  /* RTL8761AU + 8192EE */
132*8bb555aaSBjoern Hartmann     {0x8761, 0x8761, "mp_rtl8761a_fw", "rtl8761au_fw", "rtl8761a_config", NULL, 0},  /* RTL8761AU + 8192EE for LI */
133*8bb555aaSBjoern Hartmann     {0x8A60, 0x8761, "mp_rtl8761a_fw", "rtl8761au_fw", "rtl8761a_config", NULL, 0},  /* RTL8761AU + 8812AE */
134*8bb555aaSBjoern Hartmann     {0x3527, 0x8761, "mp_rtl8761a_fw", "rtl8761au_fw", "rtl8761a_config", NULL, 0},  /* RTL8761AU + 8814AE */
135*8bb555aaSBjoern Hartmann 
136*8bb555aaSBjoern Hartmann     {0x8821, 0x8821, "mp_rtl8821a_fw", "rtl8821a_fw", "rtl8821a_config", NULL, 0}, /* RTL8821AE */
137*8bb555aaSBjoern Hartmann     {0x0821, 0x8821, "mp_rtl8821a_fw", "rtl8821a_fw", "rtl8821a_config", NULL, 0}, /* RTL8821AE */
138*8bb555aaSBjoern Hartmann     {0x0823, 0x8821, "mp_rtl8821a_fw", "rtl8821a_fw", "rtl8821a_config", NULL, 0}, /* RTL8821AU */
139*8bb555aaSBjoern Hartmann     {0x3414, 0x8821, "mp_rtl8821a_fw", "rtl8821a_fw", "rtl8821a_config", NULL, 0}, /* RTL8821AE */
140*8bb555aaSBjoern Hartmann     {0x3458, 0x8821, "mp_rtl8821a_fw", "rtl8821a_fw", "rtl8821a_config", NULL, 0}, /* RTL8821AE */
141*8bb555aaSBjoern Hartmann     {0x3461, 0x8821, "mp_rtl8821a_fw", "rtl8821a_fw", "rtl8821a_config", NULL, 0}, /* RTL8821AE */
142*8bb555aaSBjoern Hartmann     {0x3462, 0x8821, "mp_rtl8821a_fw", "rtl8821a_fw", "rtl8821a_config", NULL, 0}, /* RTL8821AE */
143*8bb555aaSBjoern Hartmann 
144*8bb555aaSBjoern Hartmann     {0xb82c, 0x8822, "mp_rtl8822bu_fw", "rtl8822bu_fw", "rtl8822bu_config", NULL, 0}, /* RTL8822BU */
145*8bb555aaSBjoern Hartmann     {0xd723, 0x8723, "mp_rtl8723du_fw", "rtl8723du_fw", "rtl8723du_config", NULL, 0}, /* RTL8723DU */
146*8bb555aaSBjoern Hartmann     {0xb820, 0x8821, "mp_rtl8821cu_fw", "rtl8821cu_fw", "rtl8821cu_config", NULL, 0}, /* RTL8821CU */
147*8bb555aaSBjoern Hartmann     {0xc820, 0x8821, "mp_rtl8821cu_fw", "rtl8821cu_fw", "rtl8821cu_config", NULL, 0}, /* RTL8821CU */
148*8bb555aaSBjoern Hartmann 
149*8bb555aaSBjoern Hartmann     {0xc82c, 0x8822, "mp_rtl8822cu_fw", "rtl8822cu_fw", "rtl8822cu_config", NULL, 0}, /* RTL8822CU */
150*8bb555aaSBjoern Hartmann     {0xc822, 0x8822, "mp_rtl8822cu_fw", "rtl8822cu_fw", "rtl8822cu_config", NULL, 0}, /* RTL8822CE */
151*8bb555aaSBjoern Hartmann     {0xb00c, 0x8822, "mp_rtl8822cu_fw", "rtl8822cu_fw", "rtl8822cu_config", NULL, 0}, /* RTL8822CE */
152*8bb555aaSBjoern Hartmann     {0xc123, 0x8822, "mp_rtl8822cu_fw", "rtl8822cu_fw", "rtl8822cu_config", NULL, 0}, /* RTL8822CE */
153*8bb555aaSBjoern Hartmann     {0x3549, 0x8822, "mp_rtl8822cu_fw", "rtl8822cu_fw", "rtl8822cu_config", NULL, 0}, /* RTL8822CE for Azurewave */
154*8bb555aaSBjoern Hartmann 
155*8bb555aaSBjoern Hartmann     {0x8771, 0x8761, "mp_rtl8761bu_fw", "rtl8761bu_fw", "rtl8761bu_config", NULL, 0}, /* RTL8761BU only */
156*8bb555aaSBjoern Hartmann 
157*8bb555aaSBjoern Hartmann     /* NOTE: must append patch entries above the null entry */
158*8bb555aaSBjoern Hartmann     {0, 0, NULL, NULL, NULL, NULL, 0}};
159*8bb555aaSBjoern Hartmann 
160*8bb555aaSBjoern Hartmann uint16_t project_id[] = {
161*8bb555aaSBjoern Hartmann     ROM_LMP_8723a, ROM_LMP_8723b, ROM_LMP_8821a, ROM_LMP_8761a, ROM_LMP_NONE,
162*8bb555aaSBjoern Hartmann     ROM_LMP_NONE,  ROM_LMP_NONE,  ROM_LMP_NONE,  ROM_LMP_8822b, ROM_LMP_8723b, /* RTL8723DU */
163*8bb555aaSBjoern Hartmann     ROM_LMP_8821a,                                                             /* RTL8821CU */
164*8bb555aaSBjoern Hartmann     ROM_LMP_NONE,  ROM_LMP_NONE,  ROM_LMP_8822b,                               /* RTL8822CU */
165*8bb555aaSBjoern Hartmann     ROM_LMP_8761a,                                                             /* index 14 for 8761BU */
166*8bb555aaSBjoern Hartmann };
167*8bb555aaSBjoern Hartmann 
168*8bb555aaSBjoern Hartmann static btstack_packet_callback_registration_t hci_event_callback_registration;
169*8bb555aaSBjoern Hartmann static uint8_t                                state = STATE_READ_ROM_VERSION;
170*8bb555aaSBjoern Hartmann static uint8_t                                rom_version;
171*8bb555aaSBjoern Hartmann static uint16_t                               lmp_subversion;
172*8bb555aaSBjoern Hartmann static uint16_t                               product_id;
173*8bb555aaSBjoern Hartmann static patch_info *                           patch;
174*8bb555aaSBjoern Hartmann 
175*8bb555aaSBjoern Hartmann #ifdef HAVE_POSIX_FILE_IO
176*8bb555aaSBjoern Hartmann static const char *firmware_folder_path = ".";
177*8bb555aaSBjoern Hartmann static const char *firmware_file_path;
178*8bb555aaSBjoern Hartmann static const char *config_folder_path = ".";
179*8bb555aaSBjoern Hartmann static const char *config_file_path;
180*8bb555aaSBjoern Hartmann static char        firmware_file[1000];
181*8bb555aaSBjoern Hartmann static char        config_file[1000];
182*8bb555aaSBjoern Hartmann #endif
183*8bb555aaSBjoern Hartmann 
184*8bb555aaSBjoern Hartmann const uint8_t FW_SIGNATURE[8]        = {0x52, 0x65, 0x61, 0x6C, 0x74, 0x65, 0x63, 0x68};
185*8bb555aaSBjoern Hartmann const uint8_t EXTENSION_SIGNATURE[4] = {0x51, 0x04, 0xFD, 0x77};
186*8bb555aaSBjoern Hartmann 
187*8bb555aaSBjoern Hartmann static void hci_packet_handler(uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size) {
188*8bb555aaSBjoern Hartmann     UNUSED(channel);
189*8bb555aaSBjoern Hartmann     UNUSED(size);
190*8bb555aaSBjoern Hartmann     if (packet_type != HCI_EVENT_PACKET || hci_event_packet_get_type(packet) != HCI_EVENT_COMMAND_COMPLETE) {
191*8bb555aaSBjoern Hartmann         return;
192*8bb555aaSBjoern Hartmann     }
193*8bb555aaSBjoern Hartmann     uint16_t opcode = hci_event_command_complete_get_command_opcode(packet);
194*8bb555aaSBjoern Hartmann 
195*8bb555aaSBjoern Hartmann     switch (opcode) {
196*8bb555aaSBjoern Hartmann     case HCI_READ_ROM_VERSION:
197*8bb555aaSBjoern Hartmann         rom_version = hci_event_command_complete_get_return_parameters(packet)[1];
198*8bb555aaSBjoern Hartmann         log_info("Received ROM version 0x%02x", rom_version);
199*8bb555aaSBjoern Hartmann         break;
200*8bb555aaSBjoern Hartmann     default:
201*8bb555aaSBjoern Hartmann         break;
202*8bb555aaSBjoern Hartmann     }
203*8bb555aaSBjoern Hartmann }
204*8bb555aaSBjoern Hartmann 
205*8bb555aaSBjoern Hartmann static void chipset_init(const void *config) {
206*8bb555aaSBjoern Hartmann     UNUSED(config);
207*8bb555aaSBjoern Hartmann #ifdef HAVE_POSIX_FILE_IO
208*8bb555aaSBjoern Hartmann     // determine file path
209*8bb555aaSBjoern Hartmann     if (firmware_file_path == NULL || config_file_path == NULL) {
210*8bb555aaSBjoern Hartmann         log_info("firmware or config file path is empty. Using product id 0x%04x!", product_id);
211*8bb555aaSBjoern Hartmann         patch = NULL;
212*8bb555aaSBjoern Hartmann         for (int i = 0; i < sizeof(fw_patch_table) / sizeof(patch_info); i++) {
213*8bb555aaSBjoern Hartmann             if (fw_patch_table[i].prod_id == product_id) {
214*8bb555aaSBjoern Hartmann                 patch = &fw_patch_table[i];
215*8bb555aaSBjoern Hartmann                 break;
216*8bb555aaSBjoern Hartmann             }
217*8bb555aaSBjoern Hartmann         }
218*8bb555aaSBjoern Hartmann         if (patch == NULL) {
219*8bb555aaSBjoern Hartmann             log_info("Product id 0x%04x is unknown", product_id);
220*8bb555aaSBjoern Hartmann             state = STATE_DONE;
221*8bb555aaSBjoern Hartmann             return;
222*8bb555aaSBjoern Hartmann         }
223*8bb555aaSBjoern Hartmann         sprintf(firmware_file, "%s/%s", firmware_folder_path, patch->patch_name);
224*8bb555aaSBjoern Hartmann         sprintf(config_file, "%s/%s", config_folder_path, patch->config_name);
225*8bb555aaSBjoern Hartmann         firmware_file_path = &firmware_file[0];
226*8bb555aaSBjoern Hartmann         config_file_path   = &config_file[0];
227*8bb555aaSBjoern Hartmann         lmp_subversion     = patch->lmp_sub;
228*8bb555aaSBjoern Hartmann     }
229*8bb555aaSBjoern Hartmann     log_info("Using firmware '%s' and config '%s'", firmware_file_path, config_file_path);
230*8bb555aaSBjoern Hartmann 
231*8bb555aaSBjoern Hartmann     // activate hci callback
232*8bb555aaSBjoern Hartmann     hci_event_callback_registration.callback = &hci_packet_handler;
233*8bb555aaSBjoern Hartmann     hci_add_event_handler(&hci_event_callback_registration);
234*8bb555aaSBjoern Hartmann     state = STATE_READ_ROM_VERSION;
235*8bb555aaSBjoern Hartmann #endif
236*8bb555aaSBjoern Hartmann }
237*8bb555aaSBjoern Hartmann 
238*8bb555aaSBjoern Hartmann #ifdef HAVE_POSIX_FILE_IO
239*8bb555aaSBjoern Hartmann 
240*8bb555aaSBjoern Hartmann /**
241*8bb555aaSBjoern Hartmann  * @brief Opens the specified file and stores content to an allocated buffer
242*8bb555aaSBjoern Hartmann  *
243*8bb555aaSBjoern Hartmann  * @param file
244*8bb555aaSBjoern Hartmann  * @param buf
245*8bb555aaSBjoern Hartmann  * @param name
246*8bb555aaSBjoern Hartmann  * @return uint32_t Length of file
247*8bb555aaSBjoern Hartmann  */
248*8bb555aaSBjoern Hartmann static uint32_t read_file(FILE **file, uint8_t **buf, const char *name) {
249*8bb555aaSBjoern Hartmann     uint32_t size;
250*8bb555aaSBjoern Hartmann 
251*8bb555aaSBjoern Hartmann     // open file
252*8bb555aaSBjoern Hartmann     *file = fopen(name, "rb");
253*8bb555aaSBjoern Hartmann     if (*file == NULL) {
254*8bb555aaSBjoern Hartmann         log_info("Failed to open file %s", name);
255*8bb555aaSBjoern Hartmann         return 0;
256*8bb555aaSBjoern Hartmann     }
257*8bb555aaSBjoern Hartmann 
258*8bb555aaSBjoern Hartmann     // determine length of file
259*8bb555aaSBjoern Hartmann     fseek(*file, 0, SEEK_END);
260*8bb555aaSBjoern Hartmann     size = ftell(*file);
261*8bb555aaSBjoern Hartmann     fseek(*file, 0, SEEK_SET);
262*8bb555aaSBjoern Hartmann     if (size <= 0) {
263*8bb555aaSBjoern Hartmann         return 0;
264*8bb555aaSBjoern Hartmann     }
265*8bb555aaSBjoern Hartmann 
266*8bb555aaSBjoern Hartmann     // allocate memory
267*8bb555aaSBjoern Hartmann     *buf = malloc(size);
268*8bb555aaSBjoern Hartmann     if (*buf == NULL) {
269*8bb555aaSBjoern Hartmann         fclose(*file);
270*8bb555aaSBjoern Hartmann         *file = NULL;
271*8bb555aaSBjoern Hartmann         log_info("Failed to allocate %u bytes for file %s", size, name);
272*8bb555aaSBjoern Hartmann         return 0;
273*8bb555aaSBjoern Hartmann     }
274*8bb555aaSBjoern Hartmann 
275*8bb555aaSBjoern Hartmann     // read file
276*8bb555aaSBjoern Hartmann     uint8_t ret = fread(*buf, size, 1, *file);
277*8bb555aaSBjoern Hartmann     if (ret != 1) {
278*8bb555aaSBjoern Hartmann         log_info("Failed to read %u bytes from file %s (ret = %u)", size, name, ret);
279*8bb555aaSBjoern Hartmann         fclose(*file);
280*8bb555aaSBjoern Hartmann         free(*buf);
281*8bb555aaSBjoern Hartmann         *file = NULL;
282*8bb555aaSBjoern Hartmann         *buf  = NULL;
283*8bb555aaSBjoern Hartmann         return 0;
284*8bb555aaSBjoern Hartmann     }
285*8bb555aaSBjoern Hartmann 
286*8bb555aaSBjoern Hartmann     log_info("Opened file %s and read %u bytes", name, size);
287*8bb555aaSBjoern Hartmann     return size;
288*8bb555aaSBjoern Hartmann }
289*8bb555aaSBjoern Hartmann 
290*8bb555aaSBjoern Hartmann static void finalize_file_and_buffer(FILE **file, uint8_t **buffer) {
291*8bb555aaSBjoern Hartmann     fclose(*file);
292*8bb555aaSBjoern Hartmann     free(*buffer);
293*8bb555aaSBjoern Hartmann     *buffer = NULL;
294*8bb555aaSBjoern Hartmann     *file   = NULL;
295*8bb555aaSBjoern Hartmann }
296*8bb555aaSBjoern Hartmann 
297*8bb555aaSBjoern Hartmann static uint8_t update_firmware(const char *firmware, const char *config, uint8_t *hci_cmd_buffer) {
298*8bb555aaSBjoern Hartmann     static uint8_t *patch_buf = NULL;
299*8bb555aaSBjoern Hartmann     static uint32_t fw_total_len;
300*8bb555aaSBjoern Hartmann     static uint32_t fw_ptr;
301*8bb555aaSBjoern Hartmann     static uint8_t  index;
302*8bb555aaSBjoern Hartmann 
303*8bb555aaSBjoern Hartmann     // read firmware and config
304*8bb555aaSBjoern Hartmann     if (patch_buf == NULL) {
305*8bb555aaSBjoern Hartmann         uint16_t patch_length = 0;
306*8bb555aaSBjoern Hartmann         uint32_t offset;
307*8bb555aaSBjoern Hartmann         FILE *   fw = NULL;
308*8bb555aaSBjoern Hartmann         uint32_t fw_size;
309*8bb555aaSBjoern Hartmann         uint8_t *fw_buf = NULL;
310*8bb555aaSBjoern Hartmann 
311*8bb555aaSBjoern Hartmann         FILE *   conf = NULL;
312*8bb555aaSBjoern Hartmann         uint32_t conf_size;
313*8bb555aaSBjoern Hartmann         uint8_t *conf_buf = NULL;
314*8bb555aaSBjoern Hartmann 
315*8bb555aaSBjoern Hartmann         if (firmware == NULL || config == NULL) {
316*8bb555aaSBjoern Hartmann             log_info("Please specify realtek firmware and config file paths");
317*8bb555aaSBjoern Hartmann             return FW_DONE;
318*8bb555aaSBjoern Hartmann         }
319*8bb555aaSBjoern Hartmann 
320*8bb555aaSBjoern Hartmann         // read firmware
321*8bb555aaSBjoern Hartmann         fw_size = read_file(&fw, &fw_buf, firmware);
322*8bb555aaSBjoern Hartmann         if (fw_size == 0) {
323*8bb555aaSBjoern Hartmann             log_info("Firmware size is 0. Quit!");
324*8bb555aaSBjoern Hartmann             return FW_DONE;
325*8bb555aaSBjoern Hartmann         }
326*8bb555aaSBjoern Hartmann 
327*8bb555aaSBjoern Hartmann         // read config
328*8bb555aaSBjoern Hartmann         conf_size = read_file(&conf, &conf_buf, config);
329*8bb555aaSBjoern Hartmann         if (conf_size == 0) {
330*8bb555aaSBjoern Hartmann             log_info("Config size is 0. Quit!");
331*8bb555aaSBjoern Hartmann             fclose(fw);
332*8bb555aaSBjoern Hartmann             free(fw_buf);
333*8bb555aaSBjoern Hartmann             fw_buf = NULL;
334*8bb555aaSBjoern Hartmann             fw     = NULL;
335*8bb555aaSBjoern Hartmann             return FW_DONE;
336*8bb555aaSBjoern Hartmann             finalize_file_and_buffer(&fw, &fw_buf);
337*8bb555aaSBjoern Hartmann         }
338*8bb555aaSBjoern Hartmann 
339*8bb555aaSBjoern Hartmann         // check signature
340*8bb555aaSBjoern Hartmann         if (memcmp(fw_buf, FW_SIGNATURE, 8) != 0 || memcmp(fw_buf + fw_size - 4, EXTENSION_SIGNATURE, 4) != 0) {
341*8bb555aaSBjoern Hartmann             log_info("Wrong signature. Quit!");
342*8bb555aaSBjoern Hartmann             finalize_file_and_buffer(&fw, &fw_buf);
343*8bb555aaSBjoern Hartmann             finalize_file_and_buffer(&conf, &conf_buf);
344*8bb555aaSBjoern Hartmann             return FW_DONE;
345*8bb555aaSBjoern Hartmann         }
346*8bb555aaSBjoern Hartmann 
347*8bb555aaSBjoern Hartmann         // check project id
348*8bb555aaSBjoern Hartmann         if (lmp_subversion != project_id[*(fw_buf + fw_size - 7)]) {
349*8bb555aaSBjoern Hartmann             log_info("Wrong project id. Quit!");
350*8bb555aaSBjoern Hartmann             finalize_file_and_buffer(&fw, &fw_buf);
351*8bb555aaSBjoern Hartmann             finalize_file_and_buffer(&conf, &conf_buf);
352*8bb555aaSBjoern Hartmann             return FW_DONE;
353*8bb555aaSBjoern Hartmann         }
354*8bb555aaSBjoern Hartmann 
355*8bb555aaSBjoern Hartmann         // read firmware version
356*8bb555aaSBjoern Hartmann         uint32_t fw_version = little_endian_read_32(fw_buf, 8);
357*8bb555aaSBjoern Hartmann         log_info("Firmware version: 0x%x", fw_version);
358*8bb555aaSBjoern Hartmann 
359*8bb555aaSBjoern Hartmann         // read number of patches
360*8bb555aaSBjoern Hartmann         uint16_t fw_num_patches = little_endian_read_16(fw_buf, 12);
361*8bb555aaSBjoern Hartmann         log_info("Number of patches: %d", fw_num_patches);
362*8bb555aaSBjoern Hartmann 
363*8bb555aaSBjoern Hartmann         // find correct entry
364*8bb555aaSBjoern Hartmann         for (uint16_t i = 0; i < fw_num_patches; i++) {
365*8bb555aaSBjoern Hartmann             if (little_endian_read_16(fw_buf, 14 + 2 * i) == rom_version + 1) {
366*8bb555aaSBjoern Hartmann                 patch_length = little_endian_read_16(fw_buf, 14 + 2 * fw_num_patches + 2 * i);
367*8bb555aaSBjoern Hartmann                 offset       = little_endian_read_32(fw_buf, 14 + 4 * fw_num_patches + 4 * i);
368*8bb555aaSBjoern Hartmann                 log_info("patch_length %u, offset %u", patch_length, offset);
369*8bb555aaSBjoern Hartmann                 break;
370*8bb555aaSBjoern Hartmann             }
371*8bb555aaSBjoern Hartmann         }
372*8bb555aaSBjoern Hartmann         if (patch_length == 0) {
373*8bb555aaSBjoern Hartmann             log_debug("Failed to find valid patch");
374*8bb555aaSBjoern Hartmann             finalize_file_and_buffer(&fw, &fw_buf);
375*8bb555aaSBjoern Hartmann             finalize_file_and_buffer(&conf, &conf_buf);
376*8bb555aaSBjoern Hartmann             return FW_DONE;
377*8bb555aaSBjoern Hartmann         }
378*8bb555aaSBjoern Hartmann 
379*8bb555aaSBjoern Hartmann         // allocate patch buffer
380*8bb555aaSBjoern Hartmann         fw_total_len = patch_length + conf_size;
381*8bb555aaSBjoern Hartmann         patch_buf    = malloc(fw_total_len);
382*8bb555aaSBjoern Hartmann         if (patch_buf == NULL) {
383*8bb555aaSBjoern Hartmann             log_debug("Failed to allocate %u bytes for patch buffer", fw_total_len);
384*8bb555aaSBjoern Hartmann             finalize_file_and_buffer(&fw, &fw_buf);
385*8bb555aaSBjoern Hartmann             finalize_file_and_buffer(&conf, &conf_buf);
386*8bb555aaSBjoern Hartmann             return FW_DONE;
387*8bb555aaSBjoern Hartmann         }
388*8bb555aaSBjoern Hartmann 
389*8bb555aaSBjoern Hartmann         // copy patch
390*8bb555aaSBjoern Hartmann         memcpy(patch_buf, fw_buf + offset, patch_length);
391*8bb555aaSBjoern Hartmann         memcpy(patch_buf + patch_length - 4, &fw_version, 4);
392*8bb555aaSBjoern Hartmann         memcpy(patch_buf + patch_length, conf_buf, conf_size);
393*8bb555aaSBjoern Hartmann         fw_ptr = 0;
394*8bb555aaSBjoern Hartmann         index  = 0;
395*8bb555aaSBjoern Hartmann 
396*8bb555aaSBjoern Hartmann         // close files
397*8bb555aaSBjoern Hartmann         finalize_file_and_buffer(&fw, &fw_buf);
398*8bb555aaSBjoern Hartmann         finalize_file_and_buffer(&conf, &conf_buf);
399*8bb555aaSBjoern Hartmann     }
400*8bb555aaSBjoern Hartmann 
401*8bb555aaSBjoern Hartmann     uint8_t len;
402*8bb555aaSBjoern Hartmann     if (fw_total_len - fw_ptr > 252) {
403*8bb555aaSBjoern Hartmann         len = 252;
404*8bb555aaSBjoern Hartmann     } else {
405*8bb555aaSBjoern Hartmann         len = fw_total_len - fw_ptr;
406*8bb555aaSBjoern Hartmann         index |= 0x80;  // end
407*8bb555aaSBjoern Hartmann     }
408*8bb555aaSBjoern Hartmann 
409*8bb555aaSBjoern Hartmann     if (len) {
410*8bb555aaSBjoern Hartmann         FILL_COMMAND(hci_cmd_buffer, HCI_DOWNLOAD_FW);
411*8bb555aaSBjoern Hartmann         FILL_LENGTH(hci_cmd_buffer, len + 1);
412*8bb555aaSBjoern Hartmann         FILL_INDEX(hci_cmd_buffer, index);
413*8bb555aaSBjoern Hartmann         FILL_FW_DATA(hci_cmd_buffer, patch_buf, fw_ptr, len);
414*8bb555aaSBjoern Hartmann         index++;
415*8bb555aaSBjoern Hartmann         fw_ptr += len;
416*8bb555aaSBjoern Hartmann         return FW_MORE_TO_DO;
417*8bb555aaSBjoern Hartmann     }
418*8bb555aaSBjoern Hartmann 
419*8bb555aaSBjoern Hartmann     // cleanup and return
420*8bb555aaSBjoern Hartmann     free(patch_buf);
421*8bb555aaSBjoern Hartmann     patch_buf = NULL;
422*8bb555aaSBjoern Hartmann     return FW_DONE;
423*8bb555aaSBjoern Hartmann }
424*8bb555aaSBjoern Hartmann 
425*8bb555aaSBjoern Hartmann #endif  // HAVE_POSIX_FILE_IO
426*8bb555aaSBjoern Hartmann 
427*8bb555aaSBjoern Hartmann static btstack_chipset_result_t chipset_next_command(uint8_t *hci_cmd_buffer) {
428*8bb555aaSBjoern Hartmann #ifdef HAVE_POSIX_FILE_IO
429*8bb555aaSBjoern Hartmann     uint8_t ret;
430*8bb555aaSBjoern Hartmann     while (true) {
431*8bb555aaSBjoern Hartmann         switch (state) {
432*8bb555aaSBjoern Hartmann         case STATE_READ_ROM_VERSION:
433*8bb555aaSBjoern Hartmann             FILL_COMMAND(hci_cmd_buffer, HCI_READ_ROM_VERSION);
434*8bb555aaSBjoern Hartmann             FILL_LENGTH(hci_cmd_buffer, 0);
435*8bb555aaSBjoern Hartmann             state = STATE_LOAD_FIRMWARE;
436*8bb555aaSBjoern Hartmann             break;
437*8bb555aaSBjoern Hartmann         case STATE_LOAD_FIRMWARE:
438*8bb555aaSBjoern Hartmann             if (lmp_subversion != ROM_LMP_8723a) {
439*8bb555aaSBjoern Hartmann                 ret = update_firmware(firmware_file_path, config_file_path, hci_cmd_buffer);
440*8bb555aaSBjoern Hartmann             } else {
441*8bb555aaSBjoern Hartmann                 log_info("Realtek firmware for old patch style not implemented");
442*8bb555aaSBjoern Hartmann                 ret = FW_DONE;
443*8bb555aaSBjoern Hartmann             }
444*8bb555aaSBjoern Hartmann             if (ret != FW_DONE) {
445*8bb555aaSBjoern Hartmann                 break;
446*8bb555aaSBjoern Hartmann             }
447*8bb555aaSBjoern Hartmann             // we are done fall through
448*8bb555aaSBjoern Hartmann             state = STATE_RESET;
449*8bb555aaSBjoern Hartmann         case STATE_RESET:
450*8bb555aaSBjoern Hartmann             FILL_COMMAND(hci_cmd_buffer, HCI_RESET);
451*8bb555aaSBjoern Hartmann             FILL_LENGTH(hci_cmd_buffer, 0);
452*8bb555aaSBjoern Hartmann             state = STATE_DONE;
453*8bb555aaSBjoern Hartmann             break;
454*8bb555aaSBjoern Hartmann         case STATE_DONE:
455*8bb555aaSBjoern Hartmann             hci_remove_event_handler(&hci_event_callback_registration);
456*8bb555aaSBjoern Hartmann             return BTSTACK_CHIPSET_DONE;
457*8bb555aaSBjoern Hartmann             break;
458*8bb555aaSBjoern Hartmann         default:
459*8bb555aaSBjoern Hartmann             log_info("Invalid state %d", state);
460*8bb555aaSBjoern Hartmann             return BTSTACK_CHIPSET_DONE;
461*8bb555aaSBjoern Hartmann             break;
462*8bb555aaSBjoern Hartmann         }
463*8bb555aaSBjoern Hartmann         return BTSTACK_CHIPSET_VALID_COMMAND;
464*8bb555aaSBjoern Hartmann     }
465*8bb555aaSBjoern Hartmann #else   // HAVE_POSIX_FILE_IO
466*8bb555aaSBjoern Hartmann     log_info("Realtek without File IO is not implemented yet");
467*8bb555aaSBjoern Hartmann     return BTSTACK_CHIPSET_NO_INIT_SCRIPT;
468*8bb555aaSBjoern Hartmann #endif  // HAVE_POSIX_FILE_IO
469*8bb555aaSBjoern Hartmann }
470*8bb555aaSBjoern Hartmann 
471*8bb555aaSBjoern Hartmann void btstack_chipset_realtek_set_firmware_file_path(const char *path) {
472*8bb555aaSBjoern Hartmann #ifdef HAVE_POSIX_FILE_IO
473*8bb555aaSBjoern Hartmann     firmware_file_path = path;
474*8bb555aaSBjoern Hartmann #endif
475*8bb555aaSBjoern Hartmann }
476*8bb555aaSBjoern Hartmann 
477*8bb555aaSBjoern Hartmann void btstack_chipset_realtek_set_firmware_folder_path(const char *path) {
478*8bb555aaSBjoern Hartmann #ifdef HAVE_POSIX_FILE_IO
479*8bb555aaSBjoern Hartmann     firmware_folder_path = path;
480*8bb555aaSBjoern Hartmann #endif
481*8bb555aaSBjoern Hartmann }
482*8bb555aaSBjoern Hartmann 
483*8bb555aaSBjoern Hartmann void btstack_chipset_realtek_set_config_file_path(const char *path) {
484*8bb555aaSBjoern Hartmann #ifdef HAVE_POSIX_FILE_IO
485*8bb555aaSBjoern Hartmann     config_file_path = path;
486*8bb555aaSBjoern Hartmann #endif
487*8bb555aaSBjoern Hartmann }
488*8bb555aaSBjoern Hartmann 
489*8bb555aaSBjoern Hartmann void btstack_chipset_realtek_set_config_folder_path(const char *path) {
490*8bb555aaSBjoern Hartmann #ifdef HAVE_POSIX_FILE_IO
491*8bb555aaSBjoern Hartmann     config_folder_path = path;
492*8bb555aaSBjoern Hartmann #endif
493*8bb555aaSBjoern Hartmann }
494*8bb555aaSBjoern Hartmann 
495*8bb555aaSBjoern Hartmann void btstack_chipset_realtek_set_lmp_subversion(uint16_t version) { lmp_subversion = version; }
496*8bb555aaSBjoern Hartmann 
497*8bb555aaSBjoern Hartmann void btstack_chipset_realtek_set_product_id(uint16_t id) { product_id = id; }
498*8bb555aaSBjoern Hartmann 
499*8bb555aaSBjoern Hartmann static const btstack_chipset_t btstack_chipset_realtek = {
500*8bb555aaSBjoern Hartmann     "REALTEK", chipset_init, chipset_next_command,
501*8bb555aaSBjoern Hartmann     NULL,  // chipset_set_baudrate_command,
502*8bb555aaSBjoern Hartmann     NULL,  // chipset_set_bd_addr_command not supported or implemented
503*8bb555aaSBjoern Hartmann };
504*8bb555aaSBjoern Hartmann 
505*8bb555aaSBjoern Hartmann // MARK: public API
506*8bb555aaSBjoern Hartmann const btstack_chipset_t *btstack_chipset_realtek_instance(void) { return &btstack_chipset_realtek; }
507