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  * Platform implementation for a nq-nci extension driver.
     17  *
     18  * The driver presents the following interface on a miscdev:
     19  * - ioctl():
     20  *   - for setting and getting power.
     21  *     This handles SVDD_PWR_REQ and NFC_VEN muxing.
     22  *     (ESE_RST is not connected in this case.)
     23  * - read():
     24  *   - For reading arbitrary amounts of data.
     25  *     CS is asserted and deasserted on each call, but the clock
     26  *     also appears to do the same which keeps the ese data available
     27  *     as far as I can tell.
     28  * - write():
     29  *   - For writing arbitrary amounts of data.
     30  *     CS is asserted as with read() calls, so the less fragmented
     31  *     the better.
     32  *
     33  * All GPIO toggling and chip select requirements are handled behind this
     34  * interface.
     35  *
     36  */
     37 
     38 #include <errno.h>
     39 #include <fcntl.h>
     40 #include <limits.h>
     41 #include <stdint.h>
     42 #include <stdlib.h>
     43 #include <string.h>
     44 #include <sys/ioctl.h>
     45 #include <unistd.h>
     46 
     47 #include "../include/ese/hw/nxp/pn80t/common.h"
     48 
     49 #ifndef UNUSED
     50 #define UNUSED(x) x __attribute__((unused))
     51 #endif
     52 
     53 /* From kernel/drivers/nfc/nq-nci.h */
     54 #define ESE_SET_PWR _IOW(0xE9, 0x02, unsigned int)
     55 #define ESE_GET_PWR _IOR(0xE9, 0x03, unsigned int)
     56 #define ESE_CLEAR_GPIO _IOW(0xE9, 0x11, unsigned int)
     57 
     58 static const char kDevicePath[] = "/dev/pn81a";
     59 
     60 struct PlatformHandle {
     61   int fd;
     62 };
     63 
     64 int platform_toggle_bootloader(void *blob, int val) {
     65   const struct PlatformHandle *handle = blob;
     66   if (!handle) {
     67     return -1;
     68   }
     69   return ioctl(handle->fd, ESE_CLEAR_GPIO, val);
     70 }
     71 
     72 int platform_toggle_reset(void *blob, int val) {
     73   const struct PlatformHandle *handle = blob;
     74   if (!handle) {
     75     return -1;
     76   }
     77   /* 0=power and 1=no power in the kernel. */
     78   return ioctl(handle->fd, ESE_SET_PWR, !val);
     79 }
     80 
     81 void *platform_init(void *hwopts) {
     82   /* TODO(wad): It may make sense to pass in the dev path here. */
     83   if (hwopts != NULL) {
     84     return NULL;
     85   }
     86 
     87   struct PlatformHandle *handle = calloc(1, sizeof(*handle));
     88   if (!handle) {
     89     ALOGE("%s: unable to allocate memory for handle", __func__);
     90     return NULL;
     91   }
     92   handle->fd = open(kDevicePath, O_RDWR);
     93   if (handle->fd < 0) {
     94     ALOGE("%s: opening '%s' failed: %s", __func__, kDevicePath,
     95           strerror(errno));
     96     free(handle);
     97     return NULL;
     98   }
     99   return handle;
    100 }
    101 
    102 int platform_release(void *blob) {
    103   struct PlatformHandle *handle = blob;
    104   if (!handle) {
    105     return -1;
    106   }
    107   /* Power off and cooldown should've happened via common code. */
    108   close(handle->fd);
    109   free(handle);
    110   return 0;
    111 }
    112 
    113 int platform_wait(void *UNUSED(blob), long usec) {
    114   return usleep((useconds_t)usec);
    115 }
    116 
    117 uint32_t nq_transmit(struct EseInterface *ese, const uint8_t *buf, uint32_t len,
    118                      int UNUSED(complete)) {
    119   struct NxpState *ns = NXP_PN80T_STATE(ese);
    120   const struct Pn80tPlatform *platform = ese->ops->opts;
    121   uint32_t bytes = 0;
    122   ALOGV("nq_nci:%s: called [%d]", __func__, len);
    123   if (len > INT_MAX) {
    124     ese_set_error(ese, kNxpPn80tErrorTransmitSize);
    125     ALOGE("Unexpectedly large transfer attempted: %u", len);
    126     return 0;
    127   }
    128   if (len == 0)
    129     return len;
    130   const struct PlatformHandle *handle = ns->handle;
    131   while (bytes < len) {
    132     ssize_t ret = write(handle->fd, (void *)(buf + bytes), len - bytes);
    133     if (ret < 0) {
    134       if (errno == EAGAIN || errno == EINTR) {
    135         continue;
    136       }
    137       ese_set_error(ese, kNxpPn80tErrorTransmit);
    138       ALOGE("%s: failed to write to hw (ret=%zd, errno=%d)", __func__, ret,
    139             errno);
    140       return 0;
    141     }
    142     bytes += ret;
    143   }
    144   return len;
    145 }
    146 
    147 uint32_t nq_receive(struct EseInterface *ese, uint8_t *buf, uint32_t len,
    148                     int UNUSED(complete)) {
    149   const struct Pn80tPlatform *platform = ese->ops->opts;
    150   struct NxpState *ns = NXP_PN80T_STATE(ese);
    151   ALOGV("nq_nci:%s: called [%d]", __func__, len);
    152   if (!ns) {
    153     ALOGE("NxpState was NULL");
    154     return 0;
    155   }
    156   if (len > INT_MAX) {
    157     ese_set_error(ese, kNxpPn80tErrorReceiveSize);
    158     ALOGE("Unexpectedly large receive attempted: %u", len);
    159     return 0;
    160   }
    161   const struct PlatformHandle *handle = ns->handle;
    162   if (len == 0) {
    163     return 0;
    164   }
    165   uint32_t bytes = 0;
    166   while (bytes < len) {
    167     ssize_t ret = read(handle->fd, (void *)(buf + bytes), len - bytes);
    168     if (ret < 0) {
    169       if (errno == EAGAIN || errno == EINTR) {
    170         continue;
    171       }
    172       ALOGE("%s: failed to read from hw (ret=%zd, errno=%d)", __func__, ret,
    173             errno);
    174       ese_set_error(ese, kNxpPn80tErrorReceive);
    175       return 0;
    176     }
    177     bytes += ret;
    178   }
    179   ALOGV("%s: read bytes: %u", __func__, bytes);
    180   return len;
    181 }
    182 
    183 static const struct Pn80tPlatform kPn80tNqNciPlatform = {
    184     .initialize = &platform_init,
    185     .release = &platform_release,
    186     .toggle_reset = &platform_toggle_reset,
    187     .toggle_ven = NULL,
    188     .toggle_power_req = NULL,
    189     .toggle_bootloader = &platform_toggle_bootloader,
    190     .wait = &platform_wait,
    191 };
    192 
    193 static const struct EseOperations ops = {
    194     .name = "NXP PN80T/PN81A (NQ-NCI:PN553)",
    195     .open = &nxp_pn80t_open,
    196     .hw_receive = &nq_receive,
    197     .hw_transmit = &nq_transmit,
    198     .hw_reset = &nxp_pn80t_reset,
    199     .transceive = &nxp_pn80t_transceive,
    200     .poll = &nxp_pn80t_poll,
    201     .close = &nxp_pn80t_close,
    202     .opts = &kPn80tNqNciPlatform,
    203     .errors = kNxpPn80tErrorMessages,
    204     .errors_count = kNxpPn80tErrorMax,
    205 };
    206 __attribute__((visibility("default")))
    207 ESE_DEFINE_HW_OPS(ESE_HW_NXP_PN80T_NQ_NCI, ops);
    208