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 };
88
89 /* Externs for CY43xxx controller FW */
90 extern const uint8_t brcm_patchram_buf[];
91 extern const int brcm_patch_ram_length;
92
93 #define CYBSP_BT_PLATFORM_CFG_SLEEP_MODE_LP_ENABLED (1)
94
95 static K_SEM_DEFINE(hci_sem, 1, 1);
96 static K_SEM_DEFINE(cybt_platform_task_init_sem, 0, 1);
97
98
99 /******************************************************************************
100 * Function Declarations
101 ******************************************************************************/
102 extern void host_stack_platform_interface_init(void);
103 extern void cybt_platform_hci_wait_for_boot_fully_up(bool is_from_isr);
104 extern uint8_t *host_stack_get_acl_to_lower_buffer(wiced_bt_transport_t transport, uint32_t size);
105 extern wiced_result_t host_stack_send_acl_to_lower(wiced_bt_transport_t transport,
106 uint8_t *data, uint16_t len);
107 extern wiced_result_t host_stack_send_cmd_to_lower(uint8_t *cmd, uint16_t cmd_len);
108 extern wiced_result_t host_stack_send_iso_to_lower(uint8_t *data, uint16_t len);
109 extern cybt_result_t cybt_platform_msg_to_bt_task(const uint16_t msg, bool is_from_isr);
110 extern void cybt_bttask_deinit(void);
111
cyw208xx_bt_firmware_download(const uint8_t * firmware_image,uint32_t size)112 static int cyw208xx_bt_firmware_download(const uint8_t *firmware_image, uint32_t size)
113 {
114 uint8_t *data = (uint8_t *)firmware_image;
115 volatile uint32_t remaining_length = size;
116 struct net_buf *buf;
117 int err;
118
119 LOG_DBG("Executing Fw downloading for CYW208xx device");
120
121 /* The firmware image (.hcd format) contains a collection of hci_write_ram
122 * command + a block of the image, followed by a hci_write_ram image at the end.
123 * Parse and send each individual command and wait for the response. This is to
124 * ensure the integrity of the firmware image sent to the bluetooth chip.
125 */
126 while (remaining_length) {
127 size_t data_length = data[2]; /* data length from firmware image block */
128 uint16_t op_code = *(uint16_t *)data;
129
130 /* Allocate buffer for hci_write_ram/hci_launch_ram command. */
131 buf = bt_hci_cmd_create(op_code, data_length);
132 if (buf == NULL) {
133 LOG_ERR("Unable to allocate command buffer");
134 return err;
135 }
136
137 /* Add data part of packet */
138 net_buf_add_mem(buf, &data[3], data_length);
139
140 /* Send hci_write_ram command. */
141 err = bt_hci_cmd_send_sync(op_code, buf, NULL);
142 if (err) {
143 return err;
144 }
145
146 switch (op_code) {
147 case BT_HCI_VND_OP_WRITE_RAM:
148 /* Update remaining length and data pointer:
149 * content of data length + 2 bytes of opcode and 1 byte of data length.
150 */
151 data += data_length + 3;
152 remaining_length -= data_length + 3;
153 break;
154
155 case BT_HCI_VND_OP_LAUNCH_RAM:
156 remaining_length = 0;
157 break;
158
159 default:
160 return -ENOMEM;
161 }
162 }
163
164 LOG_DBG("Fw downloading complete");
165 return 0;
166 }
167
cyw208xx_setup(const struct device * dev,const struct bt_hci_setup_params * params)168 static int cyw208xx_setup(const struct device *dev, const struct bt_hci_setup_params *params)
169 {
170 ARG_UNUSED(dev);
171 ARG_UNUSED(params);
172 int err;
173
174 /* Send HCI_RESET */
175 err = bt_hci_cmd_send_sync(BT_HCI_OP_RESET, NULL, NULL);
176 if (err) {
177 return err;
178 }
179
180 /* BT firmware download */
181 err = cyw208xx_bt_firmware_download(brcm_patchram_buf, (uint32_t)brcm_patch_ram_length);
182 if (err) {
183 return err;
184 }
185
186 /* Waiting when BLE up after firmware launch */
187 cybt_platform_hci_wait_for_boot_fully_up(false);
188 return 0;
189 }
190
cyw208xx_open(const struct device * dev,bt_hci_recv_t recv)191 static int cyw208xx_open(const struct device *dev, bt_hci_recv_t recv)
192 {
193 int err;
194 struct cyw208xx_data *hci = dev->data;
195
196 hci->recv = recv;
197
198 /* Initialize Bluetooth platform related OS tasks. */
199 err = cybt_platform_task_init((void *)NULL);
200 if (err) {
201 return err;
202 }
203
204 /* Wait until cybt platform task starts */
205 k_sem_take(&cybt_platform_task_init_sem, K_FOREVER);
206
207 return 0;
208 }
209
cyw208xx_close(const struct device * dev)210 static int cyw208xx_close(const struct device *dev)
211 {
212 struct cyw208xx_data *hci = dev->data;
213
214 /* Send SHUTDOWN event, BT task will release resources and tervinate task */
215 cybt_platform_msg_to_bt_task(BT_EVT_TASK_SHUTDOWN, false);
216
217 cybt_bttask_deinit();
218
219 k_sem_reset(&cybt_platform_task_init_sem);
220 hci->recv = NULL;
221
222 return 0;
223 }
224
cyw208xx_send(const struct device * dev,struct net_buf * buf)225 static int cyw208xx_send(const struct device *dev, struct net_buf *buf)
226 {
227 ARG_UNUSED(dev);
228
229 int ret = 0;
230
231 k_sem_take(&hci_sem, K_FOREVER);
232
233 LOG_DBG("buf %p type %u len %u", buf, bt_buf_get_type(buf), buf->len);
234
235 switch (bt_buf_get_type(buf)) {
236 case BT_BUF_ACL_OUT:
237 uint8_t *bt_msg = host_stack_get_acl_to_lower_buffer(BT_TRANSPORT_LE, buf->len);
238
239 memcpy(bt_msg, buf->data, buf->len);
240 ret = host_stack_send_acl_to_lower(BT_TRANSPORT_LE, bt_msg, buf->len);
241 break;
242
243 case BT_BUF_CMD:
244 ret = host_stack_send_cmd_to_lower(buf->data, buf->len);
245 break;
246
247 case BT_BUF_ISO_OUT:
248 ret = host_stack_send_iso_to_lower(buf->data, buf->len);
249 break;
250
251 default:
252 LOG_ERR("Unknown type %u", bt_buf_get_type(buf));
253 ret = EIO;
254 goto done;
255 }
256
257 LOG_HEXDUMP_DBG(buf->data, buf->len, "Final HCI buffer:");
258
259 if (ret) {
260 LOG_ERR("SPI write error %d", ret);
261 }
262
263 done:
264 k_sem_give(&hci_sem);
265 net_buf_unref(buf);
266 return ret ? -EIO : 0;
267 }
268
269 static const struct bt_hci_driver_api drv = {
270 .open = cyw208xx_open,
271 .close = cyw208xx_close,
272 .send = cyw208xx_send,
273 .setup = cyw208xx_setup
274 };
275
cyw208xx_hci_init(const struct device * dev)276 static int cyw208xx_hci_init(const struct device *dev)
277 {
278 ARG_UNUSED(dev);
279
280 const cybt_platform_config_t cybsp_bt_platform_cfg = {
281 .hci_config = {
282 .hci_transport = CYBT_HCI_IPC,
283 },
284
285 .controller_config = {
286 .sleep_mode = {
287 .sleep_mode_enabled = CYBSP_BT_PLATFORM_CFG_SLEEP_MODE_LP_ENABLED,
288 },
289 }
290 };
291
292 /* Configure platform specific settings for the BT device */
293 cybt_platform_config_init(&cybsp_bt_platform_cfg);
294
295 return 0;
296 }
297
298 /* Implements wiced_bt_**** functions requreds for the btstack-integration asset */
299
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)300 wiced_result_t wiced_bt_dev_vendor_specific_command(uint16_t opcode, uint8_t param_len,
301 uint8_t *param_buf, wiced_bt_dev_vendor_specific_command_complete_cback_t cback)
302
303 {
304 /*
305 * This function is using only by btstack-integration asset
306 * for enable LPM.
307 */
308 struct net_buf *buf = NULL;
309
310 /* Allocate a HCI command buffer */
311 buf = bt_hci_cmd_create(opcode, param_len);
312 if (!buf) {
313 LOG_ERR("Unable to allocate buffer");
314 return WICED_NO_MEMORY;
315 }
316
317 /* Add data part of packet */
318 net_buf_add_mem(buf, param_buf, param_len);
319 bt_hci_cmd_send(opcode, buf);
320
321 return WICED_BT_SUCCESS;
322 }
323
wiced_bt_process_hci(hci_packet_type_t pti,uint8_t * data,uint32_t length)324 void wiced_bt_process_hci(hci_packet_type_t pti, uint8_t *data, uint32_t length)
325 {
326 const struct device *dev = DEVICE_DT_GET(DT_DRV_INST(0));
327 struct cyw208xx_data *hci = dev->data;
328 struct net_buf *buf = NULL;
329 size_t buf_tailroom = 0;
330
331 switch (pti) {
332 case HCI_PACKET_TYPE_EVENT:
333 buf = bt_buf_get_evt(data[0], 0, K_NO_WAIT);
334 if (!buf) {
335 LOG_ERR("Failed to allocate the buffer for RX: EVENT ");
336 return;
337 }
338 break;
339
340 case HCI_PACKET_TYPE_ACL:
341 buf = bt_buf_get_rx(BT_BUF_ACL_IN, K_NO_WAIT);
342 if (!buf) {
343 LOG_ERR("Failed to allocate the buffer for RX: ACL ");
344 return;
345 }
346 bt_buf_set_type(buf, BT_BUF_ACL_IN);
347 break;
348
349 case HCI_PACKET_TYPE_SCO:
350 /* NA */
351 break;
352
353 case HCI_PACKET_TYPE_ISO:
354 buf = bt_buf_get_rx(BT_BUF_ISO_IN, K_NO_WAIT);
355 if (!buf) {
356 LOG_ERR("Failed to allocate the buffer for RX: ISO ");
357 return;
358 }
359 break;
360
361 default:
362 return;
363
364 }
365
366 buf_tailroom = net_buf_tailroom(buf);
367 if (buf_tailroom < length) {
368 LOG_WRN("Not enough space for rx data");
369 return;
370 }
371 net_buf_add_mem(buf, data, length);
372
373 /* Provide the buffer to the host */
374 hci->recv(dev, buf);
375 }
376
wiced_bt_process_hci_events(uint8_t * data,uint32_t length)377 void wiced_bt_process_hci_events(uint8_t *data, uint32_t length)
378 {
379 wiced_bt_process_hci(HCI_PACKET_TYPE_EVENT, data, length);
380 }
381
wiced_bt_process_acl_data(uint8_t * data,uint32_t length)382 void wiced_bt_process_acl_data(uint8_t *data, uint32_t length)
383 {
384 wiced_bt_process_hci(HCI_PACKET_TYPE_ACL, data, length);
385 }
386
wiced_bt_process_isoc_data(uint8_t * data,uint32_t length)387 void wiced_bt_process_isoc_data(uint8_t *data, uint32_t length)
388 {
389 wiced_bt_process_hci(HCI_PACKET_TYPE_ISO, data, length);
390 }
391
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)392 void wiced_bt_stack_init_internal(wiced_bt_management_cback_t mgmt_cback,
393 wiced_bt_internal_post_stack_init_cb post_stack_cb,
394 wiced_bt_internal_stack_evt_handler_cb evt_handler_cb)
395 {
396 k_sem_give(&cybt_platform_task_init_sem);
397 }
398
399 /* Keep below empty functions, used in the btstack_integration assets for Wiced BT stack. */
wiced_bt_stack_indicate_lower_tx_complete(void)400 void wiced_bt_stack_indicate_lower_tx_complete(void)
401 {
402 /* NA for Zephyr */
403 }
404
wiced_bt_stack_shutdown(void)405 void wiced_bt_stack_shutdown(void)
406 {
407 /* NA for Zephyr */
408 }
409
wiced_bt_process_timer(void)410 void wiced_bt_process_timer(void)
411 {
412 /* NA for Zephyr */
413 }
414
415 #define CYW208XX_DEVICE_INIT(inst) \
416 static struct cyw208xx_data cyw208xx_data_##inst = { \
417 }; \
418 DEVICE_DT_INST_DEFINE(inst, cyw208xx_hci_init, NULL, &cyw208xx_data_##inst, NULL, \
419 POST_KERNEL, CONFIG_KERNEL_INIT_PRIORITY_DEVICE, &drv)
420
421 /* Only one instance supported */
422 CYW208XX_DEVICE_INIT(0)
423