· 6 years ago · Jan 16, 2020, 10:32 PM
1/******************************************************************************
2
3 @file simple_central.c
4
5 @brief This file contains the Simple Central sample application for use
6 with the CC2650 Bluetooth Low Energy Protocol Stack.
7
8 Group: WCS, BTS
9 Target Device: cc2640r2
10
11 ******************************************************************************
12
13 Copyright (c) 2013-2019, Texas Instruments Incorporated
14 All rights reserved.
15
16 Redistribution and use in source and binary forms, with or without
17 modification, are permitted provided that the following conditions
18 are met:
19
20 * Redistributions of source code must retain the above copyright
21 notice, this list of conditions and the following disclaimer.
22
23 * Redistributions in binary form must reproduce the above copyright
24 notice, this list of conditions and the following disclaimer in the
25 documentation and/or other materials provided with the distribution.
26
27 * Neither the name of Texas Instruments Incorporated nor the names of
28 its contributors may be used to endorse or promote products derived
29 from this software without specific prior written permission.
30
31 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
32 AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
33 THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
34 PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
35 CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
36 EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
37 PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
38 OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
39 WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
40 OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
41 EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
42
43 ******************************************************************************
44
45
46 *****************************************************************************/
47
48/*********************************************************************
49 * INCLUDES
50 */
51#include <string.h>
52
53#include <ti/sysbios/knl/Task.h>
54#include <ti/sysbios/knl/Clock.h>
55#include <ti/sysbios/knl/Event.h>
56#include <ti/sysbios/knl/Queue.h>
57
58#include <ti/display/Display.h>
59
60#include "bcomdef.h"
61
62#include <icall.h>
63#include "util.h"
64/* This Header file contains all BLE API and icall structure definition */
65#include "icall_ble_api.h"
66
67#include "board_key.h"
68#include "board.h"
69
70#include "ble_user_config.h"
71
72#include "simple_gatt_profile.h"
73
74#include <menu/two_btn_menu.h>
75#include "simple_central.h"
76#include "simple_central_menu.h"
77
78/*********************************************************************
79 * MACROS
80 */
81
82/*********************************************************************
83 * CONSTANTS
84 */
85
86// Application events
87#define SC_EVT_KEY_CHANGE 0x01
88#define SC_EVT_SCAN_ENABLED 0x02
89#define SC_EVT_SCAN_DISABLED 0x03
90#define SC_EVT_ADV_REPORT 0x04
91#define SC_EVT_SVC_DISC 0x05
92#define SC_EVT_READ_RSSI 0x06
93#define SC_EVT_PAIR_STATE 0x07
94#define SC_EVT_PASSCODE_NEEDED 0x08
95#define SC_EVT_READ_RPA 0x09
96#define SC_EVT_INSUFFICIENT_MEM 0x0A
97
98// Simple Central Task Events
99#define SC_ICALL_EVT ICALL_MSG_EVENT_ID // Event_Id_31
100#define SC_QUEUE_EVT UTIL_QUEUE_EVENT_ID // Event_Id_30
101
102#define SC_ALL_EVENTS (SC_ICALL_EVT | \
103 SC_QUEUE_EVT)
104
105// Address mode of the local device
106// Note: When using the DEFAULT_ADDRESS_MODE as ADDRMODE_RANDOM or
107// ADDRMODE_RP_WITH_RANDOM_ID, GAP_DeviceInit() should be called with
108// it's last parameter set to a static random address
109#define DEFAULT_ADDRESS_MODE ADDRMODE_RP_WITH_PUBLIC_ID
110
111// Default PHY for scanning and initiating
112#define DEFAULT_SCAN_PHY SCAN_PRIM_PHY_1M
113#define DEFAULT_INIT_PHY INIT_PHY_1M
114
115// Default scan duration in 10 ms
116#define DEFAULT_SCAN_DURATION 100 // 1 sec
117
118// Default RSSI polling period in ms
119#define DEFAULT_RSSI_PERIOD 3000
120
121// TRUE to filter discovery results on desired service UUID
122#define DEFAULT_DEV_DISC_BY_SVC_UUID TRUE
123
124// Minimum connection interval (units of 1.25ms) if automatic parameter update
125// request is enabled
126#define DEFAULT_UPDATE_MIN_CONN_INTERVAL 400
127
128// Maximum connection interval (units of 1.25ms) if automatic parameter update
129// request is enabled
130#define DEFAULT_UPDATE_MAX_CONN_INTERVAL 800
131
132// Slave latency to use if automatic parameter update request is enabled
133#define DEFAULT_UPDATE_SLAVE_LATENCY 0
134
135// Supervision timeout value (units of 10ms) if automatic parameter update
136// request is enabled
137#define DEFAULT_UPDATE_CONN_TIMEOUT 600
138
139// Supervision timeout conversion rate to miliseconds
140#define CONN_TIMEOUT_MS_CONVERSION 10
141
142// How often to read current current RPA (in ms)
143#define SC_READ_RPA_PERIOD 3000
144
145// Task configuration
146#define SC_TASK_PRIORITY 1
147
148#ifndef SC_TASK_STACK_SIZE
149#define SC_TASK_STACK_SIZE 1024
150#endif
151
152// Advertising report fields to keep in the list
153// Interested in only peer address type and peer address
154#define SC_ADV_RPT_FIELDS (SCAN_ADVRPT_FLD_ADDRTYPE | SCAN_ADVRPT_FLD_ADDRESS)
155
156// Size of string-converted device address ("0xXXXXXXXXXXXX")
157#define SC_ADDR_STR_SIZE 15
158
159// Row numbers for two-button menu
160#define SC_ROW_SEPARATOR (TBM_ROW_APP + 0)
161#define SC_ROW_CUR_CONN (TBM_ROW_APP + 1)
162#define SC_ROW_ANY_CONN (TBM_ROW_APP + 2)
163#define SC_ROW_NON_CONN (TBM_ROW_APP + 3)
164#define SC_ROW_NUM_CONN (TBM_ROW_APP + 4)
165#define SC_ROW_IDA (TBM_ROW_APP + 5)
166#define SC_ROW_RPA (TBM_ROW_APP + 6)
167
168// Spin if the expression is not true
169#define SIMPLECENTRAL_ASSERT(expr) if (!(expr)) SimpleCentral_spin();
170
171/*********************************************************************
172 * TYPEDEFS
173 */
174
175// Discovery states
176enum
177{
178 BLE_DISC_STATE_IDLE, // Idle
179 BLE_DISC_STATE_MTU, // Exchange ATT MTU size
180 BLE_DISC_STATE_SVC, // Service discovery
181 BLE_DISC_STATE_CHAR // Characteristic discovery
182};
183
184// App event passed from profiles.
185typedef struct
186{
187 appEvtHdr_t hdr; // event header
188 uint8_t *pData; // event data
189} scEvt_t;
190
191// Scanned device information record
192typedef struct
193{
194 uint8_t addrType; // Peer Device's Address Type
195 uint8_t addr[B_ADDR_LEN]; // Peer Device Address
196} scanRec_t;
197
198// Connected device information
199typedef struct
200{
201 uint16_t connHandle; // Connection Handle
202 uint8_t addr[B_ADDR_LEN]; // Peer Device Address
203 uint8_t charHandle; // Characteristic Handle
204 Clock_Struct *pRssiClock; // pointer to clock struct
205} connRec_t;
206
207// Container to store paring state info when passing from gapbondmgr callback
208// to app event. See the pfnPairStateCB_t documentation from the gapbondmgr.h
209// header file for more information on each parameter.
210typedef struct
211{
212 uint16_t connHandle;
213 uint8_t status;
214} scPairStateData_t;
215
216// Container to store passcode data when passing from gapbondmgr callback
217// to app event. See the pfnPasscodeCB_t documentation from the gapbondmgr.h
218// header file for more information on each parameter.
219typedef struct
220{
221 uint8_t deviceAddr[B_ADDR_LEN];
222 uint16_t connHandle;
223 uint8_t uiInputs;
224 uint8_t uiOutputs;
225 uint32_t numComparison;
226} scPasscodeData_t;
227/*********************************************************************
228 * GLOBAL VARIABLES
229 */
230
231// Display Interface
232Display_Handle dispHandle = NULL;
233
234/*********************************************************************
235 * EXTERNAL VARIABLES
236 */
237
238/*********************************************************************
239 * LOCAL VARIABLES
240 */
241
242// Entity ID globally used to check for source and/or destination of messages
243static ICall_EntityID selfEntity;
244
245// Event globally used to post local events and pend on system and
246// local events.
247static ICall_SyncHandle syncEvent;
248
249// Queue object used for app messages
250static Queue_Struct appMsg;
251static Queue_Handle appMsgQueue;
252
253// Task configuration
254Task_Struct scTask;
255#if defined __TI_COMPILER_VERSION__
256#pragma DATA_ALIGN(scTaskStack, 8)
257#else
258#pragma data_alignment=8
259#endif
260uint8_t scTaskStack[SC_TASK_STACK_SIZE];
261
262// GAP GATT Attributes
263static const uint8_t attDeviceName[GAP_DEVICE_NAME_LEN] = "Simple Central";
264
265#if (DEFAULT_DEV_DISC_BY_SVC_UUID == TRUE)
266// Number of scan results filtered by Service UUID
267static uint8_t numScanRes = 0;
268
269// Scan results filtered by Service UUID
270static scanRec_t scanList[DEFAULT_MAX_SCAN_RES];
271#endif // DEFAULT_DEV_DISC_BY_SVC_UUID
272
273// Number of connected devices
274static uint8_t numConn = 0;
275
276// List of connections
277static connRec_t connList[MAX_NUM_BLE_CONNS];
278
279// Connection handle of current connection
280static uint16_t scConnHandle = CONNHANDLE_INVALID;
281
282// Accept or reject L2CAP connection parameter update request
283static bool acceptParamUpdateReq = true;
284
285// Discovery state
286static uint8_t discState = BLE_DISC_STATE_IDLE;
287
288// Discovered service start and end handle
289static uint16_t svcStartHdl = 0;
290static uint16_t svcEndHdl = 0;
291
292// Value to write
293static uint8_t charVal = 0;
294
295// Maximum PDU size (default = 27 octets)
296static uint16_t scMaxPduSize;
297
298// Clock instance for RPA read events.
299static Clock_Struct clkRpaRead;
300
301// Address mode
302static GAP_Addr_Modes_t addrMode = DEFAULT_ADDRESS_MODE;
303
304#if defined(BLE_V42_FEATURES) && (BLE_V42_FEATURES & PRIVACY_1_2_CFG)
305// Current Random Private Address
306static uint8 rpa[B_ADDR_LEN] = {0};
307#endif // PRIVACY_1_2_CFG
308
309/*********************************************************************
310 * LOCAL FUNCTIONS
311 */
312static void SimpleCentral_init(void);
313static void SimpleCentral_taskFxn(uintptr_t a0, uintptr_t a1);
314
315static void SimpleCentral_handleKeys(uint8_t keys);
316static uint8_t SimpleCentral_processStackMsg(ICall_Hdr *pMsg);
317static void SimpleCentral_processGapMsg(gapEventHdr_t *pMsg);
318static void SimpleCentral_processGATTMsg(gattMsgEvent_t *pMsg);
319static void SimpleCentral_processAppMsg(scEvt_t *pMsg);
320static void SimpleCentral_processGATTDiscEvent(gattMsgEvent_t *pMsg);
321static void SimpleCentral_startSvcDiscovery(void);
322#if (DEFAULT_DEV_DISC_BY_SVC_UUID == TRUE)
323static bool SimpleCentral_findSvcUuid(uint16_t uuid, uint8_t *pData,
324 uint16_t dataLen);
325static void SimpleCentral_addScanInfo(uint8_t *pAddr, uint8_t addrType);
326#endif // DEFAULT_DEV_DISC_BY_SVC_UUID
327static uint8_t SimpleCentral_addConnInfo(uint16_t connHandle, uint8_t *pAddr);
328static uint8_t SimpleCentral_removeConnInfo(uint16_t connHandle);
329static uint8_t SimpleCentral_getConnIndex(uint16_t connHandle);
330#ifndef Display_DISABLE_ALL
331static char* SimpleCentral_getConnAddrStr(uint16_t connHandle);
332#endif
333static void SimpleCentral_processPairState(uint8_t state,
334 scPairStateData_t* pPairStateData);
335static void SimpleCentral_processPasscode(scPasscodeData_t *pData);
336
337static void SimpleCentral_processCmdCompleteEvt(hciEvt_CmdComplete_t *pMsg);
338static status_t SimpleCentral_StartRssi();
339static status_t SimpleCentral_CancelRssi(uint16_t connHandle);
340
341static void SimpleCentral_passcodeCb(uint8_t *deviceAddr, uint16_t connHandle,
342 uint8_t uiInputs, uint8_t uiOutputs,
343 uint32_t numComparison);
344static void SimpleCentral_pairStateCb(uint16_t connHandle, uint8_t state,
345 uint8_t status);
346
347static void SimpleCentral_keyChangeHandler(uint8 keys);
348static void SimpleCentral_clockHandler(UArg arg);
349
350static status_t SimpleCentral_enqueueMsg(uint8_t event, uint8_t status,
351 uint8_t *pData);
352
353static void SimpleCentral_scanCb(uint32_t evt, void* msg, uintptr_t arg);
354static void SimpleCentral_menuSwitchCb(tbmMenuObj_t* pMenuObjCurr,
355 tbmMenuObj_t* pMenuObjNext);
356
357/*********************************************************************
358 * EXTERN FUNCTIONS
359 */
360extern void AssertHandler(uint8 assertCause, uint8 assertSubcause);
361
362/*********************************************************************
363 * PROFILE CALLBACKS
364 */
365
366// Bond Manager Callbacks
367static gapBondCBs_t bondMgrCBs =
368{
369 SimpleCentral_passcodeCb, // Passcode callback
370 SimpleCentral_pairStateCb // Pairing/Bonding state Callback
371};
372
373/*********************************************************************
374 * PUBLIC FUNCTIONS
375 */
376
377/*********************************************************************
378 * @fn SimpleCentral_spin
379 *
380 * @brief Spin forever
381 *
382 * @param none
383 */
384static void SimpleCentral_spin(void)
385{
386 volatile uint8_t x;
387
388 while(1)
389 {
390 x++;
391 }
392}
393
394/*********************************************************************
395 * @fn SimpleCentral_createTask
396 *
397 * @brief Task creation function for the Simple Central.
398 *
399 * @param none
400 *
401 * @return none
402 */
403void SimpleCentral_createTask(void)
404{
405 Task_Params taskParams;
406
407 // Configure task
408 Task_Params_init(&taskParams);
409 taskParams.stack = scTaskStack;
410 taskParams.stackSize = SC_TASK_STACK_SIZE;
411 taskParams.priority = SC_TASK_PRIORITY;
412
413 Task_construct(&scTask, SimpleCentral_taskFxn, &taskParams, NULL);
414}
415
416/*********************************************************************
417 * @fn SimpleCentral_Init
418 *
419 * @brief Initialization function for the Simple Central App Task.
420 * This is called during initialization and should contain
421 * any application specific initialization (ie. hardware
422 * initialization/setup, table initialization, power up
423 * notification).
424 *
425 * @param none
426 *
427 * @return none
428 */
429static void SimpleCentral_init(void)
430{
431 uint8_t i;
432
433 // ******************************************************************
434 // N0 STACK API CALLS CAN OCCUR BEFORE THIS CALL TO ICall_registerApp
435 // ******************************************************************
436 // Register the current thread as an ICall dispatcher application
437 // so that the application can send and receive messages.
438 ICall_registerApp(&selfEntity, &syncEvent);
439
440 // Create an RTOS queue for message from profile to be sent to app.
441 appMsgQueue = Util_constructQueue(&appMsg);
442
443 // Initialize internal data
444 for (i = 0; i < MAX_NUM_BLE_CONNS; i++)
445 {
446 connList[i].connHandle = CONNHANDLE_INVALID;
447 connList[i].pRssiClock = NULL;
448 }
449
450 Board_initKeys(SimpleCentral_keyChangeHandler);
451
452 GGS_SetParameter(GGS_DEVICE_NAME_ATT, GAP_DEVICE_NAME_LEN,
453 (void *)attDeviceName);
454
455 //Set default values for Data Length Extension
456 //Extended Data Length Feature is already enabled by default
457 //in build_config.opt in stack project.
458 {
459 //Set initial values to maximum, RX is set to max. by default(251 octets, 2120us)
460 #define APP_SUGGESTED_PDU_SIZE 251 //default is 27 octets(TX)
461 #define APP_SUGGESTED_TX_TIME 2120 //default is 328us(TX)
462
463 //This API is documented in hci.h
464 //See the LE Data Length Extension section in the BLE5-Stack User's Guide for information on using this command:
465 //http://software-dl.ti.com/lprf/ble5stack-latest/
466 //HCI_LE_WriteSuggestedDefaultDataLenCmd(APP_SUGGESTED_PDU_SIZE, APP_SUGGESTED_TX_TIME);
467 }
468
469 // Initialize GATT Client
470 VOID GATT_InitClient();
471
472 // Register to receive incoming ATT Indications/Notifications
473 GATT_RegisterForInd(selfEntity);
474
475 // Initialize GATT attributes
476 GGS_AddService(GATT_ALL_SERVICES); // GAP
477 GATTServApp_AddService(GATT_ALL_SERVICES); // GATT attributes
478
479 // Register for GATT local events and ATT Responses pending for transmission
480 GATT_RegisterForMsgs(selfEntity);
481
482 // Set Bond Manager parameters
483 {
484 // Don't send a pairing request after connecting; the device waits for the
485 // application to start pairing
486 uint8_t pairMode = GAPBOND_PAIRING_MODE_INITIATE;
487 // Do not use authenticated pairing
488 uint8_t mitm = FALSE;
489 // This is a display only device
490 uint8_t ioCap = GAPBOND_IO_CAP_DISPLAY_ONLY;
491 // Create a bond during the pairing process
492 uint8_t bonding = TRUE;
493
494 GAPBondMgr_SetParameter(GAPBOND_PAIRING_MODE, sizeof(uint8_t), &pairMode);
495 GAPBondMgr_SetParameter(GAPBOND_MITM_PROTECTION, sizeof(uint8_t), &mitm);
496 GAPBondMgr_SetParameter(GAPBOND_IO_CAPABILITIES, sizeof(uint8_t), &ioCap);
497 GAPBondMgr_SetParameter(GAPBOND_BONDING_ENABLED, sizeof(uint8_t), &bonding);
498 }
499
500 // Start Bond Manager and register callback
501 // This must be done before initialing the GAP layer
502 VOID GAPBondMgr_Register(&bondMgrCBs);
503
504 // Accept all parameter update requests
505 GAP_SetParamValue(GAP_PARAM_LINK_UPDATE_DECISION, GAP_UPDATE_REQ_ACCEPT_ALL);
506
507 // Register with GAP for HCI/Host messages (for RSSI)
508 GAP_RegisterForMsgs(selfEntity);
509
510 // Initialize GAP layer for Central role and register to receive GAP events
511 GAP_DeviceInit(GAP_PROFILE_CENTRAL, selfEntity, addrMode, NULL);
512
513 dispHandle = Display_open(Display_Type_ANY, NULL);
514
515 // Disable all items in the main menu
516 tbm_setItemStatus(&scMenuMain, SC_ITEM_NONE, SC_ITEM_ALL);
517 // Initialize Two-button Menu
518 tbm_initTwoBtnMenu(dispHandle, &scMenuMain, 4, SimpleCentral_menuSwitchCb);
519 Display_printf(dispHandle, SC_ROW_SEPARATOR, 0, "====================");
520}
521
522/*********************************************************************
523 * @fn SimpleCentral_taskFxn
524 *
525 * @brief Application task entry point for the Simple Central.
526 *
527 * @param none
528 *
529 * @return events not processed
530 */
531static void SimpleCentral_taskFxn(uintptr_t a0, uintptr_t a1)
532{
533 // Initialize application
534 SimpleCentral_init();
535
536 // Application main loop
537 for (;;)
538 {
539 uint32_t events;
540
541 events = Event_pend(syncEvent, Event_Id_NONE, SC_ALL_EVENTS,
542 ICALL_TIMEOUT_FOREVER);
543
544 if (events)
545 {
546 ICall_EntityID dest;
547 ICall_ServiceEnum src;
548 ICall_HciExtEvt *pMsg = NULL;
549
550 if (ICall_fetchServiceMsg(&src, &dest,
551 (void **)&pMsg) == ICALL_ERRNO_SUCCESS)
552 {
553 uint8 safeToDealloc = TRUE;
554
555 if ((src == ICALL_SERVICE_CLASS_BLE) && (dest == selfEntity))
556 {
557 ICall_Stack_Event *pEvt = (ICall_Stack_Event *)pMsg;
558
559 // Check for BLE stack events first
560 if (pEvt->signature != 0xffff)
561 {
562 // Process inter-task message
563 safeToDealloc = SimpleCentral_processStackMsg((ICall_Hdr *)pMsg);
564 }
565 }
566
567 if (pMsg && safeToDealloc)
568 {
569 ICall_freeMsg(pMsg);
570 }
571 }
572
573 // If RTOS queue is not empty, process app message
574 if (events & SC_QUEUE_EVT)
575 {
576 scEvt_t *pMsg;
577 while (pMsg = (scEvt_t *)Util_dequeueMsg(appMsgQueue))
578 {
579 // Process message
580 SimpleCentral_processAppMsg(pMsg);
581
582 // Free the space from the message
583 ICall_free(pMsg);
584 }
585 }
586 }
587 }
588}
589
590/*********************************************************************
591 * @fn SimpleCentral_processStackMsg
592 *
593 * @brief Process an incoming task message.
594 *
595 * @param pMsg - message to process
596 *
597 * @return TRUE if safe to deallocate incoming message, FALSE otherwise.
598 */
599static uint8_t SimpleCentral_processStackMsg(ICall_Hdr *pMsg)
600{
601 uint8_t safeToDealloc = TRUE;
602
603 switch (pMsg->event)
604 {
605 case GAP_MSG_EVENT:
606 SimpleCentral_processGapMsg((gapEventHdr_t*) pMsg);
607 break;
608
609 case GATT_MSG_EVENT:
610 SimpleCentral_processGATTMsg((gattMsgEvent_t *)pMsg);
611 break;
612
613 case HCI_GAP_EVENT_EVENT:
614 {
615 // Process HCI message
616 switch (pMsg->status)
617 {
618 case HCI_COMMAND_COMPLETE_EVENT_CODE:
619 SimpleCentral_processCmdCompleteEvt((hciEvt_CmdComplete_t *) pMsg);
620 break;
621
622 case HCI_BLE_HARDWARE_ERROR_EVENT_CODE:
623 AssertHandler(HAL_ASSERT_CAUSE_HARDWARE_ERROR,0);
624 break;
625
626 // HCI Commands Events
627 case HCI_COMMAND_STATUS_EVENT_CODE:
628 {
629 hciEvt_CommandStatus_t *pMyMsg = (hciEvt_CommandStatus_t *)pMsg;
630 switch ( pMyMsg->cmdOpcode )
631 {
632 case HCI_LE_SET_PHY:
633 {
634 if (pMyMsg->cmdStatus ==
635 HCI_ERROR_CODE_UNSUPPORTED_REMOTE_FEATURE)
636 {
637 Display_printf(dispHandle, SC_ROW_CUR_CONN, 0,
638 "PHY Change failure, peer does not support this");
639 }
640 else
641 {
642 Display_printf(dispHandle, SC_ROW_CUR_CONN, 0,
643 "PHY Update Status: 0x%02x",
644 pMyMsg->cmdStatus);
645 }
646 }
647 break;
648 case HCI_DISCONNECT:
649 break;
650
651 default:
652 {
653 Display_printf(dispHandle, SC_ROW_NON_CONN, 0,
654 "Unknown Cmd Status: 0x%04x::0x%02x",
655 pMyMsg->cmdOpcode, pMyMsg->cmdStatus);
656 }
657 break;
658 }
659 }
660 break;
661
662 // LE Events
663 case HCI_LE_EVENT_CODE:
664 {
665 hciEvt_BLEPhyUpdateComplete_t *pPUC
666 = (hciEvt_BLEPhyUpdateComplete_t*) pMsg;
667
668 if (pPUC->BLEEventCode == HCI_BLE_PHY_UPDATE_COMPLETE_EVENT)
669 {
670 if (pPUC->status != SUCCESS)
671 {
672 Display_printf(dispHandle, SC_ROW_ANY_CONN, 0,
673 "%s: PHY change failure",
674 SimpleCentral_getConnAddrStr(pPUC->connHandle));
675 }
676 else
677 {
678 Display_printf(dispHandle, SC_ROW_ANY_CONN, 0,
679 "%s: PHY updated to %s",
680 SimpleCentral_getConnAddrStr(pPUC->connHandle),
681 // Only symmetrical PHY is supported.
682 // rxPhy should be equal to txPhy.
683 (pPUC->rxPhy == PHY_UPDATE_COMPLETE_EVENT_1M) ? "1 Mbps" :
684 (pPUC->rxPhy == PHY_UPDATE_COMPLETE_EVENT_2M) ? "2 Mbps" :
685 (pPUC->rxPhy == PHY_UPDATE_COMPLETE_EVENT_CODED) ? "CODED" : "Unexpected PHY Value");
686 }
687 }
688
689 break;
690 }
691
692 default:
693 break;
694 }
695
696 break;
697 }
698
699 case L2CAP_SIGNAL_EVENT:
700 // place holder for L2CAP Connection Parameter Reply
701 break;
702
703 default:
704 break;
705 }
706
707 return (safeToDealloc);
708}
709
710/*********************************************************************
711 * @fn SimpleCentral_processAppMsg
712 *
713 * @brief Scanner application event processing function.
714 *
715 * @param pMsg - pointer to event structure
716 *
717 * @return none
718 */
719static void SimpleCentral_processAppMsg(scEvt_t *pMsg)
720{
721 bool safeToDealloc = TRUE;
722
723 switch (pMsg->hdr.event)
724 {
725 case SC_EVT_KEY_CHANGE:
726 SimpleCentral_handleKeys(pMsg->hdr.state);
727 break;
728
729 case SC_EVT_ADV_REPORT:
730 {
731 GapScan_Evt_AdvRpt_t* pAdvRpt = (GapScan_Evt_AdvRpt_t*) (pMsg->pData);
732
733#if (DEFAULT_DEV_DISC_BY_SVC_UUID == TRUE)
734 if (SimpleCentral_findSvcUuid(SIMPLEPROFILE_SERV_UUID,
735 pAdvRpt->pData, pAdvRpt->dataLen))
736 {
737 SimpleCentral_addScanInfo(pAdvRpt->addr, pAdvRpt->addrType);
738 Display_printf(dispHandle, SC_ROW_NON_CONN, 0, "Discovered: %s",
739 Util_convertBdAddr2Str(pAdvRpt->addr));
740 }
741#else // !DEFAULT_DEV_DISC_BY_SVC_UUID
742 Display_printf(dispHandle, SC_ROW_NON_CONN, 0, "Discovered: %s",
743 Util_convertBdAddr2Str(pAdvRpt->addr));
744#endif // DEFAULT_DEV_DISC_BY_SVC_UUID
745
746 // Free report payload data
747 if (pAdvRpt->pData != NULL)
748 {
749 ICall_free(pAdvRpt->pData);
750 }
751 break;
752 }
753
754 case SC_EVT_SCAN_ENABLED:
755 // Disable everything but "Stop Discovering" on the menu
756 tbm_setItemStatus(&scMenuMain, SC_ITEM_STOPDISC,
757 (SC_ITEM_ALL & ~SC_ITEM_STOPDISC));
758 Display_printf(dispHandle, SC_ROW_NON_CONN, 0, "Discovering...");
759 break;
760
761 case SC_EVT_SCAN_DISABLED:
762 {
763 uint8_t numReport;
764 uint8_t i;
765 static uint8_t* pAddrs = NULL;
766 uint8_t* pAddrTemp;
767 uint16_t itemsToEnable = SC_ITEM_STARTDISC | SC_ITEM_SCANPHY;
768#if (DEFAULT_DEV_DISC_BY_SVC_UUID == TRUE)
769 numReport = numScanRes;
770#else // !DEFAULT_DEV_DISC_BY_SVC_UUID
771 GapScan_Evt_AdvRpt_t advRpt;
772
773 numReport = ((GapScan_Evt_End_t*) (pMsg->pData))->numReport;
774#endif // DEFAULT_DEV_DISC_BY_SVC_UUID
775
776 Display_printf(dispHandle, SC_ROW_NON_CONN, 0,
777 "%d devices discovered", numReport);
778
779 if (numReport > 0)
780 {
781 // Also enable "Connect to"
782 itemsToEnable |= SC_ITEM_CONNECT;
783 }
784
785 if (numConn > 0)
786 {
787 // Also enable "Work with"
788 itemsToEnable |= SC_ITEM_SELECTCONN;
789 }
790
791 // Enable "Discover Devices", "Set Scanning PHY", and possibly
792 // "Connect to" and/or "Work with".
793 // Disable "Stop Discovering".
794 tbm_setItemStatus(&scMenuMain, itemsToEnable, SC_ITEM_STOPDISC);
795
796 // Allocate buffer to display addresses
797 if (pAddrs != NULL)
798 {
799 // A scan has been done previously, release the previously allocated buffer
800 ICall_free(pAddrs);
801 }
802 pAddrs = ICall_malloc(numReport * SC_ADDR_STR_SIZE);
803 if (pAddrs == NULL)
804 {
805 numReport = 0;
806 }
807
808 TBM_SET_NUM_ITEM(&scMenuConnect, numReport);
809
810 if (pAddrs != NULL)
811 {
812 pAddrTemp = pAddrs;
813 for (i = 0; i < numReport; i++, pAddrTemp += SC_ADDR_STR_SIZE)
814 {
815 #if (DEFAULT_DEV_DISC_BY_SVC_UUID == TRUE)
816 // Get the address from the list, convert it to string, and
817 // copy the string to the address buffer
818 memcpy(pAddrTemp, Util_convertBdAddr2Str(scanList[i].addr),
819 SC_ADDR_STR_SIZE);
820 #else // !DEFAULT_DEV_DISC_BY_SVC_UUID
821 // Get the address from the report, convert it to string, and
822 // copy the string to the address buffer
823 GapScan_getAdvReport(i, &advRpt);
824 memcpy(pAddrTemp, Util_convertBdAddr2Str(advRpt.addr),
825 SC_ADDR_STR_SIZE);
826 #endif // DEFAULT_DEV_DISC_BY_SVC_UUID
827
828 // Assign the string to the corresponding action description of the menu
829 TBM_SET_ACTION_DESC(&scMenuConnect, i, pAddrTemp);
830 }
831
832 // Disable any non-active scan results
833 for (; i < DEFAULT_MAX_SCAN_RES; i++)
834 {
835 tbm_setItemStatus(&scMenuConnect, TBM_ITEM_NONE, (1 << i));
836 }
837
838 // Note: pAddrs is not freed since it will be used by the two button menu
839 // to display the discovered address.
840 // This implies that at least the last discovered addresses
841 // will be maintained until a new scan is done.
842 }
843 break;
844 }
845
846 case SC_EVT_SVC_DISC:
847 SimpleCentral_startSvcDiscovery();
848 break;
849
850 case SC_EVT_READ_RSSI:
851 {
852 uint8_t connIndex = pMsg->hdr.state;
853 uint16_t connHandle = connList[connIndex].connHandle;
854
855 // If link is still valid
856 if (connHandle != CONNHANDLE_INVALID)
857 {
858 // Restart timer
859 Util_startClock(connList[connIndex].pRssiClock);
860
861 // Read RSSI
862 VOID HCI_ReadRssiCmd(connHandle);
863 }
864
865 break;
866 }
867
868 // Pairing event
869 case SC_EVT_PAIR_STATE:
870 {
871 SimpleCentral_processPairState(pMsg->hdr.state,
872 (scPairStateData_t*) (pMsg->pData));
873 break;
874 }
875
876 // Passcode event
877 case SC_EVT_PASSCODE_NEEDED:
878 {
879 SimpleCentral_processPasscode((scPasscodeData_t *)(pMsg->pData));
880 break;
881 }
882
883#if defined(BLE_V42_FEATURES) && (BLE_V42_FEATURES & PRIVACY_1_2_CFG)
884 case SC_EVT_READ_RPA:
885 {
886 uint8_t* pRpaNew;
887
888 // Read the current RPA.
889 pRpaNew = GAP_GetDevAddress(FALSE);
890
891 if (memcmp(pRpaNew, rpa, B_ADDR_LEN))
892 {
893 // If the RPA has changed, update the display
894 Display_printf(dispHandle, SC_ROW_RPA, 0, "RP Addr: %s",
895 Util_convertBdAddr2Str(pRpaNew));
896 memcpy(rpa, pRpaNew, B_ADDR_LEN);
897 }
898 break;
899 }
900#endif // PRIVACY_1_2_CFG
901
902 // Insufficient memory
903 case SC_EVT_INSUFFICIENT_MEM:
904 {
905 // We are running out of memory.
906 Display_printf(dispHandle, SC_ROW_ANY_CONN, 0, "Insufficient Memory");
907
908 // We might be in the middle of scanning, try stopping it.
909 GapScan_disable();
910 break;
911 }
912
913 default:
914 // Do nothing.
915 break;
916 }
917
918 if ((safeToDealloc == TRUE) && (pMsg->pData != NULL))
919 {
920 ICall_free(pMsg->pData);
921 }
922}
923
924/*********************************************************************
925 * @fn SimpleCentral_processGapMsg
926 *
927 * @brief GAP message processing function.
928 *
929 * @param pMsg - pointer to event message structure
930 *
931 * @return none
932 */
933static void SimpleCentral_processGapMsg(gapEventHdr_t *pMsg)
934{
935 switch (pMsg->opcode)
936 {
937 case GAP_DEVICE_INIT_DONE_EVENT:
938 {
939 uint8_t temp8;
940 uint16_t temp16;
941 gapDeviceInitDoneEvent_t *pPkt = (gapDeviceInitDoneEvent_t *)pMsg;
942
943 // Setup scanning
944 // For more information, see the GAP section in the User's Guide:
945 // http://software-dl.ti.com/lprf/ble5stack-latest/
946
947 // Register callback to process Scanner events
948 GapScan_registerCb(SimpleCentral_scanCb, NULL);
949
950 // Set Scanner Event Mask
951 GapScan_setEventMask(GAP_EVT_SCAN_ENABLED | GAP_EVT_SCAN_DISABLED |
952 GAP_EVT_ADV_REPORT);
953
954 // Set Scan PHY parameters
955 GapScan_setPhyParams(DEFAULT_SCAN_PHY, SCAN_TYPE_PASSIVE,
956 SCAN_PARAM_DFLT_INTERVAL, SCAN_PARAM_DFLT_INTERVAL);
957
958 // Set Advertising report fields to keep
959 temp16 = SC_ADV_RPT_FIELDS;
960 GapScan_setParam(SCAN_PARAM_RPT_FIELDS, &temp16);
961 // Set Scanning Primary PHY
962 temp8 = DEFAULT_SCAN_PHY;
963 GapScan_setParam(SCAN_PARAM_PRIM_PHYS, &temp8);
964 // Set LL Duplicate Filter
965 temp8 = SCAN_FLT_DUP_ENABLE;
966 GapScan_setParam(SCAN_PARAM_FLT_DUP, &temp8);
967
968 // Set PDU type filter -
969 // Only 'Connectable' and 'Complete' packets are desired.
970 // It doesn't matter if received packets are
971 // whether Scannable or Non-Scannable, whether Directed or Undirected,
972 // whether Scan_Rsp's or Advertisements, and whether Legacy or Extended.
973 temp16 = SCAN_FLT_PDU_CONNECTABLE_ONLY | SCAN_FLT_PDU_COMPLETE_ONLY;
974 GapScan_setParam(SCAN_PARAM_FLT_PDU_TYPE, &temp16);
975
976 scMaxPduSize = pPkt->dataPktLen;
977
978 // Enable "Discover Devices", "Set Scanning PHY", and "Set Address Type"
979 // in the main menu
980 tbm_setItemStatus(&scMenuMain,
981 SC_ITEM_STARTDISC | SC_ITEM_SCANPHY, SC_ITEM_NONE);
982
983 Display_printf(dispHandle, SC_ROW_NON_CONN, 0, "Initialized");
984 Display_printf(dispHandle, SC_ROW_NUM_CONN, 0, "Num Conns: %d", numConn);
985
986 // Display device address
987 Display_printf(dispHandle, SC_ROW_IDA, 0, "%s Addr: %s",
988 (addrMode <= ADDRMODE_RANDOM) ? "Dev" : "ID",
989 Util_convertBdAddr2Str(pPkt->devAddr));
990
991#if defined(BLE_V42_FEATURES) && (BLE_V42_FEATURES & PRIVACY_1_2_CFG)
992 if (addrMode > ADDRMODE_RANDOM)
993 {
994 // Update the current RPA.
995 memcpy(rpa, GAP_GetDevAddress(FALSE), B_ADDR_LEN);
996
997 Display_printf(dispHandle, SC_ROW_RPA, 0, "RP Addr: %s",
998 Util_convertBdAddr2Str(rpa));
999
1000 // Create one-shot clock for RPA check event.
1001 Util_constructClock(&clkRpaRead, SimpleCentral_clockHandler,
1002 SC_READ_RPA_PERIOD, 0, true, SC_EVT_READ_RPA);
1003 }
1004#endif // PRIVACY_1_2_CFG
1005 break;
1006 }
1007
1008 case GAP_CONNECTING_CANCELLED_EVENT:
1009 {
1010 uint16_t itemsToEnable = SC_ITEM_SCANPHY | SC_ITEM_STARTDISC |
1011 SC_ITEM_CONNECT;
1012
1013 if (numConn > 0)
1014 {
1015 itemsToEnable |= SC_ITEM_SELECTCONN;
1016 }
1017
1018 Display_printf(dispHandle, SC_ROW_NON_CONN, 0,
1019 "Conneting attempt cancelled");
1020
1021 // Enable "Discover Devices", "Connect To", and "Set Scanning PHY"
1022 // and disable everything else.
1023 tbm_setItemStatus(&scMenuMain,
1024 itemsToEnable, SC_ITEM_ALL & ~itemsToEnable);
1025
1026 break;
1027 }
1028
1029 case GAP_LINK_ESTABLISHED_EVENT:
1030 {
1031 uint16_t connHandle = ((gapEstLinkReqEvent_t*) pMsg)->connectionHandle;
1032 uint8_t* pAddr = ((gapEstLinkReqEvent_t*) pMsg)->devAddr;
1033 uint8_t connIndex;
1034 uint32_t itemsToDisable = SC_ITEM_STOPDISC | SC_ITEM_CANCELCONN;
1035 uint8_t* pStrAddr;
1036 uint8_t i;
1037 uint8_t numConnectable = 0;
1038
1039 // Add this connection info to the list
1040 connIndex = SimpleCentral_addConnInfo(connHandle, pAddr);
1041
1042 // connIndex cannot be equal to or greater than MAX_NUM_BLE_CONNS
1043 SIMPLECENTRAL_ASSERT(connIndex < MAX_NUM_BLE_CONNS);
1044
1045 connList[connIndex].charHandle = 0;
1046
1047 pStrAddr = (uint8_t*) Util_convertBdAddr2Str(connList[connIndex].addr);
1048
1049 Display_printf(dispHandle, SC_ROW_NON_CONN, 0, "Connected to %s", pStrAddr);
1050 Display_printf(dispHandle, SC_ROW_NUM_CONN, 0, "Num Conns: %d", numConn);
1051
1052 // Disable "Connect To" until another discovery is performed
1053 itemsToDisable |= SC_ITEM_CONNECT;
1054
1055 // If we already have maximum allowed number of connections,
1056 // disable device discovery and additional connection making.
1057 if (numConn >= MAX_NUM_BLE_CONNS)
1058 {
1059 itemsToDisable |= SC_ITEM_SCANPHY | SC_ITEM_STARTDISC;
1060 }
1061
1062 for (i = 0; i < TBM_GET_NUM_ITEM(&scMenuConnect); i++)
1063 {
1064 if (!memcmp(TBM_GET_ACTION_DESC(&scMenuConnect, i), pStrAddr,
1065 SC_ADDR_STR_SIZE))
1066 {
1067 // Disable this device from the connection choices
1068 tbm_setItemStatus(&scMenuConnect, SC_ITEM_NONE, 1 << i);
1069 }
1070 else if (TBM_IS_ITEM_ACTIVE(&scMenuConnect, i))
1071 {
1072 numConnectable++;
1073 }
1074 }
1075
1076 // Enable/disable Main menu items properly
1077 tbm_setItemStatus(&scMenuMain,
1078 SC_ITEM_ALL & ~(itemsToDisable), itemsToDisable);
1079
1080 break;
1081 }
1082
1083 case GAP_LINK_TERMINATED_EVENT:
1084 {
1085 uint16_t connHandle = ((gapTerminateLinkEvent_t*) pMsg)->connectionHandle;
1086 uint8_t connIndex;
1087 uint32_t itemsToEnable = SC_ITEM_STARTDISC | SC_ITEM_SCANPHY;
1088 uint8_t* pStrAddr;
1089 uint8_t i;
1090 uint8_t numConnectable = 0;
1091
1092 // Cancel timers
1093 SimpleCentral_CancelRssi(connHandle);
1094
1095 // Mark this connection deleted in the connected device list.
1096 connIndex = SimpleCentral_removeConnInfo(connHandle);
1097
1098 // connIndex cannot be equal to or greater than MAX_NUM_BLE_CONNS
1099 SIMPLECENTRAL_ASSERT(connIndex < MAX_NUM_BLE_CONNS);
1100
1101 pStrAddr = (uint8_t*) Util_convertBdAddr2Str(connList[connIndex].addr);
1102
1103 Display_printf(dispHandle, SC_ROW_NON_CONN, 0, "%s is disconnected",
1104 pStrAddr);
1105 Display_printf(dispHandle, SC_ROW_NUM_CONN, 0, "Num Conns: %d", numConn);
1106
1107 for (i = 0; i < TBM_GET_NUM_ITEM(&scMenuConnect); i++)
1108 {
1109 if (!memcmp(TBM_GET_ACTION_DESC(&scMenuConnect, i), pStrAddr,
1110 SC_ADDR_STR_SIZE))
1111 {
1112 // Enable this device in the connection choices
1113 tbm_setItemStatus(&scMenuConnect, 1 << i, SC_ITEM_NONE);
1114 }
1115
1116 if (TBM_IS_ITEM_ACTIVE(&scMenuConnect, i))
1117 {
1118 numConnectable++;
1119 }
1120 }
1121
1122 if (numConn > 0)
1123 {
1124 // There still is an active connection to select
1125 itemsToEnable |= SC_ITEM_SELECTCONN;
1126 }
1127
1128 // Enable/disable items properly.
1129 tbm_setItemStatus(&scMenuMain,
1130 itemsToEnable, SC_ITEM_ALL & ~itemsToEnable);
1131
1132 // If we are in the context which the teminated connection was associated
1133 // with, go to main menu.
1134 if (connHandle == scConnHandle)
1135 {
1136 tbm_goTo(&scMenuMain);
1137 }
1138
1139 break;
1140 }
1141
1142 case GAP_UPDATE_LINK_PARAM_REQ_EVENT:
1143 {
1144 gapUpdateLinkParamReqReply_t rsp;
1145 gapUpdateLinkParamReq_t *pReq;
1146
1147 pReq = &((gapUpdateLinkParamReqEvent_t *)pMsg)->req;
1148
1149 rsp.connectionHandle = pReq->connectionHandle;
1150 rsp.signalIdentifier = pReq->signalIdentifier;
1151
1152 if (acceptParamUpdateReq)
1153 {
1154 rsp.intervalMin = pReq->intervalMin;
1155 rsp.intervalMax = pReq->intervalMax;
1156 rsp.connLatency = pReq->connLatency;
1157 rsp.connTimeout = pReq->connTimeout;
1158 rsp.accepted = TRUE;
1159 }
1160 else
1161 {
1162 // Reject the request.
1163 rsp.accepted = FALSE;
1164 }
1165
1166 // Send Reply
1167 VOID GAP_UpdateLinkParamReqReply(&rsp);
1168
1169 break;
1170 }
1171
1172 case GAP_LINK_PARAM_UPDATE_EVENT:
1173 {
1174 gapLinkUpdateEvent_t *pPkt = (gapLinkUpdateEvent_t *)pMsg;
1175 // Get the address from the connection handle
1176 linkDBInfo_t linkInfo;
1177
1178 if (linkDB_GetInfo(pPkt->connectionHandle, &linkInfo) == SUCCESS)
1179 {
1180 if(pPkt->status == SUCCESS)
1181 {
1182 Display_printf(dispHandle, SC_ROW_CUR_CONN, 0,
1183 "Updated: %s, connTimeout:%d",
1184 Util_convertBdAddr2Str(linkInfo.addr),
1185 linkInfo.connTimeout*CONN_TIMEOUT_MS_CONVERSION);
1186 }
1187 else
1188 {
1189 // Display the address of the connection update failure
1190 Display_printf(dispHandle, SC_ROW_CUR_CONN, 0,
1191 "Update Failed 0x%h: %s", pPkt->opcode,
1192 Util_convertBdAddr2Str(linkInfo.addr));
1193 }
1194 }
1195
1196 break;
1197 }
1198
1199 default:
1200 break;
1201 }
1202}
1203
1204/*********************************************************************
1205 * @fn SimpleCentral_handleKeys
1206 *
1207 * @brief Handles all key events for this device.
1208 *
1209 * @param keys - bit field for key events. Valid entries:
1210 * KEY_LEFT
1211 * KEY_RIGHT
1212 *
1213 * @return none
1214 */
1215static void SimpleCentral_handleKeys(uint8_t keys)
1216{
1217 if (keys & KEY_LEFT)
1218 {
1219 // Check if the key is still pressed. Workaround for possible bouncing.
1220 if (PIN_getInputValue(Board_PIN_BUTTON0) == 0)
1221 {
1222 tbm_buttonLeft();
1223 }
1224 }
1225 else if (keys & KEY_RIGHT)
1226 {
1227 // Check if the key is still pressed. Workaround for possible bouncing.
1228 if (PIN_getInputValue(Board_PIN_BUTTON1) == 0)
1229 {
1230 tbm_buttonRight();
1231 }
1232 }
1233}
1234/*********************************************************************
1235 * @fn SimpleCentral_processGATTMsg
1236 *
1237 * @brief Process GATT messages and events.
1238 *
1239 * @return none
1240 */
1241static void SimpleCentral_processGATTMsg(gattMsgEvent_t *pMsg)
1242{
1243 if (linkDB_Up(pMsg->connHandle))
1244 {
1245 // See if GATT server was unable to transmit an ATT response
1246 if (pMsg->hdr.status == blePending)
1247 {
1248 // No HCI buffer was available. App can try to retransmit the response
1249 // on the next connection event. Drop it for now.
1250 Display_printf(dispHandle, SC_ROW_CUR_CONN, 0,
1251 "ATT Rsp dropped %d", pMsg->method);
1252 }
1253 else if ((pMsg->method == ATT_READ_RSP) ||
1254 ((pMsg->method == ATT_ERROR_RSP) &&
1255 (pMsg->msg.errorRsp.reqOpcode == ATT_READ_REQ)))
1256 {
1257 if (pMsg->method == ATT_ERROR_RSP)
1258 {
1259 Display_printf(dispHandle, SC_ROW_CUR_CONN, 0,
1260 "Read Error %d", pMsg->msg.errorRsp.errCode);
1261 }
1262 else
1263 {
1264 // After a successful read, display the read value
1265 Display_printf(dispHandle, SC_ROW_CUR_CONN, 0,
1266 "Read rsp: 0x%02x", pMsg->msg.readRsp.pValue[0]);
1267 }
1268 }
1269 else if ((pMsg->method == ATT_WRITE_RSP) ||
1270 ((pMsg->method == ATT_ERROR_RSP) &&
1271 (pMsg->msg.errorRsp.reqOpcode == ATT_WRITE_REQ)))
1272 {
1273 if (pMsg->method == ATT_ERROR_RSP)
1274 {
1275 Display_printf(dispHandle, SC_ROW_CUR_CONN, 0,
1276 "Write Error %d", pMsg->msg.errorRsp.errCode);
1277 }
1278 else
1279 {
1280 // After a successful write, display the value that was written and
1281 // increment value
1282 Display_printf(dispHandle, SC_ROW_CUR_CONN, 0,
1283 "Write sent: 0x%02x", charVal);
1284 }
1285
1286 tbm_goTo(&scMenuPerConn);
1287 }
1288 else if (pMsg->method == ATT_FLOW_CTRL_VIOLATED_EVENT)
1289 {
1290 // ATT request-response or indication-confirmation flow control is
1291 // violated. All subsequent ATT requests or indications will be dropped.
1292 // The app is informed in case it wants to drop the connection.
1293
1294 // Display the opcode of the message that caused the violation.
1295 Display_printf(dispHandle, SC_ROW_CUR_CONN, 0,
1296 "FC Violated: %d", pMsg->msg.flowCtrlEvt.opcode);
1297 }
1298 else if (pMsg->method == ATT_MTU_UPDATED_EVENT)
1299 {
1300 // MTU size updated
1301 Display_printf(dispHandle, SC_ROW_CUR_CONN, 0,
1302 "MTU Size: %d", pMsg->msg.mtuEvt.MTU);
1303 }
1304 else if (discState != BLE_DISC_STATE_IDLE)
1305 {
1306 SimpleCentral_processGATTDiscEvent(pMsg);
1307 }
1308 } // else - in case a GATT message came after a connection has dropped, ignore it.
1309
1310 // Needed only for ATT Protocol messages
1311 GATT_bm_free(&pMsg->msg, pMsg->method);
1312}
1313
1314/*********************************************************************
1315 * @fn SimpleCentral_processCmdCompleteEvt
1316 *
1317 * @brief Process an incoming OSAL HCI Command Complete Event.
1318 *
1319 * @param pMsg - message to process
1320 *
1321 * @return none
1322 */
1323static void SimpleCentral_processCmdCompleteEvt(hciEvt_CmdComplete_t *pMsg)
1324{
1325 switch (pMsg->cmdOpcode)
1326 {
1327 case HCI_READ_RSSI:
1328 {
1329#ifndef Display_DISABLE_ALL
1330 uint16_t connHandle = BUILD_UINT16(pMsg->pReturnParam[1],
1331 pMsg->pReturnParam[2]);
1332 int8 rssi = (int8)pMsg->pReturnParam[3];
1333
1334 Display_printf(dispHandle, SC_ROW_ANY_CONN, 0, "%s: RSSI %d dBm",
1335 SimpleCentral_getConnAddrStr(connHandle), rssi);
1336
1337#endif
1338 break;
1339 }
1340
1341 default:
1342 break;
1343 }
1344}
1345
1346/*********************************************************************
1347 * @fn SimpleCentral_StartRssi
1348 *
1349 * @brief Start periodic RSSI reads on the current link.
1350 *
1351 * @return SUCCESS: RSSI Read timer started
1352 * bleIncorrectMode: Aready started
1353 * bleNoResources: No resources
1354 */
1355static status_t SimpleCentral_StartRssi(void)
1356{
1357 uint8_t connIndex = SimpleCentral_getConnIndex(scConnHandle);
1358
1359 // connIndex cannot be equal to or greater than MAX_NUM_BLE_CONNS
1360 SIMPLECENTRAL_ASSERT(connIndex < MAX_NUM_BLE_CONNS);
1361
1362 // If already running
1363 if (connList[connIndex].pRssiClock != NULL)
1364 {
1365 return bleIncorrectMode;
1366 }
1367
1368 // Create a clock object and start
1369 connList[connIndex].pRssiClock
1370 = (Clock_Struct*) ICall_malloc(sizeof(Clock_Struct));
1371
1372 if (connList[connIndex].pRssiClock)
1373 {
1374 Util_constructClock(connList[connIndex].pRssiClock,
1375 SimpleCentral_clockHandler,
1376 DEFAULT_RSSI_PERIOD, 0, true,
1377 (connIndex << 8) | SC_EVT_READ_RSSI);
1378 }
1379 else
1380 {
1381 return bleNoResources;
1382 }
1383
1384 return SUCCESS;
1385}
1386
1387/*********************************************************************
1388 * @fn SimpleCentral_CancelRssi
1389 *
1390 * @brief Cancel periodic RSSI reads on a link.
1391 *
1392 * @param connection handle
1393 *
1394 * @return SUCCESS: Operation successful
1395 * bleIncorrectMode: Has not started
1396 */
1397static status_t SimpleCentral_CancelRssi(uint16_t connHandle)
1398{
1399 uint8_t connIndex = SimpleCentral_getConnIndex(connHandle);
1400
1401 // connIndex cannot be equal to or greater than MAX_NUM_BLE_CONNS
1402 SIMPLECENTRAL_ASSERT(connIndex < MAX_NUM_BLE_CONNS);
1403
1404 // If already running
1405 if (connList[connIndex].pRssiClock == NULL)
1406 {
1407 return bleIncorrectMode;
1408 }
1409
1410 // Stop timer
1411 Util_stopClock(connList[connIndex].pRssiClock);
1412
1413 // Destroy the clock object
1414 Clock_destruct(connList[connIndex].pRssiClock);
1415
1416 // Free clock struct
1417 ICall_free(connList[connIndex].pRssiClock);
1418 connList[connIndex].pRssiClock = NULL;
1419
1420 Display_clearLine(dispHandle, SC_ROW_ANY_CONN);
1421
1422 return SUCCESS;
1423}
1424
1425/*********************************************************************
1426 * @fn SimpleCentral_processPairState
1427 *
1428 * @brief Process the new paring state.
1429 *
1430 * @return none
1431 */
1432static void SimpleCentral_processPairState(uint8_t state,
1433 scPairStateData_t* pPairData)
1434{
1435 uint8_t status = pPairData->status;
1436
1437 if (state == GAPBOND_PAIRING_STATE_STARTED)
1438 {
1439 Display_printf(dispHandle, SC_ROW_CUR_CONN, 0, "Pairing started");
1440 }
1441 else if (state == GAPBOND_PAIRING_STATE_COMPLETE)
1442 {
1443 if (status == SUCCESS)
1444 {
1445 linkDBInfo_t linkInfo;
1446
1447 Display_printf(dispHandle, SC_ROW_CUR_CONN, 0, "Pairing success");
1448
1449#if defined(BLE_V42_FEATURES) && (BLE_V42_FEATURES & PRIVACY_1_2_CFG)
1450 if (linkDB_GetInfo(pPairData->connHandle, &linkInfo) == SUCCESS)
1451 {
1452 // If the peer was using private address, update with ID address
1453 if ((linkInfo.addrType == ADDRTYPE_PUBLIC_ID ||
1454 linkInfo.addrType == ADDRTYPE_RANDOM_ID) &&
1455 !Util_isBufSet(linkInfo.addrPriv, 0, B_ADDR_LEN))
1456 {
1457 // Update the address of the peer to the ID address
1458 Display_printf(dispHandle, SC_ROW_NON_CONN, 0, "Addr updated: %s",
1459 Util_convertBdAddr2Str(linkInfo.addr));
1460
1461 // Update the connection list with the ID address
1462 uint8_t i = SimpleCentral_getConnIndex(pPairData->connHandle);
1463
1464 SIMPLECENTRAL_ASSERT(i < MAX_NUM_BLE_CONNS);
1465 memcpy(connList[i].addr, linkInfo.addr, B_ADDR_LEN);
1466 }
1467 }
1468#endif // PRIVACY_1_2_CFG
1469 }
1470 else
1471 {
1472 Display_printf(dispHandle, SC_ROW_CUR_CONN, 0, "Pairing fail: %d", status);
1473 }
1474 }
1475 else if (state == GAPBOND_PAIRING_STATE_ENCRYPTED)
1476 {
1477 if (status == SUCCESS)
1478 {
1479 Display_printf(dispHandle, SC_ROW_CUR_CONN, 0, "Encryption success");
1480 }
1481 else
1482 {
1483 Display_printf(dispHandle, SC_ROW_CUR_CONN, 0, "Encryption failed: %d", status);
1484 }
1485 }
1486 else if (state == GAPBOND_PAIRING_STATE_BOND_SAVED)
1487 {
1488 if (status == SUCCESS)
1489 {
1490 Display_printf(dispHandle, SC_ROW_CUR_CONN, 0, "Bond save success");
1491 }
1492 else
1493 {
1494 Display_printf(dispHandle, SC_ROW_CUR_CONN, 0, "Bond save failed: %d", status);
1495 }
1496 }
1497}
1498
1499/*********************************************************************
1500 * @fn SimpleCentral_processPasscode
1501 *
1502 * @brief Process the Passcode request.
1503 *
1504 * @return none
1505 */
1506static void SimpleCentral_processPasscode(scPasscodeData_t *pData)
1507{
1508 // Display passcode to user
1509 if (pData->uiOutputs != 0)
1510 {
1511 Display_printf(dispHandle, SC_ROW_CUR_CONN, 0, "Passcode: %d",
1512 B_APP_DEFAULT_PASSCODE);
1513 }
1514
1515 // Send passcode response
1516 GAPBondMgr_PasscodeRsp(pData->connHandle, SUCCESS, B_APP_DEFAULT_PASSCODE);
1517}
1518
1519/*********************************************************************
1520 * @fn SimpleCentral_startSvcDiscovery
1521 *
1522 * @brief Start service discovery.
1523 *
1524 * @return none
1525 */
1526static void SimpleCentral_startSvcDiscovery(void)
1527{
1528 attExchangeMTUReq_t req;
1529
1530 // Initialize cached handles
1531 svcStartHdl = svcEndHdl = 0;
1532
1533 discState = BLE_DISC_STATE_MTU;
1534
1535 // Discover GATT Server's Rx MTU size
1536 req.clientRxMTU = scMaxPduSize - L2CAP_HDR_SIZE;
1537
1538 // ATT MTU size should be set to the minimum of the Client Rx MTU
1539 // and Server Rx MTU values
1540 VOID GATT_ExchangeMTU(scConnHandle, &req, selfEntity);
1541}
1542
1543/*********************************************************************
1544 * @fn SimpleCentral_processGATTDiscEvent
1545 *
1546 * @brief Process GATT discovery event
1547 *
1548 * @return none
1549 */
1550static void SimpleCentral_processGATTDiscEvent(gattMsgEvent_t *pMsg)
1551{
1552 if (discState == BLE_DISC_STATE_MTU)
1553 {
1554 // MTU size response received, discover simple service
1555 if (pMsg->method == ATT_EXCHANGE_MTU_RSP)
1556 {
1557 uint8_t uuid[ATT_BT_UUID_SIZE] = { LO_UINT16(SIMPLEPROFILE_SERV_UUID),
1558 HI_UINT16(SIMPLEPROFILE_SERV_UUID) };
1559
1560 discState = BLE_DISC_STATE_SVC;
1561
1562 // Discovery simple service
1563 VOID GATT_DiscPrimaryServiceByUUID(pMsg->connHandle, uuid,
1564 ATT_BT_UUID_SIZE, selfEntity);
1565 }
1566 }
1567 else if (discState == BLE_DISC_STATE_SVC)
1568 {
1569 // Service found, store handles
1570 if (pMsg->method == ATT_FIND_BY_TYPE_VALUE_RSP &&
1571 pMsg->msg.findByTypeValueRsp.numInfo > 0)
1572 {
1573 svcStartHdl = ATT_ATTR_HANDLE(pMsg->msg.findByTypeValueRsp.pHandlesInfo, 0);
1574 svcEndHdl = ATT_GRP_END_HANDLE(pMsg->msg.findByTypeValueRsp.pHandlesInfo, 0);
1575 }
1576
1577 // If procedure complete
1578 if (((pMsg->method == ATT_FIND_BY_TYPE_VALUE_RSP) &&
1579 (pMsg->hdr.status == bleProcedureComplete)) ||
1580 (pMsg->method == ATT_ERROR_RSP))
1581 {
1582 if (svcStartHdl != 0)
1583 {
1584 attReadByTypeReq_t req;
1585
1586 // Discover characteristic
1587 discState = BLE_DISC_STATE_CHAR;
1588
1589 req.startHandle = svcStartHdl;
1590 req.endHandle = svcEndHdl;
1591 req.type.len = ATT_BT_UUID_SIZE;
1592 req.type.uuid[0] = LO_UINT16(SIMPLEPROFILE_CHAR1_UUID);
1593 req.type.uuid[1] = HI_UINT16(SIMPLEPROFILE_CHAR1_UUID);
1594
1595 VOID GATT_DiscCharsByUUID(pMsg->connHandle, &req, selfEntity);
1596 }
1597 }
1598 }
1599 else if (discState == BLE_DISC_STATE_CHAR)
1600 {
1601 // Characteristic found, store handle
1602 if ((pMsg->method == ATT_READ_BY_TYPE_RSP) &&
1603 (pMsg->msg.readByTypeRsp.numPairs > 0))
1604 {
1605 uint8_t connIndex = SimpleCentral_getConnIndex(scConnHandle);
1606
1607 // connIndex cannot be equal to or greater than MAX_NUM_BLE_CONNS
1608 SIMPLECENTRAL_ASSERT(connIndex < MAX_NUM_BLE_CONNS);
1609
1610 // Store the handle of the simpleprofile characteristic 1 value
1611 connList[connIndex].charHandle
1612 = BUILD_UINT16(pMsg->msg.readByTypeRsp.pDataList[3],
1613 pMsg->msg.readByTypeRsp.pDataList[4]);
1614
1615 Display_printf(dispHandle, SC_ROW_CUR_CONN, 0, "Simple Svc Found");
1616
1617 // Now we can use GATT Read/Write
1618 tbm_setItemStatus(&scMenuPerConn,
1619 SC_ITEM_GATTREAD | SC_ITEM_GATTWRITE, SC_ITEM_NONE);
1620 }
1621
1622 discState = BLE_DISC_STATE_IDLE;
1623 }
1624}
1625
1626#if (DEFAULT_DEV_DISC_BY_SVC_UUID == TRUE)
1627/*********************************************************************
1628 * @fn SimpleCentral_findSvcUuid
1629 *
1630 * @brief Find a given UUID in an advertiser's service UUID list.
1631 *
1632 * @return TRUE if service UUID found
1633 */
1634static bool SimpleCentral_findSvcUuid(uint16_t uuid, uint8_t *pData,
1635 uint16_t dataLen)
1636{
1637 uint8_t adLen;
1638 uint8_t adType;
1639 uint8_t *pEnd;
1640
1641 if (dataLen > 0)
1642 {
1643 pEnd = pData + dataLen - 1;
1644
1645 // While end of data not reached
1646 while (pData < pEnd)
1647 {
1648 // Get length of next AD item
1649 adLen = *pData++;
1650 if (adLen > 0)
1651 {
1652 adType = *pData;
1653
1654 // If AD type is for 16-bit service UUID
1655 if ((adType == GAP_ADTYPE_16BIT_MORE) ||
1656 (adType == GAP_ADTYPE_16BIT_COMPLETE))
1657 {
1658 pData++;
1659 adLen--;
1660
1661 // For each UUID in list
1662 while (adLen >= 2 && pData < pEnd)
1663 {
1664 // Check for match
1665 if ((pData[0] == LO_UINT16(uuid)) && (pData[1] == HI_UINT16(uuid)))
1666 {
1667 // Match found
1668 return TRUE;
1669 }
1670
1671 // Go to next
1672 pData += 2;
1673 adLen -= 2;
1674 }
1675
1676 // Handle possible erroneous extra byte in UUID list
1677 if (adLen == 1)
1678 {
1679 pData++;
1680 }
1681 }
1682 else
1683 {
1684 // Go to next item
1685 pData += adLen;
1686 }
1687 }
1688 }
1689 }
1690
1691 // Match not found
1692 return FALSE;
1693}
1694
1695/*********************************************************************
1696 * @fn SimpleCentral_addScanInfo
1697 *
1698 * @brief Add a device to the scanned device list
1699 *
1700 * @return none
1701 */
1702static void SimpleCentral_addScanInfo(uint8_t *pAddr, uint8_t addrType)
1703{
1704 uint8_t i;
1705
1706 // If result count not at max
1707 if (numScanRes < DEFAULT_MAX_SCAN_RES)
1708 {
1709 // Check if device is already in scan results
1710 for (i = 0; i < numScanRes; i++)
1711 {
1712 if (memcmp(pAddr, scanList[i].addr , B_ADDR_LEN) == 0)
1713 {
1714 return;
1715 }
1716 }
1717
1718 // Add addr to scan result list
1719 memcpy(scanList[numScanRes].addr, pAddr, B_ADDR_LEN);
1720 scanList[numScanRes].addrType = addrType;
1721
1722 // Increment scan result count
1723 numScanRes++;
1724 }
1725}
1726#endif // DEFAULT_DEV_DISC_BY_SVC_UUID
1727
1728/*********************************************************************
1729 * @fn SimpleCentral_addConnInfo
1730 *
1731 * @brief Add a device to the connected device list
1732 *
1733 * @return index of the connected device list entry where the new connection
1734 * info is put in.
1735 * if there is no room, MAX_NUM_BLE_CONNS will be returned.
1736 */
1737static uint8_t SimpleCentral_addConnInfo(uint16_t connHandle, uint8_t *pAddr)
1738{
1739 uint8_t i;
1740
1741 for (i = 0; i < MAX_NUM_BLE_CONNS; i++)
1742 {
1743 if (connList[i].connHandle == CONNHANDLE_INVALID)
1744 {
1745 // Found available entry to put a new connection info in
1746 connList[i].connHandle = connHandle;
1747 memcpy(connList[i].addr, pAddr, B_ADDR_LEN);
1748 numConn++;
1749
1750 break;
1751 }
1752 }
1753
1754 return i;
1755}
1756
1757/*********************************************************************
1758 * @fn SimpleCentral_removeConnInfo
1759 *
1760 * @brief Remove a device from the connected device list
1761 *
1762 * @return index of the connected device list entry where the new connection
1763 * info is removed from.
1764 * if connHandle is not found, MAX_NUM_BLE_CONNS will be returned.
1765 */
1766static uint8_t SimpleCentral_removeConnInfo(uint16_t connHandle)
1767{
1768 uint8_t i;
1769
1770 for (i = 0; i < MAX_NUM_BLE_CONNS; i++)
1771 {
1772 if (connList[i].connHandle == connHandle)
1773 {
1774 // Found the entry to mark as deleted
1775 connList[i].connHandle = CONNHANDLE_INVALID;
1776 numConn--;
1777
1778 break;
1779 }
1780 }
1781
1782 return i;
1783}
1784
1785/*********************************************************************
1786 * @fn SimpleCentral_getConnIndex
1787 *
1788 * @brief Find index in the connected device list by connHandle
1789 *
1790 * @return the index of the entry that has the given connection handle.
1791 * if there is no match, MAX_NUM_BLE_CONNS will be returned.
1792 */
1793static uint8_t SimpleCentral_getConnIndex(uint16_t connHandle)
1794{
1795 uint8_t i;
1796
1797 for (i = 0; i < MAX_NUM_BLE_CONNS; i++)
1798 {
1799 if (connList[i].connHandle == connHandle)
1800 {
1801 break;
1802 }
1803 }
1804
1805 return i;
1806}
1807
1808#ifndef Display_DISABLE_ALL
1809/*********************************************************************
1810 * @fn SimpleCentral_getConnAddrStr
1811 *
1812 * @brief Return, in string form, the address of the peer associated with
1813 * the connHandle.
1814 *
1815 * @return A null-terminated string of the address.
1816 * if there is no match, NULL will be returned.
1817 */
1818static char* SimpleCentral_getConnAddrStr(uint16_t connHandle)
1819{
1820 uint8_t i;
1821
1822 for (i = 0; i < MAX_NUM_BLE_CONNS; i++)
1823 {
1824 if (connList[i].connHandle == connHandle)
1825 {
1826 return Util_convertBdAddr2Str(connList[i].addr);
1827 }
1828 }
1829
1830 return NULL;
1831}
1832#endif
1833
1834/*********************************************************************
1835 * @fn SimpleCentral_pairStateCb
1836 *
1837 * @brief Pairing state callback.
1838 *
1839 * @return none
1840 */
1841static void SimpleCentral_pairStateCb(uint16_t connHandle, uint8_t state,
1842 uint8_t status)
1843{
1844 scPairStateData_t *pData;
1845
1846 // Allocate space for the event data.
1847 if ((pData = ICall_malloc(sizeof(scPairStateData_t))))
1848 {
1849 pData->connHandle = connHandle;
1850 pData->status = status;
1851
1852 // Queue the event.
1853 if(SimpleCentral_enqueueMsg(SC_EVT_PAIR_STATE, state, (uint8_t*) pData) != SUCCESS)
1854 {
1855 ICall_free(pData);
1856 }
1857 }
1858}
1859
1860/*********************************************************************
1861* @fn SimpleCentral_passcodeCb
1862*
1863* @brief Passcode callback.
1864*
1865* @param deviceAddr - pointer to device address
1866*
1867* @param connHandle - the connection handle
1868*
1869* @param uiInputs - pairing User Interface Inputs
1870*
1871* @param uiOutputs - pairing User Interface Outputs
1872*
1873* @param numComparison - numeric Comparison 20 bits
1874*
1875* @return none
1876*/
1877static void SimpleCentral_passcodeCb(uint8_t *deviceAddr, uint16_t connHandle,
1878 uint8_t uiInputs, uint8_t uiOutputs,
1879 uint32_t numComparison)
1880{
1881 scPasscodeData_t *pData = ICall_malloc(sizeof(scPasscodeData_t));
1882
1883 // Allocate space for the passcode event.
1884 if (pData)
1885 {
1886 pData->connHandle = connHandle;
1887 memcpy(pData->deviceAddr, deviceAddr, B_ADDR_LEN);
1888 pData->uiInputs = uiInputs;
1889 pData->uiOutputs = uiOutputs;
1890 pData->numComparison = numComparison;
1891
1892 // Enqueue the event.
1893 if (SimpleCentral_enqueueMsg(SC_EVT_PASSCODE_NEEDED, 0,(uint8_t *) pData) != SUCCESS)
1894 {
1895 ICall_free(pData);
1896 }
1897 }
1898}
1899
1900/*********************************************************************
1901 * @fn SimpleCentral_keyChangeHandler
1902 *
1903 * @brief Key event handler function
1904 *
1905 * @param a0 - ignored
1906 *
1907 * @return none
1908 */
1909static void SimpleCentral_keyChangeHandler(uint8 keys)
1910{
1911 SimpleCentral_enqueueMsg(SC_EVT_KEY_CHANGE, keys, NULL);
1912}
1913
1914/*********************************************************************
1915 * @fn SimpleCentral_clockHandler
1916 *
1917 * @brief clock handler function
1918 *
1919 * @param arg - argument from the clock initiator
1920 *
1921 * @return none
1922 */
1923void SimpleCentral_clockHandler(UArg arg)
1924{
1925 uint8_t evtId = (uint8_t) (arg & 0xFF);
1926
1927 switch (evtId)
1928 {
1929 case SC_EVT_READ_RSSI:
1930 SimpleCentral_enqueueMsg(SC_EVT_READ_RSSI, (uint8_t) (arg >> 8) , NULL);
1931 break;
1932
1933 case SC_EVT_READ_RPA:
1934 // Restart timer
1935 Util_startClock(&clkRpaRead);
1936 // Let the application handle the event
1937 SimpleCentral_enqueueMsg(SC_EVT_READ_RPA, 0, NULL);
1938 break;
1939
1940 default:
1941 break;
1942 }
1943}
1944
1945/*********************************************************************
1946 * @fn SimpleCentral_enqueueMsg
1947 *
1948 * @brief Creates a message and puts the message in RTOS queue.
1949 *
1950 * @param event - message event.
1951 * @param state - message state.
1952 * @param pData - message data pointer.
1953 *
1954 * @return TRUE or FALSE
1955 */
1956static status_t SimpleCentral_enqueueMsg(uint8_t event, uint8_t state,
1957 uint8_t *pData)
1958{
1959 uint8_t success;
1960 scEvt_t *pMsg = ICall_malloc(sizeof(scEvt_t));
1961
1962 // Create dynamic pointer to message.
1963 if (pMsg)
1964 {
1965 pMsg->hdr.event = event;
1966 pMsg->hdr.state = state;
1967 pMsg->pData = pData;
1968
1969 // Enqueue the message.
1970 success = Util_enqueueMsg(appMsgQueue, syncEvent, (uint8_t *)pMsg);
1971 return (success) ? SUCCESS : FAILURE;
1972 }
1973
1974 return(bleMemAllocError);
1975}
1976
1977/*********************************************************************
1978 * @fn SimpleCentral_scanCb
1979 *
1980 * @brief Callback called by GapScan module
1981 *
1982 * @param evt - event
1983 * @param msg - message coming with the event
1984 * @param arg - user argument
1985 *
1986 * @return none
1987 */
1988void SimpleCentral_scanCb(uint32_t evt, void* pMsg, uintptr_t arg)
1989{
1990 uint8_t event;
1991
1992 if (evt & GAP_EVT_ADV_REPORT)
1993 {
1994 event = SC_EVT_ADV_REPORT;
1995 }
1996 else if (evt & GAP_EVT_SCAN_ENABLED)
1997 {
1998 event = SC_EVT_SCAN_ENABLED;
1999 }
2000 else if (evt & GAP_EVT_SCAN_DISABLED)
2001 {
2002 event = SC_EVT_SCAN_DISABLED;
2003 }
2004 else if (evt & GAP_EVT_INSUFFICIENT_MEMORY)
2005 {
2006 event = SC_EVT_INSUFFICIENT_MEM;
2007 }
2008 else
2009 {
2010 return;
2011 }
2012
2013 if(SimpleCentral_enqueueMsg(event, SUCCESS, pMsg) != SUCCESS)
2014 {
2015 ICall_free(pMsg);
2016 }
2017}
2018
2019/*********************************************************************
2020 * @fn SimpleCentral_doSetScanPhy
2021 *
2022 * @brief Set PHYs for scanning.
2023 *
2024 * @param index - 0: 1M PHY
2025 * 1: CODED PHY (Long range)
2026 *
2027 * @return always true
2028 */
2029bool SimpleCentral_doSetScanPhy(uint8_t index)
2030{
2031 uint8_t temp8;
2032
2033 if (index == 0)
2034 {
2035 temp8 = SCAN_PRIM_PHY_1M;
2036 }
2037 else
2038 {
2039 temp8 = SCAN_PRIM_PHY_CODED;
2040 }
2041
2042 // Set scanning primary PHY
2043 GapScan_setParam(SCAN_PARAM_PRIM_PHYS, &temp8);
2044
2045 Display_printf(dispHandle, SC_ROW_NON_CONN, 0, "Primary Scan PHY: %s",
2046 TBM_GET_ACTION_DESC(&scMenuScanPhy, index));
2047
2048 tbm_goTo(&scMenuMain);
2049
2050 return (true);
2051}
2052
2053/*********************************************************************
2054 * @fn SimpleCentral_doDiscoverDevices
2055 *
2056 * @brief Enables scanning
2057 *
2058 * @param index - item index from the menu
2059 *
2060 * @return always true
2061 */
2062bool SimpleCentral_doDiscoverDevices(uint8_t index)
2063{
2064 (void) index;
2065
2066#if (DEFAULT_DEV_DISC_BY_SVC_UUID == TRUE)
2067 // Scanning for DEFAULT_SCAN_DURATION x 10 ms.
2068 // The stack does not need to record advertising reports
2069 // since the application will filter them by Service UUID and save.
2070
2071 // Reset number of scan results to 0 before starting scan
2072 numScanRes = 0;
2073 GapScan_enable(0, DEFAULT_SCAN_DURATION, 0);
2074#else // !DEFAULT_DEV_DISC_BY_SVC_UUID
2075 // Scanning for DEFAULT_SCAN_DURATION x 10 ms.
2076 // Let the stack record the advertising reports as many as up to DEFAULT_MAX_SCAN_RES.
2077 GapScan_enable(0, DEFAULT_SCAN_DURATION, DEFAULT_MAX_SCAN_RES);
2078#endif // DEFAULT_DEV_DISC_BY_SVC_UUID
2079
2080 // Enable only "Stop Discovering" and disable all others in the main menu
2081 tbm_setItemStatus(&scMenuMain, SC_ITEM_STOPDISC,
2082 (SC_ITEM_ALL & ~SC_ITEM_STOPDISC));
2083
2084 return (true);
2085}
2086
2087/*********************************************************************
2088 * @fn SimpleCentral_doStopDiscovering
2089 *
2090 * @brief Stop on-going scanning
2091 *
2092 * @param index - item index from the menu
2093 *
2094 * @return always true
2095 */
2096bool SimpleCentral_doStopDiscovering(uint8_t index)
2097{
2098 (void) index;
2099
2100 GapScan_disable();
2101
2102 return (true);
2103}
2104
2105/*********************************************************************
2106 * @fn SimpleCentral_doEstablishLink
2107 *
2108 * @brief Establish a link to a peer device
2109 *
2110 * @param index - item index from the menu
2111 *
2112 * @return always true
2113 */
2114bool SimpleCentral_doConnect(uint8_t index)
2115{
2116#if (DEFAULT_DEV_DISC_BY_SVC_UUID == TRUE)
2117 GapInit_connect(scanList[index].addrType & MASK_ADDRTYPE_ID,
2118 scanList[index].addr, DEFAULT_INIT_PHY, 0);
2119#else // !DEFAULT_DEV_DISC_BY_SVC_UUID
2120 GapScan_Evt_AdvRpt_t advRpt;
2121
2122 GapScan_getAdvReport(index, &advRpt);
2123
2124 GapInit_connect(advRpt.addrType & MASK_ADDRTYPE_ID,
2125 advRpt.addr, DEFAULT_INIT_PHY, 0);
2126#endif // DEFAULT_DEV_DISC_BY_SVC_UUID
2127
2128 // Enable only "Cancel Connecting" and disable all others in the main menu
2129 tbm_setItemStatus(&scMenuMain, SC_ITEM_CANCELCONN,
2130 (SC_ITEM_ALL & ~SC_ITEM_CANCELCONN));
2131
2132 Display_printf(dispHandle, SC_ROW_NON_CONN, 0, "Connecting...");
2133
2134 tbm_goTo(&scMenuMain);
2135
2136 return (true);
2137}
2138
2139/*********************************************************************
2140 * @fn SimpleCentral_doCancelConnecting
2141 *
2142 * @brief Cancel on-going connection attempt
2143 *
2144 * @param index - item index from the menu
2145 *
2146 * @return always true
2147 */
2148bool SimpleCentral_doCancelConnecting(uint8_t index)
2149{
2150 (void) index;
2151
2152 GapInit_cancelConnect();
2153
2154 return (true);
2155}
2156
2157/*********************************************************************
2158 * @fn SimpleCentral_doSelectConn
2159 *
2160 * @brief Select a connection to communicate with
2161 *
2162 * @param index - item index from the menu
2163 *
2164 * @return always true
2165 */
2166bool SimpleCentral_doSelectConn(uint8_t index)
2167{
2168 uint32_t itemsToDisable = SC_ITEM_NONE;
2169
2170 // index cannot be equal to or greater than MAX_NUM_BLE_CONNS
2171 SIMPLECENTRAL_ASSERT(index < MAX_NUM_BLE_CONNS);
2172
2173 scConnHandle = connList[index].connHandle;
2174
2175 if (connList[index].charHandle == 0)
2176 {
2177 // Initiate service discovery
2178 SimpleCentral_enqueueMsg(SC_EVT_SVC_DISC, 0, NULL);
2179
2180 // Diable GATT Read/Write until simple service is found
2181 itemsToDisable = SC_ITEM_GATTREAD | SC_ITEM_GATTWRITE;
2182 }
2183
2184 // Set the menu title and go to this connection's context
2185 TBM_SET_TITLE(&scMenuPerConn, TBM_GET_ACTION_DESC(&scMenuSelectConn, index));
2186
2187 // Set RSSI items properly depending on current state
2188 if (connList[index].pRssiClock == NULL)
2189 {
2190 tbm_setItemStatus(&scMenuPerConn,
2191 SC_ITEM_STRTRSSI, SC_ITEM_STOPRSSI | itemsToDisable);
2192 }
2193 else
2194 {
2195 tbm_setItemStatus(&scMenuPerConn,
2196 SC_ITEM_STOPRSSI, SC_ITEM_STRTRSSI | itemsToDisable);
2197 }
2198
2199 // Clear non-connection-related message
2200 Display_clearLine(dispHandle, SC_ROW_NON_CONN);
2201
2202 tbm_goTo(&scMenuPerConn);
2203
2204 return (true);
2205}
2206
2207/*********************************************************************
2208 * @fn SimpleCentral_doGattRead
2209 *
2210 * @brief GATT Read
2211 *
2212 * @param index - item index from the menu
2213 *
2214 * @return always true
2215 */
2216bool SimpleCentral_doGattRead(uint8_t index)
2217{
2218 attReadReq_t req;
2219 uint8_t connIndex = SimpleCentral_getConnIndex(scConnHandle);
2220
2221 // connIndex cannot be equal to or greater than MAX_NUM_BLE_CONNS
2222 SIMPLECENTRAL_ASSERT(connIndex < MAX_NUM_BLE_CONNS);
2223
2224 req.handle = connList[connIndex].charHandle;
2225 GATT_ReadCharValue(scConnHandle, &req, selfEntity);
2226
2227 return (true);
2228}
2229
2230/*********************************************************************
2231 * @fn SimpleCentral_doGattWrite
2232 *
2233 * @brief GATT Write
2234 *
2235 * @param index - item index from the menu
2236 *
2237 * @return always true
2238 */
2239bool SimpleCentral_doGattWrite(uint8_t index)
2240{
2241 status_t status;
2242 uint8_t charVals[4] = { 0x00, 0x55, 0xAA, 0xFF }; // Should be consistent with
2243 // those in scMenuGattWrite
2244
2245 attWriteReq_t req;
2246
2247 req.pValue = GATT_bm_alloc(scConnHandle, ATT_WRITE_REQ, 1, NULL);
2248
2249 if ( req.pValue != NULL )
2250 {
2251 uint8_t connIndex = SimpleCentral_getConnIndex(scConnHandle);
2252
2253 // connIndex cannot be equal to or greater than MAX_NUM_BLE_CONNS
2254 SIMPLECENTRAL_ASSERT(connIndex < MAX_NUM_BLE_CONNS);
2255
2256 req.handle = connList[connIndex].charHandle;
2257 req.len = 1;
2258 charVal = charVals[index];
2259 req.pValue[0] = charVal;
2260 req.sig = 0;
2261 req.cmd = 0;
2262
2263 status = GATT_WriteCharValue(scConnHandle, &req, selfEntity);
2264 if ( status != SUCCESS )
2265 {
2266 GATT_bm_free((gattMsg_t *)&req, ATT_WRITE_REQ);
2267 }
2268 }
2269
2270 return (true);
2271}
2272
2273/*********************************************************************
2274 * @fn SimpleCentral_doRssiRead
2275 *
2276 * @brief Toggle RSSI Read
2277 *
2278 * @param index - item index from the menu
2279 *
2280 * @return always true
2281 */
2282bool SimpleCentral_doRssiRead(uint8_t index)
2283{
2284 status_t status;
2285
2286 if ((1 << index) == SC_ITEM_STRTRSSI)
2287 {
2288 if ((status = SimpleCentral_StartRssi()) == SUCCESS)
2289 {
2290 tbm_setItemStatus(&scMenuPerConn, SC_ITEM_STOPRSSI, SC_ITEM_STRTRSSI);
2291 }
2292 }
2293 else // SC_ITEM_STOP_RSSI
2294 {
2295 if ((status = SimpleCentral_CancelRssi(scConnHandle)) == SUCCESS)
2296 {
2297 tbm_setItemStatus(&scMenuPerConn, SC_ITEM_STRTRSSI, SC_ITEM_STOPRSSI);
2298 }
2299 }
2300
2301 return ((status == SUCCESS) ? true : false);
2302}
2303
2304/*********************************************************************
2305 * @fn SimpleCentral_doConnUpdate
2306 *
2307 * @brief Initiate Connection Update procedure
2308 *
2309 * @param index - item index from the menu
2310 *
2311 * @return always true
2312 */
2313bool SimpleCentral_doConnUpdate(uint8_t index)
2314{
2315 gapUpdateLinkParamReq_t params;
2316
2317 (void) index;
2318
2319 params.connectionHandle = scConnHandle;
2320 params.intervalMin = DEFAULT_UPDATE_MIN_CONN_INTERVAL;
2321 params.intervalMax = DEFAULT_UPDATE_MAX_CONN_INTERVAL;
2322 params.connLatency = DEFAULT_UPDATE_SLAVE_LATENCY;
2323
2324 linkDBInfo_t linkInfo;
2325 if (linkDB_GetInfo(scConnHandle, &linkInfo) == SUCCESS)
2326 {
2327 if (linkInfo.connTimeout == DEFAULT_UPDATE_CONN_TIMEOUT)
2328 {
2329 params.connTimeout = DEFAULT_UPDATE_CONN_TIMEOUT + 200;
2330 }
2331 else
2332 {
2333 params.connTimeout = DEFAULT_UPDATE_CONN_TIMEOUT;
2334 }
2335 }
2336 else
2337 {
2338 Display_printf(dispHandle, SC_ROW_CUR_CONN, 0,
2339 "update :%s, Unable to find link information",
2340 Util_convertBdAddr2Str(linkInfo.addr));
2341 }
2342 GAP_UpdateLinkParamReq(¶ms);
2343
2344 Display_printf(dispHandle, SC_ROW_CUR_CONN, 0, "Param update Request:connTimeout =%d",
2345 params.connTimeout*CONN_TIMEOUT_MS_CONVERSION);
2346
2347 return (true);
2348}
2349
2350/*********************************************************************
2351 * @fn SimpleCentral_doSetConnPhy
2352 *
2353 * @brief Set Connection PHY preference.
2354 *
2355 * @param index - 0: 1M PHY
2356 * 1: 2M PHY
2357 * 2: 1M + 2M PHY
2358 * 3: CODED PHY (Long range)
2359 * 4: 1M + 2M + CODED PHY
2360 *
2361 * @return always true
2362 */
2363bool SimpleCentral_doSetConnPhy(uint8_t index)
2364{
2365 static uint8_t phy[] = {
2366 HCI_PHY_1_MBPS, HCI_PHY_2_MBPS, HCI_PHY_1_MBPS | HCI_PHY_2_MBPS,
2367 HCI_PHY_CODED, HCI_PHY_1_MBPS | HCI_PHY_2_MBPS | HCI_PHY_CODED,
2368 };
2369
2370 // Set Phy Preference on the current connection. Apply the same value
2371 // for RX and TX. For more information, see the LE 2M PHY section in the User's Guide:
2372 // http://software-dl.ti.com/lprf/ble5stack-latest/
2373 // Note PHYs are already enabled by default in build_config.opt in stack project.
2374 HCI_LE_SetPhyCmd(scConnHandle, 0, phy[index], phy[index], 0);
2375
2376 Display_printf(dispHandle, SC_ROW_CUR_CONN, 0, "PHY preference: %s",
2377 TBM_GET_ACTION_DESC(&scMenuConnPhy, index));
2378
2379 return (true);
2380}
2381
2382/*********************************************************************
2383 * @fn SimpleCentral_doDisconnect
2384 *
2385 * @brief Disconnect the specified link
2386 *
2387 * @param index - item index from the menu
2388 *
2389 * @return always true
2390 */
2391bool SimpleCentral_doDisconnect(uint8_t index)
2392{
2393 (void) index;
2394
2395 GAP_TerminateLinkReq(scConnHandle, HCI_DISCONNECT_REMOTE_USER_TERM);
2396
2397 return (true);
2398}
2399
2400/*********************************************************************
2401 * @fn SimpleCentral_menuSwitchCb
2402 *
2403 * @brief Detect menu context switching
2404 *
2405 * @param pMenuObjCurr - the current menu object
2406 * @param pMenuObjNext - the menu object the context is about to switch to
2407 *
2408 * @return none
2409 */
2410static void SimpleCentral_menuSwitchCb(tbmMenuObj_t* pMenuObjCurr,
2411 tbmMenuObj_t* pMenuObjNext)
2412{
2413 // interested in only the events of
2414 // entering scMenuConnect, scMenuSelectConn, and scMenuMain for now
2415 if (pMenuObjNext == &scMenuConnect)
2416 {
2417 uint8_t i, j;
2418 uint32_t itemsToDisable = SC_ITEM_NONE;
2419
2420 for (i = 0; i < TBM_GET_NUM_ITEM(&scMenuConnect); i++)
2421 {
2422 for (j = 0; j < MAX_NUM_BLE_CONNS; j++)
2423 {
2424 if ((connList[j].connHandle != CONNHANDLE_INVALID) &&
2425 !memcmp(TBM_GET_ACTION_DESC(&scMenuConnect, i),
2426 Util_convertBdAddr2Str(connList[j].addr),
2427 SC_ADDR_STR_SIZE))
2428 {
2429 // Already connected. Add to the set to be disabled.
2430 itemsToDisable |= (1 << i);
2431 }
2432 }
2433 }
2434
2435 // Eventually only non-connected device addresses will be displayed.
2436 tbm_setItemStatus(&scMenuConnect,
2437 SC_ITEM_ALL & ~itemsToDisable, itemsToDisable);
2438 }
2439 else if (pMenuObjNext == &scMenuSelectConn)
2440 {
2441 static uint8_t* pAddrs;
2442 uint8_t* pAddrTemp;
2443
2444 if (pAddrs != NULL)
2445 {
2446 ICall_free(pAddrs);
2447 }
2448
2449 // Allocate buffer to display addresses
2450 pAddrs = ICall_malloc(numConn * SC_ADDR_STR_SIZE);
2451
2452 if (pAddrs == NULL)
2453 {
2454 TBM_SET_NUM_ITEM(&scMenuSelectConn, 0);
2455 }
2456 else
2457 {
2458 uint8_t i;
2459
2460 TBM_SET_NUM_ITEM(&scMenuSelectConn, MAX_NUM_BLE_CONNS);
2461
2462 pAddrTemp = pAddrs;
2463
2464 // Add active connection info to the menu object
2465 for (i = 0; i < MAX_NUM_BLE_CONNS; i++)
2466 {
2467 if (connList[i].connHandle != CONNHANDLE_INVALID)
2468 {
2469 // This connection is active. Set the corresponding menu item with
2470 // the address of this connection and enable the item.
2471 memcpy(pAddrTemp, Util_convertBdAddr2Str(connList[i].addr),
2472 SC_ADDR_STR_SIZE);
2473 TBM_SET_ACTION_DESC(&scMenuSelectConn, i, pAddrTemp);
2474 tbm_setItemStatus(&scMenuSelectConn, (1 << i), SC_ITEM_NONE);
2475 pAddrTemp += SC_ADDR_STR_SIZE;
2476 }
2477 else
2478 {
2479 // This connection is not active. Disable the corresponding menu item.
2480 tbm_setItemStatus(&scMenuSelectConn, SC_ITEM_NONE, (1 << i));
2481 }
2482 }
2483 }
2484 }
2485 else if (pMenuObjNext == &scMenuMain)
2486 {
2487 // Now we are not in a specific connection's context
2488 scConnHandle = CONNHANDLE_INVALID;
2489
2490 // Clear connection-related message
2491 Display_clearLine(dispHandle, SC_ROW_CUR_CONN);
2492 }
2493}
2494
2495
2496/*********************************************************************
2497*********************************************************************/