a serial library to communicate with pebble time's smart strap interface

Dependents:   xadow_smartstrap_for_pebble

Committer:
KillingJacky
Date:
Wed Nov 04 09:58:41 2015 +0000
Revision:
0:e4dad9e53f06
initial commit;

Who changed what in which revision?

UserRevisionLine numberNew contents of line
KillingJacky 0:e4dad9e53f06 1 /* This library is for communicating with a Pebble Time via the accessory port for Smart Straps. */
KillingJacky 0:e4dad9e53f06 2
KillingJacky 0:e4dad9e53f06 3 #include "PebbleSerial.h"
KillingJacky 0:e4dad9e53f06 4
KillingJacky 0:e4dad9e53f06 5 #include "crc.h"
KillingJacky 0:e4dad9e53f06 6 #include "encoding.h"
KillingJacky 0:e4dad9e53f06 7
KillingJacky 0:e4dad9e53f06 8 #define PROTOCOL_VERSION 1
KillingJacky 0:e4dad9e53f06 9 #define GENERIC_SERVICE_VERSION 1
KillingJacky 0:e4dad9e53f06 10
KillingJacky 0:e4dad9e53f06 11 #define FRAME_MIN_LENGTH 8
KillingJacky 0:e4dad9e53f06 12 #define FRAME_VERSION_OFFSET 0
KillingJacky 0:e4dad9e53f06 13 #define FRAME_FLAGS_OFFSET 1
KillingJacky 0:e4dad9e53f06 14 #define FRAME_PROFILE_OFFSET 5
KillingJacky 0:e4dad9e53f06 15 #define FRAME_PAYLOAD_OFFSET 7
KillingJacky 0:e4dad9e53f06 16
KillingJacky 0:e4dad9e53f06 17 #define FLAGS_IS_READ_OFFSET 0
KillingJacky 0:e4dad9e53f06 18 #define FLAGS_IS_MASTER_OFFSET 1
KillingJacky 0:e4dad9e53f06 19 #define FLAGS_IS_NOTIFICATION_OFFSET 2
KillingJacky 0:e4dad9e53f06 20 #define FLAGS_RESERVED_OFFSET 3
KillingJacky 0:e4dad9e53f06 21 #define FLAGS_IS_READ_MASK (0x1 << FLAGS_IS_READ_OFFSET)
KillingJacky 0:e4dad9e53f06 22 #define FLAGS_IS_MASTER_MASK (0x1 << FLAGS_IS_MASTER_OFFSET)
KillingJacky 0:e4dad9e53f06 23 #define FLAGS_IS_NOTIFICATION_MASK (0x1 << FLAGS_IS_NOTIFICATION_OFFSET)
KillingJacky 0:e4dad9e53f06 24 #define FLAGS_RESERVED_MASK (~(FLAGS_IS_READ_MASK | \
KillingJacky 0:e4dad9e53f06 25 FLAGS_IS_MASTER_MASK | \
KillingJacky 0:e4dad9e53f06 26 FLAGS_IS_NOTIFICATION_MASK))
KillingJacky 0:e4dad9e53f06 27 #define FLAGS_GET(flags, mask, offset) (((flags) & mask) >> offset)
KillingJacky 0:e4dad9e53f06 28 #define FLAGS_SET(flags, mask, offset, value) \
KillingJacky 0:e4dad9e53f06 29 (flags) = ((flags) & ~mask) | (((value) << offset) & mask)
KillingJacky 0:e4dad9e53f06 30
KillingJacky 0:e4dad9e53f06 31 typedef enum {
KillingJacky 0:e4dad9e53f06 32 SmartstrapProfileInvalid = 0x00,
KillingJacky 0:e4dad9e53f06 33 SmartstrapProfileLinkControl = 0x01,
KillingJacky 0:e4dad9e53f06 34 SmartstrapProfileRawData = 0x02,
KillingJacky 0:e4dad9e53f06 35 SmartstrapProfileGenericService = 0x03,
KillingJacky 0:e4dad9e53f06 36 NumSmartstrapProfiles
KillingJacky 0:e4dad9e53f06 37 } SmartstrapProfile;
KillingJacky 0:e4dad9e53f06 38
KillingJacky 0:e4dad9e53f06 39 typedef enum {
KillingJacky 0:e4dad9e53f06 40 LinkControlTypeInvalid = 0,
KillingJacky 0:e4dad9e53f06 41 LinkControlTypeStatus = 1,
KillingJacky 0:e4dad9e53f06 42 LinkControlTypeProfiles = 2,
KillingJacky 0:e4dad9e53f06 43 LinkControlTypeBaud = 3,
KillingJacky 0:e4dad9e53f06 44 NumLinkControlTypes
KillingJacky 0:e4dad9e53f06 45 } LinkControlType;
KillingJacky 0:e4dad9e53f06 46
KillingJacky 0:e4dad9e53f06 47 typedef enum {
KillingJacky 0:e4dad9e53f06 48 LinkControlStatusOk = 0,
KillingJacky 0:e4dad9e53f06 49 LinkControlStatusBaudRate = 1,
KillingJacky 0:e4dad9e53f06 50 LinkControlStatusDisconnect = 2
KillingJacky 0:e4dad9e53f06 51 } LinkControlStatus;
KillingJacky 0:e4dad9e53f06 52
KillingJacky 0:e4dad9e53f06 53 typedef struct {
KillingJacky 0:e4dad9e53f06 54 uint8_t version;
KillingJacky 0:e4dad9e53f06 55 uint32_t flags;
KillingJacky 0:e4dad9e53f06 56 uint16_t profile;
KillingJacky 0:e4dad9e53f06 57 } FrameHeader;
KillingJacky 0:e4dad9e53f06 58
KillingJacky 0:e4dad9e53f06 59 typedef struct {
KillingJacky 0:e4dad9e53f06 60 FrameHeader header;
KillingJacky 0:e4dad9e53f06 61 uint8_t *payload;
KillingJacky 0:e4dad9e53f06 62 uint8_t checksum;
KillingJacky 0:e4dad9e53f06 63 size_t length;
KillingJacky 0:e4dad9e53f06 64 size_t max_payload_length;
KillingJacky 0:e4dad9e53f06 65 uint8_t footer_byte;
KillingJacky 0:e4dad9e53f06 66 bool should_drop;
KillingJacky 0:e4dad9e53f06 67 bool read_ready;
KillingJacky 0:e4dad9e53f06 68 bool is_read;
KillingJacky 0:e4dad9e53f06 69 EncodingStreamingContext encoding_ctx;
KillingJacky 0:e4dad9e53f06 70 } PebbleFrameInfo;
KillingJacky 0:e4dad9e53f06 71
KillingJacky 0:e4dad9e53f06 72 typedef struct __attribute__((packed)) {
KillingJacky 0:e4dad9e53f06 73 uint8_t version;
KillingJacky 0:e4dad9e53f06 74 uint16_t service_id;
KillingJacky 0:e4dad9e53f06 75 uint16_t attribute_id;
KillingJacky 0:e4dad9e53f06 76 uint8_t type;
KillingJacky 0:e4dad9e53f06 77 uint8_t error;
KillingJacky 0:e4dad9e53f06 78 uint16_t length;
KillingJacky 0:e4dad9e53f06 79 uint8_t data[];
KillingJacky 0:e4dad9e53f06 80 } GenericServicePayload;
KillingJacky 0:e4dad9e53f06 81
KillingJacky 0:e4dad9e53f06 82 static SmartstrapRequestType s_last_generic_service_type;
KillingJacky 0:e4dad9e53f06 83 static uint32_t s_last_message_time = 0;
KillingJacky 0:e4dad9e53f06 84 static PebbleFrameInfo s_frame;
KillingJacky 0:e4dad9e53f06 85 static SmartstrapCallback s_callback;
KillingJacky 0:e4dad9e53f06 86 static bool s_connected;
KillingJacky 0:e4dad9e53f06 87 static PebbleBaud s_current_baud = PebbleBaudInvalid;
KillingJacky 0:e4dad9e53f06 88 static PebbleBaud s_target_baud = PebbleBaudInvalid;
KillingJacky 0:e4dad9e53f06 89 static const uint32_t BAUDS[] = { 9600, 14400, 19200, 28800, 38400, 57600, 67500, 115200, 125000,
KillingJacky 0:e4dad9e53f06 90 230400, 250000, 460800 };
KillingJacky 0:e4dad9e53f06 91 static uint16_t s_notify_service;
KillingJacky 0:e4dad9e53f06 92 static uint16_t s_notify_attribute;
KillingJacky 0:e4dad9e53f06 93 static const uint16_t *s_supported_services;
KillingJacky 0:e4dad9e53f06 94 static uint8_t s_num_supported_services;
KillingJacky 0:e4dad9e53f06 95 static struct {
KillingJacky 0:e4dad9e53f06 96 bool can_respond;
KillingJacky 0:e4dad9e53f06 97 uint16_t service_id;
KillingJacky 0:e4dad9e53f06 98 uint16_t attribute_id;
KillingJacky 0:e4dad9e53f06 99 } s_pending_response;
KillingJacky 0:e4dad9e53f06 100
KillingJacky 0:e4dad9e53f06 101
KillingJacky 0:e4dad9e53f06 102 void prv_set_baud(PebbleBaud baud) {
KillingJacky 0:e4dad9e53f06 103 if (baud == s_current_baud) {
KillingJacky 0:e4dad9e53f06 104 return;
KillingJacky 0:e4dad9e53f06 105 }
KillingJacky 0:e4dad9e53f06 106 s_current_baud = baud;
KillingJacky 0:e4dad9e53f06 107 s_callback(SmartstrapCmdSetBaudRate, BAUDS[baud]);
KillingJacky 0:e4dad9e53f06 108 s_callback(SmartstrapCmdSetTxEnabled, true);
KillingJacky 0:e4dad9e53f06 109 s_callback(SmartstrapCmdSetTxEnabled, false);
KillingJacky 0:e4dad9e53f06 110 }
KillingJacky 0:e4dad9e53f06 111
KillingJacky 0:e4dad9e53f06 112 void pebble_init(SmartstrapCallback callback, PebbleBaud baud, const uint16_t *services,
KillingJacky 0:e4dad9e53f06 113 uint8_t num_services) {
KillingJacky 0:e4dad9e53f06 114 s_callback = callback;
KillingJacky 0:e4dad9e53f06 115 s_target_baud = baud;
KillingJacky 0:e4dad9e53f06 116 s_supported_services = services;
KillingJacky 0:e4dad9e53f06 117 s_num_supported_services = num_services;
KillingJacky 0:e4dad9e53f06 118 prv_set_baud(PebbleBaud9600);
KillingJacky 0:e4dad9e53f06 119 }
KillingJacky 0:e4dad9e53f06 120
KillingJacky 0:e4dad9e53f06 121 void pebble_prepare_for_read(uint8_t *buffer, size_t length) {
KillingJacky 0:e4dad9e53f06 122 s_frame = (PebbleFrameInfo) {
KillingJacky 0:e4dad9e53f06 123 .payload = buffer,
KillingJacky 0:e4dad9e53f06 124 .max_payload_length = length,
KillingJacky 0:e4dad9e53f06 125 .read_ready = true
KillingJacky 0:e4dad9e53f06 126 };
KillingJacky 0:e4dad9e53f06 127 }
KillingJacky 0:e4dad9e53f06 128
KillingJacky 0:e4dad9e53f06 129 static void prv_send_flag(void) {
KillingJacky 0:e4dad9e53f06 130 s_callback(SmartstrapCmdWriteByte, ENCODING_FLAG);
KillingJacky 0:e4dad9e53f06 131 }
KillingJacky 0:e4dad9e53f06 132
KillingJacky 0:e4dad9e53f06 133 static void prv_send_byte(uint8_t data, uint8_t *parity) {
KillingJacky 0:e4dad9e53f06 134 crc8_calculate_byte_streaming(data, parity);
KillingJacky 0:e4dad9e53f06 135 if (encoding_encode(&data)) {
KillingJacky 0:e4dad9e53f06 136 s_callback(SmartstrapCmdWriteByte, ENCODING_ESCAPE);
KillingJacky 0:e4dad9e53f06 137 }
KillingJacky 0:e4dad9e53f06 138 s_callback(SmartstrapCmdWriteByte, data);
KillingJacky 0:e4dad9e53f06 139 }
KillingJacky 0:e4dad9e53f06 140
KillingJacky 0:e4dad9e53f06 141 static void prv_write_internal(SmartstrapProfile profile, const uint8_t *data1, size_t length1,
KillingJacky 0:e4dad9e53f06 142 const uint8_t *data2, size_t length2, bool is_notify) {
KillingJacky 0:e4dad9e53f06 143 uint8_t parity = 0;
KillingJacky 0:e4dad9e53f06 144
KillingJacky 0:e4dad9e53f06 145 // enable tx
KillingJacky 0:e4dad9e53f06 146 s_callback(SmartstrapCmdSetTxEnabled, true);
KillingJacky 0:e4dad9e53f06 147
KillingJacky 0:e4dad9e53f06 148 // send flag
KillingJacky 0:e4dad9e53f06 149 prv_send_flag();
KillingJacky 0:e4dad9e53f06 150
KillingJacky 0:e4dad9e53f06 151 // send version
KillingJacky 0:e4dad9e53f06 152 prv_send_byte(PROTOCOL_VERSION, &parity);
KillingJacky 0:e4dad9e53f06 153
KillingJacky 0:e4dad9e53f06 154 // send header flags (currently just hard-coded)
KillingJacky 0:e4dad9e53f06 155 if (is_notify) {
KillingJacky 0:e4dad9e53f06 156 prv_send_byte(0x04, &parity);
KillingJacky 0:e4dad9e53f06 157 } else {
KillingJacky 0:e4dad9e53f06 158 prv_send_byte(0, &parity);
KillingJacky 0:e4dad9e53f06 159 }
KillingJacky 0:e4dad9e53f06 160 prv_send_byte(0, &parity);
KillingJacky 0:e4dad9e53f06 161 prv_send_byte(0, &parity);
KillingJacky 0:e4dad9e53f06 162 prv_send_byte(0, &parity);
KillingJacky 0:e4dad9e53f06 163
KillingJacky 0:e4dad9e53f06 164 // send profile (currently well within a single byte)
KillingJacky 0:e4dad9e53f06 165 prv_send_byte(profile, &parity);
KillingJacky 0:e4dad9e53f06 166 prv_send_byte(0, &parity);
KillingJacky 0:e4dad9e53f06 167
KillingJacky 0:e4dad9e53f06 168 // send data
KillingJacky 0:e4dad9e53f06 169 size_t i;
KillingJacky 0:e4dad9e53f06 170 for (i = 0; i < length1; ++i) {
KillingJacky 0:e4dad9e53f06 171 prv_send_byte(data1[i], &parity);
KillingJacky 0:e4dad9e53f06 172 }
KillingJacky 0:e4dad9e53f06 173 for (i = 0; i < length2; ++i) {
KillingJacky 0:e4dad9e53f06 174 prv_send_byte(data2[i], &parity);
KillingJacky 0:e4dad9e53f06 175 }
KillingJacky 0:e4dad9e53f06 176
KillingJacky 0:e4dad9e53f06 177 // send parity
KillingJacky 0:e4dad9e53f06 178 prv_send_byte(parity, &parity);
KillingJacky 0:e4dad9e53f06 179
KillingJacky 0:e4dad9e53f06 180 // send flag
KillingJacky 0:e4dad9e53f06 181 prv_send_flag();
KillingJacky 0:e4dad9e53f06 182
KillingJacky 0:e4dad9e53f06 183 // flush and disable tx
KillingJacky 0:e4dad9e53f06 184 s_callback(SmartstrapCmdSetTxEnabled, false);
KillingJacky 0:e4dad9e53f06 185 }
KillingJacky 0:e4dad9e53f06 186
KillingJacky 0:e4dad9e53f06 187 static bool prv_supports_raw_data_profile(void) {
KillingJacky 0:e4dad9e53f06 188 uint8_t i;
KillingJacky 0:e4dad9e53f06 189 for (i = 0; i < s_num_supported_services; i++) {
KillingJacky 0:e4dad9e53f06 190 if (s_supported_services[i] == 0x0000) {
KillingJacky 0:e4dad9e53f06 191 return true;
KillingJacky 0:e4dad9e53f06 192 }
KillingJacky 0:e4dad9e53f06 193 }
KillingJacky 0:e4dad9e53f06 194 return false;
KillingJacky 0:e4dad9e53f06 195 }
KillingJacky 0:e4dad9e53f06 196
KillingJacky 0:e4dad9e53f06 197 static bool prv_supports_generic_profile(void) {
KillingJacky 0:e4dad9e53f06 198 uint8_t i;
KillingJacky 0:e4dad9e53f06 199 for (i = 0; i < s_num_supported_services; i++) {
KillingJacky 0:e4dad9e53f06 200 if (s_supported_services[i] > 0x0000) {
KillingJacky 0:e4dad9e53f06 201 return true;
KillingJacky 0:e4dad9e53f06 202 }
KillingJacky 0:e4dad9e53f06 203 }
KillingJacky 0:e4dad9e53f06 204 return false;
KillingJacky 0:e4dad9e53f06 205 }
KillingJacky 0:e4dad9e53f06 206
KillingJacky 0:e4dad9e53f06 207 static void prv_handle_link_control(uint8_t *buffer) {
KillingJacky 0:e4dad9e53f06 208 // we will re-use the buffer for the response
KillingJacky 0:e4dad9e53f06 209 LinkControlType type = buffer[1];
KillingJacky 0:e4dad9e53f06 210 if (type == LinkControlTypeStatus) {
KillingJacky 0:e4dad9e53f06 211 if (s_current_baud != s_target_baud) {
KillingJacky 0:e4dad9e53f06 212 buffer[2] = LinkControlStatusBaudRate;
KillingJacky 0:e4dad9e53f06 213 } else {
KillingJacky 0:e4dad9e53f06 214 buffer[2] = LinkControlStatusOk;
KillingJacky 0:e4dad9e53f06 215 s_connected = true;
KillingJacky 0:e4dad9e53f06 216 }
KillingJacky 0:e4dad9e53f06 217 prv_write_internal(SmartstrapProfileLinkControl, buffer, 3, NULL, 0, false);
KillingJacky 0:e4dad9e53f06 218 } else if (type == LinkControlTypeProfiles) {
KillingJacky 0:e4dad9e53f06 219 uint16_t profiles[2];
KillingJacky 0:e4dad9e53f06 220 uint8_t num_profiles = 0;
KillingJacky 0:e4dad9e53f06 221 if (prv_supports_raw_data_profile()) {
KillingJacky 0:e4dad9e53f06 222 profiles[num_profiles++] = SmartstrapProfileRawData;
KillingJacky 0:e4dad9e53f06 223 }
KillingJacky 0:e4dad9e53f06 224 if (prv_supports_generic_profile()) {
KillingJacky 0:e4dad9e53f06 225 profiles[num_profiles++] = SmartstrapProfileGenericService;
KillingJacky 0:e4dad9e53f06 226 }
KillingJacky 0:e4dad9e53f06 227 prv_write_internal(SmartstrapProfileLinkControl, buffer, 2, (uint8_t *)profiles,
KillingJacky 0:e4dad9e53f06 228 num_profiles * sizeof(uint16_t), false);
KillingJacky 0:e4dad9e53f06 229 } else if (type == LinkControlTypeBaud) {
KillingJacky 0:e4dad9e53f06 230 buffer[2] = 0x05;
KillingJacky 0:e4dad9e53f06 231 prv_write_internal(SmartstrapProfileLinkControl, buffer, 3, NULL, 0, false);
KillingJacky 0:e4dad9e53f06 232 prv_set_baud(s_target_baud);
KillingJacky 0:e4dad9e53f06 233 }
KillingJacky 0:e4dad9e53f06 234 }
KillingJacky 0:e4dad9e53f06 235
KillingJacky 0:e4dad9e53f06 236 static bool prv_handle_generic_service(GenericServicePayload *data) {
KillingJacky 0:e4dad9e53f06 237 if (data->error != 0) {
KillingJacky 0:e4dad9e53f06 238 return true;
KillingJacky 0:e4dad9e53f06 239 }
KillingJacky 0:e4dad9e53f06 240
KillingJacky 0:e4dad9e53f06 241 uint16_t service_id = data->service_id;
KillingJacky 0:e4dad9e53f06 242 uint16_t attribute_id = data->attribute_id;
KillingJacky 0:e4dad9e53f06 243 s_last_generic_service_type = data->type;
KillingJacky 0:e4dad9e53f06 244 uint16_t length = data->length;
KillingJacky 0:e4dad9e53f06 245 if ((service_id == 0x0101) && (attribute_id == 0x0002)) {
KillingJacky 0:e4dad9e53f06 246 // notification info attribute
KillingJacky 0:e4dad9e53f06 247 if (s_notify_service) {
KillingJacky 0:e4dad9e53f06 248 uint16_t info[2] = {s_notify_service, s_notify_attribute};
KillingJacky 0:e4dad9e53f06 249 length = sizeof(info);
KillingJacky 0:e4dad9e53f06 250 s_pending_response.can_respond = true;
KillingJacky 0:e4dad9e53f06 251 s_pending_response.service_id = service_id;
KillingJacky 0:e4dad9e53f06 252 s_pending_response.attribute_id = attribute_id;
KillingJacky 0:e4dad9e53f06 253 pebble_write(true, (uint8_t *)&info, length);
KillingJacky 0:e4dad9e53f06 254 }
KillingJacky 0:e4dad9e53f06 255 return true;
KillingJacky 0:e4dad9e53f06 256 } else if ((service_id == 0x0101) && (attribute_id == 0x0001)) {
KillingJacky 0:e4dad9e53f06 257 // this is a service discovery frame
KillingJacky 0:e4dad9e53f06 258 s_pending_response.can_respond = true;
KillingJacky 0:e4dad9e53f06 259 s_pending_response.service_id = service_id;
KillingJacky 0:e4dad9e53f06 260 s_pending_response.attribute_id = attribute_id;
KillingJacky 0:e4dad9e53f06 261 pebble_write(true, (uint8_t *)s_supported_services,
KillingJacky 0:e4dad9e53f06 262 s_num_supported_services * sizeof(uint16_t));
KillingJacky 0:e4dad9e53f06 263 return true;
KillingJacky 0:e4dad9e53f06 264 }
KillingJacky 0:e4dad9e53f06 265 return false;
KillingJacky 0:e4dad9e53f06 266 }
KillingJacky 0:e4dad9e53f06 267
KillingJacky 0:e4dad9e53f06 268 static void prv_store_byte(const uint8_t data) {
KillingJacky 0:e4dad9e53f06 269 // Find which field this byte belongs to based on the number of bytes we've received so far
KillingJacky 0:e4dad9e53f06 270 if (s_frame.length >= FRAME_PAYLOAD_OFFSET) {
KillingJacky 0:e4dad9e53f06 271 // This byte is part of either the payload or the checksum
KillingJacky 0:e4dad9e53f06 272 const uint32_t payload_length = s_frame.length - FRAME_PAYLOAD_OFFSET;
KillingJacky 0:e4dad9e53f06 273 if (payload_length > s_frame.max_payload_length) {
KillingJacky 0:e4dad9e53f06 274 // The payload is longer than the payload buffer so drop this byte
KillingJacky 0:e4dad9e53f06 275 s_frame.should_drop = true;
KillingJacky 0:e4dad9e53f06 276 } else {
KillingJacky 0:e4dad9e53f06 277 // The checksum byte comes after the payload in the frame. This byte we are receiving could
KillingJacky 0:e4dad9e53f06 278 // be the checksum byte, or it could be part of the payload; we don't know at this point. So,
KillingJacky 0:e4dad9e53f06 279 // we will store it temporarily in footer_byte and if we've previously stored a byte there,
KillingJacky 0:e4dad9e53f06 280 // will copy that previous byte into the payload. This avoids us overrunning the payload
KillingJacky 0:e4dad9e53f06 281 // buffer if it's conservatively sized.
KillingJacky 0:e4dad9e53f06 282 if (payload_length > 0) {
KillingJacky 0:e4dad9e53f06 283 // put the previous byte into the payload buffer
KillingJacky 0:e4dad9e53f06 284 s_frame.payload[payload_length - 1] = s_frame.footer_byte;
KillingJacky 0:e4dad9e53f06 285 }
KillingJacky 0:e4dad9e53f06 286 s_frame.footer_byte = data;
KillingJacky 0:e4dad9e53f06 287 }
KillingJacky 0:e4dad9e53f06 288 } else if (s_frame.length >= FRAME_PROFILE_OFFSET) {
KillingJacky 0:e4dad9e53f06 289 // This byte is part of the profile field
KillingJacky 0:e4dad9e53f06 290 const uint32_t byte_offset = s_frame.length - FRAME_PROFILE_OFFSET;
KillingJacky 0:e4dad9e53f06 291 s_frame.header.profile |= (data << (byte_offset * 8));
KillingJacky 0:e4dad9e53f06 292 } else if (s_frame.length >= FRAME_FLAGS_OFFSET) {
KillingJacky 0:e4dad9e53f06 293 // This byte is part of the flags field
KillingJacky 0:e4dad9e53f06 294 const uint32_t byte_offset = s_frame.length - FRAME_FLAGS_OFFSET;
KillingJacky 0:e4dad9e53f06 295 s_frame.header.flags |= (data << (byte_offset * 8));
KillingJacky 0:e4dad9e53f06 296 } else {
KillingJacky 0:e4dad9e53f06 297 // The version field should always be first (and a single byte)
KillingJacky 0:e4dad9e53f06 298 s_frame.header.version = data;
KillingJacky 0:e4dad9e53f06 299 }
KillingJacky 0:e4dad9e53f06 300
KillingJacky 0:e4dad9e53f06 301 // increment the length run the CRC calculation
KillingJacky 0:e4dad9e53f06 302 s_frame.length++;
KillingJacky 0:e4dad9e53f06 303 crc8_calculate_byte_streaming(data, &s_frame.checksum);
KillingJacky 0:e4dad9e53f06 304 }
KillingJacky 0:e4dad9e53f06 305
KillingJacky 0:e4dad9e53f06 306 static void prv_frame_validate(void) {
KillingJacky 0:e4dad9e53f06 307 if ((s_frame.should_drop == false) &&
KillingJacky 0:e4dad9e53f06 308 (s_frame.header.version > 0) &&
KillingJacky 0:e4dad9e53f06 309 (s_frame.header.version <= PROTOCOL_VERSION) &&
KillingJacky 0:e4dad9e53f06 310 (FLAGS_GET(s_frame.header.flags, FLAGS_IS_MASTER_MASK, FLAGS_IS_MASTER_OFFSET) == 1) &&
KillingJacky 0:e4dad9e53f06 311 (FLAGS_GET(s_frame.header.flags, FLAGS_RESERVED_MASK, FLAGS_RESERVED_OFFSET) == 0) &&
KillingJacky 0:e4dad9e53f06 312 (s_frame.header.profile > SmartstrapProfileInvalid) &&
KillingJacky 0:e4dad9e53f06 313 (s_frame.header.profile < NumSmartstrapProfiles) &&
KillingJacky 0:e4dad9e53f06 314 (s_frame.length >= FRAME_MIN_LENGTH) &&
KillingJacky 0:e4dad9e53f06 315 (s_frame.checksum == 0)) {
KillingJacky 0:e4dad9e53f06 316 // this is a valid frame
KillingJacky 0:e4dad9e53f06 317 } else {
KillingJacky 0:e4dad9e53f06 318 // drop the frame
KillingJacky 0:e4dad9e53f06 319 s_frame.should_drop = true;
KillingJacky 0:e4dad9e53f06 320 }
KillingJacky 0:e4dad9e53f06 321 }
KillingJacky 0:e4dad9e53f06 322
KillingJacky 0:e4dad9e53f06 323 bool pebble_handle_byte(uint8_t data, uint16_t *service_id, uint16_t *attribute_id, size_t *length,
KillingJacky 0:e4dad9e53f06 324 SmartstrapRequestType *type, uint32_t time) {
KillingJacky 0:e4dad9e53f06 325 if (!s_frame.read_ready) {
KillingJacky 0:e4dad9e53f06 326 // we shouldn't be reading new data
KillingJacky 0:e4dad9e53f06 327 return false;
KillingJacky 0:e4dad9e53f06 328 }
KillingJacky 0:e4dad9e53f06 329
KillingJacky 0:e4dad9e53f06 330 bool encoding_err, should_store = false;
KillingJacky 0:e4dad9e53f06 331 bool is_complete = encoding_streaming_decode(&s_frame.encoding_ctx, &data, &should_store,
KillingJacky 0:e4dad9e53f06 332 &encoding_err);
KillingJacky 0:e4dad9e53f06 333 if (encoding_err) {
KillingJacky 0:e4dad9e53f06 334 s_frame.should_drop = true;
KillingJacky 0:e4dad9e53f06 335 } else if (is_complete) {
KillingJacky 0:e4dad9e53f06 336 prv_frame_validate();
KillingJacky 0:e4dad9e53f06 337 } else if (should_store) {
KillingJacky 0:e4dad9e53f06 338 prv_store_byte(data);
KillingJacky 0:e4dad9e53f06 339 }
KillingJacky 0:e4dad9e53f06 340
KillingJacky 0:e4dad9e53f06 341 if (s_frame.should_drop || is_complete) {
KillingJacky 0:e4dad9e53f06 342 // prepare the encoding context for the next frame
KillingJacky 0:e4dad9e53f06 343 encoding_streaming_decode_reset(&s_frame.encoding_ctx);
KillingJacky 0:e4dad9e53f06 344 }
KillingJacky 0:e4dad9e53f06 345
KillingJacky 0:e4dad9e53f06 346 if (is_complete) {
KillingJacky 0:e4dad9e53f06 347 bool give_to_user = false;
KillingJacky 0:e4dad9e53f06 348 if (s_frame.should_drop) {
KillingJacky 0:e4dad9e53f06 349 // reset the frame
KillingJacky 0:e4dad9e53f06 350 pebble_prepare_for_read(s_frame.payload, s_frame.max_payload_length);
KillingJacky 0:e4dad9e53f06 351 } else if (s_frame.header.profile == SmartstrapProfileLinkControl) {
KillingJacky 0:e4dad9e53f06 352 s_last_message_time = time;
KillingJacky 0:e4dad9e53f06 353 // handle this link control frame
KillingJacky 0:e4dad9e53f06 354 prv_handle_link_control(s_frame.payload);
KillingJacky 0:e4dad9e53f06 355 // prepare for the next frame
KillingJacky 0:e4dad9e53f06 356 pebble_prepare_for_read(s_frame.payload, s_frame.max_payload_length);
KillingJacky 0:e4dad9e53f06 357 } else if (s_frame.header.profile == SmartstrapProfileGenericService) {
KillingJacky 0:e4dad9e53f06 358 GenericServicePayload header = *(GenericServicePayload *)s_frame.payload;
KillingJacky 0:e4dad9e53f06 359 memmove(s_frame.payload, &s_frame.payload[sizeof(header)], header.length);
KillingJacky 0:e4dad9e53f06 360 // handle this generic service frame
KillingJacky 0:e4dad9e53f06 361 if (prv_handle_generic_service(&header)) {
KillingJacky 0:e4dad9e53f06 362 s_last_message_time = time;
KillingJacky 0:e4dad9e53f06 363 // we handled it, so prepare for the next frame
KillingJacky 0:e4dad9e53f06 364 pebble_prepare_for_read(s_frame.payload, s_frame.max_payload_length);
KillingJacky 0:e4dad9e53f06 365 } else {
KillingJacky 0:e4dad9e53f06 366 // pass up to user to handle
KillingJacky 0:e4dad9e53f06 367 give_to_user = true;
KillingJacky 0:e4dad9e53f06 368 *service_id = header.service_id;
KillingJacky 0:e4dad9e53f06 369 *attribute_id = header.attribute_id;
KillingJacky 0:e4dad9e53f06 370 *length = header.length;
KillingJacky 0:e4dad9e53f06 371 *type = header.type;
KillingJacky 0:e4dad9e53f06 372 }
KillingJacky 0:e4dad9e53f06 373 } else {
KillingJacky 0:e4dad9e53f06 374 give_to_user = true;
KillingJacky 0:e4dad9e53f06 375 *service_id = 0;
KillingJacky 0:e4dad9e53f06 376 *attribute_id = 0;
KillingJacky 0:e4dad9e53f06 377 *length = s_frame.length - FRAME_MIN_LENGTH;
KillingJacky 0:e4dad9e53f06 378 if (FLAGS_GET(s_frame.header.flags, FLAGS_IS_READ_MASK, FLAGS_IS_READ_OFFSET)) {
KillingJacky 0:e4dad9e53f06 379 if (*length) {
KillingJacky 0:e4dad9e53f06 380 *type = SmartstrapRequestTypeWriteRead;
KillingJacky 0:e4dad9e53f06 381 } else {
KillingJacky 0:e4dad9e53f06 382 *type = SmartstrapRequestTypeRead;
KillingJacky 0:e4dad9e53f06 383 }
KillingJacky 0:e4dad9e53f06 384 } else {
KillingJacky 0:e4dad9e53f06 385 *type = SmartstrapRequestTypeWrite;
KillingJacky 0:e4dad9e53f06 386 }
KillingJacky 0:e4dad9e53f06 387 }
KillingJacky 0:e4dad9e53f06 388 if (give_to_user) {
KillingJacky 0:e4dad9e53f06 389 s_last_message_time = time;
KillingJacky 0:e4dad9e53f06 390 s_frame.read_ready = false;
KillingJacky 0:e4dad9e53f06 391 s_pending_response.service_id = *service_id;
KillingJacky 0:e4dad9e53f06 392 s_pending_response.attribute_id = *attribute_id;
KillingJacky 0:e4dad9e53f06 393 s_pending_response.can_respond = true;
KillingJacky 0:e4dad9e53f06 394 return true;
KillingJacky 0:e4dad9e53f06 395 }
KillingJacky 0:e4dad9e53f06 396 }
KillingJacky 0:e4dad9e53f06 397
KillingJacky 0:e4dad9e53f06 398 if (time < s_last_message_time) {
KillingJacky 0:e4dad9e53f06 399 // wrapped around
KillingJacky 0:e4dad9e53f06 400 s_last_message_time = time;
KillingJacky 0:e4dad9e53f06 401 } else if (time - s_last_message_time > 10000) {
KillingJacky 0:e4dad9e53f06 402 // haven't received a valid frame in over 10 seconds so reset the baudrate
KillingJacky 0:e4dad9e53f06 403 prv_set_baud(PebbleBaud9600);
KillingJacky 0:e4dad9e53f06 404 s_connected = false;
KillingJacky 0:e4dad9e53f06 405 }
KillingJacky 0:e4dad9e53f06 406
KillingJacky 0:e4dad9e53f06 407 return false;
KillingJacky 0:e4dad9e53f06 408 }
KillingJacky 0:e4dad9e53f06 409
KillingJacky 0:e4dad9e53f06 410 bool pebble_write(bool success, const uint8_t *buffer, uint16_t length) {
KillingJacky 0:e4dad9e53f06 411 if (!s_pending_response.can_respond) {
KillingJacky 0:e4dad9e53f06 412 return false;
KillingJacky 0:e4dad9e53f06 413 }
KillingJacky 0:e4dad9e53f06 414 if (s_pending_response.service_id == 0) {
KillingJacky 0:e4dad9e53f06 415 if (s_pending_response.attribute_id != 0) {
KillingJacky 0:e4dad9e53f06 416 return false;
KillingJacky 0:e4dad9e53f06 417 }
KillingJacky 0:e4dad9e53f06 418 prv_write_internal(SmartstrapProfileRawData, buffer, length, NULL, 0, false);
KillingJacky 0:e4dad9e53f06 419 } else if (s_pending_response.service_id < 0x00FF) {
KillingJacky 0:e4dad9e53f06 420 return false;
KillingJacky 0:e4dad9e53f06 421 } else {
KillingJacky 0:e4dad9e53f06 422 GenericServicePayload frame = (GenericServicePayload ) {
KillingJacky 0:e4dad9e53f06 423 .version = GENERIC_SERVICE_VERSION,
KillingJacky 0:e4dad9e53f06 424 .service_id = s_pending_response.service_id,
KillingJacky 0:e4dad9e53f06 425 .attribute_id = s_pending_response.attribute_id,
KillingJacky 0:e4dad9e53f06 426 .type = s_last_generic_service_type,
KillingJacky 0:e4dad9e53f06 427 .error = success ? 0 : 1,
KillingJacky 0:e4dad9e53f06 428 .length = length
KillingJacky 0:e4dad9e53f06 429 };
KillingJacky 0:e4dad9e53f06 430 prv_write_internal(SmartstrapProfileGenericService, (uint8_t *)&frame, sizeof(frame), buffer,
KillingJacky 0:e4dad9e53f06 431 length, false);
KillingJacky 0:e4dad9e53f06 432 }
KillingJacky 0:e4dad9e53f06 433 s_pending_response.can_respond = false;
KillingJacky 0:e4dad9e53f06 434 return true;
KillingJacky 0:e4dad9e53f06 435 }
KillingJacky 0:e4dad9e53f06 436
KillingJacky 0:e4dad9e53f06 437 void pebble_notify(uint16_t service_id, uint16_t attribute_id) {
KillingJacky 0:e4dad9e53f06 438 s_notify_service = service_id;
KillingJacky 0:e4dad9e53f06 439 s_notify_attribute = attribute_id;
KillingJacky 0:e4dad9e53f06 440 SmartstrapProfile profile;
KillingJacky 0:e4dad9e53f06 441 if (service_id == 0) {
KillingJacky 0:e4dad9e53f06 442 profile = SmartstrapProfileRawData;
KillingJacky 0:e4dad9e53f06 443 } else {
KillingJacky 0:e4dad9e53f06 444 profile = SmartstrapProfileGenericService;
KillingJacky 0:e4dad9e53f06 445 }
KillingJacky 0:e4dad9e53f06 446 s_callback(SmartstrapCmdSetTxEnabled, true);
KillingJacky 0:e4dad9e53f06 447 s_callback(SmartstrapCmdWriteBreak, 0);
KillingJacky 0:e4dad9e53f06 448 s_callback(SmartstrapCmdWriteBreak, 0);
KillingJacky 0:e4dad9e53f06 449 s_callback(SmartstrapCmdWriteBreak, 0);
KillingJacky 0:e4dad9e53f06 450 s_callback(SmartstrapCmdSetTxEnabled, false);
KillingJacky 0:e4dad9e53f06 451 prv_write_internal(profile, NULL, 0, NULL, 0, true);
KillingJacky 0:e4dad9e53f06 452 }
KillingJacky 0:e4dad9e53f06 453
KillingJacky 0:e4dad9e53f06 454 bool pebble_is_connected(uint32_t time) {
KillingJacky 0:e4dad9e53f06 455 if (time - s_last_message_time > 10000) {
KillingJacky 0:e4dad9e53f06 456 prv_set_baud(PebbleBaud9600);
KillingJacky 0:e4dad9e53f06 457 s_connected = false;
KillingJacky 0:e4dad9e53f06 458 }
KillingJacky 0:e4dad9e53f06 459 return s_connected;
KillingJacky 0:e4dad9e53f06 460 }