/* * Copyright (c) 2018 ARM Limited * * Licensed 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 "CordioBLE.h" #include "CordioHCIDriver.h" #include "hci_defs.h" #include "hci_api.h" #include "hci_cmd.h" #include "hci_core.h" #include "bstream.h" #include "wsf_buf.h" #include <stdbool.h> #include "hci_mbed_os_adaptation.h" #include "H4TransportDriver.h" #include "OdinCordioInterface.h" namespace ble { namespace vendor { namespace odin_w2 { class HCIDriver : public cordio::CordioHCIDriver { public: HCIDriver(cordio::CordioHCITransportDriver &transport_driver, PinName shutdown_name, PinName hci_rts_name) : cordio::CordioHCIDriver(transport_driver), shutdown(shutdown_name, 0), hci_rts(hci_rts_name, 0), service_pack_index(0), service_pack_transfered(false) { }; virtual cordio::buf_pool_desc_t get_buffer_pool_description(); virtual void do_initialize(); virtual void do_terminate(); virtual void start_reset_sequence(); virtual void handle_reset_sequence(uint8_t *pMsg); private: void start_service_pack_transfer(void) { service_pack_index = 0; service_pack_transfered = false; send_service_pack_command(); } void send_service_pack_command(void) { uint16_t cmd_len = odin_service_pack[service_pack_index + HCI_CMD_HDR_LEN]; cmd_opcode_ack_expected = (odin_service_pack[service_pack_index + 2] << 8) | odin_service_pack[service_pack_index + 1]; uint8_t *pBuf = hciCmdAlloc(cmd_opcode_ack_expected, cmd_len); if (pBuf) { memcpy(pBuf, odin_service_pack + service_pack_index + 1, cmd_len + HCI_CMD_HDR_LEN); hciCmdSend(pBuf); } else { printf("Error cannot allocate memory for the buffer"); } } void ack_service_pack_command(uint16_t opcode, uint8_t *msg) { /* check if response opcode is same as expected command opcode */ MBED_ASSERT (cmd_opcode_ack_expected == opcode); /* update service pack index */ service_pack_index += (1 + HCI_CMD_HDR_LEN + odin_service_pack[service_pack_index + HCI_CMD_HDR_LEN]); if (service_pack_index < service_pack_size) send_service_pack_command(); else if (opcode == HCID_VS_WRITE_BD_ADDR) { /* send an HCI Reset command to start the sequence */ HciResetCmd(); service_pack_transfered = true; } else { /* send BT device hardware address write command */ send_hci_vs_cmd(HCID_VS_WRITE_BD_ADDR); cmd_opcode_ack_expected = HCID_VS_WRITE_BD_ADDR; } } void hci_read_resolving_list_size(void) { /* if LL Privacy is supported by Controller and included */ if ((hciCoreCb.leSupFeat & HCI_LE_SUP_FEAT_PRIVACY) && (hciLeSupFeatCfg & HCI_LE_SUP_FEAT_PRIVACY)) { /* send next command in sequence */ HciLeReadResolvingListSize(); } else { hciCoreCb.resListSize = 0; /* send next command in sequence */ hci_read_max_data_len(); } } void hci_read_max_data_len(void) { /* if LE Data Packet Length Extensions is supported by Controller and included */ if ((hciCoreCb.leSupFeat & HCI_LE_SUP_FEAT_DATA_LEN_EXT) && (hciLeSupFeatCfg & HCI_LE_SUP_FEAT_DATA_LEN_EXT)) { /* send next command in sequence */ HciLeReadMaxDataLen(); } else { /* send next command in sequence */ HciLeRandCmd(); } } DigitalOut shutdown; // power/shutdown pin for bt device DigitalOut hci_rts; // request to sent pin size_t service_pack_index; // Index of command to be recently sent over hci bool service_pack_transfered; // Flag to notify if service pack is completely transferred or not uint16_t cmd_opcode_ack_expected; // Command against which acknowledgment is expected uint32_t service_pack_size; // size of service pack char *odin_service_pack ; // Service pack needs to be provided by driver vs_cmd_send_t send_hci_vs_cmd ; // callback function to call vendor specific call handler }; } // namespace odin_w2 } // namespace vendor } // namespace ble ble::vendor::cordio::buf_pool_desc_t ble::vendor::odin_w2::HCIDriver::get_buffer_pool_description() { // Use default buffer pool return ble::vendor::cordio::CordioHCIDriver::get_default_buffer_pool_description(); } void ble::vendor::odin_w2::HCIDriver::do_initialize() { cordio_callback_s callback; hci_rts = 1; // Flow Control is OFF shutdown = 0; // BT Power is OFF ThisThread::sleep_for(20); shutdown = 1; // BT Power is ON ThisThread::sleep_for(500); hci_rts = 0; // Flow Control is ON /* ODIN ble driver initialization function */ cbCordio_Btinit(&callback); odin_service_pack = callback.Service_pack; send_hci_vs_cmd = callback.vs_command_callback; service_pack_size = callback.service_pack_size; } void ble::vendor::odin_w2::HCIDriver::do_terminate() { } void ble::vendor::odin_w2::HCIDriver::start_reset_sequence() { /* Update baudrate of ble to speed up setup time */ send_hci_vs_cmd(HCID_VS_UPDATE_UART_BAUD_RATE); } void ble::vendor::odin_w2::HCIDriver::handle_reset_sequence(uint8_t *pMsg) { uint16_t opcode; static uint8_t randCnt; /* if event is a command complete event */ if (*pMsg == HCI_CMD_CMPL_EVT) { /* parse parameters */ pMsg += HCI_EVT_HDR_LEN; pMsg++; /* skip num packets */ BSTREAM_TO_UINT16(opcode, pMsg); pMsg++; /* skip status */ if (opcode == HCID_VS_UPDATE_UART_BAUD_RATE) { update_uart_baud_rate(); start_service_pack_transfer(); return; } if (service_pack_transfered == false) { ack_service_pack_command(opcode, pMsg); return; } /* decode opcode */ switch (opcode) { case HCI_OPCODE_RESET: /* Send (fast and slow) clock configuration command */ send_hci_vs_cmd(HCID_VS_FAST_CLOCK_CONFIG_BTIP); break; case HCID_VS_FAST_CLOCK_CONFIG_BTIP: /* Send deep-sleep behavior control command (setting retransmission, inactivity and rts pulse width for Bt) */ send_hci_vs_cmd(HCID_VS_HCILL_PARS_CFG); break; case HCID_VS_HCILL_PARS_CFG: /* Send sleep mode configuration command */ send_hci_vs_cmd(HCID_VS_SLEEP_PROTOCOLS_CFG); break; case HCID_VS_SLEEP_PROTOCOLS_CFG: /* initialize rand command count */ randCnt = 0; /* send next command in sequence */ HciSetEventMaskCmd((uint8_t *)hciEventMask); break; case HCI_OPCODE_SET_EVENT_MASK: /* send next command in sequence */ HciLeSetEventMaskCmd((uint8_t *)hciLeEventMask); break; case HCI_OPCODE_LE_SET_EVENT_MASK: /* send next command in sequence */ HciSetEventMaskPage2Cmd((uint8_t *)hciEventMaskPage2); break; case HCI_OPCODE_SET_EVENT_MASK_PAGE2: /* send next command in sequence */ HciReadBdAddrCmd(); break; case HCI_OPCODE_READ_BD_ADDR: /* parse and store event parameters */ BdaCpy(hciCoreCb.bdAddr, pMsg); /* send next command in sequence */ HciLeReadBufSizeCmd(); break; case HCI_OPCODE_LE_READ_BUF_SIZE: /* parse and store event parameters */ BSTREAM_TO_UINT16(hciCoreCb.bufSize, pMsg); BSTREAM_TO_UINT8(hciCoreCb.numBufs, pMsg); /* initialize ACL buffer accounting */ hciCoreCb.availBufs = hciCoreCb.numBufs; /* send next command in sequence */ HciLeReadSupStatesCmd(); break; case HCI_OPCODE_LE_READ_SUP_STATES: /* parse and store event parameters */ memcpy(hciCoreCb.leStates, pMsg, HCI_LE_STATES_LEN); /* send next command in sequence */ HciLeReadWhiteListSizeCmd(); break; case HCI_OPCODE_LE_READ_WHITE_LIST_SIZE: /* parse and store event parameters */ BSTREAM_TO_UINT8(hciCoreCb.whiteListSize, pMsg); /* send next command in sequence */ HciLeReadLocalSupFeatCmd(); break; case HCI_OPCODE_LE_READ_LOCAL_SUP_FEAT: /* parse and store event parameters */ BSTREAM_TO_UINT16(hciCoreCb.leSupFeat, pMsg); /* send next command in sequence */ hci_read_resolving_list_size(); break; case HCI_OPCODE_LE_READ_RES_LIST_SIZE: /* parse and store event parameters */ BSTREAM_TO_UINT8(hciCoreCb.resListSize, pMsg); /* send next command in sequence */ hci_read_max_data_len(); break; case HCI_OPCODE_LE_READ_MAX_DATA_LEN: uint16_t maxTxOctets; uint16_t maxTxTime; BSTREAM_TO_UINT16(maxTxOctets, pMsg); BSTREAM_TO_UINT16(maxTxTime, pMsg); /* use Controller's maximum supported payload octets and packet duration times * for transmission as Host's suggested values for maximum transmission number * of payload octets and maximum packet transmission time for new connections. */ HciLeWriteDefDataLen(maxTxOctets, maxTxTime); break; case HCI_OPCODE_LE_WRITE_DEF_DATA_LEN: if (hciCoreCb.extResetSeq) { /* send first extended command */ (*hciCoreCb.extResetSeq)(pMsg, opcode); } else { /* initialize extended parameters */ hciCoreCb.maxAdvDataLen = 0; hciCoreCb.numSupAdvSets = 0; hciCoreCb.perAdvListSize = 0; /* send next command in sequence */ HciLeRandCmd(); } break; case HCI_OPCODE_LE_READ_MAX_ADV_DATA_LEN: case HCI_OPCODE_LE_READ_NUM_SUP_ADV_SETS: case HCI_OPCODE_LE_READ_PER_ADV_LIST_SIZE: if (hciCoreCb.extResetSeq) { /* send next extended command in sequence */ (*hciCoreCb.extResetSeq)(pMsg, opcode); } break; case HCI_OPCODE_LE_RAND: /* check if need to send second rand command */ if (randCnt < (HCI_RESET_RAND_CNT - 1)) { randCnt++; HciLeRandCmd(); } else { uint8_t addr[6] = { 0 }; memcpy(addr, pMsg, sizeof(addr)); DM_RAND_ADDR_SET(addr, DM_RAND_ADDR_STATIC); // note: will invoke set rand address set_random_static_address(addr); } break; case HCI_OPCODE_LE_SET_RAND_ADDR: /* send next command in sequence */ signal_reset_sequence_done(); break; default: break; } } } ble::vendor::cordio::CordioHCIDriver& ble_cordio_get_hci_driver() { static ble::vendor::cordio::H4TransportDriver transport_driver (/* cbCFG_PIO_PIN_BT_HCI_TX */ PG_14, /* cbCFG_PIO_PIN_BT_HCI_RX */ PC_7, /* cbCFG_PIO_PIN_BT_HCI_CTS */ PG_15, /* cbCFG_PIO_PIN_BT_HCI_RTS */ PG_12, 115200); static ble::vendor::odin_w2::HCIDriver hci_driver ( transport_driver, /* cbCFG_PIO_PIN_BT_ENABLE */ PG_7, /* cbCFG_PIO_PIN_BT_HCI_RTS */ PG_12); return hci_driver; }