WXK
2024-09-18 05e2e954bd127de378a9d1dfbb0ed95d725aad63
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
/*
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you under the Apache License, Version 2.0 (the
 * "License"); you may not use this file except in compliance
 * with the License.  You may obtain a copy of the License at
 *
 *  http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing,
 * software distributed under the License is distributed on an
 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 * KIND, either express or implied.  See the License for the
 * specific language governing permissions and limitations
 * under the License.
 */
 
#include <assert.h>
#include <string.h>
#include <os/mynewt.h>
#include <nimble/hci_common.h>
#include <nimble/transport.h>
#include <nimble/transport/hci_h4.h>
 
#include "am_mcu_apollo.h"
 
#define HCI_CMD_HDR_LEN (3) /*!< \brief Command packet header length */
 
/* Tx power level in dBm. */
typedef enum {
  TX_POWER_LEVEL_MINUS_10P0_dBm = 0x4,
  TX_POWER_LEVEL_MINUS_5P0_dBm = 0x5,
  TX_POWER_LEVEL_0P0_dBm = 0x8,
  TX_POWER_LEVEL_PLUS_3P0_dBm = 0xF,
  TX_POWER_LEVEL_INVALID = 0x10,
} txPowerLevel_t;
 
/* Structure for holding outgoing HCI packets. */
typedef struct {
    uint32_t len;
    uint32_t data[MYNEWT_VAL(BLE_TRANSPORT_APOLLO3_MAX_TX_PACKET) / sizeof(uint32_t)];
} hci_drv_write_t;
 
uint32_t g_read_buf[MYNEWT_VAL(BLE_TRANSPORT_APOLLO3_MAX_RX_PACKET) / sizeof(uint32_t)];
 
/* BLE module handle for Ambiq HAL functions */
void *ble_handle;
 
uint8_t g_ble_mac_address[6] = {0};
 
static struct hci_h4_sm hci_apollo3_h4sm;
 
static void
apollo3_ble_hci_trans_rx_process(void)
{
    uint32_t len;
    int rlen;
    uint8_t *buf = (uint8_t *)g_read_buf;
 
    am_hal_ble_blocking_hci_read(ble_handle, (uint32_t *)buf, &len);
 
    /* NOTE: Ambiq Apollo3 controller does not have local supported lmp features implemented
     * The command will always return 0 so we overwrite the buffer here
     */
    if(buf[4] == 0x03 && buf[5] == 0x10 && len == 15) {
        memset(&buf[11], 0x60, sizeof(uint8_t));
    }
 
    rlen = hci_h4_sm_rx(&hci_apollo3_h4sm, buf, len);
    assert(rlen >= 0);
}
 
/* Interrupt handler that looks for BLECIRQ. This gets set by BLE core when there is something to read */
static void
apollo3_hci_int(void)
{
    uint32_t int_status;
 
    /* Read and clear the interrupt status. */
    int_status = am_hal_ble_int_status(ble_handle, true);
    am_hal_ble_int_clear(ble_handle, int_status);
 
    /* Handle any DMA or Command Complete interrupts. */
    am_hal_ble_int_service(ble_handle, int_status);
 
    /* If this was a BLEIRQ interrupt, attempt to start a read operation. */
    if (int_status & AM_HAL_BLE_INT_BLECIRQ)
    {
        /* Lower WAKE */
        am_hal_ble_wakeup_set(ble_handle, 0);
 
        /* Call read function to pull in data from controller */
        apollo3_ble_hci_trans_rx_process();
    }
    else {
        assert(0);
    }
}
 
/* Boot the radio. */
uint32_t
apollo3_hci_radio_boot(bool is_cold_boot)
{
    uint32_t xtal_retry_cnt = 0;
    uint32_t am_boot_status;
    am_hal_mcuctrl_device_t device_info;
    am_hal_ble_config_t ble_config =
    {
        /* Configure the HCI interface clock for 6 MHz */
        .ui32SpiClkCfg = AM_HAL_BLE_HCI_CLK_DIV8,
 
        /* Set HCI read and write thresholds to 32 bytes each. */
        .ui32ReadThreshold = 32,
        .ui32WriteThreshold = 32,
 
        /* The MCU will supply the clock to the BLE core. */
        .ui32BleClockConfig = AM_HAL_BLE_CORE_MCU_CLK,
 
        /* Note: These settings only apply to Apollo3 A1/A2 silicon, not B0 silicon.
         * Default settings for expected BLE clock drift (measured in PPM).
         */
        .ui32ClockDrift = 0,
        .ui32SleepClockDrift = 50,
 
        /* Default setting - AGC Enabled */
        .bAgcEnabled = true,
 
        /* Default setting - Sleep Algo enabled */
        .bSleepEnabled = true,
 
        /* Apply the default patches when am_hal_ble_boot() is called. */
        .bUseDefaultPatches = true,
    };
 
    /* Configure and enable the BLE interface. */
    am_boot_status = AM_HAL_STATUS_FAIL;
    while (am_boot_status != AM_HAL_STATUS_SUCCESS) {
        am_hal_pwrctrl_low_power_init();
        am_hal_ble_initialize(0, &ble_handle);
        am_hal_ble_power_control(ble_handle, AM_HAL_BLE_POWER_ACTIVE);
 
        am_hal_ble_config(ble_handle, &ble_config);
 
        /*
         * Delay 1s for 32768Hz clock stability. This isn't required unless this is
         * our first run immediately after a power-up.
         */
        if (is_cold_boot) {
            os_time_delay(OS_TICKS_PER_SEC);
        }
 
        /* Attempt to boot the radio. */
        am_boot_status = am_hal_ble_boot(ble_handle);
 
        /* Check our status, exit if radio is running */
        if (am_boot_status == AM_HAL_STATUS_SUCCESS) {
            break;
        }
        else if (am_boot_status == AM_HAL_BLE_32K_CLOCK_UNSTABLE) {
            /* If the radio is running, but the clock looks bad, we can try to restart. */
            am_hal_ble_power_control(ble_handle, AM_HAL_BLE_POWER_OFF);
            am_hal_ble_deinitialize(ble_handle);
 
            /* We won't restart forever. After we hit the maximum number of retries, we'll just return with failure. */
            if (xtal_retry_cnt++ < MYNEWT_VAL(BLE_TRANSPORT_APOLLO3_MAX_XTAL_RETRIES)) {
                os_time_delay(OS_TICKS_PER_SEC);
            }
            else {
                return SYS_EUNKNOWN;
            }
        }
        else {
            am_hal_ble_power_control(ble_handle, AM_HAL_BLE_POWER_OFF);
            am_hal_ble_deinitialize(ble_handle);
            /*
             * If the radio failed for some reason other than 32K Clock
             * instability, we should just report the failure and return.
             */
            return SYS_EUNKNOWN;
        }
    }
 
    /* Set the BLE TX Output power to 0dBm. */
    am_hal_ble_tx_power_set(ble_handle, TX_POWER_LEVEL_0P0_dBm);
 
    /* Enable interrupts for the BLE module. */
    am_hal_ble_int_clear(ble_handle, (AM_HAL_BLE_INT_CMDCMP |
                               AM_HAL_BLE_INT_DCMP |
                               AM_HAL_BLE_INT_BLECIRQ));
 
    am_hal_ble_int_enable(ble_handle, (AM_HAL_BLE_INT_CMDCMP |
                                AM_HAL_BLE_INT_DCMP |
                                AM_HAL_BLE_INT_BLECIRQ));
 
    /* When it's is_cold_boot, it will use Apollo's Device ID to form Bluetooth address. */
    if (is_cold_boot) {
        am_hal_mcuctrl_info_get(AM_HAL_MCUCTRL_INFO_DEVICEID, &device_info);
 
        /* Bluetooth address formed by ChipID1 (32 bits) and ChipID0 (8-23 bits). */
        memcpy(g_ble_mac_address, &device_info.ui32ChipID1, sizeof(device_info.ui32ChipID1));
 
        /* ui32ChipID0 bit 8-31 is test time during chip manufacturing */
        g_ble_mac_address[4] = (device_info.ui32ChipID0 >> 8) & 0xFF;
        g_ble_mac_address[5] = (device_info.ui32ChipID0 >> 16) & 0xFF;
    }
 
    NVIC_EnableIRQ(BLE_IRQn);
 
    return 0;
}
 
/* Wake update helper function */
static void
apollo3_update_wake(void)
{
    AM_CRITICAL_BEGIN;
 
    /* Set WAKE if there's something in the write queue, but not if SPISTATUS or IRQ is high. */
    if ((BLEIFn(0)->BSTATUS_b.SPISTATUS == 0) && (BLEIF->BSTATUS_b.BLEIRQ == false)) {
        am_hal_ble_wakeup_set(ble_handle, 1);
 
        /* If we've set wakeup, but IRQ came up at the same time, we should just lower WAKE again. */
        if (BLEIF->BSTATUS_b.BLEIRQ == true) {
            am_hal_ble_wakeup_set(ble_handle, 0);
        }
    }
 
    AM_CRITICAL_END;
}
 
/*
 * Function used by the BLE stack to send HCI messages to the BLE controller.
 * The payload is placed into a queue and the controller is turned on. When it is ready
 * an interrupt will fire to handle sending a message
 */
static uint8_t
apollo3_hci_write(uint8_t type, uint16_t len, uint8_t *data)
{
    uint8_t *write_ptr;
    hci_drv_write_t write_buf;
 
    /* comparison compensates for the type byte at index 0. */
    if (len > (MYNEWT_VAL(BLE_TRANSPORT_APOLLO3_MAX_TX_PACKET)-1)) {
        return 0;
    }
 
    /* Set all of the fields in the hci write structure. */
    write_buf.len = len + 1;
 
    write_ptr = (uint8_t *) write_buf.data;
 
    *write_ptr++ = type;
 
    for (uint32_t i = 0; i < len; i++) {
        write_ptr[i] = data[i];
    }
 
    /* Wake up the BLE controller. */
    apollo3_update_wake();
 
    /* Wait on SPI status before writing */
    while (BLEIFn(0)->BSTATUS_b.SPISTATUS) {
        os_time_delay(1);
    }
 
    am_hal_ble_blocking_hci_write(ble_handle, AM_HAL_BLE_RAW, write_buf.data, write_buf.len);
 
    return 0;
}
 
static int
apollo3_ble_hci_acl_tx(struct os_mbuf *om)
{
    struct os_mbuf *x;
    int rc = 0;
 
    x = om;
    while (x) {
        rc = apollo3_hci_write(HCI_H4_ACL, x->om_len, x->om_data);
        if (rc < 0) {
            break;
        }
        x = SLIST_NEXT(x, om_next);
    }
 
    os_mbuf_free_chain(om);
 
    return (rc < 0) ? BLE_ERR_MEM_CAPACITY : 0;
}
 
static int
apollo3_ble_hci_frame_cb(uint8_t pkt_type, void *data)
{
    int rc;
 
    switch (pkt_type) {
    case HCI_H4_ACL:
        rc = ble_transport_to_hs_acl(data);
        break;
    case HCI_H4_EVT:
        rc = ble_transport_to_hs_evt(data);
        break;
    default:
        assert(0);
        break;
    }
 
    return rc;
}
 
static void
apollo3_ble_hci_init(void)
{
    SYSINIT_ASSERT_ACTIVE();
 
    /* Enable interrupt to handle read based on BLECIRQ */
    NVIC_SetVector(BLE_IRQn, (uint32_t)apollo3_hci_int);
 
    /* Initial coldboot configuration */
    apollo3_hci_radio_boot(1);
}
 
int
ble_transport_to_ll_cmd_impl(void *buf)
{
    int rc;
    uint8_t *cmd = buf;
    int len = HCI_CMD_HDR_LEN + cmd[2];
 
    rc = apollo3_hci_write(HCI_H4_CMD, len, cmd);
 
    ble_transport_free(cmd);
 
    return (rc < 0) ? BLE_ERR_MEM_CAPACITY :  0;
}
 
int
ble_transport_to_ll_acl_impl(struct os_mbuf *om)
{
    return apollo3_ble_hci_acl_tx(om);
}
 
void
ble_transport_ll_init(void)
{
    hci_h4_sm_init(&hci_apollo3_h4sm, &hci_h4_allocs_from_ll,
                   apollo3_ble_hci_frame_cb);
    apollo3_ble_hci_init();
}