High level Bluetooth Low Energy API and radio abstraction layer

Fork of BLE_API by Bluetooth Low Energy

Committer:
Vincent Coubard
Date:
Wed Sep 14 14:17:55 2016 +0100
Branch:
cd1b77dd31654b728a317d0ac7e9bc4e04b17468
Revision:
1203:d4f1ef46d97b
Parent:
1201:9b71aac42d14
Sync with cd1b77dd31654b728a317d0ac7e9bc4e04b17468

2016-08-31 09:54:29+01:00: Vincent Coubard
Synchronize with ble folder in mbed-os 5.

Who changed what in which revision?

UserRevisionLine numberNew contents of line
Vincent Coubard 1201:9b71aac42d14 1 /* mbed Microcontroller Library
Vincent Coubard 1201:9b71aac42d14 2 * Copyright (c) 2006-2013 ARM Limited
Vincent Coubard 1201:9b71aac42d14 3 *
Vincent Coubard 1201:9b71aac42d14 4 * Licensed under the Apache License, Version 2.0 (the "License");
Vincent Coubard 1201:9b71aac42d14 5 * you may not use this file except in compliance with the License.
Vincent Coubard 1201:9b71aac42d14 6 * You may obtain a copy of the License at
Vincent Coubard 1201:9b71aac42d14 7 *
Vincent Coubard 1201:9b71aac42d14 8 * http://www.apache.org/licenses/LICENSE-2.0
Vincent Coubard 1201:9b71aac42d14 9 *
Vincent Coubard 1201:9b71aac42d14 10 * Unless required by applicable law or agreed to in writing, software
Vincent Coubard 1201:9b71aac42d14 11 * distributed under the License is distributed on an "AS IS" BASIS,
Vincent Coubard 1201:9b71aac42d14 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
Vincent Coubard 1201:9b71aac42d14 13 * See the License for the specific language governing permissions and
Vincent Coubard 1201:9b71aac42d14 14 * limitations under the License.
Vincent Coubard 1201:9b71aac42d14 15 */
Vincent Coubard 1201:9b71aac42d14 16
Vincent Coubard 1201:9b71aac42d14 17 #include "ble/BLE.h"
Vincent Coubard 1201:9b71aac42d14 18 #include "ble/BLEInstanceBase.h"
Vincent Coubard 1201:9b71aac42d14 19
Vincent Coubard 1201:9b71aac42d14 20 #if defined(TARGET_OTA_ENABLED)
Vincent Coubard 1201:9b71aac42d14 21 #include "ble/services/DFUService.h"
Vincent Coubard 1201:9b71aac42d14 22 #endif
Vincent Coubard 1201:9b71aac42d14 23
Vincent Coubard 1201:9b71aac42d14 24 #ifdef YOTTA_CFG_MBED_OS
Vincent Coubard 1201:9b71aac42d14 25 #include <minar/minar.h>
Vincent Coubard 1201:9b71aac42d14 26 #endif
Vincent Coubard 1201:9b71aac42d14 27
Vincent Coubard 1203:d4f1ef46d97b 28 #if !defined(YOTTA_CFG_MBED_OS)
Vincent Coubard 1203:d4f1ef46d97b 29 #include <mbed_error.h>
Vincent Coubard 1203:d4f1ef46d97b 30 #include <toolchain.h>
Vincent Coubard 1203:d4f1ef46d97b 31 #endif
Vincent Coubard 1203:d4f1ef46d97b 32
Vincent Coubard 1201:9b71aac42d14 33 ble_error_t
Vincent Coubard 1201:9b71aac42d14 34 BLE::initImplementation(FunctionPointerWithContext<InitializationCompleteCallbackContext*> callback)
Vincent Coubard 1201:9b71aac42d14 35 {
Vincent Coubard 1201:9b71aac42d14 36 ble_error_t err = transport->init(instanceID, callback);
Vincent Coubard 1201:9b71aac42d14 37 if (err != BLE_ERROR_NONE) {
Vincent Coubard 1201:9b71aac42d14 38 return err;
Vincent Coubard 1201:9b71aac42d14 39 }
Vincent Coubard 1201:9b71aac42d14 40
Vincent Coubard 1201:9b71aac42d14 41 /* Platforms enabled for DFU should introduce the DFU Service into
Vincent Coubard 1201:9b71aac42d14 42 * applications automatically. */
Vincent Coubard 1201:9b71aac42d14 43 #if defined(TARGET_OTA_ENABLED)
Vincent Coubard 1201:9b71aac42d14 44 static DFUService dfu(*this); // defined static so that the object remains alive
Vincent Coubard 1201:9b71aac42d14 45 #endif // TARGET_OTA_ENABLED
Vincent Coubard 1201:9b71aac42d14 46
Vincent Coubard 1201:9b71aac42d14 47 return BLE_ERROR_NONE;
Vincent Coubard 1201:9b71aac42d14 48 }
Vincent Coubard 1201:9b71aac42d14 49
Vincent Coubard 1201:9b71aac42d14 50 /**
Vincent Coubard 1201:9b71aac42d14 51 * BLE::Instance() and BLE constructor rely upon a static array of initializers
Vincent Coubard 1201:9b71aac42d14 52 * to create actual BLE transport instances. A description of these instances
Vincent Coubard 1201:9b71aac42d14 53 * and initializers is supposed to be put in some .json file contributing to
Vincent Coubard 1201:9b71aac42d14 54 * yotta's configuration (typically in the target definition described by
Vincent Coubard 1201:9b71aac42d14 55 * target.json). Here's a sample:
Vincent Coubard 1201:9b71aac42d14 56 *
Vincent Coubard 1201:9b71aac42d14 57 * "config": {
Vincent Coubard 1201:9b71aac42d14 58 * ...
Vincent Coubard 1201:9b71aac42d14 59 * "ble_instances": {
Vincent Coubard 1201:9b71aac42d14 60 * "count": 1,
Vincent Coubard 1201:9b71aac42d14 61 * "0" : {
Vincent Coubard 1201:9b71aac42d14 62 * "initializer" : "createBLEInstance"
Vincent Coubard 1201:9b71aac42d14 63 * }
Vincent Coubard 1201:9b71aac42d14 64 * }
Vincent Coubard 1201:9b71aac42d14 65 * ...
Vincent Coubard 1201:9b71aac42d14 66 * }
Vincent Coubard 1201:9b71aac42d14 67 *
Vincent Coubard 1201:9b71aac42d14 68 * The following macros result in translating the above config into a static
Vincent Coubard 1201:9b71aac42d14 69 * array: instanceConstructors.
Vincent Coubard 1201:9b71aac42d14 70 */
Vincent Coubard 1201:9b71aac42d14 71 #ifdef YOTTA_CFG_BLE_INSTANCES_COUNT
Vincent Coubard 1201:9b71aac42d14 72 #define CONCATENATE(A, B) A ## B
Vincent Coubard 1201:9b71aac42d14 73 #define EXPAND(X) X /* this adds a level of indirection needed to allow macro-expansion following a token-paste operation (see use of CONCATENATE() below). */
Vincent Coubard 1201:9b71aac42d14 74
Vincent Coubard 1201:9b71aac42d14 75 #define INITIALIZER_LIST_FOR_INSTANCE_CONSTRUCTORS_1 YOTTA_CFG_BLE_INSTANCES_0_INITIALIZER
Vincent Coubard 1201:9b71aac42d14 76 #define INITIALIZER_LIST_FOR_INSTANCE_CONSTRUCTORS_2 INITIALIZER_LIST_FOR_INSTANCE_CONSTRUCTORS_1, YOTTA_CFG_BLE_INSTANCES_1_INITIALIZER
Vincent Coubard 1201:9b71aac42d14 77 #define INITIALIZER_LIST_FOR_INSTANCE_CONSTRUCTORS_3 INITIALIZER_LIST_FOR_INSTANCE_CONSTRUCTORS_2, YOTTA_CFG_BLE_INSTANCES_2_INITIALIZER
Vincent Coubard 1201:9b71aac42d14 78 #define INITIALIZER_LIST_FOR_INSTANCE_CONSTRUCTORS_4 INITIALIZER_LIST_FOR_INSTANCE_CONSTRUCTORS_3, YOTTA_CFG_BLE_INSTANCES_3_INITIALIZER
Vincent Coubard 1201:9b71aac42d14 79 #define INITIALIZER_LIST_FOR_INSTANCE_CONSTRUCTORS_5 INITIALIZER_LIST_FOR_INSTANCE_CONSTRUCTORS_4, YOTTA_CFG_BLE_INSTANCES_4_INITIALIZER
Vincent Coubard 1201:9b71aac42d14 80 /* ... add more of the above if ever needed */
Vincent Coubard 1201:9b71aac42d14 81
Vincent Coubard 1201:9b71aac42d14 82 #define INITIALIZER_LIST_FOR_INSTANCE_CONSTRUCTORS(N) EXPAND(CONCATENATE(INITIALIZER_LIST_FOR_INSTANCE_CONSTRUCTORS_, N))
Vincent Coubard 1201:9b71aac42d14 83 #elif !defined(INITIALIZER_LIST_FOR_INSTANCE_CONSTRUCTORS)
Vincent Coubard 1201:9b71aac42d14 84 /*
Vincent Coubard 1201:9b71aac42d14 85 * The following applies when building without yotta. By default BLE_API provides
Vincent Coubard 1201:9b71aac42d14 86 * a trivial initializer list containing a single constructor: createBLEInstance.
Vincent Coubard 1201:9b71aac42d14 87 * This may be overridden.
Vincent Coubard 1201:9b71aac42d14 88 */
Vincent Coubard 1201:9b71aac42d14 89 #define INITIALIZER_LIST_FOR_INSTANCE_CONSTRUCTORS createBLEInstance
Vincent Coubard 1203:d4f1ef46d97b 90
Vincent Coubard 1203:d4f1ef46d97b 91 // yotta unlike mbed-cli has proper dependency mechanisms
Vincent Coubard 1203:d4f1ef46d97b 92 // It is not required to defined a stub for createBLEInstance
Vincent Coubard 1203:d4f1ef46d97b 93 #if !defined(YOTTA_CFG_MBED_OS)
Vincent Coubard 1203:d4f1ef46d97b 94
Vincent Coubard 1203:d4f1ef46d97b 95 // this stub is required by ARMCC otherwise link will systematically fail
Vincent Coubard 1203:d4f1ef46d97b 96 MBED_WEAK BLEInstanceBase* createBLEInstance() {
Vincent Coubard 1203:d4f1ef46d97b 97 error("Please provide an implementation for mbed BLE");
Vincent Coubard 1203:d4f1ef46d97b 98 return NULL;
Vincent Coubard 1203:d4f1ef46d97b 99 }
Vincent Coubard 1203:d4f1ef46d97b 100
Vincent Coubard 1203:d4f1ef46d97b 101 #endif
Vincent Coubard 1203:d4f1ef46d97b 102
Vincent Coubard 1203:d4f1ef46d97b 103
Vincent Coubard 1201:9b71aac42d14 104 #endif /* YOTTA_CFG_BLE_INSTANCES_COUNT */
Vincent Coubard 1201:9b71aac42d14 105
Vincent Coubard 1201:9b71aac42d14 106 typedef BLEInstanceBase *(*InstanceConstructor_t)(void);
Vincent Coubard 1201:9b71aac42d14 107 static const InstanceConstructor_t instanceConstructors[BLE::NUM_INSTANCES] = {
Vincent Coubard 1201:9b71aac42d14 108 #ifndef YOTTA_CFG_BLE_INSTANCES_COUNT
Vincent Coubard 1201:9b71aac42d14 109 INITIALIZER_LIST_FOR_INSTANCE_CONSTRUCTORS
Vincent Coubard 1201:9b71aac42d14 110 #else
Vincent Coubard 1201:9b71aac42d14 111 INITIALIZER_LIST_FOR_INSTANCE_CONSTRUCTORS(YOTTA_CFG_BLE_INSTANCES_COUNT)
Vincent Coubard 1201:9b71aac42d14 112 #endif
Vincent Coubard 1201:9b71aac42d14 113 };
Vincent Coubard 1201:9b71aac42d14 114
Vincent Coubard 1201:9b71aac42d14 115 BLE &
Vincent Coubard 1201:9b71aac42d14 116 BLE::Instance(InstanceID_t id)
Vincent Coubard 1201:9b71aac42d14 117 {
Vincent Coubard 1201:9b71aac42d14 118 static BLE *singletons[NUM_INSTANCES];
Vincent Coubard 1201:9b71aac42d14 119 if (id < NUM_INSTANCES) {
Vincent Coubard 1201:9b71aac42d14 120 if (singletons[id] == NULL) {
Vincent Coubard 1201:9b71aac42d14 121 singletons[id] = new BLE(id); /* This object will never be freed. */
Vincent Coubard 1201:9b71aac42d14 122 }
Vincent Coubard 1201:9b71aac42d14 123
Vincent Coubard 1201:9b71aac42d14 124 return *singletons[id];
Vincent Coubard 1201:9b71aac42d14 125 }
Vincent Coubard 1201:9b71aac42d14 126
Vincent Coubard 1201:9b71aac42d14 127 /* we come here only in the case of a bad interfaceID. */
Vincent Coubard 1201:9b71aac42d14 128 static BLE badSingleton(NUM_INSTANCES /* this is a bad index; and will result in a NULL transport. */);
Vincent Coubard 1201:9b71aac42d14 129 return badSingleton;
Vincent Coubard 1201:9b71aac42d14 130 }
Vincent Coubard 1201:9b71aac42d14 131
Vincent Coubard 1201:9b71aac42d14 132 #ifdef YOTTA_CFG_MBED_OS
Vincent Coubard 1201:9b71aac42d14 133 void defaultSchedulingCallback(BLE::OnEventsToProcessCallbackContext* params) {
Vincent Coubard 1201:9b71aac42d14 134 minar::Scheduler::postCallback(&params->ble, &BLE::processEvents);
Vincent Coubard 1201:9b71aac42d14 135 }
Vincent Coubard 1201:9b71aac42d14 136 #else
Vincent Coubard 1201:9b71aac42d14 137 #define defaultSchedulingCallback NULL
Vincent Coubard 1201:9b71aac42d14 138 #endif
Vincent Coubard 1201:9b71aac42d14 139
Vincent Coubard 1201:9b71aac42d14 140
Vincent Coubard 1201:9b71aac42d14 141 BLE::BLE(InstanceID_t instanceIDIn) : instanceID(instanceIDIn), transport(),
Vincent Coubard 1201:9b71aac42d14 142 whenEventsToProcess(defaultSchedulingCallback)
Vincent Coubard 1201:9b71aac42d14 143 {
Vincent Coubard 1201:9b71aac42d14 144 static BLEInstanceBase *transportInstances[NUM_INSTANCES];
Vincent Coubard 1201:9b71aac42d14 145
Vincent Coubard 1201:9b71aac42d14 146 if (instanceID < NUM_INSTANCES) {
Vincent Coubard 1201:9b71aac42d14 147 if (!transportInstances[instanceID]) {
Vincent Coubard 1201:9b71aac42d14 148 transportInstances[instanceID] = instanceConstructors[instanceID](); /* Call the stack's initializer for the transport object. */
Vincent Coubard 1201:9b71aac42d14 149 }
Vincent Coubard 1201:9b71aac42d14 150 transport = transportInstances[instanceID];
Vincent Coubard 1201:9b71aac42d14 151 } else {
Vincent Coubard 1201:9b71aac42d14 152 transport = NULL;
Vincent Coubard 1201:9b71aac42d14 153 }
Vincent Coubard 1201:9b71aac42d14 154 }
Vincent Coubard 1201:9b71aac42d14 155
Vincent Coubard 1201:9b71aac42d14 156 bool BLE::hasInitialized(void) const
Vincent Coubard 1201:9b71aac42d14 157 {
Vincent Coubard 1201:9b71aac42d14 158 if (!transport) {
Vincent Coubard 1201:9b71aac42d14 159 error("bad handle to underlying transport");
Vincent Coubard 1201:9b71aac42d14 160 }
Vincent Coubard 1201:9b71aac42d14 161
Vincent Coubard 1201:9b71aac42d14 162 return transport->hasInitialized();
Vincent Coubard 1201:9b71aac42d14 163 }
Vincent Coubard 1201:9b71aac42d14 164
Vincent Coubard 1201:9b71aac42d14 165 ble_error_t BLE::shutdown(void)
Vincent Coubard 1201:9b71aac42d14 166 {
Vincent Coubard 1201:9b71aac42d14 167 if (!transport) {
Vincent Coubard 1201:9b71aac42d14 168 error("bad handle to underlying transport");
Vincent Coubard 1201:9b71aac42d14 169 }
Vincent Coubard 1201:9b71aac42d14 170
Vincent Coubard 1201:9b71aac42d14 171 return transport->shutdown();
Vincent Coubard 1201:9b71aac42d14 172 }
Vincent Coubard 1201:9b71aac42d14 173
Vincent Coubard 1201:9b71aac42d14 174 const char *BLE::getVersion(void)
Vincent Coubard 1201:9b71aac42d14 175 {
Vincent Coubard 1201:9b71aac42d14 176 if (!transport) {
Vincent Coubard 1201:9b71aac42d14 177 error("bad handle to underlying transport");
Vincent Coubard 1201:9b71aac42d14 178 }
Vincent Coubard 1201:9b71aac42d14 179
Vincent Coubard 1201:9b71aac42d14 180 return transport->getVersion();
Vincent Coubard 1201:9b71aac42d14 181 }
Vincent Coubard 1201:9b71aac42d14 182
Vincent Coubard 1201:9b71aac42d14 183 const Gap &BLE::gap() const
Vincent Coubard 1201:9b71aac42d14 184 {
Vincent Coubard 1201:9b71aac42d14 185 if (!transport) {
Vincent Coubard 1201:9b71aac42d14 186 error("bad handle to underlying transport");
Vincent Coubard 1201:9b71aac42d14 187 }
Vincent Coubard 1201:9b71aac42d14 188
Vincent Coubard 1201:9b71aac42d14 189 return transport->getGap();
Vincent Coubard 1201:9b71aac42d14 190 }
Vincent Coubard 1201:9b71aac42d14 191
Vincent Coubard 1201:9b71aac42d14 192 Gap &BLE::gap()
Vincent Coubard 1201:9b71aac42d14 193 {
Vincent Coubard 1201:9b71aac42d14 194 if (!transport) {
Vincent Coubard 1201:9b71aac42d14 195 error("bad handle to underlying transport");
Vincent Coubard 1201:9b71aac42d14 196 }
Vincent Coubard 1201:9b71aac42d14 197
Vincent Coubard 1201:9b71aac42d14 198 return transport->getGap();
Vincent Coubard 1201:9b71aac42d14 199 }
Vincent Coubard 1201:9b71aac42d14 200
Vincent Coubard 1201:9b71aac42d14 201 const GattServer& BLE::gattServer() const
Vincent Coubard 1201:9b71aac42d14 202 {
Vincent Coubard 1201:9b71aac42d14 203 if (!transport) {
Vincent Coubard 1201:9b71aac42d14 204 error("bad handle to underlying transport");
Vincent Coubard 1201:9b71aac42d14 205 }
Vincent Coubard 1201:9b71aac42d14 206
Vincent Coubard 1201:9b71aac42d14 207 return transport->getGattServer();
Vincent Coubard 1201:9b71aac42d14 208 }
Vincent Coubard 1201:9b71aac42d14 209
Vincent Coubard 1201:9b71aac42d14 210 GattServer& BLE::gattServer()
Vincent Coubard 1201:9b71aac42d14 211 {
Vincent Coubard 1201:9b71aac42d14 212 if (!transport) {
Vincent Coubard 1201:9b71aac42d14 213 error("bad handle to underlying transport");
Vincent Coubard 1201:9b71aac42d14 214 }
Vincent Coubard 1201:9b71aac42d14 215
Vincent Coubard 1201:9b71aac42d14 216 return transport->getGattServer();
Vincent Coubard 1201:9b71aac42d14 217 }
Vincent Coubard 1201:9b71aac42d14 218
Vincent Coubard 1201:9b71aac42d14 219 const GattClient& BLE::gattClient() const
Vincent Coubard 1201:9b71aac42d14 220 {
Vincent Coubard 1201:9b71aac42d14 221 if (!transport) {
Vincent Coubard 1201:9b71aac42d14 222 error("bad handle to underlying transport");
Vincent Coubard 1201:9b71aac42d14 223 }
Vincent Coubard 1201:9b71aac42d14 224
Vincent Coubard 1201:9b71aac42d14 225 return transport->getGattClient();
Vincent Coubard 1201:9b71aac42d14 226 }
Vincent Coubard 1201:9b71aac42d14 227
Vincent Coubard 1201:9b71aac42d14 228 GattClient& BLE::gattClient()
Vincent Coubard 1201:9b71aac42d14 229 {
Vincent Coubard 1201:9b71aac42d14 230 if (!transport) {
Vincent Coubard 1201:9b71aac42d14 231 error("bad handle to underlying transport");
Vincent Coubard 1201:9b71aac42d14 232 }
Vincent Coubard 1201:9b71aac42d14 233
Vincent Coubard 1201:9b71aac42d14 234 return transport->getGattClient();
Vincent Coubard 1201:9b71aac42d14 235 }
Vincent Coubard 1201:9b71aac42d14 236
Vincent Coubard 1201:9b71aac42d14 237 const SecurityManager& BLE::securityManager() const
Vincent Coubard 1201:9b71aac42d14 238 {
Vincent Coubard 1201:9b71aac42d14 239 if (!transport) {
Vincent Coubard 1201:9b71aac42d14 240 error("bad handle to underlying transport");
Vincent Coubard 1201:9b71aac42d14 241 }
Vincent Coubard 1201:9b71aac42d14 242
Vincent Coubard 1201:9b71aac42d14 243 return transport->getSecurityManager();
Vincent Coubard 1201:9b71aac42d14 244 }
Vincent Coubard 1201:9b71aac42d14 245
Vincent Coubard 1201:9b71aac42d14 246 SecurityManager& BLE::securityManager()
Vincent Coubard 1201:9b71aac42d14 247 {
Vincent Coubard 1201:9b71aac42d14 248 if (!transport) {
Vincent Coubard 1201:9b71aac42d14 249 error("bad handle to underlying transport");
Vincent Coubard 1201:9b71aac42d14 250 }
Vincent Coubard 1201:9b71aac42d14 251
Vincent Coubard 1201:9b71aac42d14 252 return transport->getSecurityManager();
Vincent Coubard 1201:9b71aac42d14 253 }
Vincent Coubard 1201:9b71aac42d14 254
Vincent Coubard 1201:9b71aac42d14 255 void BLE::waitForEvent(void)
Vincent Coubard 1201:9b71aac42d14 256 {
Vincent Coubard 1201:9b71aac42d14 257 if (!transport) {
Vincent Coubard 1201:9b71aac42d14 258 error("bad handle to underlying transport");
Vincent Coubard 1201:9b71aac42d14 259 }
Vincent Coubard 1201:9b71aac42d14 260
Vincent Coubard 1201:9b71aac42d14 261 transport->waitForEvent();
Vincent Coubard 1201:9b71aac42d14 262 }
Vincent Coubard 1201:9b71aac42d14 263
Vincent Coubard 1201:9b71aac42d14 264 void BLE::processEvents()
Vincent Coubard 1201:9b71aac42d14 265 {
Vincent Coubard 1201:9b71aac42d14 266 if (!transport) {
Vincent Coubard 1201:9b71aac42d14 267 error("bad handle to underlying transport");
Vincent Coubard 1201:9b71aac42d14 268 }
Vincent Coubard 1201:9b71aac42d14 269
Vincent Coubard 1201:9b71aac42d14 270 transport->processEvents();
Vincent Coubard 1201:9b71aac42d14 271 }
Vincent Coubard 1201:9b71aac42d14 272
Vincent Coubard 1201:9b71aac42d14 273 void BLE::onEventsToProcess(const BLE::OnEventsToProcessCallback_t& callback)
Vincent Coubard 1201:9b71aac42d14 274 {
Vincent Coubard 1201:9b71aac42d14 275 whenEventsToProcess = callback;
Vincent Coubard 1201:9b71aac42d14 276 }
Vincent Coubard 1201:9b71aac42d14 277
Vincent Coubard 1201:9b71aac42d14 278 void BLE::signalEventsToProcess()
Vincent Coubard 1201:9b71aac42d14 279 {
Vincent Coubard 1201:9b71aac42d14 280 if (whenEventsToProcess) {
Vincent Coubard 1201:9b71aac42d14 281 OnEventsToProcessCallbackContext params = {
Vincent Coubard 1201:9b71aac42d14 282 *this
Vincent Coubard 1201:9b71aac42d14 283 };
Vincent Coubard 1201:9b71aac42d14 284 whenEventsToProcess(&params);
Vincent Coubard 1201:9b71aac42d14 285 }
rgrover1 712:b04b5db36865 286 }