Home | History | Annotate | Download | only in pn80t
      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  * Support SPI communication with NXP PN553/PN80T secure element.
     17  */
     18 
     19 #include "include/ese/hw/nxp/pn80t/common.h"
     20 
     21 #ifndef INT_MAX
     22 #define INT_MAX 2147483647
     23 #endif
     24 
     25 int nxp_pn80t_preprocess(const struct Teq1ProtocolOptions *const opts,
     26                          struct Teq1Frame *frame, int tx) {
     27   if (tx) {
     28     /* Recompute the LRC with the NAD of 0x00 */
     29     frame->header.NAD = 0x00;
     30     frame->INF[frame->header.LEN] = teq1_compute_LRC(frame);
     31     frame->header.NAD = opts->node_address;
     32     ALOGV("interface is preprocessing outbound frame");
     33   } else {
     34     /* Replace the NAD with 0x00 so the LRC check passes. */
     35     ALOGV("interface is preprocessing inbound frame (%x->%x)",
     36           frame->header.NAD, 0x00);
     37     if (frame->header.NAD != opts->host_address) {
     38       ALOGV("Rewriting from unknown NAD: %x", frame->header.NAD);
     39     }
     40     frame->header.NAD = 0x00;
     41     ALOGV("Frame length: %x", frame->header.LEN);
     42   }
     43   return 0;
     44 }
     45 
     46 static const struct Teq1ProtocolOptions kTeq1Options = {
     47     .host_address = 0xA5,
     48     .node_address = 0x5A,
     49     .bwt = 1.624f,   /* cwt by default would be ~8k * 1.05s */
     50     .etu = 0.00105f, /* seconds */
     51     .preprocess = &nxp_pn80t_preprocess,
     52 };
     53 
     54 int nxp_pn80t_open(struct EseInterface *ese, void *board) {
     55   struct NxpState *ns;
     56   const struct Pn80tPlatform *platform;
     57   if (sizeof(ese->pad) < sizeof(struct NxpState *)) {
     58     /* This is a compile-time correctable error only. */
     59     ALOGE("Pad size too small to use NXP HW (%zu < %zu)", sizeof(ese->pad),
     60           sizeof(struct NxpState));
     61     return -1;
     62   }
     63   platform = ese->ops->opts;
     64 
     65   /* Ensure all required functions exist */
     66   if (!platform->initialize || !platform->release || !platform->toggle_reset ||
     67       !platform->wait) {
     68     ALOGE("Required functions not implemented in supplied platform");
     69     return -1;
     70   }
     71 
     72   ns = NXP_PN80T_STATE(ese);
     73   TEQ1_INIT_CARD_STATE((struct Teq1CardState *)(&ese->pad[0]));
     74   ns->handle = platform->initialize(board);
     75   if (!ns->handle) {
     76     ALOGE("platform initialization failed");
     77     ese_set_error(ese, kNxpPn80tErrorPlatformInit);
     78     return -1;
     79   }
     80   /* Toggle all required power GPIOs.
     81    * Each platform may prefer to handle the power
     82    * muxing specific. E.g., if NFC is in use, it would
     83    * be unwise to unset VEN.  However, the implementation
     84    * here will attempt it if supported.
     85    */
     86   if (platform->toggle_ven) {
     87     platform->toggle_ven(ns->handle, 1);
     88   }
     89   if (platform->toggle_power_req) {
     90     platform->toggle_power_req(ns->handle, 1);
     91   }
     92   /* Power on eSE */
     93   platform->toggle_reset(ns->handle, 1);
     94   /* Let the eSE boot. */
     95   platform->wait(ns->handle, 5000);
     96   return 0;
     97 }
     98 
     99 int nxp_pn80t_reset(struct EseInterface *ese) {
    100   const struct Pn80tPlatform *platform = ese->ops->opts;
    101   struct NxpState *ns = NXP_PN80T_STATE(ese);
    102   if (platform->toggle_reset(ns->handle, 0) < 0) {
    103     ese_set_error(ese, kNxpPn80tErrorResetToggle);
    104     return -1;
    105   }
    106   platform->wait(ns->handle, 1000);
    107   if (platform->toggle_reset(ns->handle, 1) < 0) {
    108     ese_set_error(ese, kNxpPn80tErrorResetToggle);
    109     return -1;
    110   }
    111   return 0;
    112 }
    113 
    114 int nxp_pn80t_poll(struct EseInterface *ese, uint8_t poll_for, float timeout,
    115                    int complete) {
    116   struct NxpState *ns = NXP_PN80T_STATE(ese);
    117   const struct Pn80tPlatform *platform = ese->ops->opts;
    118   /* Attempt to read a 8-bit character once per 8-bit character transmission
    119    * window (in seconds). */
    120   int intervals = (int)(0.5f + timeout / (7.0f * kTeq1Options.etu));
    121   uint8_t byte = 0xff;
    122   ALOGV("interface polling for start of frame/host node address: %x", poll_for);
    123   /* If we had interrupts, we could just get notified by the driver. */
    124   do {
    125     /*
    126      * In practice, if complete=true, then no transmission
    127      * should attempt again until after 1000usec.
    128      */
    129     if (ese->ops->hw_receive(ese, &byte, 1, complete) != 1) {
    130       ALOGE("failed to read one byte");
    131       ese_set_error(ese, kNxpPn80tErrorPollRead);
    132       return -1;
    133     }
    134     if (byte == poll_for) {
    135       ALOGV("Polled for byte seen: %x with %d intervals remaining.", poll_for,
    136             intervals);
    137       ALOGV("RX[0]: %.2X", byte);
    138       return 1;
    139     } else {
    140       ALOGV("No match (saw %x)", byte);
    141     }
    142     platform->wait(ns->handle,
    143                    7.0f * kTeq1Options.etu * 1000000.0f); /* s -> us */
    144     ALOGV("poll interval %d: no match.", intervals);
    145   } while (intervals-- > 0);
    146   ALOGW("polling timed out.");
    147   return -1;
    148 }
    149 
    150 uint32_t nxp_pn80t_transceive(struct EseInterface *ese,
    151                               const uint8_t *const tx_buf, uint32_t tx_len,
    152                               uint8_t *rx_buf, uint32_t rx_len) {
    153   /* TODO(wad) Should we toggle power on each call? */
    154   return teq1_transceive(ese, &kTeq1Options, tx_buf, tx_len, rx_buf, rx_len);
    155 }
    156 
    157 uint32_t nxp_pn80t_send_cooldown(struct EseInterface *ese) {
    158   const struct Pn80tPlatform *platform = ese->ops->opts;
    159   const uint8_t kCooldown[] = {0xa5, 0xc5, 0x00, 0xc5};
    160   uint8_t rx_buf[8];
    161   uint32_t *res = (uint32_t *)(&rx_buf[3]);
    162   ese->ops->hw_transmit(ese, kCooldown, sizeof(kCooldown), 1);
    163   nxp_pn80t_poll(ese, kTeq1Options.host_address, 5.0f, 0);
    164   ese->ops->hw_receive(ese, rx_buf, sizeof(rx_buf), 1);
    165   if (rx_buf[2] == 4) {
    166     ALOGI("Cooldown value is %u", *res);
    167     return *res;
    168   } else {
    169     ALOGI("Cooldown value unavailable");
    170   }
    171   return 0;
    172 }
    173 
    174 void nxp_pn80t_close(struct EseInterface *ese) {
    175   struct NxpState *ns;
    176   const struct Pn80tPlatform *platform = ese->ops->opts;
    177   ALOGV("%s: called", __func__);
    178   ns = NXP_PN80T_STATE(ese);
    179   nxp_pn80t_send_cooldown(ese);
    180   platform->toggle_reset(ns->handle, 0);
    181   if (platform->toggle_power_req) {
    182     platform->toggle_power_req(ns->handle, 0);
    183   }
    184   if (platform->toggle_ven) {
    185     platform->toggle_ven(ns->handle, 0);
    186   }
    187   platform->release(ns->handle);
    188   ns->handle = NULL;
    189 }
    190 
    191 const char *kNxpPn80tErrorMessages[] = {
    192     /* The first three are required by teq1_transceive use. */
    193     TEQ1_ERROR_MESSAGES,
    194     /* The rest are pn80t impl specific. */
    195     [kNxpPn80tErrorPlatformInit] = "unable to initialize platform",
    196     [kNxpPn80tErrorPollRead] = "failed to read one byte",
    197     [kNxpPn80tErrorReceive] = "failed to read",
    198     [kNxpPn80tErrorReceiveSize] = "attempted to receive too much data",
    199     [kNxpPn80tErrorTransmitSize] = "attempted to transfer too much data",
    200     [kNxpPn80tErrorTransmit] = "failed to transmit",
    201     [kNxpPn80tErrorResetToggle] = "failed to toggle reset",
    202 };
    203