1c942fddfSThomas Gleixner // SPDX-License-Identifier: GPL-2.0-or-later
2db33c77dSCarlo Caione /*
3db33c77dSCarlo Caione * Bluetooth support for Realtek devices
4db33c77dSCarlo Caione *
5db33c77dSCarlo Caione * Copyright (C) 2015 Endless Mobile, Inc.
6db33c77dSCarlo Caione */
7db33c77dSCarlo Caione
8db33c77dSCarlo Caione #include <linux/module.h>
9db33c77dSCarlo Caione #include <linux/firmware.h>
10db33c77dSCarlo Caione #include <asm/unaligned.h>
11db33c77dSCarlo Caione #include <linux/usb.h>
12db33c77dSCarlo Caione
13db33c77dSCarlo Caione #include <net/bluetooth/bluetooth.h>
14db33c77dSCarlo Caione #include <net/bluetooth/hci_core.h>
15db33c77dSCarlo Caione
16db33c77dSCarlo Caione #include "btrtl.h"
17db33c77dSCarlo Caione
18db33c77dSCarlo Caione #define VERSION "0.1"
19db33c77dSCarlo Caione
20c0123cb6SVasily Khoruzhick #define RTL_CHIP_8723CS_CG 3
21c0123cb6SVasily Khoruzhick #define RTL_CHIP_8723CS_VF 4
22c0123cb6SVasily Khoruzhick #define RTL_CHIP_8723CS_XX 5
23db33c77dSCarlo Caione #define RTL_EPATCH_SIGNATURE "Realtech"
249a24ce5eSMax Chou #define RTL_EPATCH_SIGNATURE_V2 "RTBTCore"
25c0123cb6SVasily Khoruzhick #define RTL_ROM_LMP_8703B 0x8703
26db33c77dSCarlo Caione #define RTL_ROM_LMP_8723A 0x1200
27db33c77dSCarlo Caione #define RTL_ROM_LMP_8723B 0x8723
28db33c77dSCarlo Caione #define RTL_ROM_LMP_8821A 0x8821
29db33c77dSCarlo Caione #define RTL_ROM_LMP_8761A 0x8761
301110a2dbSLarry Finger #define RTL_ROM_LMP_8822B 0x8822
310d484db6SMax Chou #define RTL_ROM_LMP_8852A 0x8852
327948fe1cSMax Chou #define RTL_ROM_LMP_8851B 0x8851
33b85b0ee1SMartin Blumenstingl #define RTL_CONFIG_MAGIC 0x8723ab55
34db33c77dSCarlo Caione
35044014ceSHilda Wu #define RTL_VSC_OP_COREDUMP 0xfcff
36044014ceSHilda Wu
37907f8499SAlex Lu #define IC_MATCH_FL_LMPSUBV (1 << 0)
38907f8499SAlex Lu #define IC_MATCH_FL_HCIREV (1 << 1)
39c50903e3SMartin Blumenstingl #define IC_MATCH_FL_HCIVER (1 << 2)
40c50903e3SMartin Blumenstingl #define IC_MATCH_FL_HCIBUS (1 << 3)
41c0123cb6SVasily Khoruzhick #define IC_MATCH_FL_CHIP_TYPE (1 << 4)
426f9ff246SMax Chou #define IC_INFO(lmps, hcir, hciv, bus) \
436f9ff246SMax Chou .match_flags = IC_MATCH_FL_LMPSUBV | IC_MATCH_FL_HCIREV | \
446f9ff246SMax Chou IC_MATCH_FL_HCIVER | IC_MATCH_FL_HCIBUS, \
45907f8499SAlex Lu .lmp_subver = (lmps), \
466f9ff246SMax Chou .hci_rev = (hcir), \
476f9ff246SMax Chou .hci_ver = (hciv), \
486f9ff246SMax Chou .hci_bus = (bus)
49907f8499SAlex Lu
509a24ce5eSMax Chou #define RTL_CHIP_SUBVER (&(struct rtl_vendor_cmd) {{0x10, 0x38, 0x04, 0x28, 0x80}})
519a24ce5eSMax Chou #define RTL_CHIP_REV (&(struct rtl_vendor_cmd) {{0x10, 0x3A, 0x04, 0x28, 0x80}})
529a24ce5eSMax Chou #define RTL_SEC_PROJ (&(struct rtl_vendor_cmd) {{0x10, 0xA4, 0x0D, 0x00, 0xb0}})
539a24ce5eSMax Chou
549a24ce5eSMax Chou #define RTL_PATCH_SNIPPETS 0x01
559a24ce5eSMax Chou #define RTL_PATCH_DUMMY_HEADER 0x02
569a24ce5eSMax Chou #define RTL_PATCH_SECURITY_HEADER 0x03
579a24ce5eSMax Chou
589ab9235fSMax Chou enum btrtl_chip_id {
599ab9235fSMax Chou CHIP_ID_8723A,
609ab9235fSMax Chou CHIP_ID_8723B,
619ab9235fSMax Chou CHIP_ID_8821A,
629ab9235fSMax Chou CHIP_ID_8761A,
639ab9235fSMax Chou CHIP_ID_8822B = 8,
649ab9235fSMax Chou CHIP_ID_8723D,
659ab9235fSMax Chou CHIP_ID_8821C,
669ab9235fSMax Chou CHIP_ID_8822C = 13,
679ab9235fSMax Chou CHIP_ID_8761B,
689ab9235fSMax Chou CHIP_ID_8852A = 18,
6918e8055cSMax Chou CHIP_ID_8852B = 20,
708b1d66b5SMax Chou CHIP_ID_8852C = 25,
717948fe1cSMax Chou CHIP_ID_8851B = 36,
729ab9235fSMax Chou };
739ab9235fSMax Chou
74907f8499SAlex Lu struct id_table {
75907f8499SAlex Lu __u16 match_flags;
76907f8499SAlex Lu __u16 lmp_subver;
77907f8499SAlex Lu __u16 hci_rev;
78c50903e3SMartin Blumenstingl __u8 hci_ver;
79c50903e3SMartin Blumenstingl __u8 hci_bus;
80c0123cb6SVasily Khoruzhick __u8 chip_type;
81907f8499SAlex Lu bool config_needed;
8226503ad2SMartin Blumenstingl bool has_rom_version;
83f4bcba0eSMarcel Holtmann bool has_msft_ext;
84907f8499SAlex Lu char *fw_name;
85907f8499SAlex Lu char *cfg_name;
86044014ceSHilda Wu char *hw_info;
87907f8499SAlex Lu };
88907f8499SAlex Lu
8926503ad2SMartin Blumenstingl struct btrtl_device_info {
9026503ad2SMartin Blumenstingl const struct id_table *ic_info;
9126503ad2SMartin Blumenstingl u8 rom_version;
9226503ad2SMartin Blumenstingl u8 *fw_data;
9326503ad2SMartin Blumenstingl int fw_len;
9426503ad2SMartin Blumenstingl u8 *cfg_data;
9526503ad2SMartin Blumenstingl int cfg_len;
961996d9caSKai-Heng Feng bool drop_fw;
979ab9235fSMax Chou int project_id;
989a24ce5eSMax Chou u8 key_id;
999a24ce5eSMax Chou struct list_head patch_subsecs;
10026503ad2SMartin Blumenstingl };
10126503ad2SMartin Blumenstingl
102907f8499SAlex Lu static const struct id_table ic_id_table[] = {
1036f9ff246SMax Chou /* 8723A */
1046f9ff246SMax Chou { IC_INFO(RTL_ROM_LMP_8723A, 0xb, 0x6, HCI_USB),
10526503ad2SMartin Blumenstingl .config_needed = false,
10626503ad2SMartin Blumenstingl .has_rom_version = false,
107bd003fb3SMax Chou .fw_name = "rtl_bt/rtl8723a_fw",
108044014ceSHilda Wu .cfg_name = NULL,
109044014ceSHilda Wu .hw_info = "rtl8723au" },
11026503ad2SMartin Blumenstingl
111c50903e3SMartin Blumenstingl /* 8723BS */
1126f9ff246SMax Chou { IC_INFO(RTL_ROM_LMP_8723B, 0xb, 0x6, HCI_UART),
113c50903e3SMartin Blumenstingl .config_needed = true,
114c50903e3SMartin Blumenstingl .has_rom_version = true,
115bd003fb3SMax Chou .fw_name = "rtl_bt/rtl8723bs_fw",
116044014ceSHilda Wu .cfg_name = "rtl_bt/rtl8723bs_config",
117044014ceSHilda Wu .hw_info = "rtl8723bs" },
118c50903e3SMartin Blumenstingl
119907f8499SAlex Lu /* 8723B */
1206f9ff246SMax Chou { IC_INFO(RTL_ROM_LMP_8723B, 0xb, 0x6, HCI_USB),
121907f8499SAlex Lu .config_needed = false,
12226503ad2SMartin Blumenstingl .has_rom_version = true,
123bd003fb3SMax Chou .fw_name = "rtl_bt/rtl8723b_fw",
124044014ceSHilda Wu .cfg_name = "rtl_bt/rtl8723b_config",
125044014ceSHilda Wu .hw_info = "rtl8723bu" },
126907f8499SAlex Lu
127c0123cb6SVasily Khoruzhick /* 8723CS-CG */
128c0123cb6SVasily Khoruzhick { .match_flags = IC_MATCH_FL_LMPSUBV | IC_MATCH_FL_CHIP_TYPE |
129c0123cb6SVasily Khoruzhick IC_MATCH_FL_HCIBUS,
130c0123cb6SVasily Khoruzhick .lmp_subver = RTL_ROM_LMP_8703B,
131c0123cb6SVasily Khoruzhick .chip_type = RTL_CHIP_8723CS_CG,
132c0123cb6SVasily Khoruzhick .hci_bus = HCI_UART,
133c0123cb6SVasily Khoruzhick .config_needed = true,
134c0123cb6SVasily Khoruzhick .has_rom_version = true,
135bd003fb3SMax Chou .fw_name = "rtl_bt/rtl8723cs_cg_fw",
136044014ceSHilda Wu .cfg_name = "rtl_bt/rtl8723cs_cg_config",
137044014ceSHilda Wu .hw_info = "rtl8723cs-cg" },
138c0123cb6SVasily Khoruzhick
139c0123cb6SVasily Khoruzhick /* 8723CS-VF */
140c0123cb6SVasily Khoruzhick { .match_flags = IC_MATCH_FL_LMPSUBV | IC_MATCH_FL_CHIP_TYPE |
141c0123cb6SVasily Khoruzhick IC_MATCH_FL_HCIBUS,
142c0123cb6SVasily Khoruzhick .lmp_subver = RTL_ROM_LMP_8703B,
143c0123cb6SVasily Khoruzhick .chip_type = RTL_CHIP_8723CS_VF,
144c0123cb6SVasily Khoruzhick .hci_bus = HCI_UART,
145c0123cb6SVasily Khoruzhick .config_needed = true,
146c0123cb6SVasily Khoruzhick .has_rom_version = true,
147bd003fb3SMax Chou .fw_name = "rtl_bt/rtl8723cs_vf_fw",
148044014ceSHilda Wu .cfg_name = "rtl_bt/rtl8723cs_vf_config",
149044014ceSHilda Wu .hw_info = "rtl8723cs-vf" },
150c0123cb6SVasily Khoruzhick
151c0123cb6SVasily Khoruzhick /* 8723CS-XX */
152c0123cb6SVasily Khoruzhick { .match_flags = IC_MATCH_FL_LMPSUBV | IC_MATCH_FL_CHIP_TYPE |
153c0123cb6SVasily Khoruzhick IC_MATCH_FL_HCIBUS,
154c0123cb6SVasily Khoruzhick .lmp_subver = RTL_ROM_LMP_8703B,
155c0123cb6SVasily Khoruzhick .chip_type = RTL_CHIP_8723CS_XX,
156c0123cb6SVasily Khoruzhick .hci_bus = HCI_UART,
157c0123cb6SVasily Khoruzhick .config_needed = true,
158c0123cb6SVasily Khoruzhick .has_rom_version = true,
159bd003fb3SMax Chou .fw_name = "rtl_bt/rtl8723cs_xx_fw",
160044014ceSHilda Wu .cfg_name = "rtl_bt/rtl8723cs_xx_config",
161044014ceSHilda Wu .hw_info = "rtl8723cs" },
162c0123cb6SVasily Khoruzhick
163907f8499SAlex Lu /* 8723D */
1646f9ff246SMax Chou { IC_INFO(RTL_ROM_LMP_8723B, 0xd, 0x8, HCI_USB),
165907f8499SAlex Lu .config_needed = true,
16626503ad2SMartin Blumenstingl .has_rom_version = true,
167bd003fb3SMax Chou .fw_name = "rtl_bt/rtl8723d_fw",
168044014ceSHilda Wu .cfg_name = "rtl_bt/rtl8723d_config",
169044014ceSHilda Wu .hw_info = "rtl8723du" },
170907f8499SAlex Lu
171c50903e3SMartin Blumenstingl /* 8723DS */
1726f9ff246SMax Chou { IC_INFO(RTL_ROM_LMP_8723B, 0xd, 0x8, HCI_UART),
173c50903e3SMartin Blumenstingl .config_needed = true,
174c50903e3SMartin Blumenstingl .has_rom_version = true,
175bd003fb3SMax Chou .fw_name = "rtl_bt/rtl8723ds_fw",
176044014ceSHilda Wu .cfg_name = "rtl_bt/rtl8723ds_config",
177044014ceSHilda Wu .hw_info = "rtl8723ds" },
178c50903e3SMartin Blumenstingl
179907f8499SAlex Lu /* 8821A */
1806f9ff246SMax Chou { IC_INFO(RTL_ROM_LMP_8821A, 0xa, 0x6, HCI_USB),
181907f8499SAlex Lu .config_needed = false,
18226503ad2SMartin Blumenstingl .has_rom_version = true,
183bd003fb3SMax Chou .fw_name = "rtl_bt/rtl8821a_fw",
184044014ceSHilda Wu .cfg_name = "rtl_bt/rtl8821a_config",
185044014ceSHilda Wu .hw_info = "rtl8821au" },
186907f8499SAlex Lu
187907f8499SAlex Lu /* 8821C */
1886f9ff246SMax Chou { IC_INFO(RTL_ROM_LMP_8821A, 0xc, 0x8, HCI_USB),
189907f8499SAlex Lu .config_needed = false,
19026503ad2SMartin Blumenstingl .has_rom_version = true,
1914d51fb04SMarcel Holtmann .has_msft_ext = true,
192bd003fb3SMax Chou .fw_name = "rtl_bt/rtl8821c_fw",
193044014ceSHilda Wu .cfg_name = "rtl_bt/rtl8821c_config",
194044014ceSHilda Wu .hw_info = "rtl8821cu" },
195907f8499SAlex Lu
19610c9e010SChris Morgan /* 8821CS */
19710c9e010SChris Morgan { IC_INFO(RTL_ROM_LMP_8821A, 0xc, 0x8, HCI_UART),
19810c9e010SChris Morgan .config_needed = true,
19910c9e010SChris Morgan .has_rom_version = true,
20010c9e010SChris Morgan .has_msft_ext = true,
201bd003fb3SMax Chou .fw_name = "rtl_bt/rtl8821cs_fw",
202044014ceSHilda Wu .cfg_name = "rtl_bt/rtl8821cs_config",
203044014ceSHilda Wu .hw_info = "rtl8821cs" },
20410c9e010SChris Morgan
205907f8499SAlex Lu /* 8761A */
2066f9ff246SMax Chou { IC_INFO(RTL_ROM_LMP_8761A, 0xa, 0x6, HCI_USB),
207907f8499SAlex Lu .config_needed = false,
20826503ad2SMartin Blumenstingl .has_rom_version = true,
209bd003fb3SMax Chou .fw_name = "rtl_bt/rtl8761a_fw",
210044014ceSHilda Wu .cfg_name = "rtl_bt/rtl8761a_config",
211044014ceSHilda Wu .hw_info = "rtl8761au" },
212907f8499SAlex Lu
21304896832SZiqian SUN (Zamir) /* 8761B */
2149fd2e294SJoakim Tjernlund { IC_INFO(RTL_ROM_LMP_8761A, 0xb, 0xa, HCI_UART),
21504896832SZiqian SUN (Zamir) .config_needed = false,
21604896832SZiqian SUN (Zamir) .has_rom_version = true,
217f4bcba0eSMarcel Holtmann .has_msft_ext = true,
218bd003fb3SMax Chou .fw_name = "rtl_bt/rtl8761b_fw",
219044014ceSHilda Wu .cfg_name = "rtl_bt/rtl8761b_config",
220044014ceSHilda Wu .hw_info = "rtl8761btv" },
22104896832SZiqian SUN (Zamir)
2229fd2e294SJoakim Tjernlund /* 8761BU */
2239fd2e294SJoakim Tjernlund { IC_INFO(RTL_ROM_LMP_8761A, 0xb, 0xa, HCI_USB),
2249fd2e294SJoakim Tjernlund .config_needed = false,
2259fd2e294SJoakim Tjernlund .has_rom_version = true,
226bd003fb3SMax Chou .fw_name = "rtl_bt/rtl8761bu_fw",
227044014ceSHilda Wu .cfg_name = "rtl_bt/rtl8761bu_config",
228044014ceSHilda Wu .hw_info = "rtl8761bu" },
2299fd2e294SJoakim Tjernlund
230848fc616SMax Chou /* 8822C with UART interface */
231b050c5bbSVyacheslav Bocharov { IC_INFO(RTL_ROM_LMP_8822B, 0xc, 0x8, HCI_UART),
232b050c5bbSVyacheslav Bocharov .config_needed = true,
233b050c5bbSVyacheslav Bocharov .has_rom_version = true,
234b050c5bbSVyacheslav Bocharov .has_msft_ext = true,
235bd003fb3SMax Chou .fw_name = "rtl_bt/rtl8822cs_fw",
236044014ceSHilda Wu .cfg_name = "rtl_bt/rtl8822cs_config",
237044014ceSHilda Wu .hw_info = "rtl8822cs" },
238b050c5bbSVyacheslav Bocharov
239b050c5bbSVyacheslav Bocharov /* 8822C with UART interface */
2406f9ff246SMax Chou { IC_INFO(RTL_ROM_LMP_8822B, 0xc, 0xa, HCI_UART),
241848fc616SMax Chou .config_needed = true,
242848fc616SMax Chou .has_rom_version = true,
243f4bcba0eSMarcel Holtmann .has_msft_ext = true,
244bd003fb3SMax Chou .fw_name = "rtl_bt/rtl8822cs_fw",
245044014ceSHilda Wu .cfg_name = "rtl_bt/rtl8822cs_config",
246044014ceSHilda Wu .hw_info = "rtl8822cs" },
247848fc616SMax Chou
2488ecfdc95SAlex Lu /* 8822C with USB interface */
2496f9ff246SMax Chou { IC_INFO(RTL_ROM_LMP_8822B, 0xc, 0xa, HCI_USB),
2508ecfdc95SAlex Lu .config_needed = false,
2518ecfdc95SAlex Lu .has_rom_version = true,
252f4bcba0eSMarcel Holtmann .has_msft_ext = true,
253bd003fb3SMax Chou .fw_name = "rtl_bt/rtl8822cu_fw",
254044014ceSHilda Wu .cfg_name = "rtl_bt/rtl8822cu_config",
255044014ceSHilda Wu .hw_info = "rtl8822cu" },
2568ecfdc95SAlex Lu
257907f8499SAlex Lu /* 8822B */
2586f9ff246SMax Chou { IC_INFO(RTL_ROM_LMP_8822B, 0xb, 0x7, HCI_USB),
259907f8499SAlex Lu .config_needed = true,
26026503ad2SMartin Blumenstingl .has_rom_version = true,
261f4bcba0eSMarcel Holtmann .has_msft_ext = true,
262bd003fb3SMax Chou .fw_name = "rtl_bt/rtl8822b_fw",
263044014ceSHilda Wu .cfg_name = "rtl_bt/rtl8822b_config",
264044014ceSHilda Wu .hw_info = "rtl8822bu" },
2650d484db6SMax Chou
2660d484db6SMax Chou /* 8852A */
2670d484db6SMax Chou { IC_INFO(RTL_ROM_LMP_8852A, 0xa, 0xb, HCI_USB),
2680d484db6SMax Chou .config_needed = false,
2690d484db6SMax Chou .has_rom_version = true,
270f4bcba0eSMarcel Holtmann .has_msft_ext = true,
271bd003fb3SMax Chou .fw_name = "rtl_bt/rtl8852au_fw",
272044014ceSHilda Wu .cfg_name = "rtl_bt/rtl8852au_config",
273044014ceSHilda Wu .hw_info = "rtl8852au" },
27418e8055cSMax Chou
275fe4b71b5SVictor Hassan /* 8852B with UART interface */
276fe4b71b5SVictor Hassan { IC_INFO(RTL_ROM_LMP_8852A, 0xb, 0xb, HCI_UART),
277fe4b71b5SVictor Hassan .config_needed = true,
278fe4b71b5SVictor Hassan .has_rom_version = true,
279fe4b71b5SVictor Hassan .has_msft_ext = true,
280bd003fb3SMax Chou .fw_name = "rtl_bt/rtl8852bs_fw",
281044014ceSHilda Wu .cfg_name = "rtl_bt/rtl8852bs_config",
282044014ceSHilda Wu .hw_info = "rtl8852bs" },
283fe4b71b5SVictor Hassan
28418e8055cSMax Chou /* 8852B */
28518e8055cSMax Chou { IC_INFO(RTL_ROM_LMP_8852A, 0xb, 0xb, HCI_USB),
28618e8055cSMax Chou .config_needed = false,
28718e8055cSMax Chou .has_rom_version = true,
28818e8055cSMax Chou .has_msft_ext = true,
289bd003fb3SMax Chou .fw_name = "rtl_bt/rtl8852bu_fw",
290044014ceSHilda Wu .cfg_name = "rtl_bt/rtl8852bu_config",
291044014ceSHilda Wu .hw_info = "rtl8852bu" },
2928b1d66b5SMax Chou
2938b1d66b5SMax Chou /* 8852C */
2948b1d66b5SMax Chou { IC_INFO(RTL_ROM_LMP_8852A, 0xc, 0xc, HCI_USB),
2958b1d66b5SMax Chou .config_needed = false,
2968b1d66b5SMax Chou .has_rom_version = true,
2978b1d66b5SMax Chou .has_msft_ext = true,
298bd003fb3SMax Chou .fw_name = "rtl_bt/rtl8852cu_fw",
299044014ceSHilda Wu .cfg_name = "rtl_bt/rtl8852cu_config",
300044014ceSHilda Wu .hw_info = "rtl8852cu" },
3017948fe1cSMax Chou
3027948fe1cSMax Chou /* 8851B */
3037948fe1cSMax Chou { IC_INFO(RTL_ROM_LMP_8851B, 0xb, 0xc, HCI_USB),
3047948fe1cSMax Chou .config_needed = false,
3057948fe1cSMax Chou .has_rom_version = true,
3067948fe1cSMax Chou .has_msft_ext = false,
307bd003fb3SMax Chou .fw_name = "rtl_bt/rtl8851bu_fw",
308044014ceSHilda Wu .cfg_name = "rtl_bt/rtl8851bu_config",
309044014ceSHilda Wu .hw_info = "rtl8851bu" },
310907f8499SAlex Lu };
311907f8499SAlex Lu
btrtl_match_ic(u16 lmp_subver,u16 hci_rev,u8 hci_ver,u8 hci_bus,u8 chip_type)312c50903e3SMartin Blumenstingl static const struct id_table *btrtl_match_ic(u16 lmp_subver, u16 hci_rev,
313c0123cb6SVasily Khoruzhick u8 hci_ver, u8 hci_bus,
314c0123cb6SVasily Khoruzhick u8 chip_type)
31526503ad2SMartin Blumenstingl {
31626503ad2SMartin Blumenstingl int i;
31726503ad2SMartin Blumenstingl
31826503ad2SMartin Blumenstingl for (i = 0; i < ARRAY_SIZE(ic_id_table); i++) {
31926503ad2SMartin Blumenstingl if ((ic_id_table[i].match_flags & IC_MATCH_FL_LMPSUBV) &&
32026503ad2SMartin Blumenstingl (ic_id_table[i].lmp_subver != lmp_subver))
32126503ad2SMartin Blumenstingl continue;
32226503ad2SMartin Blumenstingl if ((ic_id_table[i].match_flags & IC_MATCH_FL_HCIREV) &&
32326503ad2SMartin Blumenstingl (ic_id_table[i].hci_rev != hci_rev))
32426503ad2SMartin Blumenstingl continue;
325c50903e3SMartin Blumenstingl if ((ic_id_table[i].match_flags & IC_MATCH_FL_HCIVER) &&
326c50903e3SMartin Blumenstingl (ic_id_table[i].hci_ver != hci_ver))
327c50903e3SMartin Blumenstingl continue;
328c50903e3SMartin Blumenstingl if ((ic_id_table[i].match_flags & IC_MATCH_FL_HCIBUS) &&
329c50903e3SMartin Blumenstingl (ic_id_table[i].hci_bus != hci_bus))
330c50903e3SMartin Blumenstingl continue;
331c0123cb6SVasily Khoruzhick if ((ic_id_table[i].match_flags & IC_MATCH_FL_CHIP_TYPE) &&
332c0123cb6SVasily Khoruzhick (ic_id_table[i].chip_type != chip_type))
333c0123cb6SVasily Khoruzhick continue;
33426503ad2SMartin Blumenstingl
33526503ad2SMartin Blumenstingl break;
33626503ad2SMartin Blumenstingl }
33726503ad2SMartin Blumenstingl if (i >= ARRAY_SIZE(ic_id_table))
33826503ad2SMartin Blumenstingl return NULL;
33926503ad2SMartin Blumenstingl
34026503ad2SMartin Blumenstingl return &ic_id_table[i];
34126503ad2SMartin Blumenstingl }
34226503ad2SMartin Blumenstingl
btrtl_read_local_version(struct hci_dev * hdev)343240b64a8SAlex Lu static struct sk_buff *btrtl_read_local_version(struct hci_dev *hdev)
344240b64a8SAlex Lu {
345240b64a8SAlex Lu struct sk_buff *skb;
346240b64a8SAlex Lu
347240b64a8SAlex Lu skb = __hci_cmd_sync(hdev, HCI_OP_READ_LOCAL_VERSION, 0, NULL,
348240b64a8SAlex Lu HCI_INIT_TIMEOUT);
349240b64a8SAlex Lu if (IS_ERR(skb)) {
350240b64a8SAlex Lu rtl_dev_err(hdev, "HCI_OP_READ_LOCAL_VERSION failed (%ld)",
351240b64a8SAlex Lu PTR_ERR(skb));
352240b64a8SAlex Lu return skb;
353240b64a8SAlex Lu }
354240b64a8SAlex Lu
355240b64a8SAlex Lu if (skb->len != sizeof(struct hci_rp_read_local_version)) {
356240b64a8SAlex Lu rtl_dev_err(hdev, "HCI_OP_READ_LOCAL_VERSION event length mismatch");
357240b64a8SAlex Lu kfree_skb(skb);
358240b64a8SAlex Lu return ERR_PTR(-EIO);
359240b64a8SAlex Lu }
360240b64a8SAlex Lu
361240b64a8SAlex Lu return skb;
362240b64a8SAlex Lu }
363240b64a8SAlex Lu
rtl_read_rom_version(struct hci_dev * hdev,u8 * version)364db33c77dSCarlo Caione static int rtl_read_rom_version(struct hci_dev *hdev, u8 *version)
365db33c77dSCarlo Caione {
366db33c77dSCarlo Caione struct rtl_rom_version_evt *rom_version;
367db33c77dSCarlo Caione struct sk_buff *skb;
368db33c77dSCarlo Caione
369db33c77dSCarlo Caione /* Read RTL ROM version command */
370db33c77dSCarlo Caione skb = __hci_cmd_sync(hdev, 0xfc6d, 0, NULL, HCI_INIT_TIMEOUT);
371db33c77dSCarlo Caione if (IS_ERR(skb)) {
372f1300c03SAlex Lu rtl_dev_err(hdev, "Read ROM version failed (%ld)",
373a5c76e67SHans de Goede PTR_ERR(skb));
374db33c77dSCarlo Caione return PTR_ERR(skb);
375db33c77dSCarlo Caione }
376db33c77dSCarlo Caione
377db33c77dSCarlo Caione if (skb->len != sizeof(*rom_version)) {
378f1300c03SAlex Lu rtl_dev_err(hdev, "version event length mismatch");
379db33c77dSCarlo Caione kfree_skb(skb);
380db33c77dSCarlo Caione return -EIO;
381db33c77dSCarlo Caione }
382db33c77dSCarlo Caione
383db33c77dSCarlo Caione rom_version = (struct rtl_rom_version_evt *)skb->data;
384f1300c03SAlex Lu rtl_dev_info(hdev, "rom_version status=%x version=%x",
3852064ee33SMarcel Holtmann rom_version->status, rom_version->version);
386db33c77dSCarlo Caione
387db33c77dSCarlo Caione *version = rom_version->version;
388db33c77dSCarlo Caione
389db33c77dSCarlo Caione kfree_skb(skb);
390db33c77dSCarlo Caione return 0;
391db33c77dSCarlo Caione }
392db33c77dSCarlo Caione
btrtl_vendor_read_reg16(struct hci_dev * hdev,struct rtl_vendor_cmd * cmd,u8 * rp)3939a24ce5eSMax Chou static int btrtl_vendor_read_reg16(struct hci_dev *hdev,
3949a24ce5eSMax Chou struct rtl_vendor_cmd *cmd, u8 *rp)
3959a24ce5eSMax Chou {
3969a24ce5eSMax Chou struct sk_buff *skb;
3979a24ce5eSMax Chou int err = 0;
3989a24ce5eSMax Chou
3999a24ce5eSMax Chou skb = __hci_cmd_sync(hdev, 0xfc61, sizeof(*cmd), cmd,
4009a24ce5eSMax Chou HCI_INIT_TIMEOUT);
4019a24ce5eSMax Chou if (IS_ERR(skb)) {
4029a24ce5eSMax Chou err = PTR_ERR(skb);
4039a24ce5eSMax Chou rtl_dev_err(hdev, "RTL: Read reg16 failed (%d)", err);
4049a24ce5eSMax Chou return err;
4059a24ce5eSMax Chou }
4069a24ce5eSMax Chou
4079a24ce5eSMax Chou if (skb->len != 3 || skb->data[0]) {
4089a24ce5eSMax Chou bt_dev_err(hdev, "RTL: Read reg16 length mismatch");
4099a24ce5eSMax Chou kfree_skb(skb);
4109a24ce5eSMax Chou return -EIO;
4119a24ce5eSMax Chou }
4129a24ce5eSMax Chou
4139a24ce5eSMax Chou if (rp)
4149a24ce5eSMax Chou memcpy(rp, skb->data + 1, 2);
4159a24ce5eSMax Chou
4169a24ce5eSMax Chou kfree_skb(skb);
4179a24ce5eSMax Chou
4189a24ce5eSMax Chou return 0;
4199a24ce5eSMax Chou }
4209a24ce5eSMax Chou
rtl_iov_pull_data(struct rtl_iovec * iov,u32 len)4219a24ce5eSMax Chou static void *rtl_iov_pull_data(struct rtl_iovec *iov, u32 len)
4229a24ce5eSMax Chou {
4239a24ce5eSMax Chou void *data = iov->data;
4249a24ce5eSMax Chou
4259a24ce5eSMax Chou if (iov->len < len)
4269a24ce5eSMax Chou return NULL;
4279a24ce5eSMax Chou
4289a24ce5eSMax Chou iov->data += len;
4299a24ce5eSMax Chou iov->len -= len;
4309a24ce5eSMax Chou
4319a24ce5eSMax Chou return data;
4329a24ce5eSMax Chou }
4339a24ce5eSMax Chou
btrtl_insert_ordered_subsec(struct rtl_subsection * node,struct btrtl_device_info * btrtl_dev)4349a24ce5eSMax Chou static void btrtl_insert_ordered_subsec(struct rtl_subsection *node,
4359a24ce5eSMax Chou struct btrtl_device_info *btrtl_dev)
4369a24ce5eSMax Chou {
4379a24ce5eSMax Chou struct list_head *pos;
4389a24ce5eSMax Chou struct list_head *next;
4399a24ce5eSMax Chou struct rtl_subsection *subsec;
4409a24ce5eSMax Chou
4419a24ce5eSMax Chou list_for_each_safe(pos, next, &btrtl_dev->patch_subsecs) {
4429a24ce5eSMax Chou subsec = list_entry(pos, struct rtl_subsection, list);
4439a24ce5eSMax Chou if (subsec->prio >= node->prio)
4449a24ce5eSMax Chou break;
4459a24ce5eSMax Chou }
4469a24ce5eSMax Chou __list_add(&node->list, pos->prev, pos);
4479a24ce5eSMax Chou }
4489a24ce5eSMax Chou
btrtl_parse_section(struct hci_dev * hdev,struct btrtl_device_info * btrtl_dev,u32 opcode,u8 * data,u32 len)4499a24ce5eSMax Chou static int btrtl_parse_section(struct hci_dev *hdev,
4509a24ce5eSMax Chou struct btrtl_device_info *btrtl_dev, u32 opcode,
4519a24ce5eSMax Chou u8 *data, u32 len)
4529a24ce5eSMax Chou {
4539a24ce5eSMax Chou struct rtl_section_hdr *hdr;
4549a24ce5eSMax Chou struct rtl_subsection *subsec;
4559a24ce5eSMax Chou struct rtl_common_subsec *common_subsec;
4569a24ce5eSMax Chou struct rtl_sec_hdr *sec_hdr;
4579a24ce5eSMax Chou int i;
4589a24ce5eSMax Chou u8 *ptr;
4599a24ce5eSMax Chou u16 num_subsecs;
4609a24ce5eSMax Chou u32 subsec_len;
4619a24ce5eSMax Chou int rc = 0;
4629a24ce5eSMax Chou struct rtl_iovec iov = {
4639a24ce5eSMax Chou .data = data,
4649a24ce5eSMax Chou .len = len,
4659a24ce5eSMax Chou };
4669a24ce5eSMax Chou
4679a24ce5eSMax Chou hdr = rtl_iov_pull_data(&iov, sizeof(*hdr));
4689a24ce5eSMax Chou if (!hdr)
4699a24ce5eSMax Chou return -EINVAL;
4709a24ce5eSMax Chou num_subsecs = le16_to_cpu(hdr->num);
4719a24ce5eSMax Chou
4729a24ce5eSMax Chou for (i = 0; i < num_subsecs; i++) {
4739a24ce5eSMax Chou common_subsec = rtl_iov_pull_data(&iov, sizeof(*common_subsec));
4749a24ce5eSMax Chou if (!common_subsec)
4759a24ce5eSMax Chou break;
4769a24ce5eSMax Chou subsec_len = le32_to_cpu(common_subsec->len);
4779a24ce5eSMax Chou
4789a24ce5eSMax Chou rtl_dev_dbg(hdev, "subsec, eco 0x%02x, len %08x",
4799a24ce5eSMax Chou common_subsec->eco, subsec_len);
4809a24ce5eSMax Chou
4819a24ce5eSMax Chou ptr = rtl_iov_pull_data(&iov, subsec_len);
4829a24ce5eSMax Chou if (!ptr)
4839a24ce5eSMax Chou break;
4849a24ce5eSMax Chou
4859a24ce5eSMax Chou if (common_subsec->eco != btrtl_dev->rom_version + 1)
4869a24ce5eSMax Chou continue;
4879a24ce5eSMax Chou
4889a24ce5eSMax Chou switch (opcode) {
4899a24ce5eSMax Chou case RTL_PATCH_SECURITY_HEADER:
4909a24ce5eSMax Chou sec_hdr = (void *)common_subsec;
4919a24ce5eSMax Chou if (sec_hdr->key_id != btrtl_dev->key_id)
4929a24ce5eSMax Chou continue;
4939a24ce5eSMax Chou break;
4949a24ce5eSMax Chou }
4959a24ce5eSMax Chou
4969a24ce5eSMax Chou subsec = kzalloc(sizeof(*subsec), GFP_KERNEL);
4979a24ce5eSMax Chou if (!subsec)
4989a24ce5eSMax Chou return -ENOMEM;
4999a24ce5eSMax Chou subsec->opcode = opcode;
5009a24ce5eSMax Chou subsec->prio = common_subsec->prio;
5019a24ce5eSMax Chou subsec->len = subsec_len;
5029a24ce5eSMax Chou subsec->data = ptr;
5039a24ce5eSMax Chou btrtl_insert_ordered_subsec(subsec, btrtl_dev);
5049a24ce5eSMax Chou rc += subsec_len;
5059a24ce5eSMax Chou }
5069a24ce5eSMax Chou
5079a24ce5eSMax Chou return rc;
5089a24ce5eSMax Chou }
5099a24ce5eSMax Chou
rtlbt_parse_firmware_v2(struct hci_dev * hdev,struct btrtl_device_info * btrtl_dev,unsigned char ** _buf)5109a24ce5eSMax Chou static int rtlbt_parse_firmware_v2(struct hci_dev *hdev,
5119a24ce5eSMax Chou struct btrtl_device_info *btrtl_dev,
5129a24ce5eSMax Chou unsigned char **_buf)
5139a24ce5eSMax Chou {
5149a24ce5eSMax Chou struct rtl_epatch_header_v2 *hdr;
5159a24ce5eSMax Chou int rc;
5169a24ce5eSMax Chou u8 reg_val[2];
5179a24ce5eSMax Chou u8 key_id;
5189a24ce5eSMax Chou u32 num_sections;
5199a24ce5eSMax Chou struct rtl_section *section;
5209a24ce5eSMax Chou struct rtl_subsection *entry, *tmp;
5219a24ce5eSMax Chou u32 section_len;
5229a24ce5eSMax Chou u32 opcode;
5239a24ce5eSMax Chou int len = 0;
5249a24ce5eSMax Chou int i;
5259a24ce5eSMax Chou u8 *ptr;
5269a24ce5eSMax Chou struct rtl_iovec iov = {
5279a24ce5eSMax Chou .data = btrtl_dev->fw_data,
5289a24ce5eSMax Chou .len = btrtl_dev->fw_len - 7, /* Cut the tail */
5299a24ce5eSMax Chou };
5309a24ce5eSMax Chou
5319a24ce5eSMax Chou rc = btrtl_vendor_read_reg16(hdev, RTL_SEC_PROJ, reg_val);
5329a24ce5eSMax Chou if (rc < 0)
5339a24ce5eSMax Chou return -EIO;
5349a24ce5eSMax Chou key_id = reg_val[0];
5359a24ce5eSMax Chou
5369a24ce5eSMax Chou rtl_dev_dbg(hdev, "%s: key id %u", __func__, key_id);
5379a24ce5eSMax Chou
5389a24ce5eSMax Chou btrtl_dev->key_id = key_id;
5399a24ce5eSMax Chou
5409a24ce5eSMax Chou hdr = rtl_iov_pull_data(&iov, sizeof(*hdr));
5419a24ce5eSMax Chou if (!hdr)
5429a24ce5eSMax Chou return -EINVAL;
5439a24ce5eSMax Chou num_sections = le32_to_cpu(hdr->num_sections);
5449a24ce5eSMax Chou
5459a24ce5eSMax Chou rtl_dev_dbg(hdev, "FW version %08x-%08x", *((u32 *)hdr->fw_version),
5469a24ce5eSMax Chou *((u32 *)(hdr->fw_version + 4)));
5479a24ce5eSMax Chou
5489a24ce5eSMax Chou for (i = 0; i < num_sections; i++) {
5499a24ce5eSMax Chou section = rtl_iov_pull_data(&iov, sizeof(*section));
5509a24ce5eSMax Chou if (!section)
5519a24ce5eSMax Chou break;
5529a24ce5eSMax Chou section_len = le32_to_cpu(section->len);
5539a24ce5eSMax Chou opcode = le32_to_cpu(section->opcode);
5549a24ce5eSMax Chou
5559a24ce5eSMax Chou rtl_dev_dbg(hdev, "opcode 0x%04x", section->opcode);
5569a24ce5eSMax Chou
5579a24ce5eSMax Chou ptr = rtl_iov_pull_data(&iov, section_len);
5589a24ce5eSMax Chou if (!ptr)
5599a24ce5eSMax Chou break;
5609a24ce5eSMax Chou
5619a24ce5eSMax Chou switch (opcode) {
5629a24ce5eSMax Chou case RTL_PATCH_SNIPPETS:
5639a24ce5eSMax Chou rc = btrtl_parse_section(hdev, btrtl_dev, opcode,
5649a24ce5eSMax Chou ptr, section_len);
5659a24ce5eSMax Chou break;
5669a24ce5eSMax Chou case RTL_PATCH_SECURITY_HEADER:
5679a24ce5eSMax Chou /* If key_id from chip is zero, ignore all security
5689a24ce5eSMax Chou * headers.
5699a24ce5eSMax Chou */
5709a24ce5eSMax Chou if (!key_id)
5719a24ce5eSMax Chou break;
5729a24ce5eSMax Chou rc = btrtl_parse_section(hdev, btrtl_dev, opcode,
5739a24ce5eSMax Chou ptr, section_len);
5749a24ce5eSMax Chou break;
5759a24ce5eSMax Chou case RTL_PATCH_DUMMY_HEADER:
5769a24ce5eSMax Chou rc = btrtl_parse_section(hdev, btrtl_dev, opcode,
5779a24ce5eSMax Chou ptr, section_len);
5789a24ce5eSMax Chou break;
5799a24ce5eSMax Chou default:
5809a24ce5eSMax Chou rc = 0;
5819a24ce5eSMax Chou break;
5829a24ce5eSMax Chou }
5839a24ce5eSMax Chou if (rc < 0) {
5849a24ce5eSMax Chou rtl_dev_err(hdev, "RTL: Parse section (%u) err %d",
5859a24ce5eSMax Chou opcode, rc);
5869a24ce5eSMax Chou return rc;
5879a24ce5eSMax Chou }
5889a24ce5eSMax Chou len += rc;
5899a24ce5eSMax Chou }
5909a24ce5eSMax Chou
5919a24ce5eSMax Chou if (!len)
5929a24ce5eSMax Chou return -ENODATA;
5939a24ce5eSMax Chou
5949a24ce5eSMax Chou /* Allocate mem and copy all found subsecs. */
5959a24ce5eSMax Chou ptr = kvmalloc(len, GFP_KERNEL);
5969a24ce5eSMax Chou if (!ptr)
5979a24ce5eSMax Chou return -ENOMEM;
5989a24ce5eSMax Chou
5999a24ce5eSMax Chou len = 0;
6009a24ce5eSMax Chou list_for_each_entry_safe(entry, tmp, &btrtl_dev->patch_subsecs, list) {
6019a24ce5eSMax Chou rtl_dev_dbg(hdev, "RTL: opcode %08x, addr %p, len 0x%x",
6029a24ce5eSMax Chou entry->opcode, entry->data, entry->len);
6039a24ce5eSMax Chou memcpy(ptr + len, entry->data, entry->len);
6049a24ce5eSMax Chou len += entry->len;
6059a24ce5eSMax Chou }
6069a24ce5eSMax Chou
6079a24ce5eSMax Chou if (!len)
6089a24ce5eSMax Chou return -EPERM;
6099a24ce5eSMax Chou
6109a24ce5eSMax Chou *_buf = ptr;
6119a24ce5eSMax Chou return len;
6129a24ce5eSMax Chou }
6139a24ce5eSMax Chou
rtlbt_parse_firmware(struct hci_dev * hdev,struct btrtl_device_info * btrtl_dev,unsigned char ** _buf)61426503ad2SMartin Blumenstingl static int rtlbt_parse_firmware(struct hci_dev *hdev,
61526503ad2SMartin Blumenstingl struct btrtl_device_info *btrtl_dev,
616db33c77dSCarlo Caione unsigned char **_buf)
617db33c77dSCarlo Caione {
618e5070e07SColin Ian King static const u8 extension_sig[] = { 0x51, 0x04, 0xfd, 0x77 };
619044014ceSHilda Wu struct btrealtek_data *coredump_info = hci_get_priv(hdev);
620db33c77dSCarlo Caione struct rtl_epatch_header *epatch_info;
621db33c77dSCarlo Caione unsigned char *buf;
62226503ad2SMartin Blumenstingl int i, len;
623db33c77dSCarlo Caione size_t min_size;
62426503ad2SMartin Blumenstingl u8 opcode, length, data;
625db33c77dSCarlo Caione int project_id = -1;
626db33c77dSCarlo Caione const unsigned char *fwptr, *chip_id_base;
627db33c77dSCarlo Caione const unsigned char *patch_length_base, *patch_offset_base;
628db33c77dSCarlo Caione u32 patch_offset = 0;
629db33c77dSCarlo Caione u16 patch_length, num_patches;
6301110a2dbSLarry Finger static const struct {
6311110a2dbSLarry Finger __u16 lmp_subver;
6321110a2dbSLarry Finger __u8 id;
6331110a2dbSLarry Finger } project_id_to_lmp_subver[] = {
6341110a2dbSLarry Finger { RTL_ROM_LMP_8723A, 0 },
6351110a2dbSLarry Finger { RTL_ROM_LMP_8723B, 1 },
6361110a2dbSLarry Finger { RTL_ROM_LMP_8821A, 2 },
6371110a2dbSLarry Finger { RTL_ROM_LMP_8761A, 3 },
638c0123cb6SVasily Khoruzhick { RTL_ROM_LMP_8703B, 7 },
6391110a2dbSLarry Finger { RTL_ROM_LMP_8822B, 8 },
640907f8499SAlex Lu { RTL_ROM_LMP_8723B, 9 }, /* 8723D */
641907f8499SAlex Lu { RTL_ROM_LMP_8821A, 10 }, /* 8821C */
6428ecfdc95SAlex Lu { RTL_ROM_LMP_8822B, 13 }, /* 8822C */
64304896832SZiqian SUN (Zamir) { RTL_ROM_LMP_8761A, 14 }, /* 8761B */
6440d484db6SMax Chou { RTL_ROM_LMP_8852A, 18 }, /* 8852A */
64518e8055cSMax Chou { RTL_ROM_LMP_8852A, 20 }, /* 8852B */
6468b1d66b5SMax Chou { RTL_ROM_LMP_8852A, 25 }, /* 8852C */
6477948fe1cSMax Chou { RTL_ROM_LMP_8851B, 36 }, /* 8851B */
648db33c77dSCarlo Caione };
649db33c77dSCarlo Caione
6509a24ce5eSMax Chou if (btrtl_dev->fw_len <= 8)
6519a24ce5eSMax Chou return -EINVAL;
6529a24ce5eSMax Chou
6539a24ce5eSMax Chou if (!memcmp(btrtl_dev->fw_data, RTL_EPATCH_SIGNATURE, 8))
6549a24ce5eSMax Chou min_size = sizeof(struct rtl_epatch_header) +
6559a24ce5eSMax Chou sizeof(extension_sig) + 3;
6569a24ce5eSMax Chou else if (!memcmp(btrtl_dev->fw_data, RTL_EPATCH_SIGNATURE_V2, 8))
6579a24ce5eSMax Chou min_size = sizeof(struct rtl_epatch_header_v2) +
6589a24ce5eSMax Chou sizeof(extension_sig) + 3;
6599a24ce5eSMax Chou else
6609a24ce5eSMax Chou return -EINVAL;
6619a24ce5eSMax Chou
66226503ad2SMartin Blumenstingl if (btrtl_dev->fw_len < min_size)
663db33c77dSCarlo Caione return -EINVAL;
664db33c77dSCarlo Caione
66526503ad2SMartin Blumenstingl fwptr = btrtl_dev->fw_data + btrtl_dev->fw_len - sizeof(extension_sig);
666db33c77dSCarlo Caione if (memcmp(fwptr, extension_sig, sizeof(extension_sig)) != 0) {
667f1300c03SAlex Lu rtl_dev_err(hdev, "extension section signature mismatch");
668db33c77dSCarlo Caione return -EINVAL;
669db33c77dSCarlo Caione }
670db33c77dSCarlo Caione
671db33c77dSCarlo Caione /* Loop from the end of the firmware parsing instructions, until
672db33c77dSCarlo Caione * we find an instruction that identifies the "project ID" for the
673db33c77dSCarlo Caione * hardware supported by this firwmare file.
67438f230f1Sshaomin Deng * Once we have that, we double-check that project_id is suitable
675db33c77dSCarlo Caione * for the hardware we are working with.
676db33c77dSCarlo Caione */
67726503ad2SMartin Blumenstingl while (fwptr >= btrtl_dev->fw_data + (sizeof(*epatch_info) + 3)) {
678db33c77dSCarlo Caione opcode = *--fwptr;
679db33c77dSCarlo Caione length = *--fwptr;
680db33c77dSCarlo Caione data = *--fwptr;
681db33c77dSCarlo Caione
682db33c77dSCarlo Caione BT_DBG("check op=%x len=%x data=%x", opcode, length, data);
683db33c77dSCarlo Caione
684db33c77dSCarlo Caione if (opcode == 0xff) /* EOF */
685db33c77dSCarlo Caione break;
686db33c77dSCarlo Caione
687db33c77dSCarlo Caione if (length == 0) {
688f1300c03SAlex Lu rtl_dev_err(hdev, "found instruction with length 0");
689db33c77dSCarlo Caione return -EINVAL;
690db33c77dSCarlo Caione }
691db33c77dSCarlo Caione
692db33c77dSCarlo Caione if (opcode == 0 && length == 1) {
693db33c77dSCarlo Caione project_id = data;
694db33c77dSCarlo Caione break;
695db33c77dSCarlo Caione }
696db33c77dSCarlo Caione
697db33c77dSCarlo Caione fwptr -= length;
698db33c77dSCarlo Caione }
699db33c77dSCarlo Caione
700db33c77dSCarlo Caione if (project_id < 0) {
701f1300c03SAlex Lu rtl_dev_err(hdev, "failed to find version instruction");
702db33c77dSCarlo Caione return -EINVAL;
703db33c77dSCarlo Caione }
704db33c77dSCarlo Caione
7051110a2dbSLarry Finger /* Find project_id in table */
7061110a2dbSLarry Finger for (i = 0; i < ARRAY_SIZE(project_id_to_lmp_subver); i++) {
7079ab9235fSMax Chou if (project_id == project_id_to_lmp_subver[i].id) {
7089ab9235fSMax Chou btrtl_dev->project_id = project_id;
7091110a2dbSLarry Finger break;
7101110a2dbSLarry Finger }
7119ab9235fSMax Chou }
7121110a2dbSLarry Finger
7131110a2dbSLarry Finger if (i >= ARRAY_SIZE(project_id_to_lmp_subver)) {
714f1300c03SAlex Lu rtl_dev_err(hdev, "unknown project id %d", project_id);
715db33c77dSCarlo Caione return -EINVAL;
716db33c77dSCarlo Caione }
717db33c77dSCarlo Caione
71826503ad2SMartin Blumenstingl if (btrtl_dev->ic_info->lmp_subver !=
71926503ad2SMartin Blumenstingl project_id_to_lmp_subver[i].lmp_subver) {
720f1300c03SAlex Lu rtl_dev_err(hdev, "firmware is for %x but this is a %x",
72126503ad2SMartin Blumenstingl project_id_to_lmp_subver[i].lmp_subver,
72226503ad2SMartin Blumenstingl btrtl_dev->ic_info->lmp_subver);
723db33c77dSCarlo Caione return -EINVAL;
724db33c77dSCarlo Caione }
725db33c77dSCarlo Caione
7269a24ce5eSMax Chou if (memcmp(btrtl_dev->fw_data, RTL_EPATCH_SIGNATURE, 8) != 0) {
7279a24ce5eSMax Chou if (!memcmp(btrtl_dev->fw_data, RTL_EPATCH_SIGNATURE_V2, 8))
7289a24ce5eSMax Chou return rtlbt_parse_firmware_v2(hdev, btrtl_dev, _buf);
729f1300c03SAlex Lu rtl_dev_err(hdev, "bad EPATCH signature");
730db33c77dSCarlo Caione return -EINVAL;
731db33c77dSCarlo Caione }
732db33c77dSCarlo Caione
7339a24ce5eSMax Chou epatch_info = (struct rtl_epatch_header *)btrtl_dev->fw_data;
734db33c77dSCarlo Caione num_patches = le16_to_cpu(epatch_info->num_patches);
735044014ceSHilda Wu
736db33c77dSCarlo Caione BT_DBG("fw_version=%x, num_patches=%d",
737db33c77dSCarlo Caione le32_to_cpu(epatch_info->fw_version), num_patches);
738044014ceSHilda Wu coredump_info->rtl_dump.fw_version = le32_to_cpu(epatch_info->fw_version);
739db33c77dSCarlo Caione
740db33c77dSCarlo Caione /* After the rtl_epatch_header there is a funky patch metadata section.
741db33c77dSCarlo Caione * Assuming 2 patches, the layout is:
742db33c77dSCarlo Caione * ChipID1 ChipID2 PatchLength1 PatchLength2 PatchOffset1 PatchOffset2
743db33c77dSCarlo Caione *
744db33c77dSCarlo Caione * Find the right patch for this chip.
745db33c77dSCarlo Caione */
746db33c77dSCarlo Caione min_size += 8 * num_patches;
74726503ad2SMartin Blumenstingl if (btrtl_dev->fw_len < min_size)
748db33c77dSCarlo Caione return -EINVAL;
749db33c77dSCarlo Caione
75026503ad2SMartin Blumenstingl chip_id_base = btrtl_dev->fw_data + sizeof(struct rtl_epatch_header);
751db33c77dSCarlo Caione patch_length_base = chip_id_base + (sizeof(u16) * num_patches);
752db33c77dSCarlo Caione patch_offset_base = patch_length_base + (sizeof(u16) * num_patches);
753db33c77dSCarlo Caione for (i = 0; i < num_patches; i++) {
754db33c77dSCarlo Caione u16 chip_id = get_unaligned_le16(chip_id_base +
755db33c77dSCarlo Caione (i * sizeof(u16)));
75626503ad2SMartin Blumenstingl if (chip_id == btrtl_dev->rom_version + 1) {
757db33c77dSCarlo Caione patch_length = get_unaligned_le16(patch_length_base +
758db33c77dSCarlo Caione (i * sizeof(u16)));
759db33c77dSCarlo Caione patch_offset = get_unaligned_le32(patch_offset_base +
760db33c77dSCarlo Caione (i * sizeof(u32)));
761db33c77dSCarlo Caione break;
762db33c77dSCarlo Caione }
763db33c77dSCarlo Caione }
764db33c77dSCarlo Caione
765db33c77dSCarlo Caione if (!patch_offset) {
766a5c76e67SHans de Goede rtl_dev_err(hdev, "didn't find patch for chip id %d",
767a5c76e67SHans de Goede btrtl_dev->rom_version);
768db33c77dSCarlo Caione return -EINVAL;
769db33c77dSCarlo Caione }
770db33c77dSCarlo Caione
771db33c77dSCarlo Caione BT_DBG("length=%x offset=%x index %d", patch_length, patch_offset, i);
772db33c77dSCarlo Caione min_size = patch_offset + patch_length;
77326503ad2SMartin Blumenstingl if (btrtl_dev->fw_len < min_size)
774db33c77dSCarlo Caione return -EINVAL;
775db33c77dSCarlo Caione
776db33c77dSCarlo Caione /* Copy the firmware into a new buffer and write the version at
777db33c77dSCarlo Caione * the end.
778db33c77dSCarlo Caione */
779db33c77dSCarlo Caione len = patch_length;
780268d3636SMaxim Mikityanskiy buf = kvmalloc(patch_length, GFP_KERNEL);
781db33c77dSCarlo Caione if (!buf)
782db33c77dSCarlo Caione return -ENOMEM;
783db33c77dSCarlo Caione
784268d3636SMaxim Mikityanskiy memcpy(buf, btrtl_dev->fw_data + patch_offset, patch_length - 4);
785db33c77dSCarlo Caione memcpy(buf + patch_length - 4, &epatch_info->fw_version, 4);
786db33c77dSCarlo Caione
787db33c77dSCarlo Caione *_buf = buf;
788db33c77dSCarlo Caione return len;
789db33c77dSCarlo Caione }
790db33c77dSCarlo Caione
rtl_download_firmware(struct hci_dev * hdev,const unsigned char * data,int fw_len)791db33c77dSCarlo Caione static int rtl_download_firmware(struct hci_dev *hdev,
792db33c77dSCarlo Caione const unsigned char *data, int fw_len)
793db33c77dSCarlo Caione {
794db33c77dSCarlo Caione struct rtl_download_cmd *dl_cmd;
795db33c77dSCarlo Caione int frag_num = fw_len / RTL_FRAG_LEN + 1;
796db33c77dSCarlo Caione int frag_len = RTL_FRAG_LEN;
797db33c77dSCarlo Caione int ret = 0;
798db33c77dSCarlo Caione int i;
7999a24ce5eSMax Chou int j = 0;
800240b64a8SAlex Lu struct sk_buff *skb;
801240b64a8SAlex Lu struct hci_rp_read_local_version *rp;
802db33c77dSCarlo Caione
803db33c77dSCarlo Caione dl_cmd = kmalloc(sizeof(struct rtl_download_cmd), GFP_KERNEL);
804db33c77dSCarlo Caione if (!dl_cmd)
805db33c77dSCarlo Caione return -ENOMEM;
806db33c77dSCarlo Caione
807db33c77dSCarlo Caione for (i = 0; i < frag_num; i++) {
808db33c77dSCarlo Caione struct sk_buff *skb;
809db33c77dSCarlo Caione
8109a24ce5eSMax Chou dl_cmd->index = j++;
8119a24ce5eSMax Chou if (dl_cmd->index == 0x7f)
8129a24ce5eSMax Chou j = 1;
813cf0d9a70SMax Chou
814db33c77dSCarlo Caione if (i == (frag_num - 1)) {
815db33c77dSCarlo Caione dl_cmd->index |= 0x80; /* data end */
816db33c77dSCarlo Caione frag_len = fw_len % RTL_FRAG_LEN;
817db33c77dSCarlo Caione }
8189a24ce5eSMax Chou rtl_dev_dbg(hdev, "download fw (%d/%d). index = %d", i,
8199a24ce5eSMax Chou frag_num, dl_cmd->index);
820db33c77dSCarlo Caione memcpy(dl_cmd->data, data, frag_len);
821db33c77dSCarlo Caione
822db33c77dSCarlo Caione /* Send download command */
823db33c77dSCarlo Caione skb = __hci_cmd_sync(hdev, 0xfc20, frag_len + 1, dl_cmd,
824db33c77dSCarlo Caione HCI_INIT_TIMEOUT);
825db33c77dSCarlo Caione if (IS_ERR(skb)) {
826f1300c03SAlex Lu rtl_dev_err(hdev, "download fw command failed (%ld)",
827a5c76e67SHans de Goede PTR_ERR(skb));
828d171dfb6SMax Chou ret = PTR_ERR(skb);
829db33c77dSCarlo Caione goto out;
830db33c77dSCarlo Caione }
831db33c77dSCarlo Caione
832db33c77dSCarlo Caione if (skb->len != sizeof(struct rtl_download_response)) {
833f1300c03SAlex Lu rtl_dev_err(hdev, "download fw event length mismatch");
834db33c77dSCarlo Caione kfree_skb(skb);
835db33c77dSCarlo Caione ret = -EIO;
836db33c77dSCarlo Caione goto out;
837db33c77dSCarlo Caione }
838db33c77dSCarlo Caione
839db33c77dSCarlo Caione kfree_skb(skb);
840db33c77dSCarlo Caione data += RTL_FRAG_LEN;
841db33c77dSCarlo Caione }
842db33c77dSCarlo Caione
843240b64a8SAlex Lu skb = btrtl_read_local_version(hdev);
844240b64a8SAlex Lu if (IS_ERR(skb)) {
845240b64a8SAlex Lu ret = PTR_ERR(skb);
846240b64a8SAlex Lu rtl_dev_err(hdev, "read local version failed");
847240b64a8SAlex Lu goto out;
848240b64a8SAlex Lu }
849240b64a8SAlex Lu
850240b64a8SAlex Lu rp = (struct hci_rp_read_local_version *)skb->data;
851240b64a8SAlex Lu rtl_dev_info(hdev, "fw version 0x%04x%04x",
852240b64a8SAlex Lu __le16_to_cpu(rp->hci_rev), __le16_to_cpu(rp->lmp_subver));
853240b64a8SAlex Lu kfree_skb(skb);
854240b64a8SAlex Lu
855db33c77dSCarlo Caione out:
856db33c77dSCarlo Caione kfree(dl_cmd);
857db33c77dSCarlo Caione return ret;
858db33c77dSCarlo Caione }
859db33c77dSCarlo Caione
rtl_load_file(struct hci_dev * hdev,const char * name,u8 ** buff)86026503ad2SMartin Blumenstingl static int rtl_load_file(struct hci_dev *hdev, const char *name, u8 **buff)
8611110a2dbSLarry Finger {
8621110a2dbSLarry Finger const struct firmware *fw;
8631110a2dbSLarry Finger int ret;
8641110a2dbSLarry Finger
865f1300c03SAlex Lu rtl_dev_info(hdev, "loading %s", name);
8661110a2dbSLarry Finger ret = request_firmware(&fw, name, &hdev->dev);
867abed84a0SLarry Finger if (ret < 0)
8681110a2dbSLarry Finger return ret;
8691110a2dbSLarry Finger ret = fw->size;
870268d3636SMaxim Mikityanskiy *buff = kvmalloc(fw->size, GFP_KERNEL);
871268d3636SMaxim Mikityanskiy if (*buff)
872268d3636SMaxim Mikityanskiy memcpy(*buff, fw->data, ret);
873268d3636SMaxim Mikityanskiy else
874c3327bdeSDan Carpenter ret = -ENOMEM;
8751110a2dbSLarry Finger
8761110a2dbSLarry Finger release_firmware(fw);
8771110a2dbSLarry Finger
8781110a2dbSLarry Finger return ret;
8791110a2dbSLarry Finger }
8801110a2dbSLarry Finger
btrtl_setup_rtl8723a(struct hci_dev * hdev,struct btrtl_device_info * btrtl_dev)88126503ad2SMartin Blumenstingl static int btrtl_setup_rtl8723a(struct hci_dev *hdev,
88226503ad2SMartin Blumenstingl struct btrtl_device_info *btrtl_dev)
883db33c77dSCarlo Caione {
88426503ad2SMartin Blumenstingl if (btrtl_dev->fw_len < 8)
88526503ad2SMartin Blumenstingl return -EINVAL;
886db33c77dSCarlo Caione
887db33c77dSCarlo Caione /* Check that the firmware doesn't have the epatch signature
888db33c77dSCarlo Caione * (which is only for RTL8723B and newer).
889db33c77dSCarlo Caione */
89026503ad2SMartin Blumenstingl if (!memcmp(btrtl_dev->fw_data, RTL_EPATCH_SIGNATURE, 8)) {
891f1300c03SAlex Lu rtl_dev_err(hdev, "unexpected EPATCH signature!");
892907f8499SAlex Lu return -EINVAL;
893907f8499SAlex Lu }
894907f8499SAlex Lu
89526503ad2SMartin Blumenstingl return rtl_download_firmware(hdev, btrtl_dev->fw_data,
89626503ad2SMartin Blumenstingl btrtl_dev->fw_len);
897db33c77dSCarlo Caione }
898db33c77dSCarlo Caione
btrtl_setup_rtl8723b(struct hci_dev * hdev,struct btrtl_device_info * btrtl_dev)89926503ad2SMartin Blumenstingl static int btrtl_setup_rtl8723b(struct hci_dev *hdev,
90026503ad2SMartin Blumenstingl struct btrtl_device_info *btrtl_dev)
90126503ad2SMartin Blumenstingl {
90226503ad2SMartin Blumenstingl unsigned char *fw_data = NULL;
90326503ad2SMartin Blumenstingl int ret;
90426503ad2SMartin Blumenstingl u8 *tbuff;
90526503ad2SMartin Blumenstingl
90626503ad2SMartin Blumenstingl ret = rtlbt_parse_firmware(hdev, btrtl_dev, &fw_data);
907db33c77dSCarlo Caione if (ret < 0)
908db33c77dSCarlo Caione goto out;
909db33c77dSCarlo Caione
91026503ad2SMartin Blumenstingl if (btrtl_dev->cfg_len > 0) {
911268d3636SMaxim Mikityanskiy tbuff = kvzalloc(ret + btrtl_dev->cfg_len, GFP_KERNEL);
9121110a2dbSLarry Finger if (!tbuff) {
9131110a2dbSLarry Finger ret = -ENOMEM;
914db33c77dSCarlo Caione goto out;
9151110a2dbSLarry Finger }
9161110a2dbSLarry Finger
9171110a2dbSLarry Finger memcpy(tbuff, fw_data, ret);
918268d3636SMaxim Mikityanskiy kvfree(fw_data);
9191110a2dbSLarry Finger
92026503ad2SMartin Blumenstingl memcpy(tbuff + ret, btrtl_dev->cfg_data, btrtl_dev->cfg_len);
92126503ad2SMartin Blumenstingl ret += btrtl_dev->cfg_len;
9221110a2dbSLarry Finger
9231110a2dbSLarry Finger fw_data = tbuff;
9241110a2dbSLarry Finger }
9251110a2dbSLarry Finger
926f1300c03SAlex Lu rtl_dev_info(hdev, "cfg_sz %d, total sz %d", btrtl_dev->cfg_len, ret);
9271110a2dbSLarry Finger
9281110a2dbSLarry Finger ret = rtl_download_firmware(hdev, fw_data, ret);
929db33c77dSCarlo Caione
930db33c77dSCarlo Caione out:
931268d3636SMaxim Mikityanskiy kvfree(fw_data);
932db33c77dSCarlo Caione return ret;
933db33c77dSCarlo Caione }
934db33c77dSCarlo Caione
btrtl_coredump(struct hci_dev * hdev)935044014ceSHilda Wu static void btrtl_coredump(struct hci_dev *hdev)
936044014ceSHilda Wu {
937044014ceSHilda Wu static const u8 param[] = { 0x00, 0x00 };
938044014ceSHilda Wu
939044014ceSHilda Wu __hci_cmd_send(hdev, RTL_VSC_OP_COREDUMP, sizeof(param), param);
940044014ceSHilda Wu }
941044014ceSHilda Wu
btrtl_dmp_hdr(struct hci_dev * hdev,struct sk_buff * skb)942044014ceSHilda Wu static void btrtl_dmp_hdr(struct hci_dev *hdev, struct sk_buff *skb)
943044014ceSHilda Wu {
944044014ceSHilda Wu struct btrealtek_data *coredump_info = hci_get_priv(hdev);
945044014ceSHilda Wu char buf[80];
946044014ceSHilda Wu
947044014ceSHilda Wu if (coredump_info->rtl_dump.controller)
948044014ceSHilda Wu snprintf(buf, sizeof(buf), "Controller Name: %s\n",
949044014ceSHilda Wu coredump_info->rtl_dump.controller);
950044014ceSHilda Wu else
951044014ceSHilda Wu snprintf(buf, sizeof(buf), "Controller Name: Unknown\n");
952044014ceSHilda Wu skb_put_data(skb, buf, strlen(buf));
953044014ceSHilda Wu
954044014ceSHilda Wu snprintf(buf, sizeof(buf), "Firmware Version: 0x%X\n",
955044014ceSHilda Wu coredump_info->rtl_dump.fw_version);
956044014ceSHilda Wu skb_put_data(skb, buf, strlen(buf));
957044014ceSHilda Wu
958044014ceSHilda Wu snprintf(buf, sizeof(buf), "Driver: %s\n", coredump_info->rtl_dump.driver_name);
959044014ceSHilda Wu skb_put_data(skb, buf, strlen(buf));
960044014ceSHilda Wu
961044014ceSHilda Wu snprintf(buf, sizeof(buf), "Vendor: Realtek\n");
962044014ceSHilda Wu skb_put_data(skb, buf, strlen(buf));
963044014ceSHilda Wu }
964044014ceSHilda Wu
btrtl_register_devcoredump_support(struct hci_dev * hdev)9659ee25286SMax Chou static void btrtl_register_devcoredump_support(struct hci_dev *hdev)
966044014ceSHilda Wu {
9679ee25286SMax Chou hci_devcd_register(hdev, btrtl_coredump, btrtl_dmp_hdr, NULL);
968044014ceSHilda Wu
969044014ceSHilda Wu }
970044014ceSHilda Wu
btrtl_set_driver_name(struct hci_dev * hdev,const char * driver_name)971044014ceSHilda Wu void btrtl_set_driver_name(struct hci_dev *hdev, const char *driver_name)
972044014ceSHilda Wu {
973044014ceSHilda Wu struct btrealtek_data *coredump_info = hci_get_priv(hdev);
974044014ceSHilda Wu
975044014ceSHilda Wu coredump_info->rtl_dump.driver_name = driver_name;
976044014ceSHilda Wu }
977044014ceSHilda Wu EXPORT_SYMBOL_GPL(btrtl_set_driver_name);
978044014ceSHilda Wu
rtl_has_chip_type(u16 lmp_subver)979c0123cb6SVasily Khoruzhick static bool rtl_has_chip_type(u16 lmp_subver)
980c0123cb6SVasily Khoruzhick {
981c0123cb6SVasily Khoruzhick switch (lmp_subver) {
982c0123cb6SVasily Khoruzhick case RTL_ROM_LMP_8703B:
983c0123cb6SVasily Khoruzhick return true;
984c0123cb6SVasily Khoruzhick default:
985c0123cb6SVasily Khoruzhick break;
986c0123cb6SVasily Khoruzhick }
987c0123cb6SVasily Khoruzhick
988c0123cb6SVasily Khoruzhick return false;
989c0123cb6SVasily Khoruzhick }
990c0123cb6SVasily Khoruzhick
rtl_read_chip_type(struct hci_dev * hdev,u8 * type)991c0123cb6SVasily Khoruzhick static int rtl_read_chip_type(struct hci_dev *hdev, u8 *type)
992c0123cb6SVasily Khoruzhick {
993c0123cb6SVasily Khoruzhick struct rtl_chip_type_evt *chip_type;
994c0123cb6SVasily Khoruzhick struct sk_buff *skb;
995c0123cb6SVasily Khoruzhick const unsigned char cmd_buf[] = {0x00, 0x94, 0xa0, 0x00, 0xb0};
996c0123cb6SVasily Khoruzhick
997c0123cb6SVasily Khoruzhick /* Read RTL chip type command */
998c0123cb6SVasily Khoruzhick skb = __hci_cmd_sync(hdev, 0xfc61, 5, cmd_buf, HCI_INIT_TIMEOUT);
999c0123cb6SVasily Khoruzhick if (IS_ERR(skb)) {
1000c0123cb6SVasily Khoruzhick rtl_dev_err(hdev, "Read chip type failed (%ld)",
1001c0123cb6SVasily Khoruzhick PTR_ERR(skb));
1002c0123cb6SVasily Khoruzhick return PTR_ERR(skb);
1003c0123cb6SVasily Khoruzhick }
1004c0123cb6SVasily Khoruzhick
1005c0123cb6SVasily Khoruzhick chip_type = skb_pull_data(skb, sizeof(*chip_type));
1006c0123cb6SVasily Khoruzhick if (!chip_type) {
1007c0123cb6SVasily Khoruzhick rtl_dev_err(hdev, "RTL chip type event length mismatch");
1008c0123cb6SVasily Khoruzhick kfree_skb(skb);
1009c0123cb6SVasily Khoruzhick return -EIO;
1010c0123cb6SVasily Khoruzhick }
1011c0123cb6SVasily Khoruzhick
1012c0123cb6SVasily Khoruzhick rtl_dev_info(hdev, "chip_type status=%x type=%x",
1013c0123cb6SVasily Khoruzhick chip_type->status, chip_type->type);
1014c0123cb6SVasily Khoruzhick
1015c0123cb6SVasily Khoruzhick *type = chip_type->type & 0x0f;
1016c0123cb6SVasily Khoruzhick
1017c0123cb6SVasily Khoruzhick kfree_skb(skb);
1018c0123cb6SVasily Khoruzhick return 0;
1019c0123cb6SVasily Khoruzhick }
1020c0123cb6SVasily Khoruzhick
btrtl_free(struct btrtl_device_info * btrtl_dev)102126503ad2SMartin Blumenstingl void btrtl_free(struct btrtl_device_info *btrtl_dev)
1022db33c77dSCarlo Caione {
10239a24ce5eSMax Chou struct rtl_subsection *entry, *tmp;
10249a24ce5eSMax Chou
1025268d3636SMaxim Mikityanskiy kvfree(btrtl_dev->fw_data);
1026268d3636SMaxim Mikityanskiy kvfree(btrtl_dev->cfg_data);
10279a24ce5eSMax Chou
10289a24ce5eSMax Chou list_for_each_entry_safe(entry, tmp, &btrtl_dev->patch_subsecs, list) {
10299a24ce5eSMax Chou list_del(&entry->list);
10309a24ce5eSMax Chou kfree(entry);
10319a24ce5eSMax Chou }
10329a24ce5eSMax Chou
103326503ad2SMartin Blumenstingl kfree(btrtl_dev);
103426503ad2SMartin Blumenstingl }
103526503ad2SMartin Blumenstingl EXPORT_SYMBOL_GPL(btrtl_free);
103626503ad2SMartin Blumenstingl
btrtl_initialize(struct hci_dev * hdev,const char * postfix)10371cc194caSHans de Goede struct btrtl_device_info *btrtl_initialize(struct hci_dev *hdev,
10381cc194caSHans de Goede const char *postfix)
103926503ad2SMartin Blumenstingl {
1040044014ceSHilda Wu struct btrealtek_data *coredump_info = hci_get_priv(hdev);
104126503ad2SMartin Blumenstingl struct btrtl_device_info *btrtl_dev;
1042db33c77dSCarlo Caione struct sk_buff *skb;
1043db33c77dSCarlo Caione struct hci_rp_read_local_version *resp;
10446b42f04eSMax Chou struct hci_command_hdr *cmd;
1045bd003fb3SMax Chou char fw_name[40];
10461cc194caSHans de Goede char cfg_name[40];
1047907f8499SAlex Lu u16 hci_rev, lmp_subver;
10489a24ce5eSMax Chou u8 hci_ver, lmp_ver, chip_type = 0;
104926503ad2SMartin Blumenstingl int ret;
10509a24ce5eSMax Chou u8 reg_val[2];
105126503ad2SMartin Blumenstingl
105226503ad2SMartin Blumenstingl btrtl_dev = kzalloc(sizeof(*btrtl_dev), GFP_KERNEL);
105326503ad2SMartin Blumenstingl if (!btrtl_dev) {
105426503ad2SMartin Blumenstingl ret = -ENOMEM;
105526503ad2SMartin Blumenstingl goto err_alloc;
105626503ad2SMartin Blumenstingl }
1057db33c77dSCarlo Caione
10589a24ce5eSMax Chou INIT_LIST_HEAD(&btrtl_dev->patch_subsecs);
10599a24ce5eSMax Chou
10609a24ce5eSMax Chou check_version:
10619a24ce5eSMax Chou ret = btrtl_vendor_read_reg16(hdev, RTL_CHIP_SUBVER, reg_val);
10629a24ce5eSMax Chou if (ret < 0)
10639a24ce5eSMax Chou goto err_free;
10649a24ce5eSMax Chou lmp_subver = get_unaligned_le16(reg_val);
10659a24ce5eSMax Chou
10669a24ce5eSMax Chou if (lmp_subver == RTL_ROM_LMP_8822B) {
10679a24ce5eSMax Chou ret = btrtl_vendor_read_reg16(hdev, RTL_CHIP_REV, reg_val);
10689a24ce5eSMax Chou if (ret < 0)
10699a24ce5eSMax Chou goto err_free;
10709a24ce5eSMax Chou hci_rev = get_unaligned_le16(reg_val);
10719a24ce5eSMax Chou
10729a24ce5eSMax Chou /* 8822E */
10739a24ce5eSMax Chou if (hci_rev == 0x000e) {
10749a24ce5eSMax Chou hci_ver = 0x0c;
10759a24ce5eSMax Chou lmp_ver = 0x0c;
10769a24ce5eSMax Chou btrtl_dev->ic_info = btrtl_match_ic(lmp_subver, hci_rev,
10779a24ce5eSMax Chou hci_ver, hdev->bus,
10789a24ce5eSMax Chou chip_type);
10799a24ce5eSMax Chou goto next;
10809a24ce5eSMax Chou }
10819a24ce5eSMax Chou }
10829a24ce5eSMax Chou
1083db33c77dSCarlo Caione skb = btrtl_read_local_version(hdev);
108426503ad2SMartin Blumenstingl if (IS_ERR(skb)) {
108526503ad2SMartin Blumenstingl ret = PTR_ERR(skb);
108626503ad2SMartin Blumenstingl goto err_free;
108726503ad2SMartin Blumenstingl }
1088db33c77dSCarlo Caione
1089db33c77dSCarlo Caione resp = (struct hci_rp_read_local_version *)skb->data;
1090db33c77dSCarlo Caione
1091c50903e3SMartin Blumenstingl hci_ver = resp->hci_ver;
1092907f8499SAlex Lu hci_rev = le16_to_cpu(resp->hci_rev);
10939a24ce5eSMax Chou lmp_ver = resp->lmp_ver;
1094db33c77dSCarlo Caione lmp_subver = le16_to_cpu(resp->lmp_subver);
10951996d9caSKai-Heng Feng
10969a24ce5eSMax Chou kfree_skb(skb);
10979a24ce5eSMax Chou
1098c0123cb6SVasily Khoruzhick if (rtl_has_chip_type(lmp_subver)) {
1099c0123cb6SVasily Khoruzhick ret = rtl_read_chip_type(hdev, &chip_type);
1100c0123cb6SVasily Khoruzhick if (ret)
1101c0123cb6SVasily Khoruzhick goto err_free;
1102c0123cb6SVasily Khoruzhick }
1103c0123cb6SVasily Khoruzhick
1104cd36742aSHilda Wu btrtl_dev->ic_info = btrtl_match_ic(lmp_subver, hci_rev, hci_ver,
1105c0123cb6SVasily Khoruzhick hdev->bus, chip_type);
1106cd36742aSHilda Wu
11079a24ce5eSMax Chou next:
11089a24ce5eSMax Chou rtl_dev_info(hdev, "examining hci_ver=%02x hci_rev=%04x lmp_ver=%02x lmp_subver=%04x",
11099a24ce5eSMax Chou hci_ver, hci_rev,
11109a24ce5eSMax Chou lmp_ver, lmp_subver);
11119a24ce5eSMax Chou
11129a24ce5eSMax Chou if (!btrtl_dev->ic_info && !btrtl_dev->drop_fw)
11131996d9caSKai-Heng Feng btrtl_dev->drop_fw = true;
11149a24ce5eSMax Chou else
11159a24ce5eSMax Chou btrtl_dev->drop_fw = false;
11161996d9caSKai-Heng Feng
11171996d9caSKai-Heng Feng if (btrtl_dev->drop_fw) {
11186b42f04eSMax Chou skb = bt_skb_alloc(sizeof(*cmd), GFP_KERNEL);
1119f5e8e215SColin Ian King if (!skb)
11209a24ce5eSMax Chou goto err_free;
11211996d9caSKai-Heng Feng
11226b42f04eSMax Chou cmd = skb_put(skb, HCI_COMMAND_HDR_SIZE);
11236b42f04eSMax Chou cmd->opcode = cpu_to_le16(0xfc66);
11246b42f04eSMax Chou cmd->plen = 0;
11256b42f04eSMax Chou
11261996d9caSKai-Heng Feng hci_skb_pkt_type(skb) = HCI_COMMAND_PKT;
11271996d9caSKai-Heng Feng
11289a24ce5eSMax Chou ret = hdev->send(hdev, skb);
11299a24ce5eSMax Chou if (ret < 0) {
11309a24ce5eSMax Chou bt_dev_err(hdev, "sending frame failed (%d)", ret);
11319a24ce5eSMax Chou kfree_skb(skb);
11329a24ce5eSMax Chou goto err_free;
11339a24ce5eSMax Chou }
11341996d9caSKai-Heng Feng
11351996d9caSKai-Heng Feng /* Ensure the above vendor command is sent to controller and
11361996d9caSKai-Heng Feng * process has done.
11371996d9caSKai-Heng Feng */
11381996d9caSKai-Heng Feng msleep(200);
11391996d9caSKai-Heng Feng
11409a24ce5eSMax Chou goto check_version;
11411996d9caSKai-Heng Feng }
11421996d9caSKai-Heng Feng
114326503ad2SMartin Blumenstingl if (!btrtl_dev->ic_info) {
1144d182215dSAlex Lu rtl_dev_info(hdev, "unknown IC info, lmp subver %04x, hci rev %04x, hci ver %04x",
1145c50903e3SMartin Blumenstingl lmp_subver, hci_rev, hci_ver);
114600df214bSKai-Heng Feng return btrtl_dev;
114726503ad2SMartin Blumenstingl }
114826503ad2SMartin Blumenstingl
114926503ad2SMartin Blumenstingl if (btrtl_dev->ic_info->has_rom_version) {
115026503ad2SMartin Blumenstingl ret = rtl_read_rom_version(hdev, &btrtl_dev->rom_version);
115126503ad2SMartin Blumenstingl if (ret)
115226503ad2SMartin Blumenstingl goto err_free;
115326503ad2SMartin Blumenstingl }
115426503ad2SMartin Blumenstingl
1155bd003fb3SMax Chou if (!btrtl_dev->ic_info->fw_name) {
1156bd003fb3SMax Chou ret = -ENOMEM;
1157bd003fb3SMax Chou goto err_free;
1158bd003fb3SMax Chou }
1159bd003fb3SMax Chou
1160bd003fb3SMax Chou btrtl_dev->fw_len = -EIO;
1161bd003fb3SMax Chou if (lmp_subver == RTL_ROM_LMP_8852A && hci_rev == 0x000c) {
1162bd003fb3SMax Chou snprintf(fw_name, sizeof(fw_name), "%s_v2.bin",
1163bd003fb3SMax Chou btrtl_dev->ic_info->fw_name);
1164bd003fb3SMax Chou btrtl_dev->fw_len = rtl_load_file(hdev, fw_name,
116526503ad2SMartin Blumenstingl &btrtl_dev->fw_data);
1166bd003fb3SMax Chou }
1167bd003fb3SMax Chou
1168bd003fb3SMax Chou if (btrtl_dev->fw_len < 0) {
1169bd003fb3SMax Chou snprintf(fw_name, sizeof(fw_name), "%s.bin",
1170bd003fb3SMax Chou btrtl_dev->ic_info->fw_name);
1171bd003fb3SMax Chou btrtl_dev->fw_len = rtl_load_file(hdev, fw_name,
1172bd003fb3SMax Chou &btrtl_dev->fw_data);
1173bd003fb3SMax Chou }
1174bd003fb3SMax Chou
117526503ad2SMartin Blumenstingl if (btrtl_dev->fw_len < 0) {
1176f1300c03SAlex Lu rtl_dev_err(hdev, "firmware file %s not found",
117726503ad2SMartin Blumenstingl btrtl_dev->ic_info->fw_name);
117826503ad2SMartin Blumenstingl ret = btrtl_dev->fw_len;
117926503ad2SMartin Blumenstingl goto err_free;
118026503ad2SMartin Blumenstingl }
118126503ad2SMartin Blumenstingl
118226503ad2SMartin Blumenstingl if (btrtl_dev->ic_info->cfg_name) {
11831cc194caSHans de Goede if (postfix) {
11841cc194caSHans de Goede snprintf(cfg_name, sizeof(cfg_name), "%s-%s.bin",
11851cc194caSHans de Goede btrtl_dev->ic_info->cfg_name, postfix);
11861cc194caSHans de Goede } else {
11871cc194caSHans de Goede snprintf(cfg_name, sizeof(cfg_name), "%s.bin",
11881cc194caSHans de Goede btrtl_dev->ic_info->cfg_name);
11891cc194caSHans de Goede }
11901cc194caSHans de Goede btrtl_dev->cfg_len = rtl_load_file(hdev, cfg_name,
119126503ad2SMartin Blumenstingl &btrtl_dev->cfg_data);
119226503ad2SMartin Blumenstingl if (btrtl_dev->ic_info->config_needed &&
119326503ad2SMartin Blumenstingl btrtl_dev->cfg_len <= 0) {
1194f1300c03SAlex Lu rtl_dev_err(hdev, "mandatory config file %s not found",
119526503ad2SMartin Blumenstingl btrtl_dev->ic_info->cfg_name);
119626503ad2SMartin Blumenstingl ret = btrtl_dev->cfg_len;
119726503ad2SMartin Blumenstingl goto err_free;
119826503ad2SMartin Blumenstingl }
119926503ad2SMartin Blumenstingl }
120026503ad2SMartin Blumenstingl
12017f6a750aSArchie Pusaka /* The following chips supports the Microsoft vendor extension,
12027f6a750aSArchie Pusaka * therefore set the corresponding VsMsftOpCode.
1203673fae14SMiao-chen Chou */
1204f4bcba0eSMarcel Holtmann if (btrtl_dev->ic_info->has_msft_ext)
1205673fae14SMiao-chen Chou hci_set_msft_opcode(hdev, 0xFCF0);
1206673fae14SMiao-chen Chou
1207044014ceSHilda Wu if (btrtl_dev->ic_info)
1208044014ceSHilda Wu coredump_info->rtl_dump.controller = btrtl_dev->ic_info->hw_info;
1209044014ceSHilda Wu
121026503ad2SMartin Blumenstingl return btrtl_dev;
121126503ad2SMartin Blumenstingl
121226503ad2SMartin Blumenstingl err_free:
121326503ad2SMartin Blumenstingl btrtl_free(btrtl_dev);
121426503ad2SMartin Blumenstingl err_alloc:
121526503ad2SMartin Blumenstingl return ERR_PTR(ret);
121626503ad2SMartin Blumenstingl }
121726503ad2SMartin Blumenstingl EXPORT_SYMBOL_GPL(btrtl_initialize);
121826503ad2SMartin Blumenstingl
btrtl_download_firmware(struct hci_dev * hdev,struct btrtl_device_info * btrtl_dev)121926503ad2SMartin Blumenstingl int btrtl_download_firmware(struct hci_dev *hdev,
122026503ad2SMartin Blumenstingl struct btrtl_device_info *btrtl_dev)
122126503ad2SMartin Blumenstingl {
1222044014ceSHilda Wu int err = 0;
1223044014ceSHilda Wu
1224db33c77dSCarlo Caione /* Match a set of subver values that correspond to stock firmware,
1225db33c77dSCarlo Caione * which is not compatible with standard btusb.
1226db33c77dSCarlo Caione * If matched, upload an alternative firmware that does conform to
1227db33c77dSCarlo Caione * standard btusb. Once that firmware is uploaded, the subver changes
1228db33c77dSCarlo Caione * to a different value.
1229db33c77dSCarlo Caione */
123000df214bSKai-Heng Feng if (!btrtl_dev->ic_info) {
1231f1300c03SAlex Lu rtl_dev_info(hdev, "assuming no firmware upload needed");
1232044014ceSHilda Wu err = 0;
1233044014ceSHilda Wu goto done;
123400df214bSKai-Heng Feng }
123500df214bSKai-Heng Feng
123626503ad2SMartin Blumenstingl switch (btrtl_dev->ic_info->lmp_subver) {
1237db33c77dSCarlo Caione case RTL_ROM_LMP_8723A:
1238044014ceSHilda Wu err = btrtl_setup_rtl8723a(hdev, btrtl_dev);
1239044014ceSHilda Wu break;
1240db33c77dSCarlo Caione case RTL_ROM_LMP_8723B:
1241db33c77dSCarlo Caione case RTL_ROM_LMP_8821A:
1242db33c77dSCarlo Caione case RTL_ROM_LMP_8761A:
12431110a2dbSLarry Finger case RTL_ROM_LMP_8822B:
12440d484db6SMax Chou case RTL_ROM_LMP_8852A:
1245c0123cb6SVasily Khoruzhick case RTL_ROM_LMP_8703B:
12467948fe1cSMax Chou case RTL_ROM_LMP_8851B:
1247044014ceSHilda Wu err = btrtl_setup_rtl8723b(hdev, btrtl_dev);
1248044014ceSHilda Wu break;
1249db33c77dSCarlo Caione default:
1250f1300c03SAlex Lu rtl_dev_info(hdev, "assuming no firmware upload needed");
1251044014ceSHilda Wu break;
1252db33c77dSCarlo Caione }
1253044014ceSHilda Wu
1254044014ceSHilda Wu done:
12559ee25286SMax Chou btrtl_register_devcoredump_support(hdev);
1256044014ceSHilda Wu
1257044014ceSHilda Wu return err;
1258db33c77dSCarlo Caione }
125926503ad2SMartin Blumenstingl EXPORT_SYMBOL_GPL(btrtl_download_firmware);
126026503ad2SMartin Blumenstingl
btrtl_set_quirks(struct hci_dev * hdev,struct btrtl_device_info * btrtl_dev)12613011faa2SArchie Pusaka void btrtl_set_quirks(struct hci_dev *hdev, struct btrtl_device_info *btrtl_dev)
126226503ad2SMartin Blumenstingl {
126365251e2eSAlex Lu /* Enable controller to do both LE scan and BR/EDR inquiry
126465251e2eSAlex Lu * simultaneously.
126565251e2eSAlex Lu */
126665251e2eSAlex Lu set_bit(HCI_QUIRK_SIMULTANEOUS_DISCOVERY, &hdev->quirks);
126765251e2eSAlex Lu
126805672a2cSAbhishek Pandit-Subedi /* Enable central-peripheral role (able to create new connections with
126905672a2cSAbhishek Pandit-Subedi * an existing connection in slave role).
127005672a2cSAbhishek Pandit-Subedi */
12719ab9235fSMax Chou /* Enable WBS supported for the specific Realtek devices. */
12729ab9235fSMax Chou switch (btrtl_dev->project_id) {
12739ab9235fSMax Chou case CHIP_ID_8822C:
12749ab9235fSMax Chou case CHIP_ID_8852A:
127518e8055cSMax Chou case CHIP_ID_8852B:
12768b1d66b5SMax Chou case CHIP_ID_8852C:
12777948fe1cSMax Chou case CHIP_ID_8851B:
127805672a2cSAbhishek Pandit-Subedi set_bit(HCI_QUIRK_VALID_LE_STATES, &hdev->quirks);
12799ab9235fSMax Chou set_bit(HCI_QUIRK_WIDEBAND_SPEECH_SUPPORTED, &hdev->quirks);
1280a479e713SHilda Wu
1281a479e713SHilda Wu /* RTL8852C needs to transmit mSBC data continuously without
1282a479e713SHilda Wu * the zero length of USB packets for the ALT 6 supported chips
1283a479e713SHilda Wu */
1284a479e713SHilda Wu if (btrtl_dev->project_id == CHIP_ID_8852C)
1285a479e713SHilda Wu btrealtek_set_flag(hdev, REALTEK_ALT6_CONTINUOUS_TX_CHIP);
1286a479e713SHilda Wu
12879e14606dSHilda Wu if (btrtl_dev->project_id == CHIP_ID_8852A ||
1288*cc026a7fSHilda Wu btrtl_dev->project_id == CHIP_ID_8852B ||
12899e14606dSHilda Wu btrtl_dev->project_id == CHIP_ID_8852C)
12909e14606dSHilda Wu set_bit(HCI_QUIRK_USE_MSFT_EXT_ADDRESS_FILTER, &hdev->quirks);
12919e14606dSHilda Wu
1292099c6d31SJoseph Hwang hci_set_aosp_capable(hdev);
129305672a2cSAbhishek Pandit-Subedi break;
129405672a2cSAbhishek Pandit-Subedi default:
129505672a2cSAbhishek Pandit-Subedi rtl_dev_dbg(hdev, "Central-peripheral role not enabled.");
12969ab9235fSMax Chou rtl_dev_dbg(hdev, "WBS supported not enabled.");
129705672a2cSAbhishek Pandit-Subedi break;
129805672a2cSAbhishek Pandit-Subedi }
1299c0123cb6SVasily Khoruzhick
1300253cf30eSMax Chou if (!btrtl_dev->ic_info)
1301253cf30eSMax Chou return;
1302253cf30eSMax Chou
1303c0123cb6SVasily Khoruzhick switch (btrtl_dev->ic_info->lmp_subver) {
1304c0123cb6SVasily Khoruzhick case RTL_ROM_LMP_8703B:
1305c0123cb6SVasily Khoruzhick /* 8723CS reports two pages for local ext features,
1306c0123cb6SVasily Khoruzhick * but it doesn't support any features from page 2 -
1307c0123cb6SVasily Khoruzhick * it either responds with garbage or with error status
1308c0123cb6SVasily Khoruzhick */
1309c0123cb6SVasily Khoruzhick set_bit(HCI_QUIRK_BROKEN_LOCAL_EXT_FEATURES_PAGE_2,
1310c0123cb6SVasily Khoruzhick &hdev->quirks);
1311c0123cb6SVasily Khoruzhick break;
1312c0123cb6SVasily Khoruzhick default:
1313c0123cb6SVasily Khoruzhick break;
1314c0123cb6SVasily Khoruzhick }
13153011faa2SArchie Pusaka }
13163011faa2SArchie Pusaka EXPORT_SYMBOL_GPL(btrtl_set_quirks);
13173011faa2SArchie Pusaka
btrtl_setup_realtek(struct hci_dev * hdev)13183011faa2SArchie Pusaka int btrtl_setup_realtek(struct hci_dev *hdev)
13193011faa2SArchie Pusaka {
13203011faa2SArchie Pusaka struct btrtl_device_info *btrtl_dev;
13213011faa2SArchie Pusaka int ret;
13223011faa2SArchie Pusaka
13233011faa2SArchie Pusaka btrtl_dev = btrtl_initialize(hdev, NULL);
13243011faa2SArchie Pusaka if (IS_ERR(btrtl_dev))
13253011faa2SArchie Pusaka return PTR_ERR(btrtl_dev);
13263011faa2SArchie Pusaka
13273011faa2SArchie Pusaka ret = btrtl_download_firmware(hdev, btrtl_dev);
13283011faa2SArchie Pusaka
13293011faa2SArchie Pusaka btrtl_set_quirks(hdev, btrtl_dev);
133005672a2cSAbhishek Pandit-Subedi
133105672a2cSAbhishek Pandit-Subedi btrtl_free(btrtl_dev);
133226503ad2SMartin Blumenstingl return ret;
133326503ad2SMartin Blumenstingl }
1334db33c77dSCarlo Caione EXPORT_SYMBOL_GPL(btrtl_setup_realtek);
1335db33c77dSCarlo Caione
btrtl_shutdown_realtek(struct hci_dev * hdev)13367af3f558SJian-Hong Pan int btrtl_shutdown_realtek(struct hci_dev *hdev)
13377af3f558SJian-Hong Pan {
13387af3f558SJian-Hong Pan struct sk_buff *skb;
13397af3f558SJian-Hong Pan int ret;
13407af3f558SJian-Hong Pan
13417af3f558SJian-Hong Pan /* According to the vendor driver, BT must be reset on close to avoid
13427af3f558SJian-Hong Pan * firmware crash.
13437af3f558SJian-Hong Pan */
13447af3f558SJian-Hong Pan skb = __hci_cmd_sync(hdev, HCI_OP_RESET, 0, NULL, HCI_INIT_TIMEOUT);
13457af3f558SJian-Hong Pan if (IS_ERR(skb)) {
13467af3f558SJian-Hong Pan ret = PTR_ERR(skb);
13477af3f558SJian-Hong Pan bt_dev_err(hdev, "HCI reset during shutdown failed");
13487af3f558SJian-Hong Pan return ret;
13497af3f558SJian-Hong Pan }
13507af3f558SJian-Hong Pan kfree_skb(skb);
13517af3f558SJian-Hong Pan
13527af3f558SJian-Hong Pan return 0;
13537af3f558SJian-Hong Pan }
13547af3f558SJian-Hong Pan EXPORT_SYMBOL_GPL(btrtl_shutdown_realtek);
13557af3f558SJian-Hong Pan
btrtl_convert_baudrate(u32 device_baudrate)1356b85b0ee1SMartin Blumenstingl static unsigned int btrtl_convert_baudrate(u32 device_baudrate)
1357b85b0ee1SMartin Blumenstingl {
1358b85b0ee1SMartin Blumenstingl switch (device_baudrate) {
1359b85b0ee1SMartin Blumenstingl case 0x0252a00a:
1360b85b0ee1SMartin Blumenstingl return 230400;
1361b85b0ee1SMartin Blumenstingl
1362b85b0ee1SMartin Blumenstingl case 0x05f75004:
1363b85b0ee1SMartin Blumenstingl return 921600;
1364b85b0ee1SMartin Blumenstingl
1365b85b0ee1SMartin Blumenstingl case 0x00005004:
1366b85b0ee1SMartin Blumenstingl return 1000000;
1367b85b0ee1SMartin Blumenstingl
1368b85b0ee1SMartin Blumenstingl case 0x04928002:
1369b85b0ee1SMartin Blumenstingl case 0x01128002:
1370b85b0ee1SMartin Blumenstingl return 1500000;
1371b85b0ee1SMartin Blumenstingl
1372b85b0ee1SMartin Blumenstingl case 0x00005002:
1373b85b0ee1SMartin Blumenstingl return 2000000;
1374b85b0ee1SMartin Blumenstingl
1375b85b0ee1SMartin Blumenstingl case 0x0000b001:
1376b85b0ee1SMartin Blumenstingl return 2500000;
1377b85b0ee1SMartin Blumenstingl
1378b85b0ee1SMartin Blumenstingl case 0x04928001:
1379b85b0ee1SMartin Blumenstingl return 3000000;
1380b85b0ee1SMartin Blumenstingl
1381b85b0ee1SMartin Blumenstingl case 0x052a6001:
1382b85b0ee1SMartin Blumenstingl return 3500000;
1383b85b0ee1SMartin Blumenstingl
1384b85b0ee1SMartin Blumenstingl case 0x00005001:
1385b85b0ee1SMartin Blumenstingl return 4000000;
1386b85b0ee1SMartin Blumenstingl
1387b85b0ee1SMartin Blumenstingl case 0x0252c014:
1388b85b0ee1SMartin Blumenstingl default:
1389b85b0ee1SMartin Blumenstingl return 115200;
1390b85b0ee1SMartin Blumenstingl }
1391b85b0ee1SMartin Blumenstingl }
1392b85b0ee1SMartin Blumenstingl
btrtl_get_uart_settings(struct hci_dev * hdev,struct btrtl_device_info * btrtl_dev,unsigned int * controller_baudrate,u32 * device_baudrate,bool * flow_control)1393b85b0ee1SMartin Blumenstingl int btrtl_get_uart_settings(struct hci_dev *hdev,
1394b85b0ee1SMartin Blumenstingl struct btrtl_device_info *btrtl_dev,
1395b85b0ee1SMartin Blumenstingl unsigned int *controller_baudrate,
1396b85b0ee1SMartin Blumenstingl u32 *device_baudrate, bool *flow_control)
1397b85b0ee1SMartin Blumenstingl {
1398b85b0ee1SMartin Blumenstingl struct rtl_vendor_config *config;
1399b85b0ee1SMartin Blumenstingl struct rtl_vendor_config_entry *entry;
1400b85b0ee1SMartin Blumenstingl int i, total_data_len;
1401b85b0ee1SMartin Blumenstingl bool found = false;
1402b85b0ee1SMartin Blumenstingl
1403b85b0ee1SMartin Blumenstingl total_data_len = btrtl_dev->cfg_len - sizeof(*config);
1404b85b0ee1SMartin Blumenstingl if (total_data_len <= 0) {
1405f1300c03SAlex Lu rtl_dev_warn(hdev, "no config loaded");
1406b85b0ee1SMartin Blumenstingl return -EINVAL;
1407b85b0ee1SMartin Blumenstingl }
1408b85b0ee1SMartin Blumenstingl
1409b85b0ee1SMartin Blumenstingl config = (struct rtl_vendor_config *)btrtl_dev->cfg_data;
1410b85b0ee1SMartin Blumenstingl if (le32_to_cpu(config->signature) != RTL_CONFIG_MAGIC) {
1411f1300c03SAlex Lu rtl_dev_err(hdev, "invalid config magic");
1412b85b0ee1SMartin Blumenstingl return -EINVAL;
1413b85b0ee1SMartin Blumenstingl }
1414b85b0ee1SMartin Blumenstingl
1415b85b0ee1SMartin Blumenstingl if (total_data_len < le16_to_cpu(config->total_len)) {
1416f1300c03SAlex Lu rtl_dev_err(hdev, "config is too short");
1417b85b0ee1SMartin Blumenstingl return -EINVAL;
1418b85b0ee1SMartin Blumenstingl }
1419b85b0ee1SMartin Blumenstingl
1420b85b0ee1SMartin Blumenstingl for (i = 0; i < total_data_len; ) {
1421b85b0ee1SMartin Blumenstingl entry = ((void *)config->entry) + i;
1422b85b0ee1SMartin Blumenstingl
1423b85b0ee1SMartin Blumenstingl switch (le16_to_cpu(entry->offset)) {
1424b85b0ee1SMartin Blumenstingl case 0xc:
1425b85b0ee1SMartin Blumenstingl if (entry->len < sizeof(*device_baudrate)) {
1426f1300c03SAlex Lu rtl_dev_err(hdev, "invalid UART config entry");
1427b85b0ee1SMartin Blumenstingl return -EINVAL;
1428b85b0ee1SMartin Blumenstingl }
1429b85b0ee1SMartin Blumenstingl
1430b85b0ee1SMartin Blumenstingl *device_baudrate = get_unaligned_le32(entry->data);
1431b85b0ee1SMartin Blumenstingl *controller_baudrate = btrtl_convert_baudrate(
1432b85b0ee1SMartin Blumenstingl *device_baudrate);
1433b85b0ee1SMartin Blumenstingl
1434b85b0ee1SMartin Blumenstingl if (entry->len >= 13)
1435b85b0ee1SMartin Blumenstingl *flow_control = !!(entry->data[12] & BIT(2));
1436b85b0ee1SMartin Blumenstingl else
1437b85b0ee1SMartin Blumenstingl *flow_control = false;
1438b85b0ee1SMartin Blumenstingl
1439b85b0ee1SMartin Blumenstingl found = true;
1440b85b0ee1SMartin Blumenstingl break;
1441b85b0ee1SMartin Blumenstingl
1442b85b0ee1SMartin Blumenstingl default:
1443f1300c03SAlex Lu rtl_dev_dbg(hdev, "skipping config entry 0x%x (len %u)",
1444b85b0ee1SMartin Blumenstingl le16_to_cpu(entry->offset), entry->len);
1445b85b0ee1SMartin Blumenstingl break;
1446515d6798SYueHaibing }
1447b85b0ee1SMartin Blumenstingl
1448b85b0ee1SMartin Blumenstingl i += sizeof(*entry) + entry->len;
1449b85b0ee1SMartin Blumenstingl }
1450b85b0ee1SMartin Blumenstingl
1451b85b0ee1SMartin Blumenstingl if (!found) {
1452f1300c03SAlex Lu rtl_dev_err(hdev, "no UART config entry found");
1453b85b0ee1SMartin Blumenstingl return -ENOENT;
1454b85b0ee1SMartin Blumenstingl }
1455b85b0ee1SMartin Blumenstingl
1456f1300c03SAlex Lu rtl_dev_dbg(hdev, "device baudrate = 0x%08x", *device_baudrate);
1457f1300c03SAlex Lu rtl_dev_dbg(hdev, "controller baudrate = %u", *controller_baudrate);
1458f1300c03SAlex Lu rtl_dev_dbg(hdev, "flow control %d", *flow_control);
1459b85b0ee1SMartin Blumenstingl
1460b85b0ee1SMartin Blumenstingl return 0;
1461b85b0ee1SMartin Blumenstingl }
1462b85b0ee1SMartin Blumenstingl EXPORT_SYMBOL_GPL(btrtl_get_uart_settings);
1463b85b0ee1SMartin Blumenstingl
1464db33c77dSCarlo Caione MODULE_AUTHOR("Daniel Drake <drake@endlessm.com>");
1465db33c77dSCarlo Caione MODULE_DESCRIPTION("Bluetooth support for Realtek devices ver " VERSION);
1466db33c77dSCarlo Caione MODULE_VERSION(VERSION);
1467db33c77dSCarlo Caione MODULE_LICENSE("GPL");
1468f96dbd32SMartin Blumenstingl MODULE_FIRMWARE("rtl_bt/rtl8723a_fw.bin");
1469f96dbd32SMartin Blumenstingl MODULE_FIRMWARE("rtl_bt/rtl8723b_fw.bin");
1470f96dbd32SMartin Blumenstingl MODULE_FIRMWARE("rtl_bt/rtl8723b_config.bin");
1471c50903e3SMartin Blumenstingl MODULE_FIRMWARE("rtl_bt/rtl8723bs_fw.bin");
1472c50903e3SMartin Blumenstingl MODULE_FIRMWARE("rtl_bt/rtl8723bs_config.bin");
1473c0123cb6SVasily Khoruzhick MODULE_FIRMWARE("rtl_bt/rtl8723cs_cg_fw.bin");
1474c0123cb6SVasily Khoruzhick MODULE_FIRMWARE("rtl_bt/rtl8723cs_cg_config.bin");
1475c0123cb6SVasily Khoruzhick MODULE_FIRMWARE("rtl_bt/rtl8723cs_vf_fw.bin");
1476c0123cb6SVasily Khoruzhick MODULE_FIRMWARE("rtl_bt/rtl8723cs_vf_config.bin");
1477c0123cb6SVasily Khoruzhick MODULE_FIRMWARE("rtl_bt/rtl8723cs_xx_fw.bin");
1478c0123cb6SVasily Khoruzhick MODULE_FIRMWARE("rtl_bt/rtl8723cs_xx_config.bin");
1479bb23f07cSDan Gora MODULE_FIRMWARE("rtl_bt/rtl8723d_fw.bin");
1480bb23f07cSDan Gora MODULE_FIRMWARE("rtl_bt/rtl8723d_config.bin");
1481c50903e3SMartin Blumenstingl MODULE_FIRMWARE("rtl_bt/rtl8723ds_fw.bin");
1482c50903e3SMartin Blumenstingl MODULE_FIRMWARE("rtl_bt/rtl8723ds_config.bin");
1483f96dbd32SMartin Blumenstingl MODULE_FIRMWARE("rtl_bt/rtl8761a_fw.bin");
1484f96dbd32SMartin Blumenstingl MODULE_FIRMWARE("rtl_bt/rtl8761a_config.bin");
1485bb23f07cSDan Gora MODULE_FIRMWARE("rtl_bt/rtl8761b_fw.bin");
1486bb23f07cSDan Gora MODULE_FIRMWARE("rtl_bt/rtl8761b_config.bin");
1487bb23f07cSDan Gora MODULE_FIRMWARE("rtl_bt/rtl8761bu_fw.bin");
1488bb23f07cSDan Gora MODULE_FIRMWARE("rtl_bt/rtl8761bu_config.bin");
1489f96dbd32SMartin Blumenstingl MODULE_FIRMWARE("rtl_bt/rtl8821a_fw.bin");
1490f96dbd32SMartin Blumenstingl MODULE_FIRMWARE("rtl_bt/rtl8821a_config.bin");
1491bb23f07cSDan Gora MODULE_FIRMWARE("rtl_bt/rtl8821c_fw.bin");
1492bb23f07cSDan Gora MODULE_FIRMWARE("rtl_bt/rtl8821c_config.bin");
1493bb23f07cSDan Gora MODULE_FIRMWARE("rtl_bt/rtl8821cs_fw.bin");
1494bb23f07cSDan Gora MODULE_FIRMWARE("rtl_bt/rtl8821cs_config.bin");
1495f96dbd32SMartin Blumenstingl MODULE_FIRMWARE("rtl_bt/rtl8822b_fw.bin");
1496f96dbd32SMartin Blumenstingl MODULE_FIRMWARE("rtl_bt/rtl8822b_config.bin");
1497bb23f07cSDan Gora MODULE_FIRMWARE("rtl_bt/rtl8822cs_fw.bin");
1498bb23f07cSDan Gora MODULE_FIRMWARE("rtl_bt/rtl8822cs_config.bin");
1499bb23f07cSDan Gora MODULE_FIRMWARE("rtl_bt/rtl8822cu_fw.bin");
1500bb23f07cSDan Gora MODULE_FIRMWARE("rtl_bt/rtl8822cu_config.bin");
1501bb23f07cSDan Gora MODULE_FIRMWARE("rtl_bt/rtl8851bu_fw.bin");
1502bb23f07cSDan Gora MODULE_FIRMWARE("rtl_bt/rtl8851bu_config.bin");
15030d484db6SMax Chou MODULE_FIRMWARE("rtl_bt/rtl8852au_fw.bin");
15040d484db6SMax Chou MODULE_FIRMWARE("rtl_bt/rtl8852au_config.bin");
1505fe4b71b5SVictor Hassan MODULE_FIRMWARE("rtl_bt/rtl8852bs_fw.bin");
1506fe4b71b5SVictor Hassan MODULE_FIRMWARE("rtl_bt/rtl8852bs_config.bin");
150718e8055cSMax Chou MODULE_FIRMWARE("rtl_bt/rtl8852bu_fw.bin");
150818e8055cSMax Chou MODULE_FIRMWARE("rtl_bt/rtl8852bu_config.bin");
15098b1d66b5SMax Chou MODULE_FIRMWARE("rtl_bt/rtl8852cu_fw.bin");
1510bd003fb3SMax Chou MODULE_FIRMWARE("rtl_bt/rtl8852cu_fw_v2.bin");
15118b1d66b5SMax Chou MODULE_FIRMWARE("rtl_bt/rtl8852cu_config.bin");
1512