Will Drewry | d4ae528 | 2017-01-03 22:06:26 -0600 | [diff] [blame] | 1 | /* |
| 2 | * Copyright (C) 2017 The Android Open Source Project |
| 3 | * |
| 4 | * Licensed under the Apache License, Version 2.0 (the "License"); |
| 5 | * you may not use this file except in compliance with the License. |
| 6 | * You may obtain a copy of the License at |
| 7 | * |
| 8 | * http://www.apache.org/licenses/LICENSE-2.0 |
| 9 | * |
| 10 | * Unless required by applicable law or agreed to in writing, software |
| 11 | * distributed under the License is distributed on an "AS IS" BASIS, |
| 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| 13 | * See the License for the specific language governing permissions and |
| 14 | * limitations under the License. |
| 15 | */ |
| 16 | |
Will Drewry | de2cad7 | 2017-03-10 15:53:34 -0600 | [diff] [blame] | 17 | #include "include/ese/teq1.h" |
| 18 | #include "../libese/include/ese/ese.h" |
| 19 | #include "../libese/include/ese/log.h" |
Will Drewry | d4ae528 | 2017-01-03 22:06:26 -0600 | [diff] [blame] | 20 | |
| 21 | #include "teq1_private.h" |
| 22 | |
| 23 | const char *teq1_rule_result_to_name(enum RuleResult result) { |
| 24 | switch (result) { |
| 25 | case kRuleResultComplete: |
| 26 | return "Complete"; |
| 27 | case kRuleResultAbort: |
| 28 | return "Abort"; |
| 29 | case kRuleResultContinue: |
| 30 | return "Continue"; |
| 31 | case kRuleResultHardFail: |
| 32 | return "Hard failure"; |
| 33 | case kRuleResultResetDevice: |
| 34 | return "Reset device"; |
| 35 | case kRuleResultResetSession: |
| 36 | return "Reset session"; |
| 37 | case kRuleResultRetransmit: |
| 38 | return "Retransmit"; |
| 39 | case kRuleResultSingleShot: |
| 40 | return "Single shot"; |
| 41 | }; |
| 42 | } |
| 43 | |
| 44 | const char *teq1_pcb_to_name(uint8_t pcb) { |
| 45 | switch (pcb) { |
| 46 | case I(0, 0): |
| 47 | return "I(0, 0)"; |
| 48 | case I(0, 1): |
| 49 | return "I(0, 1)"; |
| 50 | case I(1, 0): |
| 51 | return "I(1, 0)"; |
| 52 | case I(1, 1): |
| 53 | return "I(1, 1)"; |
| 54 | case R(0, 0, 0): |
| 55 | return "R(0, 0, 0)"; |
| 56 | case R(0, 0, 1): |
| 57 | return "R(0, 0, 1)"; |
| 58 | case R(0, 1, 0): |
| 59 | return "R(0, 1, 0)"; |
| 60 | case R(0, 1, 1): |
| 61 | return "R(0, 1, 1)"; |
| 62 | case R(1, 0, 0): |
| 63 | return "R(1, 0, 0)"; |
| 64 | case R(1, 0, 1): |
| 65 | return "R(1, 0, 1)"; |
| 66 | case R(1, 1, 0): |
| 67 | return "R(1, 1, 0)"; |
| 68 | case R(1, 1, 1): |
| 69 | return "R(1, 1, 1)"; |
| 70 | case S(RESYNC, REQUEST): |
| 71 | return "S(RESYNC, REQUEST)"; |
| 72 | case S(RESYNC, RESPONSE): |
| 73 | return "S(RESYNC, RESPONSE)"; |
| 74 | case S(IFS, REQUEST): |
| 75 | return "S(IFS, REQUEST)"; |
| 76 | case S(IFS, RESPONSE): |
| 77 | return "S(IFS, RESPONSE)"; |
| 78 | case S(ABORT, REQUEST): |
| 79 | return "S(ABORT, REQUEST)"; |
| 80 | case S(ABORT, RESPONSE): |
| 81 | return "S(ABORT, RESPONSE)"; |
| 82 | case S(WTX, REQUEST): |
| 83 | return "S(WTX, REQUEST)"; |
| 84 | case S(WTX, RESPONSE): |
| 85 | return "S(WTX, RESPONSE)"; |
| 86 | case 255: |
| 87 | return "INTERNAL-ERROR"; |
| 88 | default: |
| 89 | return "???"; |
| 90 | } |
| 91 | } |
| 92 | |
Will Drewry | de2cad7 | 2017-03-10 15:53:34 -0600 | [diff] [blame] | 93 | void teq1_dump_buf(const char *prefix, const uint8_t *buf, uint32_t len) { |
| 94 | uint32_t recvd = 0; |
| 95 | for (recvd = 0; recvd < len; ++recvd) |
| 96 | ALOGV("%s[%u]: %.2X", prefix, recvd, buf[recvd]); |
| 97 | } |
Will Drewry | d4ae528 | 2017-01-03 22:06:26 -0600 | [diff] [blame] | 98 | |
Will Drewry | de2cad7 | 2017-03-10 15:53:34 -0600 | [diff] [blame] | 99 | int teq1_transmit(struct EseInterface *ese, |
| 100 | const struct Teq1ProtocolOptions *opts, |
| 101 | struct Teq1Frame *frame) { |
Will Drewry | d4ae528 | 2017-01-03 22:06:26 -0600 | [diff] [blame] | 102 | /* Set correct node address. */ |
| 103 | frame->header.NAD = opts->node_address; |
| 104 | |
| 105 | /* Compute the LRC */ |
| 106 | frame->INF[frame->header.LEN] = teq1_compute_LRC(frame); |
| 107 | |
| 108 | /* |
| 109 | * If the card does something weird, like expect an CRC/LRC based on a |
Will Drewry | de2cad7 | 2017-03-10 15:53:34 -0600 | [diff] [blame] | 110 | * different header value, the preprocessing can handle it. |
Will Drewry | d4ae528 | 2017-01-03 22:06:26 -0600 | [diff] [blame] | 111 | */ |
Will Drewry | de2cad7 | 2017-03-10 15:53:34 -0600 | [diff] [blame] | 112 | if (opts->preprocess) { |
Will Drewry | d4ae528 | 2017-01-03 22:06:26 -0600 | [diff] [blame] | 113 | opts->preprocess(opts, frame, 1); |
Will Drewry | de2cad7 | 2017-03-10 15:53:34 -0600 | [diff] [blame] | 114 | } |
Will Drewry | d4ae528 | 2017-01-03 22:06:26 -0600 | [diff] [blame] | 115 | |
| 116 | /* |
| 117 | * Begins transmission and ignore errors. |
| 118 | * Failed transmissions will result eventually in a resync then reset. |
| 119 | */ |
Will Drewry | de2cad7 | 2017-03-10 15:53:34 -0600 | [diff] [blame] | 120 | teq1_trace_transmit(frame->header.PCB, frame->header.LEN); |
| 121 | teq1_dump_transmit(frame->val, sizeof(frame->header) + frame->header.LEN + 1); |
Will Drewry | d4ae528 | 2017-01-03 22:06:26 -0600 | [diff] [blame] | 122 | ese->ops->hw_transmit(ese, frame->val, |
| 123 | sizeof(frame->header) + frame->header.LEN + 1, 1); |
Will Drewry | de2cad7 | 2017-03-10 15:53:34 -0600 | [diff] [blame] | 124 | /* |
| 125 | * Even though in practice any WTX BWT extension starts when the above |
| 126 | * transmit ends, it is easier to implement it in the polling timeout of |
| 127 | * receive. |
Will Drewry | d4ae528 | 2017-01-03 22:06:26 -0600 | [diff] [blame] | 128 | */ |
| 129 | return 0; |
| 130 | } |
| 131 | |
Will Drewry | de2cad7 | 2017-03-10 15:53:34 -0600 | [diff] [blame] | 132 | int teq1_receive(struct EseInterface *ese, |
| 133 | const struct Teq1ProtocolOptions *opts, float timeout, |
Will Drewry | d4ae528 | 2017-01-03 22:06:26 -0600 | [diff] [blame] | 134 | struct Teq1Frame *frame) { |
Will Drewry | d4ae528 | 2017-01-03 22:06:26 -0600 | [diff] [blame] | 135 | /* Poll the bus until we see the start of frame indicator, the interface NAD. |
| 136 | */ |
Will Drewry | de2cad7 | 2017-03-10 15:53:34 -0600 | [diff] [blame] | 137 | int bytes_consumed = ese->ops->poll(ese, opts->host_address, timeout, 0); |
| 138 | if (bytes_consumed < 0 || bytes_consumed > 1) { |
Will Drewry | d4ae528 | 2017-01-03 22:06:26 -0600 | [diff] [blame] | 139 | /* Timed out or comm error. */ |
Will Drewry | de2cad7 | 2017-03-10 15:53:34 -0600 | [diff] [blame] | 140 | ALOGV("%s: comm error: %d", __func__, bytes_consumed); |
Will Drewry | d4ae528 | 2017-01-03 22:06:26 -0600 | [diff] [blame] | 141 | return -1; |
| 142 | } |
Will Drewry | de2cad7 | 2017-03-10 15:53:34 -0600 | [diff] [blame] | 143 | /* We polled for the NAD above -- if it was consumed, set it here. */ |
| 144 | if (bytes_consumed) { |
| 145 | frame->header.NAD = opts->host_address; |
| 146 | } |
Will Drewry | d4ae528 | 2017-01-03 22:06:26 -0600 | [diff] [blame] | 147 | /* Get the remainder of the header, but keep the line &open. */ |
Will Drewry | de2cad7 | 2017-03-10 15:53:34 -0600 | [diff] [blame] | 148 | ese->ops->hw_receive(ese, (uint8_t *)(&frame->header.NAD + bytes_consumed), |
| 149 | sizeof(frame->header) - bytes_consumed, 0); |
| 150 | teq1_dump_receive((uint8_t *)(&frame->header.NAD + bytes_consumed), |
| 151 | sizeof(frame->header) - bytes_consumed); |
Will Drewry | d4ae528 | 2017-01-03 22:06:26 -0600 | [diff] [blame] | 152 | if (frame->header.LEN == 255) { |
| 153 | ALOGV("received invalid LEN of 255"); |
| 154 | /* Close the receive window and return failure. */ |
| 155 | ese->ops->hw_receive(ese, NULL, 0, 1); |
| 156 | return -1; |
| 157 | } |
| 158 | /* |
| 159 | * Get the data and the first byte of CRC data. |
| 160 | * Note, CRC support is not implemented. Only a single LRC byte is expected. |
| 161 | */ |
| 162 | ese->ops->hw_receive(ese, (uint8_t *)(&(frame->INF[0])), |
| 163 | frame->header.LEN + 1, 1); |
Will Drewry | de2cad7 | 2017-03-10 15:53:34 -0600 | [diff] [blame] | 164 | teq1_dump_receive((uint8_t *)(&(frame->INF[0])), frame->header.LEN + 1); |
| 165 | teq1_trace_receive(frame->header.PCB, frame->header.LEN); |
Will Drewry | d4ae528 | 2017-01-03 22:06:26 -0600 | [diff] [blame] | 166 | |
| 167 | /* |
| 168 | * If the card does something weird, like expect an CRC/LRC based on a |
| 169 | * different |
| 170 | * header value, the preprocessing should fix up here prior to the LRC check. |
| 171 | */ |
Will Drewry | de2cad7 | 2017-03-10 15:53:34 -0600 | [diff] [blame] | 172 | if (opts->preprocess) { |
Will Drewry | d4ae528 | 2017-01-03 22:06:26 -0600 | [diff] [blame] | 173 | opts->preprocess(opts, frame, 0); |
Will Drewry | de2cad7 | 2017-03-10 15:53:34 -0600 | [diff] [blame] | 174 | } |
Will Drewry | d4ae528 | 2017-01-03 22:06:26 -0600 | [diff] [blame] | 175 | |
| 176 | /* LRC and other protocol goodness checks are not done here. */ |
| 177 | return frame->header.LEN; /* Return data bytes read. */ |
| 178 | } |
| 179 | |
| 180 | uint8_t teq1_fill_info_block(struct Teq1State *state, struct Teq1Frame *frame) { |
| 181 | uint32_t inf_len = INF_LEN; |
Will Drewry | de2cad7 | 2017-03-10 15:53:34 -0600 | [diff] [blame] | 182 | if (state->ifs < inf_len) { |
Will Drewry | d4ae528 | 2017-01-03 22:06:26 -0600 | [diff] [blame] | 183 | inf_len = state->ifs; |
Will Drewry | de2cad7 | 2017-03-10 15:53:34 -0600 | [diff] [blame] | 184 | } |
| 185 | switch (bs_get(PCB.type, frame->header.PCB)) { |
Will Drewry | d4ae528 | 2017-01-03 22:06:26 -0600 | [diff] [blame] | 186 | case kPcbTypeInfo0: |
| 187 | case kPcbTypeInfo1: { |
Will Drewry | 8f367fc | 2017-03-30 22:07:48 -0500 | [diff] [blame] | 188 | uint32_t len = state->app_data.tx_total; |
| 189 | uint32_t copied = 0; |
Will Drewry | de2cad7 | 2017-03-10 15:53:34 -0600 | [diff] [blame] | 190 | if (len > inf_len) { |
Will Drewry | d4ae528 | 2017-01-03 22:06:26 -0600 | [diff] [blame] | 191 | len = inf_len; |
Will Drewry | de2cad7 | 2017-03-10 15:53:34 -0600 | [diff] [blame] | 192 | } |
Will Drewry | 8f367fc | 2017-03-30 22:07:48 -0500 | [diff] [blame] | 193 | copied = ese_sg_to_buf(state->app_data.tx, state->app_data.tx_count, |
| 194 | state->app_data.tx_offset, len, frame->INF); |
| 195 | if (copied != len) { |
| 196 | ALOGE("Failed to copy %x bytes of app data for transmission", |
| 197 | frame->header.LEN); |
| 198 | /* TODO(wad): This return code is largely ignored. Is the precondition |
| 199 | * checking elsewhere enough? */ |
| 200 | return 255; |
| 201 | } |
Will Drewry | d4ae528 | 2017-01-03 22:06:26 -0600 | [diff] [blame] | 202 | frame->header.LEN = (len & 0xff); |
| 203 | ALOGV("Copying %x bytes of app data for transmission", frame->header.LEN); |
| 204 | /* Incrementing here means the caller MUST handle retransmit with prepared |
| 205 | * data. */ |
Will Drewry | 8f367fc | 2017-03-30 22:07:48 -0500 | [diff] [blame] | 206 | state->app_data.tx_offset += copied; |
| 207 | state->app_data.tx_total -= copied; |
Will Drewry | d4ae528 | 2017-01-03 22:06:26 -0600 | [diff] [blame] | 208 | /* Perform chained transmission if needed. */ |
Will Drewry | de2cad7 | 2017-03-10 15:53:34 -0600 | [diff] [blame] | 209 | bs_assign(&frame->header.PCB, PCB.I.more_data, 0); |
Will Drewry | 8f367fc | 2017-03-30 22:07:48 -0500 | [diff] [blame] | 210 | if (state->app_data.tx_total > 0) { |
Will Drewry | de2cad7 | 2017-03-10 15:53:34 -0600 | [diff] [blame] | 211 | frame->header.PCB |= bs_mask(PCB.I.more_data, 1); |
| 212 | } |
Will Drewry | d4ae528 | 2017-01-03 22:06:26 -0600 | [diff] [blame] | 213 | return len; |
| 214 | } |
| 215 | case kPcbTypeSupervisory: |
| 216 | case kPcbTypeReceiveReady: |
| 217 | default: |
| 218 | break; |
| 219 | } |
| 220 | return 255; /* Invalid block type. */ |
| 221 | } |
| 222 | |
Will Drewry | 8f367fc | 2017-03-30 22:07:48 -0500 | [diff] [blame] | 223 | void teq1_get_app_data(struct Teq1State *state, const struct Teq1Frame *frame) { |
Will Drewry | de2cad7 | 2017-03-10 15:53:34 -0600 | [diff] [blame] | 224 | switch (bs_get(PCB.type, frame->header.PCB)) { |
Will Drewry | d4ae528 | 2017-01-03 22:06:26 -0600 | [diff] [blame] | 225 | case kPcbTypeInfo0: |
| 226 | case kPcbTypeInfo1: { |
Will Drewry | 8f367fc | 2017-03-30 22:07:48 -0500 | [diff] [blame] | 227 | uint32_t len = frame->header.LEN; |
Will Drewry | d4ae528 | 2017-01-03 22:06:26 -0600 | [diff] [blame] | 228 | /* TODO(wad): Some data will be left on the table. Should this error out? */ |
Will Drewry | 8f367fc | 2017-03-30 22:07:48 -0500 | [diff] [blame] | 229 | if (len > state->app_data.rx_total) { |
| 230 | len = state->app_data.rx_total; |
Will Drewry | de2cad7 | 2017-03-10 15:53:34 -0600 | [diff] [blame] | 231 | } |
Will Drewry | 8f367fc | 2017-03-30 22:07:48 -0500 | [diff] [blame] | 232 | ese_sg_from_buf(state->app_data.rx, state->app_data.rx_count, |
| 233 | state->app_data.rx_offset, len, frame->INF); |
Will Drewry | d4ae528 | 2017-01-03 22:06:26 -0600 | [diff] [blame] | 234 | /* The original caller must retain the starting pointer to determine |
| 235 | * actual available data. |
| 236 | */ |
Will Drewry | 8f367fc | 2017-03-30 22:07:48 -0500 | [diff] [blame] | 237 | state->app_data.rx_total -= len; |
| 238 | state->app_data.rx_offset += len; |
Will Drewry | d4ae528 | 2017-01-03 22:06:26 -0600 | [diff] [blame] | 239 | return; |
| 240 | } |
| 241 | case kPcbTypeReceiveReady: |
| 242 | case kPcbTypeSupervisory: |
| 243 | default: |
| 244 | break; |
| 245 | } |
| 246 | return; |
| 247 | } |
| 248 | |
| 249 | /* Returns an R(0) frame with error bits set. */ |
| 250 | uint8_t teq1_frame_error_check(struct Teq1State *state, |
| 251 | struct Teq1Frame *tx_frame, |
| 252 | struct Teq1Frame *rx_frame) { |
| 253 | uint8_t lrc = 0; |
| 254 | int chained = 0; |
Will Drewry | de2cad7 | 2017-03-10 15:53:34 -0600 | [diff] [blame] | 255 | if (rx_frame->header.PCB == 255) { |
Will Drewry | d4ae528 | 2017-01-03 22:06:26 -0600 | [diff] [blame] | 256 | return R(0, 1, 0); /* Other error */ |
Will Drewry | de2cad7 | 2017-03-10 15:53:34 -0600 | [diff] [blame] | 257 | } |
Will Drewry | d4ae528 | 2017-01-03 22:06:26 -0600 | [diff] [blame] | 258 | |
| 259 | lrc = teq1_compute_LRC(rx_frame); |
| 260 | if (rx_frame->INF[rx_frame->header.LEN] != lrc) { |
Will Drewry | 8f83023 | 2017-03-13 13:07:54 -0500 | [diff] [blame] | 261 | ALOGE("Invalid LRC %x instead of %x", rx_frame->INF[rx_frame->header.LEN], |
Will Drewry | d4ae528 | 2017-01-03 22:06:26 -0600 | [diff] [blame] | 262 | lrc); |
| 263 | return R(0, 0, 1); /* Parity error */ |
| 264 | } |
| 265 | |
| 266 | /* Check if we were chained and increment the last sent sequence. */ |
Will Drewry | de2cad7 | 2017-03-10 15:53:34 -0600 | [diff] [blame] | 267 | switch (bs_get(PCB.type, tx_frame->header.PCB)) { |
Will Drewry | d4ae528 | 2017-01-03 22:06:26 -0600 | [diff] [blame] | 268 | case kPcbTypeInfo0: |
| 269 | case kPcbTypeInfo1: |
Will Drewry | de2cad7 | 2017-03-10 15:53:34 -0600 | [diff] [blame] | 270 | chained = bs_get(PCB.I.more_data, tx_frame->header.PCB); |
| 271 | state->card_state->seq.interface = |
| 272 | bs_get(PCB.I.send_seq, tx_frame->header.PCB); |
Will Drewry | d4ae528 | 2017-01-03 22:06:26 -0600 | [diff] [blame] | 273 | } |
| 274 | |
| 275 | /* Check if we've gone down an easy to catch error hole. The rest will turn up |
| 276 | * on the |
| 277 | * txrx switch. |
| 278 | */ |
Will Drewry | de2cad7 | 2017-03-10 15:53:34 -0600 | [diff] [blame] | 279 | switch (bs_get(PCB.type, rx_frame->header.PCB)) { |
Will Drewry | d4ae528 | 2017-01-03 22:06:26 -0600 | [diff] [blame] | 280 | case kPcbTypeSupervisory: |
Will Drewry | de2cad7 | 2017-03-10 15:53:34 -0600 | [diff] [blame] | 281 | if (rx_frame->header.PCB != S(RESYNC, RESPONSE) && |
| 282 | rx_frame->header.LEN != 1) { |
Will Drewry | d660eb4 | 2017-07-25 13:33:55 -0500 | [diff] [blame] | 283 | ALOGE("Invalid supervisory RX frame."); |
Will Drewry | d4ae528 | 2017-01-03 22:06:26 -0600 | [diff] [blame] | 284 | return R(0, 1, 0); |
Will Drewry | de2cad7 | 2017-03-10 15:53:34 -0600 | [diff] [blame] | 285 | } |
Will Drewry | d4ae528 | 2017-01-03 22:06:26 -0600 | [diff] [blame] | 286 | break; |
| 287 | case kPcbTypeReceiveReady: |
Will Drewry | de2cad7 | 2017-03-10 15:53:34 -0600 | [diff] [blame] | 288 | if (rx_frame->header.LEN != 0) { |
Will Drewry | d660eb4 | 2017-07-25 13:33:55 -0500 | [diff] [blame] | 289 | ALOGE("Invalid ReceiveReady RX frame."); |
Will Drewry | d4ae528 | 2017-01-03 22:06:26 -0600 | [diff] [blame] | 290 | return R(0, 1, 0); |
Will Drewry | de2cad7 | 2017-03-10 15:53:34 -0600 | [diff] [blame] | 291 | } |
Will Drewry | d4ae528 | 2017-01-03 22:06:26 -0600 | [diff] [blame] | 292 | break; |
| 293 | case kPcbTypeInfo0: |
| 294 | case kPcbTypeInfo1: |
| 295 | /* I-blocks must always alternate for each endpoint. */ |
Will Drewry | de2cad7 | 2017-03-10 15:53:34 -0600 | [diff] [blame] | 296 | if ((bs_get(PCB.I.send_seq, rx_frame->header.PCB)) == |
| 297 | state->card_state->seq.card) { |
Will Drewry | 8f83023 | 2017-03-13 13:07:54 -0500 | [diff] [blame] | 298 | ALOGW("Got seq %d expected %d", |
Will Drewry | de2cad7 | 2017-03-10 15:53:34 -0600 | [diff] [blame] | 299 | bs_get(PCB.I.send_seq, rx_frame->header.PCB), |
Will Drewry | d4ae528 | 2017-01-03 22:06:26 -0600 | [diff] [blame] | 300 | state->card_state->seq.card); |
Will Drewry | d660eb4 | 2017-07-25 13:33:55 -0500 | [diff] [blame] | 301 | ALOGE("Invalid Info RX frame."); |
Will Drewry | d4ae528 | 2017-01-03 22:06:26 -0600 | [diff] [blame] | 302 | return R(0, 1, 0); |
| 303 | } |
| 304 | /* Update the card's last I-block seq. */ |
Will Drewry | de2cad7 | 2017-03-10 15:53:34 -0600 | [diff] [blame] | 305 | state->card_state->seq.card = bs_get(PCB.I.send_seq, rx_frame->header.PCB); |
Will Drewry | d4ae528 | 2017-01-03 22:06:26 -0600 | [diff] [blame] | 306 | default: |
| 307 | break; |
| 308 | }; |
| 309 | return 0; |
| 310 | } |
| 311 | |
| 312 | enum RuleResult teq1_rules(struct Teq1State *state, struct Teq1Frame *tx_frame, |
| 313 | struct Teq1Frame *rx_frame, |
| 314 | struct Teq1Frame *next_tx) { |
| 315 | /* Rule 1 is enforced by first call∴ Start with I(0,M). */ |
| 316 | /* 0 = TX, 1 = RX */ |
| 317 | /* msb = tx pcb, lsb = rx pcb */ |
| 318 | /* BUG_ON(!rx_frame && !tx_frame && !next_tx); */ |
Will Drewry | de2cad7 | 2017-03-10 15:53:34 -0600 | [diff] [blame] | 319 | uint16_t txrx = TEQ1_RULE(tx_frame->header.PCB, rx_frame->header.PCB); |
| 320 | uint8_t R_err; |
Will Drewry | d4ae528 | 2017-01-03 22:06:26 -0600 | [diff] [blame] | 321 | |
| 322 | while (1) { |
| 323 | /* Timeout errors come like invalid frames: 255. */ |
Will Drewry | de2cad7 | 2017-03-10 15:53:34 -0600 | [diff] [blame] | 324 | if ((R_err = teq1_frame_error_check(state, tx_frame, rx_frame)) != 0) { |
Will Drewry | 8f83023 | 2017-03-13 13:07:54 -0500 | [diff] [blame] | 325 | ALOGW("incoming frame failed the error check"); |
Will Drewry | d4ae528 | 2017-01-03 22:06:26 -0600 | [diff] [blame] | 326 | state->last_error_message = "Invalid frame received"; |
| 327 | /* Mark the frame as bad for our rule evaluation. */ |
Will Drewry | de2cad7 | 2017-03-10 15:53:34 -0600 | [diff] [blame] | 328 | txrx = TEQ1_RULE(tx_frame->header.PCB, 255); |
Will Drewry | d4ae528 | 2017-01-03 22:06:26 -0600 | [diff] [blame] | 329 | state->errors++; |
| 330 | /* Rule 6.4 */ |
Will Drewry | de2cad7 | 2017-03-10 15:53:34 -0600 | [diff] [blame] | 331 | if (state->errors >= 6) { |
Will Drewry | d4ae528 | 2017-01-03 22:06:26 -0600 | [diff] [blame] | 332 | return kRuleResultResetDevice; |
| 333 | } |
| 334 | /* Rule 7.4.2 */ |
| 335 | if (state->errors >= 3) { |
| 336 | /* Rule 7.4.1: state should start with error count = 2 */ |
Will Drewry | 578c504 | 2017-04-21 15:20:45 -0500 | [diff] [blame] | 337 | if (tx_frame->header.PCB != S(RESYNC, REQUEST)) { |
| 338 | next_tx->header.PCB = S(RESYNC, REQUEST); |
| 339 | return kRuleResultContinue; |
| 340 | } |
| 341 | return kRuleResultRetransmit; |
Will Drewry | d4ae528 | 2017-01-03 22:06:26 -0600 | [diff] [blame] | 342 | } |
| 343 | } |
| 344 | |
| 345 | /* Specific matches */ |
| 346 | switch (txrx) { |
| 347 | /*** Rule 2.1: I() -> I() ***/ |
| 348 | /* Error check will determine if the card seq is right. */ |
| 349 | case TEQ1_RULE(I(0, 0), I(0, 0)): |
| 350 | case TEQ1_RULE(I(0, 0), I(1, 0)): |
| 351 | case TEQ1_RULE(I(1, 0), I(1, 0)): |
| 352 | case TEQ1_RULE(I(1, 0), I(0, 0)): |
| 353 | /* Read app data & return. */ |
| 354 | teq1_get_app_data(state, rx_frame); |
| 355 | return kRuleResultComplete; |
| 356 | |
Will Drewry | d4ae528 | 2017-01-03 22:06:26 -0600 | [diff] [blame] | 357 | /* Card begins chained response. */ |
| 358 | case TEQ1_RULE(I(0, 0), I(0, 1)): |
| 359 | case TEQ1_RULE(I(1, 0), I(1, 1)): |
| 360 | /* Prep R(N(S)) */ |
| 361 | teq1_get_app_data(state, rx_frame); |
Will Drewry | de2cad7 | 2017-03-10 15:53:34 -0600 | [diff] [blame] | 362 | next_tx->header.PCB = |
| 363 | TEQ1_R(!bs_get(PCB.I.send_seq, rx_frame->header.PCB), 0, 0); |
Will Drewry | d4ae528 | 2017-01-03 22:06:26 -0600 | [diff] [blame] | 364 | next_tx->header.LEN = 0; |
| 365 | return kRuleResultContinue; |
| 366 | |
| 367 | /*** Rule 2.2, Rule 5: Chained transmission ***/ |
| 368 | case TEQ1_RULE(I(0, 1), R(1, 0, 0)): |
| 369 | case TEQ1_RULE(I(1, 1), R(0, 0, 0)): |
Will Drewry | de2cad7 | 2017-03-10 15:53:34 -0600 | [diff] [blame] | 370 | /* Send next block -- error-checking assures the R seq is our next seq. */ |
| 371 | next_tx->header.PCB = |
| 372 | TEQ1_I(bs_get(PCB.R.next_seq, rx_frame->header.PCB), 0); |
Will Drewry | d4ae528 | 2017-01-03 22:06:26 -0600 | [diff] [blame] | 373 | teq1_fill_info_block(state, next_tx); /* Sets M-bit and LEN. */ |
| 374 | return kRuleResultContinue; |
| 375 | |
| 376 | /*** Rule 3 ***/ |
| 377 | case TEQ1_RULE(I(0, 0), S(WTX, REQUEST)): |
| 378 | case TEQ1_RULE(I(1, 0), S(WTX, REQUEST)): |
| 379 | /* Note: Spec is unclear on if WTX can occur during chaining so we make it |
| 380 | an error for now. |
| 381 | case TEQ1_RULE(I(0, 1), S(WTX, REQUEST)): |
| 382 | case TEQ1_RULE(I(1, 1), S(WTX, REQUEST)): |
| 383 | */ |
| 384 | /* Send S(WTX, RESPONSE) with same INF */ |
Will Drewry | de2cad7 | 2017-03-10 15:53:34 -0600 | [diff] [blame] | 385 | next_tx->header.PCB = S(WTX, RESPONSE); |
Will Drewry | d4ae528 | 2017-01-03 22:06:26 -0600 | [diff] [blame] | 386 | next_tx->header.LEN = 1; |
| 387 | next_tx->INF[0] = rx_frame->INF[0]; |
| 388 | state->wait_mult = rx_frame->INF[0]; |
| 389 | /* Then wait BWT*INF[0] after transmission. */ |
| 390 | return kRuleResultSingleShot; /* Send then call back in with same tx_frame |
| 391 | and new rx_frame. */ |
| 392 | |
| 393 | /*** Rule 4 ***/ |
| 394 | case TEQ1_RULE(S(IFS, REQUEST), S(IFS, RESPONSE)): |
| 395 | /* XXX: Check INFs match. */ |
| 396 | return kRuleResultComplete; /* This is treated as an unique operation. */ |
| 397 | case TEQ1_RULE(I(0, 0), S(IFS, REQUEST)): |
| 398 | case TEQ1_RULE(I(0, 1), S(IFS, REQUEST)): |
| 399 | case TEQ1_RULE(I(1, 0), S(IFS, REQUEST)): |
| 400 | case TEQ1_RULE(I(1, 1), S(IFS, REQUEST)): |
| 401 | /* Don't support a IFS_REQUEST if we sent an error R-block. */ |
| 402 | case TEQ1_RULE(R(0, 0, 0), S(IFS, REQUEST)): |
| 403 | case TEQ1_RULE(R(1, 0, 0), S(IFS, REQUEST)): |
Will Drewry | de2cad7 | 2017-03-10 15:53:34 -0600 | [diff] [blame] | 404 | next_tx->header.PCB = S(IFS, RESPONSE); |
Will Drewry | d4ae528 | 2017-01-03 22:06:26 -0600 | [diff] [blame] | 405 | next_tx->header.LEN = 1; |
| 406 | next_tx->INF[0] = rx_frame->INF[0]; |
| 407 | state->ifs = rx_frame->INF[0]; |
| 408 | return kRuleResultSingleShot; |
| 409 | |
| 410 | /*** Rule 5 (see Rule 2.2 for the chained-tx side. ) ***/ |
| 411 | case TEQ1_RULE(R(0, 0, 0), I(0, 0)): |
| 412 | case TEQ1_RULE(R(1, 0, 0), I(1, 0)): |
| 413 | /* Chaining ended with terminal I-block. */ |
| 414 | teq1_get_app_data(state, rx_frame); |
| 415 | return kRuleResultComplete; |
| 416 | case TEQ1_RULE(R(0, 0, 0), I(0, 1)): |
| 417 | case TEQ1_RULE(R(1, 0, 0), I(1, 1)): |
| 418 | /* Chaining continued; consume partial data and send R(N(S)) */ |
| 419 | teq1_get_app_data(state, rx_frame); |
Will Drewry | de2cad7 | 2017-03-10 15:53:34 -0600 | [diff] [blame] | 420 | /* The card seq bit will be tracked/validated earlier. */ |
| 421 | next_tx->header.PCB = |
| 422 | TEQ1_R(!bs_get(PCB.I.send_seq, rx_frame->header.PCB), 0, 0); |
Will Drewry | d4ae528 | 2017-01-03 22:06:26 -0600 | [diff] [blame] | 423 | return kRuleResultContinue; |
| 424 | |
| 425 | /* Rule 6: Interface can send a RESYNC */ |
| 426 | /* Rule 6.1: timeout BWT right. No case here. */ |
| 427 | /* Rule 6.2, 6.3 */ |
| 428 | case TEQ1_RULE(S(RESYNC, REQUEST), S(RESYNC, RESPONSE)): |
| 429 | /* Rule 6.5: indicates that the card should assume its prior |
| 430 | * block was lost _and_ the interface gets transmit privilege, |
| 431 | * so we just start fresh. |
| 432 | */ |
| 433 | return kRuleResultResetSession; /* Start a new exchange (rule 6.3) */ |
| 434 | case TEQ1_RULE(S(RESYNC, REQUEST), 255): |
| 435 | /* Retransmit the same frame up to 3 times. */ |
| 436 | return kRuleResultRetransmit; |
| 437 | |
| 438 | /* Rule 7.1, 7.5, 7.6 */ |
| 439 | case TEQ1_RULE(I(0, 0), 255): |
| 440 | case TEQ1_RULE(I(1, 0), 255): |
| 441 | case TEQ1_RULE(I(0, 1), 255): |
| 442 | case TEQ1_RULE(I(1, 1), 255): |
Will Drewry | de2cad7 | 2017-03-10 15:53:34 -0600 | [diff] [blame] | 443 | next_tx->header.PCB = R_err; |
| 444 | bs_assign(&next_tx->header.PCB, PCB.R.next_seq, |
| 445 | bs_get(PCB.I.send_seq, tx_frame->header.PCB)); |
Will Drewry | 8f83023 | 2017-03-13 13:07:54 -0500 | [diff] [blame] | 446 | ALOGW("Rule 7.1,7.5,7.6: bad rx - sending error R: %x = %s", |
Will Drewry | de2cad7 | 2017-03-10 15:53:34 -0600 | [diff] [blame] | 447 | next_tx->header.PCB, teq1_pcb_to_name(next_tx->header.PCB)); |
Will Drewry | d4ae528 | 2017-01-03 22:06:26 -0600 | [diff] [blame] | 448 | return kRuleResultSingleShot; /* So we still can retransmit the original. |
| 449 | */ |
| 450 | |
| 451 | /* Caught in the error check. */ |
| 452 | case TEQ1_RULE(I(0, 0), R(1, 0, 0)): |
| 453 | case TEQ1_RULE(I(0, 0), R(1, 0, 1)): |
| 454 | case TEQ1_RULE(I(0, 0), R(1, 1, 0)): |
| 455 | case TEQ1_RULE(I(0, 0), R(1, 1, 1)): |
| 456 | case TEQ1_RULE(I(1, 0), R(0, 0, 0)): |
| 457 | case TEQ1_RULE(I(1, 0), R(0, 0, 1)): |
| 458 | case TEQ1_RULE(I(1, 0), R(0, 1, 0)): |
| 459 | case TEQ1_RULE(I(1, 0), R(0, 1, 1)): |
Will Drewry | de2cad7 | 2017-03-10 15:53:34 -0600 | [diff] [blame] | 460 | next_tx->header.PCB = |
| 461 | TEQ1_R(bs_get(PCB.I.send_seq, tx_frame->header.PCB), 0, 0); |
Will Drewry | 8f83023 | 2017-03-13 13:07:54 -0500 | [diff] [blame] | 462 | ALOGW("Rule 7.1,7.5,7.6: weird rx - sending error R: %x = %s", |
Will Drewry | de2cad7 | 2017-03-10 15:53:34 -0600 | [diff] [blame] | 463 | next_tx->header.PCB, teq1_pcb_to_name(next_tx->header.PCB)); |
Will Drewry | d4ae528 | 2017-01-03 22:06:26 -0600 | [diff] [blame] | 464 | return kRuleResultSingleShot; |
| 465 | |
| 466 | /* Rule 7.2: Retransmit the _same_ R-block. */ |
| 467 | /* The remainder of this rule is implemented in the next switch. */ |
| 468 | case TEQ1_RULE(R(0, 0, 0), 255): |
| 469 | case TEQ1_RULE(R(0, 0, 1), 255): |
| 470 | case TEQ1_RULE(R(0, 1, 0), 255): |
| 471 | case TEQ1_RULE(R(0, 1, 1), 255): |
| 472 | case TEQ1_RULE(R(1, 0, 0), 255): |
| 473 | case TEQ1_RULE(R(1, 0, 1), 255): |
| 474 | case TEQ1_RULE(R(1, 1, 0), 255): |
| 475 | case TEQ1_RULE(R(1, 1, 1), 255): |
| 476 | return kRuleResultRetransmit; |
| 477 | |
| 478 | /* Rule 7.3 request */ |
| 479 | /* Note, 7.3 for transmission of S(*, RESPONSE) won't be seen because they |
| 480 | * are |
| 481 | * single shots. |
| 482 | * Instead, the invalid block will be handled as invalid for the prior TX. |
| 483 | * This should yield the correct R-block. |
| 484 | */ |
| 485 | case TEQ1_RULE(I(0, 0), R(0, 0, 0)): |
| 486 | case TEQ1_RULE(I(0, 0), R(0, 0, 1)): |
| 487 | case TEQ1_RULE(I(0, 0), R(0, 1, 0)): |
| 488 | case TEQ1_RULE(I(0, 0), R(0, 1, 1)): |
| 489 | case TEQ1_RULE(I(1, 0), R(1, 0, 0)): |
| 490 | case TEQ1_RULE(I(1, 0), R(1, 1, 0)): |
| 491 | case TEQ1_RULE(I(1, 0), R(1, 0, 1)): |
| 492 | case TEQ1_RULE(I(1, 0), R(1, 1, 1)): |
| 493 | case TEQ1_RULE(I(0, 1), R(0, 0, 0)): |
| 494 | case TEQ1_RULE(I(0, 1), R(0, 1, 0)): |
| 495 | case TEQ1_RULE(I(0, 1), R(0, 0, 1)): |
| 496 | case TEQ1_RULE(I(0, 1), R(0, 1, 1)): |
| 497 | case TEQ1_RULE(I(1, 1), R(1, 0, 0)): |
| 498 | case TEQ1_RULE(I(1, 1), R(1, 1, 0)): |
| 499 | case TEQ1_RULE(I(1, 1), R(1, 0, 1)): |
| 500 | case TEQ1_RULE(I(1, 1), R(1, 1, 1)): |
| 501 | /* Retransmit I-block */ |
| 502 | return kRuleResultRetransmit; |
| 503 | |
| 504 | /* Rule 8 is card only. */ |
| 505 | /* Rule 9: aborting a chain. |
| 506 | * If a S(ABORT) is injected into this engine, then we may have sent an |
| 507 | * abort. |
| 508 | */ |
| 509 | case TEQ1_RULE(S(ABORT, REQUEST), S(ABORT, RESPONSE)): |
| 510 | /* No need to send back a R() because we want to keep transmit. */ |
| 511 | return kRuleResultComplete; /* If we sent it, then we are complete. */ |
| 512 | case TEQ1_RULE(S(ABORT, RESPONSE), R(0, 0, 0)): |
| 513 | case TEQ1_RULE(S(ABORT, RESPONSE), R(1, 0, 0)): |
| 514 | /* Card triggered abortion complete but we can resume sending. */ |
| 515 | return kRuleResultAbort; |
| 516 | /* An abort request can interrupt a chain anywhere and could occur |
| 517 | * after a failure path too. |
| 518 | */ |
| 519 | case TEQ1_RULE(I(0, 1), S(ABORT, REQUEST)): |
| 520 | case TEQ1_RULE(I(1, 1), S(ABORT, REQUEST)): |
| 521 | case TEQ1_RULE(R(0, 0, 0), S(ABORT, REQUEST)): |
| 522 | case TEQ1_RULE(R(0, 0, 1), S(ABORT, REQUEST)): |
| 523 | case TEQ1_RULE(R(0, 1, 0), S(ABORT, REQUEST)): |
| 524 | case TEQ1_RULE(R(0, 1, 1), S(ABORT, REQUEST)): |
| 525 | case TEQ1_RULE(R(1, 0, 0), S(ABORT, REQUEST)): |
| 526 | case TEQ1_RULE(R(1, 0, 1), S(ABORT, REQUEST)): |
| 527 | case TEQ1_RULE(R(1, 1, 0), S(ABORT, REQUEST)): |
| 528 | case TEQ1_RULE(R(1, 1, 1), S(ABORT, REQUEST)): |
Will Drewry | de2cad7 | 2017-03-10 15:53:34 -0600 | [diff] [blame] | 529 | next_tx->header.PCB = S(ABORT, REQUEST); |
Will Drewry | d4ae528 | 2017-01-03 22:06:26 -0600 | [diff] [blame] | 530 | return kRuleResultContinue; /* Takes over prior flow. */ |
| 531 | case TEQ1_RULE(S(ABORT, RESPONSE), 255): |
| 532 | return kRuleResultRetransmit; |
| 533 | /* Note, other blocks should be caught below. */ |
| 534 | default: |
| 535 | break; |
| 536 | } |
| 537 | |
| 538 | /* Note, only S(ABORT, REQUEST) and S(IFS, REQUEST) are supported |
| 539 | * for transmitting to the card. Others will result in error |
| 540 | * flows. |
| 541 | * |
| 542 | * For supported flows: If an operation was paused to |
| 543 | * send it, the caller may then switch to that state and resume. |
| 544 | */ |
Will Drewry | de2cad7 | 2017-03-10 15:53:34 -0600 | [diff] [blame] | 545 | if (rx_frame->header.PCB != 255) { |
Will Drewry | 8f83023 | 2017-03-13 13:07:54 -0500 | [diff] [blame] | 546 | ALOGW("Unexpected frame. Marking error and re-evaluating."); |
Will Drewry | de2cad7 | 2017-03-10 15:53:34 -0600 | [diff] [blame] | 547 | rx_frame->header.PCB = 255; |
Will Drewry | d4ae528 | 2017-01-03 22:06:26 -0600 | [diff] [blame] | 548 | continue; |
| 549 | } |
| 550 | |
| 551 | return kRuleResultHardFail; |
| 552 | } |
| 553 | } |
| 554 | |
| 555 | /* |
| 556 | * TODO(wad): Consider splitting teq1_transcieve() into |
| 557 | * teq1_transcieve_init() and teq1_transceive_process_one() |
| 558 | * if testing becomes onerous given the loop below. |
| 559 | */ |
Will Drewry | 8f367fc | 2017-03-30 22:07:48 -0500 | [diff] [blame] | 560 | ESE_API uint32_t teq1_transceive(struct EseInterface *ese, |
| 561 | const struct Teq1ProtocolOptions *opts, |
| 562 | const struct EseSgBuffer *tx_bufs, |
| 563 | uint8_t tx_segs, struct EseSgBuffer *rx_bufs, |
| 564 | uint8_t rx_segs) { |
Will Drewry | d4ae528 | 2017-01-03 22:06:26 -0600 | [diff] [blame] | 565 | struct Teq1Frame tx_frame[2]; |
| 566 | struct Teq1Frame rx_frame; |
| 567 | struct Teq1Frame *tx = &tx_frame[0]; |
Will Drewry | d4ae528 | 2017-01-03 22:06:26 -0600 | [diff] [blame] | 568 | int active = 0; |
Will Drewry | de2cad7 | 2017-03-10 15:53:34 -0600 | [diff] [blame] | 569 | bool was_reset = false; |
Will Drewry | d660eb4 | 2017-07-25 13:33:55 -0500 | [diff] [blame] | 570 | bool needs_hw_reset = false; |
| 571 | int session_resets = 0; |
Will Drewry | de2cad7 | 2017-03-10 15:53:34 -0600 | [diff] [blame] | 572 | bool done = false; |
Will Drewry | d4ae528 | 2017-01-03 22:06:26 -0600 | [diff] [blame] | 573 | enum RuleResult result = kRuleResultComplete; |
Will Drewry | 8f367fc | 2017-03-30 22:07:48 -0500 | [diff] [blame] | 574 | uint32_t rx_total = ese_sg_length(rx_bufs, rx_segs); |
Will Drewry | d4ae528 | 2017-01-03 22:06:26 -0600 | [diff] [blame] | 575 | struct Teq1CardState *card_state = (struct Teq1CardState *)(&ese->pad[0]); |
Will Drewry | 8f367fc | 2017-03-30 22:07:48 -0500 | [diff] [blame] | 576 | struct Teq1State init_state = TEQ1_INIT_STATE( |
| 577 | tx_bufs, tx_segs, ese_sg_length(tx_bufs, tx_segs), rx_bufs, rx_segs, |
| 578 | ese_sg_length(rx_bufs, rx_segs), card_state); |
| 579 | struct Teq1State state = TEQ1_INIT_STATE( |
| 580 | tx_bufs, tx_segs, ese_sg_length(tx_bufs, tx_segs), rx_bufs, rx_segs, |
| 581 | ese_sg_length(rx_bufs, rx_segs), card_state); |
Will Drewry | d4ae528 | 2017-01-03 22:06:26 -0600 | [diff] [blame] | 582 | |
Will Drewry | de2cad7 | 2017-03-10 15:53:34 -0600 | [diff] [blame] | 583 | _static_assert(TEQ1HEADER_SIZE == sizeof(struct Teq1Header), |
| 584 | "Ensure compiler alignment/padding matches wire protocol."); |
| 585 | _static_assert(TEQ1FRAME_SIZE == sizeof(struct Teq1Frame), |
| 586 | "Ensure compiler alignment/padding matches wire protocol."); |
| 587 | |
Will Drewry | d4ae528 | 2017-01-03 22:06:26 -0600 | [diff] [blame] | 588 | /* First I-block is always I(0, M). After that, modulo 2. */ |
Will Drewry | de2cad7 | 2017-03-10 15:53:34 -0600 | [diff] [blame] | 589 | tx->header.PCB = TEQ1_I(!card_state->seq.interface, 0); |
Will Drewry | d4ae528 | 2017-01-03 22:06:26 -0600 | [diff] [blame] | 590 | teq1_fill_info_block(&state, tx); |
| 591 | |
| 592 | teq1_trace_header(); |
| 593 | while (!done) { |
| 594 | /* Populates the node address and LRC prior to attempting to transmit. */ |
Will Drewry | de2cad7 | 2017-03-10 15:53:34 -0600 | [diff] [blame] | 595 | teq1_transmit(ese, opts, tx); |
Will Drewry | d4ae528 | 2017-01-03 22:06:26 -0600 | [diff] [blame] | 596 | |
| 597 | /* If tx was pointed to the inactive frame for a single shot, restore it |
| 598 | * now. */ |
| 599 | tx = &tx_frame[active]; |
| 600 | |
| 601 | /* Clear the RX frame. */ |
Will Drewry | de2cad7 | 2017-03-10 15:53:34 -0600 | [diff] [blame] | 602 | ese_memset(&rx_frame, 0xff, sizeof(rx_frame)); |
Will Drewry | d4ae528 | 2017-01-03 22:06:26 -0600 | [diff] [blame] | 603 | |
| 604 | /* -1 indicates a timeout or failure from hardware. */ |
Will Drewry | de2cad7 | 2017-03-10 15:53:34 -0600 | [diff] [blame] | 605 | if (teq1_receive(ese, opts, opts->bwt * (float)state.wait_mult, &rx_frame) < |
| 606 | 0) { |
Will Drewry | d4ae528 | 2017-01-03 22:06:26 -0600 | [diff] [blame] | 607 | /* TODO(wad): If the ese_error(ese) == 1, should this go ahead and fail? |
| 608 | */ |
| 609 | /* Failures are considered invalid blocks in the rule engine below. */ |
Will Drewry | de2cad7 | 2017-03-10 15:53:34 -0600 | [diff] [blame] | 610 | rx_frame.header.PCB = 255; |
Will Drewry | d4ae528 | 2017-01-03 22:06:26 -0600 | [diff] [blame] | 611 | } |
| 612 | /* Always reset |wait_mult| once we have calculated the timeout. */ |
| 613 | state.wait_mult = 1; |
| 614 | |
| 615 | /* Clear the inactive frame header for use as |next_tx|. */ |
Will Drewry | de2cad7 | 2017-03-10 15:53:34 -0600 | [diff] [blame] | 616 | ese_memset(&tx_frame[!active].header, 0, sizeof(tx_frame[!active].header)); |
Will Drewry | d4ae528 | 2017-01-03 22:06:26 -0600 | [diff] [blame] | 617 | |
| 618 | result = teq1_rules(&state, tx, &rx_frame, &tx_frame[!active]); |
| 619 | ALOGV("[ %s ]", teq1_rule_result_to_name(result)); |
| 620 | switch (result) { |
| 621 | case kRuleResultComplete: |
Will Drewry | de2cad7 | 2017-03-10 15:53:34 -0600 | [diff] [blame] | 622 | done = true; |
Will Drewry | d4ae528 | 2017-01-03 22:06:26 -0600 | [diff] [blame] | 623 | break; |
| 624 | case kRuleResultRetransmit: |
| 625 | /* TODO(wad) Find a clean way to move into teq1_rules(). */ |
Will Drewry | de2cad7 | 2017-03-10 15:53:34 -0600 | [diff] [blame] | 626 | if (state.retransmits++ < 3) { |
Will Drewry | d4ae528 | 2017-01-03 22:06:26 -0600 | [diff] [blame] | 627 | continue; |
Will Drewry | de2cad7 | 2017-03-10 15:53:34 -0600 | [diff] [blame] | 628 | } |
Will Drewry | 578c504 | 2017-04-21 15:20:45 -0500 | [diff] [blame] | 629 | ALOGE("More than three retransmits have occurred"); |
Will Drewry | de2cad7 | 2017-03-10 15:53:34 -0600 | [diff] [blame] | 630 | if (tx->header.PCB == S(RESYNC, REQUEST)) { |
Will Drewry | d660eb4 | 2017-07-25 13:33:55 -0500 | [diff] [blame] | 631 | /* More than three RESYNC retranmits have occurred. */ |
Will Drewry | de2cad7 | 2017-03-10 15:53:34 -0600 | [diff] [blame] | 632 | ese_set_error(ese, kTeq1ErrorHardFail); |
Will Drewry | d4ae528 | 2017-01-03 22:06:26 -0600 | [diff] [blame] | 633 | return 0; |
| 634 | } |
| 635 | /* Fall through */ |
Will Drewry | 578c504 | 2017-04-21 15:20:45 -0500 | [diff] [blame] | 636 | ALOGE("Triggering resynchronization."); |
Will Drewry | de2cad7 | 2017-03-10 15:53:34 -0600 | [diff] [blame] | 637 | tx_frame[!active].header.PCB = S(RESYNC, REQUEST); |
Will Drewry | d4ae528 | 2017-01-03 22:06:26 -0600 | [diff] [blame] | 638 | case kRuleResultContinue: |
| 639 | active = !active; |
| 640 | tx = &tx_frame[active]; |
Will Drewry | d660eb4 | 2017-07-25 13:33:55 -0500 | [diff] [blame] | 641 | /* Reset this to 0 to use the counter for RESYNC transmits. */ |
Will Drewry | d4ae528 | 2017-01-03 22:06:26 -0600 | [diff] [blame] | 642 | state.retransmits = 0; |
Will Drewry | d660eb4 | 2017-07-25 13:33:55 -0500 | [diff] [blame] | 643 | /* Errors are not reset until the session is reset. */ |
Will Drewry | d4ae528 | 2017-01-03 22:06:26 -0600 | [diff] [blame] | 644 | continue; |
| 645 | case kRuleResultHardFail: |
Will Drewry | de2cad7 | 2017-03-10 15:53:34 -0600 | [diff] [blame] | 646 | ese_set_error(ese, kTeq1ErrorHardFail); |
Will Drewry | d4ae528 | 2017-01-03 22:06:26 -0600 | [diff] [blame] | 647 | return 0; |
| 648 | case kRuleResultAbort: |
Will Drewry | de2cad7 | 2017-03-10 15:53:34 -0600 | [diff] [blame] | 649 | ese_set_error(ese, kTeq1ErrorAbort); |
Will Drewry | d4ae528 | 2017-01-03 22:06:26 -0600 | [diff] [blame] | 650 | return 0; |
| 651 | case kRuleResultSingleShot: |
| 652 | /* |
| 653 | * Send the next_tx on loop, but tell the rule engine that |
| 654 | * the last sent state hasn't changed. This allows for easy error |
| 655 | * and supervisory block paths without nesting state. |
| 656 | */ |
| 657 | tx = &tx_frame[!active]; |
| 658 | continue; |
| 659 | case kRuleResultResetDevice: |
Will Drewry | d660eb4 | 2017-07-25 13:33:55 -0500 | [diff] [blame] | 660 | needs_hw_reset = true; |
Will Drewry | d4ae528 | 2017-01-03 22:06:26 -0600 | [diff] [blame] | 661 | /* Fall through to session reset. */ |
| 662 | case kRuleResultResetSession: |
Will Drewry | d660eb4 | 2017-07-25 13:33:55 -0500 | [diff] [blame] | 663 | /* Reset to initial state and possibly do hw reset */ |
| 664 | if (session_resets++ > 4) { |
| 665 | /* If there have been more than 4 resyncs without a |
| 666 | * physical reset, we should pull the plug. |
| 667 | */ |
| 668 | needs_hw_reset = true; |
| 669 | } |
| 670 | if (needs_hw_reset) { |
| 671 | needs_hw_reset = false; |
| 672 | if (was_reset || !ese->ops->hw_reset || ese->ops->hw_reset(ese) == -1) { |
| 673 | ese_set_error(ese, kTeq1ErrorDeviceReset); |
| 674 | return 0; /* Don't keep resetting -- hard fail. */ |
| 675 | } |
| 676 | was_reset = true; |
| 677 | session_resets = 0; |
| 678 | } |
Will Drewry | d4ae528 | 2017-01-03 22:06:26 -0600 | [diff] [blame] | 679 | state = init_state; |
| 680 | TEQ1_INIT_CARD_STATE(state.card_state); |
| 681 | /* Reset the active frame. */ |
Will Drewry | de2cad7 | 2017-03-10 15:53:34 -0600 | [diff] [blame] | 682 | ese_memset(tx, 0, sizeof(*tx)); |
Will Drewry | d4ae528 | 2017-01-03 22:06:26 -0600 | [diff] [blame] | 683 | /* Load initial I-block. */ |
Will Drewry | de2cad7 | 2017-03-10 15:53:34 -0600 | [diff] [blame] | 684 | tx->header.PCB = I(0, 0); |
Will Drewry | d4ae528 | 2017-01-03 22:06:26 -0600 | [diff] [blame] | 685 | teq1_fill_info_block(&state, tx); |
| 686 | continue; |
| 687 | } |
| 688 | } |
Will Drewry | 8f367fc | 2017-03-30 22:07:48 -0500 | [diff] [blame] | 689 | /* Return the number of bytes used in the RX buffers. */ |
| 690 | return rx_total - state.app_data.rx_total; |
Will Drewry | d4ae528 | 2017-01-03 22:06:26 -0600 | [diff] [blame] | 691 | } |
| 692 | |
Will Drewry | 8f367fc | 2017-03-30 22:07:48 -0500 | [diff] [blame] | 693 | ESE_API uint8_t teq1_compute_LRC(const struct Teq1Frame *frame) { |
Will Drewry | d4ae528 | 2017-01-03 22:06:26 -0600 | [diff] [blame] | 694 | uint8_t lrc = 0; |
| 695 | const uint8_t *buffer = frame->val; |
| 696 | const uint8_t *end = buffer + frame->header.LEN + sizeof(frame->header); |
| 697 | while (buffer < end) { |
| 698 | lrc ^= *buffer++; |
| 699 | } |
| 700 | return lrc; |
| 701 | } |