diff options
author | Marcel Holtmann <marcel@holtmann.org> | 2015-04-06 01:52:13 -0400 |
---|---|---|
committer | Marcel Holtmann <marcel@holtmann.org> | 2015-04-07 12:47:11 -0400 |
commit | 1c8ba6d013c553bd13c50c139d652daac3348685 (patch) | |
tree | 3e443a70dd7b9b56789ce1560faae3d4a79ab826 | |
parent | 3e0ac12a1a610b4ab47282a25ee5945064228e35 (diff) |
Bluetooth: btbcm: Add support for Broadcom controller setup
To unify the controller setup of Broadcom devices between USB and UART
transport, add the patchram download support into the Broadcom module.
Signed-off-by: Marcel Holtmann <marcel@holtmann.org>
Signed-off-by: Johan Hedberg <johan.hedberg@intel.com>
-rw-r--r-- | drivers/bluetooth/Kconfig | 1 | ||||
-rw-r--r-- | drivers/bluetooth/btbcm.c | 258 | ||||
-rw-r--r-- | drivers/bluetooth/btbcm.h | 13 |
3 files changed, 272 insertions, 0 deletions
diff --git a/drivers/bluetooth/Kconfig b/drivers/bluetooth/Kconfig index 9f68e11c1671..0801649a3b41 100644 --- a/drivers/bluetooth/Kconfig +++ b/drivers/bluetooth/Kconfig | |||
@@ -4,6 +4,7 @@ menu "Bluetooth device drivers" | |||
4 | 4 | ||
5 | config BT_BCM | 5 | config BT_BCM |
6 | tristate | 6 | tristate |
7 | select FW_LOADER | ||
7 | 8 | ||
8 | config BT_HCIBTUSB | 9 | config BT_HCIBTUSB |
9 | tristate "HCI USB driver" | 10 | tristate "HCI USB driver" |
diff --git a/drivers/bluetooth/btbcm.c b/drivers/bluetooth/btbcm.c index 20c744a9a3f1..17565ab610f4 100644 --- a/drivers/bluetooth/btbcm.c +++ b/drivers/bluetooth/btbcm.c | |||
@@ -22,6 +22,8 @@ | |||
22 | */ | 22 | */ |
23 | 23 | ||
24 | #include <linux/module.h> | 24 | #include <linux/module.h> |
25 | #include <linux/firmware.h> | ||
26 | #include <asm/unaligned.h> | ||
25 | 27 | ||
26 | #include <net/bluetooth/bluetooth.h> | 28 | #include <net/bluetooth/bluetooth.h> |
27 | #include <net/bluetooth/hci_core.h> | 29 | #include <net/bluetooth/hci_core.h> |
@@ -93,6 +95,262 @@ int btbcm_set_bdaddr(struct hci_dev *hdev, const bdaddr_t *bdaddr) | |||
93 | } | 95 | } |
94 | EXPORT_SYMBOL_GPL(btbcm_set_bdaddr); | 96 | EXPORT_SYMBOL_GPL(btbcm_set_bdaddr); |
95 | 97 | ||
98 | static int btbcm_reset(struct hci_dev *hdev) | ||
99 | { | ||
100 | struct sk_buff *skb; | ||
101 | |||
102 | skb = __hci_cmd_sync(hdev, HCI_OP_RESET, 0, NULL, HCI_INIT_TIMEOUT); | ||
103 | if (IS_ERR(skb)) { | ||
104 | int err = PTR_ERR(skb); | ||
105 | BT_ERR("%s: BCM: Reset failed (%d)", hdev->name, err); | ||
106 | return err; | ||
107 | } | ||
108 | kfree_skb(skb); | ||
109 | |||
110 | return 0; | ||
111 | } | ||
112 | |||
113 | static struct sk_buff *btbcm_read_local_version(struct hci_dev *hdev) | ||
114 | { | ||
115 | struct sk_buff *skb; | ||
116 | |||
117 | skb = __hci_cmd_sync(hdev, HCI_OP_READ_LOCAL_VERSION, 0, NULL, | ||
118 | HCI_INIT_TIMEOUT); | ||
119 | if (IS_ERR(skb)) { | ||
120 | BT_ERR("%s: BCM: Reading local version info failed (%ld)", | ||
121 | hdev->name, PTR_ERR(skb)); | ||
122 | return skb; | ||
123 | } | ||
124 | |||
125 | if (skb->len != sizeof(struct hci_rp_read_local_version)) { | ||
126 | BT_ERR("%s: BCM: Local version length mismatch", hdev->name); | ||
127 | kfree_skb(skb); | ||
128 | return ERR_PTR(-EIO); | ||
129 | } | ||
130 | |||
131 | return skb; | ||
132 | } | ||
133 | |||
134 | static struct sk_buff *btbcm_read_verbose_config(struct hci_dev *hdev) | ||
135 | { | ||
136 | struct sk_buff *skb; | ||
137 | |||
138 | skb = __hci_cmd_sync(hdev, 0xfc79, 0, NULL, HCI_INIT_TIMEOUT); | ||
139 | if (IS_ERR(skb)) { | ||
140 | BT_ERR("%s: BCM: Read verbose config info failed (%ld)", | ||
141 | hdev->name, PTR_ERR(skb)); | ||
142 | return skb; | ||
143 | } | ||
144 | |||
145 | if (skb->len != 7) { | ||
146 | BT_ERR("%s: BCM: Verbose config length mismatch", hdev->name); | ||
147 | kfree_skb(skb); | ||
148 | return ERR_PTR(-EIO); | ||
149 | } | ||
150 | |||
151 | return skb; | ||
152 | } | ||
153 | |||
154 | static struct sk_buff *btbcm_read_usb_product(struct hci_dev *hdev) | ||
155 | { | ||
156 | struct sk_buff *skb; | ||
157 | |||
158 | skb = __hci_cmd_sync(hdev, 0xfc5a, 0, NULL, HCI_INIT_TIMEOUT); | ||
159 | if (IS_ERR(skb)) { | ||
160 | BT_ERR("%s: BCM: Read USB product info failed (%ld)", | ||
161 | hdev->name, PTR_ERR(skb)); | ||
162 | return skb; | ||
163 | } | ||
164 | |||
165 | if (skb->len != 5) { | ||
166 | BT_ERR("%s: BCM: USB product length mismatch", hdev->name); | ||
167 | kfree_skb(skb); | ||
168 | return ERR_PTR(-EIO); | ||
169 | } | ||
170 | |||
171 | return skb; | ||
172 | } | ||
173 | |||
174 | static const struct { | ||
175 | u16 subver; | ||
176 | const char *name; | ||
177 | } bcm_subver_table[] = { | ||
178 | { 0x210b, "BCM43142A0" }, /* 001.001.011 */ | ||
179 | { 0x2112, "BCM4314A0" }, /* 001.001.018 */ | ||
180 | { 0x2118, "BCM20702A0" }, /* 001.001.024 */ | ||
181 | { 0x2126, "BCM4335A0" }, /* 001.001.038 */ | ||
182 | { 0x220e, "BCM20702A1" }, /* 001.002.014 */ | ||
183 | { 0x230f, "BCM4354A2" }, /* 001.003.015 */ | ||
184 | { 0x4106, "BCM4335B0" }, /* 002.001.006 */ | ||
185 | { 0x410e, "BCM20702B0" }, /* 002.001.014 */ | ||
186 | { 0x6109, "BCM4335C0" }, /* 003.001.009 */ | ||
187 | { 0x610c, "BCM4354" }, /* 003.001.012 */ | ||
188 | { } | ||
189 | }; | ||
190 | |||
191 | int btbcm_setup_patchram(struct hci_dev *hdev) | ||
192 | { | ||
193 | const struct hci_command_hdr *cmd; | ||
194 | const struct firmware *fw; | ||
195 | const u8 *fw_ptr; | ||
196 | size_t fw_size; | ||
197 | char fw_name[64]; | ||
198 | u16 opcode, subver, rev, pid, vid; | ||
199 | const char *hw_name = NULL; | ||
200 | struct sk_buff *skb; | ||
201 | struct hci_rp_read_local_version *ver; | ||
202 | int i, err; | ||
203 | |||
204 | /* Reset */ | ||
205 | err = btbcm_reset(hdev); | ||
206 | if (err) | ||
207 | return err; | ||
208 | |||
209 | /* Read Local Version Info */ | ||
210 | skb = btbcm_read_local_version(hdev); | ||
211 | if (IS_ERR(skb)) | ||
212 | return PTR_ERR(skb); | ||
213 | |||
214 | ver = (struct hci_rp_read_local_version *)skb->data; | ||
215 | rev = le16_to_cpu(ver->hci_rev); | ||
216 | subver = le16_to_cpu(ver->lmp_subver); | ||
217 | kfree_skb(skb); | ||
218 | |||
219 | /* Read Verbose Config Version Info */ | ||
220 | skb = btbcm_read_verbose_config(hdev); | ||
221 | if (IS_ERR(skb)) | ||
222 | return PTR_ERR(skb); | ||
223 | |||
224 | BT_INFO("%s: BCM: chip id %u", hdev->name, skb->data[1]); | ||
225 | kfree_skb(skb); | ||
226 | |||
227 | /* Read USB Product Info */ | ||
228 | skb = btbcm_read_usb_product(hdev); | ||
229 | if (IS_ERR(skb)) | ||
230 | return PTR_ERR(skb); | ||
231 | |||
232 | vid = get_unaligned_le16(skb->data + 1); | ||
233 | pid = get_unaligned_le16(skb->data + 3); | ||
234 | kfree_skb(skb); | ||
235 | |||
236 | for (i = 0; bcm_subver_table[i].name; i++) { | ||
237 | if (subver == bcm_subver_table[i].subver) { | ||
238 | hw_name = bcm_subver_table[i].name; | ||
239 | break; | ||
240 | } | ||
241 | } | ||
242 | |||
243 | BT_INFO("%s: %s (%3.3u.%3.3u.%3.3u) build %4.4u", hdev->name, | ||
244 | hw_name ? : "BCM", (subver & 0x7000) >> 13, | ||
245 | (subver & 0x1f00) >> 8, (subver & 0x00ff), rev & 0x0fff); | ||
246 | |||
247 | snprintf(fw_name, sizeof(fw_name), "brcm/%s-%4.4x-%4.4x.hcd", | ||
248 | hw_name ? : "BCM", vid, pid); | ||
249 | |||
250 | err = request_firmware(&fw, fw_name, &hdev->dev); | ||
251 | if (err < 0) { | ||
252 | BT_INFO("%s: BCM: patch %s not found", hdev->name, fw_name); | ||
253 | return 0; | ||
254 | } | ||
255 | |||
256 | /* Start Download */ | ||
257 | skb = __hci_cmd_sync(hdev, 0xfc2e, 0, NULL, HCI_INIT_TIMEOUT); | ||
258 | if (IS_ERR(skb)) { | ||
259 | err = PTR_ERR(skb); | ||
260 | BT_ERR("%s: BCM: Download Minidrv command failed (%d)", | ||
261 | hdev->name, err); | ||
262 | goto reset; | ||
263 | } | ||
264 | kfree_skb(skb); | ||
265 | |||
266 | /* 50 msec delay after Download Minidrv completes */ | ||
267 | msleep(50); | ||
268 | |||
269 | fw_ptr = fw->data; | ||
270 | fw_size = fw->size; | ||
271 | |||
272 | while (fw_size >= sizeof(*cmd)) { | ||
273 | const u8 *cmd_param; | ||
274 | |||
275 | cmd = (struct hci_command_hdr *)fw_ptr; | ||
276 | fw_ptr += sizeof(*cmd); | ||
277 | fw_size -= sizeof(*cmd); | ||
278 | |||
279 | if (fw_size < cmd->plen) { | ||
280 | BT_ERR("%s: BCM: patch %s is corrupted", hdev->name, | ||
281 | fw_name); | ||
282 | err = -EINVAL; | ||
283 | goto reset; | ||
284 | } | ||
285 | |||
286 | cmd_param = fw_ptr; | ||
287 | fw_ptr += cmd->plen; | ||
288 | fw_size -= cmd->plen; | ||
289 | |||
290 | opcode = le16_to_cpu(cmd->opcode); | ||
291 | |||
292 | skb = __hci_cmd_sync(hdev, opcode, cmd->plen, cmd_param, | ||
293 | HCI_INIT_TIMEOUT); | ||
294 | if (IS_ERR(skb)) { | ||
295 | err = PTR_ERR(skb); | ||
296 | BT_ERR("%s: BCM: patch command %04x failed (%d)", | ||
297 | hdev->name, opcode, err); | ||
298 | goto reset; | ||
299 | } | ||
300 | kfree_skb(skb); | ||
301 | } | ||
302 | |||
303 | /* 250 msec delay after Launch Ram completes */ | ||
304 | msleep(250); | ||
305 | |||
306 | reset: | ||
307 | /* Reset */ | ||
308 | err = btbcm_reset(hdev); | ||
309 | if (err) | ||
310 | goto done; | ||
311 | |||
312 | /* Read Local Version Info */ | ||
313 | skb = btbcm_read_local_version(hdev); | ||
314 | if (IS_ERR(skb)) { | ||
315 | err = PTR_ERR(skb); | ||
316 | goto done; | ||
317 | } | ||
318 | |||
319 | ver = (struct hci_rp_read_local_version *)skb->data; | ||
320 | rev = le16_to_cpu(ver->hci_rev); | ||
321 | subver = le16_to_cpu(ver->lmp_subver); | ||
322 | kfree_skb(skb); | ||
323 | |||
324 | BT_INFO("%s: %s (%3.3u.%3.3u.%3.3u) build %4.4u", hdev->name, | ||
325 | hw_name ? : "BCM", (subver & 0x7000) >> 13, | ||
326 | (subver & 0x1f00) >> 8, (subver & 0x00ff), rev & 0x0fff); | ||
327 | |||
328 | btbcm_check_bdaddr(hdev); | ||
329 | |||
330 | done: | ||
331 | release_firmware(fw); | ||
332 | |||
333 | return err; | ||
334 | } | ||
335 | EXPORT_SYMBOL_GPL(btbcm_setup_patchram); | ||
336 | |||
337 | int btbcm_setup_apple(struct hci_dev *hdev) | ||
338 | { | ||
339 | struct sk_buff *skb; | ||
340 | |||
341 | /* Read Verbose Config Version Info */ | ||
342 | skb = btbcm_read_verbose_config(hdev); | ||
343 | if (IS_ERR(skb)) | ||
344 | return PTR_ERR(skb); | ||
345 | |||
346 | BT_INFO("%s: BCM: chip id %u build %4.4u", hdev->name, skb->data[1], | ||
347 | get_unaligned_le16(skb->data + 5)); | ||
348 | kfree_skb(skb); | ||
349 | |||
350 | return 0; | ||
351 | } | ||
352 | EXPORT_SYMBOL_GPL(btbcm_setup_apple); | ||
353 | |||
96 | MODULE_AUTHOR("Marcel Holtmann <marcel@holtmann.org>"); | 354 | MODULE_AUTHOR("Marcel Holtmann <marcel@holtmann.org>"); |
97 | MODULE_DESCRIPTION("Bluetooth support for Broadcom devices ver " VERSION); | 355 | MODULE_DESCRIPTION("Bluetooth support for Broadcom devices ver " VERSION); |
98 | MODULE_VERSION(VERSION); | 356 | MODULE_VERSION(VERSION); |
diff --git a/drivers/bluetooth/btbcm.h b/drivers/bluetooth/btbcm.h index 813f7e7e191d..34268ae3eb46 100644 --- a/drivers/bluetooth/btbcm.h +++ b/drivers/bluetooth/btbcm.h | |||
@@ -26,6 +26,9 @@ | |||
26 | int btbcm_check_bdaddr(struct hci_dev *hdev); | 26 | int btbcm_check_bdaddr(struct hci_dev *hdev); |
27 | int btbcm_set_bdaddr(struct hci_dev *hdev, const bdaddr_t *bdaddr); | 27 | int btbcm_set_bdaddr(struct hci_dev *hdev, const bdaddr_t *bdaddr); |
28 | 28 | ||
29 | int btbcm_setup_patchram(struct hci_dev *hdev); | ||
30 | int btbcm_setup_apple(struct hci_dev *hdev); | ||
31 | |||
29 | #else | 32 | #else |
30 | 33 | ||
31 | static inline int btbcm_check_bdaddr(struct hci_dev *hdev) | 34 | static inline int btbcm_check_bdaddr(struct hci_dev *hdev) |
@@ -38,4 +41,14 @@ static inline int btbcm_set_bdaddr(struct hci_dev *hdev, const bdaddr_t *bdaddr) | |||
38 | return -EOPNOTSUPP; | 41 | return -EOPNOTSUPP; |
39 | } | 42 | } |
40 | 43 | ||
44 | static inline int btbcm_setup_patchram(struct hci_dev *hdev) | ||
45 | { | ||
46 | return 0; | ||
47 | } | ||
48 | |||
49 | static inline int btbcm_setup_apple(struct hci_dev *hdev) | ||
50 | { | ||
51 | return 0; | ||
52 | } | ||
53 | |||
41 | #endif | 54 | #endif |