1 /*
2 * Copyright (c) 2024 Cypress Semiconductor Corporation (an Infineon company) or
3 * an affiliate of Cypress Semiconductor Corporation
4 *
5 * SPDX-License-Identifier: Apache-2.0
6 */
7
8 /**
9 * @brief Zephyr CYW20829 driver.
10 *
11 * This driver uses btstack-integration asset as hosts platform adaptation layer
12 * (porting layer) for CYW20829. btstack-integration layer implements/
13 * invokes the interfaces defined by BTSTACK to enable communication
14 * with the BT controller by using IPC_BTSS (IPC Bluetooth sub-system interface).
15 * Zephyr CYW20829 driver implements wiced_bt_**** functions requreds for
16 * btstack-integration asset and Zephyr Bluetooth driver interface
17 * (defined in struct bt_hci_driver).
18 *
19 * CM33 (application core)
20 * |=========================================|
21 * | |-------------------------| |
22 * | | Zephyr application | |
23 * | |-------------------------| |
24 * | | |
25 * | |------------| |
26 * | | Zephyr | |
27 * | | Bluetooth | |
28 * CM33 (BTSS core) | | Host | |
29 * |=====================| | |------------| |
30 * | | | | |
31 * | |---------------| | | |--------------| | -----------| |
32 * | | Bluetooth | | IPC_BTSS | | btstack- | | Zephyr | |
33 * | | Controller FW | | <--------|-> | integration | ---- | CYW20829 | |
34 * | |---------------| | | | asset | | driver | |
35 * | | | |--------------| |------------| |
36 * |=====================| | |
37 * | |=========================================|
38 * |====================|
39 * | CYW20829 |
40 * | Bluetooth |
41 * |====================|
42 *
43 * NOTE:
44 * cyw920829 requires fetch binary files of Bluetooth controller firmware.
45 * To fetch Binary Blobs: west blobs fetch hal_infineon
46 *
47 */
48
49 #include <errno.h>
50 #include <stddef.h>
51 #include <string.h>
52
53 #include <zephyr/arch/cpu.h>
54 #include <zephyr/bluetooth/bluetooth.h>
55 #include <zephyr/bluetooth/hci.h>
56 #include <zephyr/drivers/bluetooth.h>
57 #include <zephyr/drivers/uart.h>
58 #include <zephyr/init.h>
59 #include <zephyr/sys/byteorder.h>
60 #include <zephyr/sys/util.h>
61 #include <zephyr/logging/log.h>
62
63 #include <wiced_bt_stack_platform.h>
64 #include <cybt_platform_config.h>
65 #include <cybt_platform_trace.h>
66 #include <cybt_platform_hci.h>
67 #include <cybt_platform_task.h>
68
69 #include <cyabs_rtos.h>
70 #include <cybt_result.h>
71
72 #define LOG_LEVEL CONFIG_BT_HCI_DRIVER_LOG_LEVEL
73 #include <zephyr/logging/log.h>
74 LOG_MODULE_REGISTER(cyw208xx);
75
76 #define DT_DRV_COMPAT infineon_cyw208xx_hci
77
78 struct cyw208xx_data {
79 bt_hci_recv_t recv;
80 };
81
82 enum {
83 BT_HCI_VND_OP_DOWNLOAD_MINIDRIVER = 0xFC2E,
84 BT_HCI_VND_OP_WRITE_RAM = 0xFC4C,
85 BT_HCI_VND_OP_LAUNCH_RAM = 0xFC4E,
86 BT_HCI_VND_OP_UPDATE_BAUDRATE = 0xFC18,
87 BT_HCI_VND_OP_SET_LOCAL_DEV_ADDR = 0xFC01,
88 };
89
90 /* Externs for CY43xxx controller FW */
91 extern const uint8_t brcm_patchram_buf[];
92 extern const int brcm_patch_ram_length;
93
94 #define CYBSP_BT_PLATFORM_CFG_SLEEP_MODE_LP_ENABLED (0)
95 #define BTM_SET_LOCAL_DEV_ADDR_LENGTH 6
96
97 static K_SEM_DEFINE(hci_sem, 1, 1);
98 static K_SEM_DEFINE(cybt_platform_task_init_sem, 0, 1);
99
100
101 /******************************************************************************
102 * Function Declarations
103 ******************************************************************************/
104 extern void host_stack_platform_interface_init(void);
105 extern void cybt_platform_hci_wait_for_boot_fully_up(bool is_from_isr);
106 extern uint8_t *host_stack_get_acl_to_lower_buffer(wiced_bt_transport_t transport, uint32_t size);
107 extern wiced_result_t host_stack_send_acl_to_lower(wiced_bt_transport_t transport,
108 uint8_t *data, uint16_t len);
109 extern wiced_result_t host_stack_send_cmd_to_lower(uint8_t *cmd, uint16_t cmd_len);
110 extern wiced_result_t host_stack_send_iso_to_lower(uint8_t *data, uint16_t len);
111 extern cybt_result_t cybt_platform_msg_to_bt_task(const uint16_t msg, bool is_from_isr);
112 extern void cybt_bttask_deinit(void);
113
cyw208xx_bt_firmware_download(const uint8_t * firmware_image,uint32_t size)114 static int cyw208xx_bt_firmware_download(const uint8_t *firmware_image, uint32_t size)
115 {
116 uint8_t *data = (uint8_t *)firmware_image;
117 volatile uint32_t remaining_length = size;
118 struct net_buf *buf;
119 int err;
120
121 LOG_DBG("Executing Fw downloading for CYW208xx device");
122
123 /* The firmware image (.hcd format) contains a collection of hci_write_ram
124 * command + a block of the image, followed by a hci_write_ram image at the end.
125 * Parse and send each individual command and wait for the response. This is to
126 * ensure the integrity of the firmware image sent to the bluetooth chip.
127 */
128 while (remaining_length) {
129 size_t data_length = data[2]; /* data length from firmware image block */
130 uint16_t op_code = *(uint16_t *)data;
131
132 /* Allocate buffer for hci_write_ram/hci_launch_ram command. */
133 buf = bt_hci_cmd_create(op_code, data_length);
134 if (buf == NULL) {
135 LOG_ERR("Unable to allocate command buffer");
136 return err;
137 }
138
139 /* Add data part of packet */
140 net_buf_add_mem(buf, &data[3], data_length);
141
142 /* Send hci_write_ram command. */
143 err = bt_hci_cmd_send_sync(op_code, buf, NULL);
144 if (err) {
145 return err;
146 }
147
148 switch (op_code) {
149 case BT_HCI_VND_OP_WRITE_RAM:
150 /* Update remaining length and data pointer:
151 * content of data length + 2 bytes of opcode and 1 byte of data length.
152 */
153 data += data_length + 3;
154 remaining_length -= data_length + 3;
155 break;
156
157 case BT_HCI_VND_OP_LAUNCH_RAM:
158 remaining_length = 0;
159 break;
160
161 default:
162 return -ENOMEM;
163 }
164 }
165
166 LOG_DBG("Fw downloading complete");
167 return 0;
168 }
169
cyw208xx_setup(const struct device * dev,const struct bt_hci_setup_params * params)170 static int cyw208xx_setup(const struct device *dev, const struct bt_hci_setup_params *params)
171 {
172 ARG_UNUSED(dev);
173
174 int err;
175 struct net_buf *buf;
176
177 /* Send HCI_RESET */
178 err = bt_hci_cmd_send_sync(BT_HCI_OP_RESET, NULL, NULL);
179 if (err) {
180 return err;
181 }
182
183 /* BT firmware download */
184 err = cyw208xx_bt_firmware_download(brcm_patchram_buf, (uint32_t)brcm_patch_ram_length);
185 if (err) {
186 return err;
187 }
188
189 /* Waiting when BLE up after firmware launch */
190 cybt_platform_hci_wait_for_boot_fully_up(false);
191
192 /* Set public address */
193 buf = bt_hci_cmd_create(BT_HCI_VND_OP_SET_LOCAL_DEV_ADDR, BTM_SET_LOCAL_DEV_ADDR_LENGTH);
194 if (buf == NULL) {
195 LOG_ERR("Unable to allocate command buffer");
196 return -ENOMEM;
197 }
198
199 bt_addr_t *data = net_buf_add(buf, BTM_SET_LOCAL_DEV_ADDR_LENGTH);
200
201 bt_addr_copy(data, &(params->public_addr));
202
203 /* NOTE: By default, the CYW208xx controller sets some hard-coded static address.
204 * To avoid address duplication, let's always override the default address by using
205 * the HCI command BT_HCI_VND_OP_SET_LOCAL_DEV_ADDR. So
206 *
207 * 1. when cyw208xx_setup gets BT_ADDR_ANY from the host, it will overwrite the
208 * default address, and the host will switch to using a random address (set in
209 * the hci_init function).
210 *
211 * 2. If user set the static address (by using bt_id_create) before bt_enable,
212 * cyw208xx_setup will set user defined static address.
213 */
214
215 err = bt_hci_cmd_send_sync(BT_HCI_VND_OP_SET_LOCAL_DEV_ADDR, buf, NULL);
216 if (err) {
217 LOG_ERR("Failed to set public address (%d)", err);
218 return err;
219 }
220
221 return 0;
222 }
223
cyw208xx_open(const struct device * dev,bt_hci_recv_t recv)224 static int cyw208xx_open(const struct device *dev, bt_hci_recv_t recv)
225 {
226 int err;
227 struct cyw208xx_data *hci = dev->data;
228
229 hci->recv = recv;
230
231 /* Initialize Bluetooth platform related OS tasks. */
232 err = cybt_platform_task_init((void *)NULL);
233 if (err) {
234 return err;
235 }
236
237 /* Wait until cybt platform task starts */
238 k_sem_take(&cybt_platform_task_init_sem, K_FOREVER);
239
240 return 0;
241 }
242
cyw208xx_close(const struct device * dev)243 static int cyw208xx_close(const struct device *dev)
244 {
245 struct cyw208xx_data *hci = dev->data;
246
247 /* Send SHUTDOWN event, BT task will release resources and tervinate task */
248 cybt_platform_msg_to_bt_task(BT_EVT_TASK_SHUTDOWN, false);
249
250 cybt_bttask_deinit();
251
252 k_sem_reset(&cybt_platform_task_init_sem);
253 hci->recv = NULL;
254
255 return 0;
256 }
257
cyw208xx_send(const struct device * dev,struct net_buf * buf)258 static int cyw208xx_send(const struct device *dev, struct net_buf *buf)
259 {
260 ARG_UNUSED(dev);
261
262 int ret = 0;
263
264 k_sem_take(&hci_sem, K_FOREVER);
265
266 LOG_DBG("buf %p type %u len %u", buf, bt_buf_get_type(buf), buf->len);
267
268 switch (bt_buf_get_type(buf)) {
269 case BT_BUF_ACL_OUT:
270 uint8_t *bt_msg = host_stack_get_acl_to_lower_buffer(BT_TRANSPORT_LE, buf->len);
271
272 memcpy(bt_msg, buf->data, buf->len);
273 ret = host_stack_send_acl_to_lower(BT_TRANSPORT_LE, bt_msg, buf->len);
274 break;
275
276 case BT_BUF_CMD:
277 ret = host_stack_send_cmd_to_lower(buf->data, buf->len);
278 break;
279
280 case BT_BUF_ISO_OUT:
281 ret = host_stack_send_iso_to_lower(buf->data, buf->len);
282 break;
283
284 default:
285 LOG_ERR("Unknown type %u", bt_buf_get_type(buf));
286 ret = EIO;
287 goto done;
288 }
289
290 LOG_HEXDUMP_DBG(buf->data, buf->len, "Final HCI buffer:");
291
292 if (ret) {
293 LOG_ERR("SPI write error %d", ret);
294 }
295
296 done:
297 k_sem_give(&hci_sem);
298 net_buf_unref(buf);
299 return ret ? -EIO : 0;
300 }
301
302 static DEVICE_API(bt_hci, drv) = {
303 .open = cyw208xx_open,
304 .close = cyw208xx_close,
305 .send = cyw208xx_send,
306 .setup = cyw208xx_setup
307 };
308
cyw208xx_hci_init(const struct device * dev)309 static int cyw208xx_hci_init(const struct device *dev)
310 {
311 ARG_UNUSED(dev);
312
313 const cybt_platform_config_t cybsp_bt_platform_cfg = {
314 .hci_config = {
315 .hci_transport = CYBT_HCI_IPC,
316 },
317
318 .controller_config = {
319 .sleep_mode = {
320 .sleep_mode_enabled = CYBSP_BT_PLATFORM_CFG_SLEEP_MODE_LP_ENABLED,
321 },
322 }
323 };
324
325 /* Configure platform specific settings for the BT device */
326 cybt_platform_config_init(&cybsp_bt_platform_cfg);
327
328 return 0;
329 }
330
331 /* Implements wiced_bt_**** functions requreds for the btstack-integration asset */
332
wiced_bt_dev_vendor_specific_command(uint16_t opcode,uint8_t param_len,uint8_t * param_buf,wiced_bt_dev_vendor_specific_command_complete_cback_t cback)333 wiced_result_t wiced_bt_dev_vendor_specific_command(uint16_t opcode, uint8_t param_len,
334 uint8_t *param_buf, wiced_bt_dev_vendor_specific_command_complete_cback_t cback)
335
336 {
337 /*
338 * This function is using only by btstack-integration asset
339 * for enable LPM.
340 */
341 struct net_buf *buf = NULL;
342
343 /* Allocate a HCI command buffer */
344 buf = bt_hci_cmd_create(opcode, param_len);
345 if (!buf) {
346 LOG_ERR("Unable to allocate buffer");
347 return WICED_NO_MEMORY;
348 }
349
350 /* Add data part of packet */
351 net_buf_add_mem(buf, param_buf, param_len);
352 bt_hci_cmd_send(opcode, buf);
353
354 return WICED_BT_SUCCESS;
355 }
356
wiced_bt_process_hci(hci_packet_type_t pti,uint8_t * data,uint32_t length)357 void wiced_bt_process_hci(hci_packet_type_t pti, uint8_t *data, uint32_t length)
358 {
359 const struct device *dev = DEVICE_DT_GET(DT_DRV_INST(0));
360 struct cyw208xx_data *hci = dev->data;
361 struct net_buf *buf = NULL;
362 size_t buf_tailroom = 0;
363
364 switch (pti) {
365 case HCI_PACKET_TYPE_EVENT:
366 buf = bt_buf_get_evt(data[0], 0, K_NO_WAIT);
367 if (!buf) {
368 LOG_ERR("Failed to allocate the buffer for RX: EVENT ");
369 return;
370 }
371 break;
372
373 case HCI_PACKET_TYPE_ACL:
374 buf = bt_buf_get_rx(BT_BUF_ACL_IN, K_NO_WAIT);
375 if (!buf) {
376 LOG_ERR("Failed to allocate the buffer for RX: ACL ");
377 return;
378 }
379 bt_buf_set_type(buf, BT_BUF_ACL_IN);
380 break;
381
382 case HCI_PACKET_TYPE_SCO:
383 /* NA */
384 break;
385
386 case HCI_PACKET_TYPE_ISO:
387 buf = bt_buf_get_rx(BT_BUF_ISO_IN, K_NO_WAIT);
388 if (!buf) {
389 LOG_ERR("Failed to allocate the buffer for RX: ISO ");
390 return;
391 }
392 break;
393
394 default:
395 return;
396
397 }
398
399 buf_tailroom = net_buf_tailroom(buf);
400 if (buf_tailroom < length) {
401 LOG_WRN("Not enough space for rx data");
402 return;
403 }
404 net_buf_add_mem(buf, data, length);
405
406 /* Provide the buffer to the host */
407 hci->recv(dev, buf);
408 }
409
wiced_bt_process_hci_events(uint8_t * data,uint32_t length)410 void wiced_bt_process_hci_events(uint8_t *data, uint32_t length)
411 {
412 wiced_bt_process_hci(HCI_PACKET_TYPE_EVENT, data, length);
413 }
414
wiced_bt_process_acl_data(uint8_t * data,uint32_t length)415 void wiced_bt_process_acl_data(uint8_t *data, uint32_t length)
416 {
417 wiced_bt_process_hci(HCI_PACKET_TYPE_ACL, data, length);
418 }
419
wiced_bt_process_isoc_data(uint8_t * data,uint32_t length)420 void wiced_bt_process_isoc_data(uint8_t *data, uint32_t length)
421 {
422 wiced_bt_process_hci(HCI_PACKET_TYPE_ISO, data, length);
423 }
424
wiced_bt_stack_init_internal(wiced_bt_management_cback_t mgmt_cback,wiced_bt_internal_post_stack_init_cb post_stack_cb,wiced_bt_internal_stack_evt_handler_cb evt_handler_cb)425 void wiced_bt_stack_init_internal(wiced_bt_management_cback_t mgmt_cback,
426 wiced_bt_internal_post_stack_init_cb post_stack_cb,
427 wiced_bt_internal_stack_evt_handler_cb evt_handler_cb)
428 {
429 k_sem_give(&cybt_platform_task_init_sem);
430 }
431
432 /* Keep below empty functions, used in the btstack_integration assets for Wiced BT stack. */
wiced_bt_stack_indicate_lower_tx_complete(void)433 void wiced_bt_stack_indicate_lower_tx_complete(void)
434 {
435 /* NA for Zephyr */
436 }
437
wiced_bt_stack_shutdown(void)438 void wiced_bt_stack_shutdown(void)
439 {
440 /* NA for Zephyr */
441 }
442
wiced_bt_process_timer(void)443 void wiced_bt_process_timer(void)
444 {
445 /* NA for Zephyr */
446 }
447
448 #define CYW208XX_DEVICE_INIT(inst) \
449 static struct cyw208xx_data cyw208xx_data_##inst = { \
450 }; \
451 DEVICE_DT_INST_DEFINE(inst, cyw208xx_hci_init, NULL, &cyw208xx_data_##inst, NULL, \
452 POST_KERNEL, CONFIG_KERNEL_INIT_PRIORITY_DEVICE, &drv)
453
454 /* Only one instance supported */
455 CYW208XX_DEVICE_INIT(0)
456