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  * Defines the PN80T spidev device and platform wrappers consumed in
     17  * the common code.
     18  */
     19 
     20 #include <fcntl.h>
     21 #include <limits.h>
     22 #include <linux/spi/spidev.h>
     23 #include <stdint.h>
     24 #include <stdio.h>
     25 #include <stdlib.h>
     26 #include <string.h>
     27 #include <sys/ioctl.h>
     28 #include <unistd.h>
     29 
     30 #include "../include/ese/hw/nxp/pn80t/common.h"
     31 #include "../include/ese/hw/nxp/spi_board.h"
     32 
     33 struct Handle {
     34   int spi_fd;
     35   struct NxpSpiBoard *board;
     36 };
     37 
     38 int gpio_set(int num, int val) {
     39   char val_path[256];
     40   char val_chr = (val ? '1' : '0');
     41   int fd;
     42   if (num < 0) {
     43     return 0;
     44   }
     45   if (snprintf(val_path, sizeof(val_path), "/sys/class/gpio/gpio%d/value",
     46                num) >= (int)sizeof(val_path)) {
     47     return -1;
     48   }
     49   printf("Gpio @ %s\n", val_path);
     50   fd = open(val_path, O_WRONLY);
     51   if (fd < 0) {
     52     return -1;
     53   }
     54   if (write(fd, &val_chr, 1) < 0) {
     55     close(fd);
     56     return -1;
     57   }
     58   close(fd);
     59   return 0;
     60 }
     61 
     62 int platform_toggle_ven(void *blob, int val) {
     63   struct Handle *handle = blob;
     64   printf("Toggling VEN: %d\n", val);
     65   return gpio_set(handle->board->gpios[kBoardGpioNfcVen], val);
     66 }
     67 
     68 int platform_toggle_reset(void *blob, int val) {
     69   struct Handle *handle = blob;
     70   printf("Toggling RST: %d\n", val);
     71   return gpio_set(handle->board->gpios[kBoardGpioEseRst], val);
     72 }
     73 
     74 int platform_toggle_power_req(void *blob, int val) {
     75   struct Handle *handle = blob;
     76   printf("Toggling SVDD_PWR_REQ: %d\n", val);
     77   return gpio_set(handle->board->gpios[kBoardGpioEseSvddPwrReq], val);
     78 }
     79 
     80 int gpio_configure(int num, int out, int val) {
     81   char dir_path[256];
     82   char numstr[8];
     83   char dir[5];
     84   int fd;
     85   /* <0 is unmapped. No work to do! */
     86   if (num < 0) {
     87     return 0;
     88   }
     89   if (snprintf(dir, sizeof(dir), "%s", (out ? "out" : "in")) >=
     90       (int)sizeof(dir)) {
     91     return -1;
     92   }
     93   if (snprintf(dir_path, sizeof(dir_path), "/sys/class/gpio/gpio%d/direction",
     94                num) >= (int)sizeof(dir_path)) {
     95     return -1;
     96   }
     97   if (snprintf(numstr, sizeof(numstr), "%d", num) >= (int)sizeof(numstr)) {
     98     return -1;
     99   }
    100   fd = open("/sys/class/gpio/export", O_WRONLY);
    101   if (fd < 0) {
    102     return -1;
    103   }
    104   /* Exporting can only happen once, so instead of stat()ing, just ignore
    105    * errors. */
    106   (void)write(fd, numstr, strlen(numstr));
    107   close(fd);
    108 
    109   fd = open(dir_path, O_WRONLY);
    110   if (fd < 0) {
    111     return -1;
    112   }
    113   if (write(fd, dir, strlen(dir)) < 0) {
    114     close(fd);
    115     return -1;
    116   }
    117   close(fd);
    118   return gpio_set(num, val);
    119 }
    120 
    121 void *platform_init(void *hwopts) {
    122   struct NxpSpiBoard *board = hwopts;
    123   struct Handle *handle;
    124   int gpio = 0;
    125 
    126   handle = malloc(sizeof(*handle));
    127   if (!handle) {
    128     return NULL;
    129   }
    130   handle->board = board;
    131 
    132   /* Initialize the mapped GPIOs */
    133   for (; gpio < kBoardGpioMax; ++gpio) {
    134     if (gpio_configure(board->gpios[gpio], 1, 1) < 0) {
    135       free(handle);
    136       return NULL;
    137     }
    138   }
    139 
    140   handle->spi_fd = open(board->dev_path, O_RDWR);
    141   if (handle->spi_fd < 0) {
    142     free(handle);
    143     return NULL;
    144   }
    145   /* If we need anything fancier, we'll need MODE32 in the headers. */
    146   if (ioctl(handle->spi_fd, SPI_IOC_WR_MODE, &board->mode) < 0) {
    147     close(handle->spi_fd);
    148     free(handle);
    149     return NULL;
    150   }
    151   if (ioctl(handle->spi_fd, SPI_IOC_WR_BITS_PER_WORD, &board->bits) < 0) {
    152     close(handle->spi_fd);
    153     free(handle);
    154     return NULL;
    155   }
    156   if (ioctl(handle->spi_fd, SPI_IOC_WR_MAX_SPEED_HZ, &board->speed) < 0) {
    157     close(handle->spi_fd);
    158     free(handle);
    159     return NULL;
    160   }
    161   printf("Linux SPIDev initialized\n");
    162   return (void *)handle;
    163 }
    164 
    165 int platform_release(void *blob) {
    166   struct Handle *handle = blob;
    167   close(handle->spi_fd);
    168   free(handle);
    169   /* Note, we don't unconfigure the GPIOs. */
    170   return 0;
    171 }
    172 
    173 int platform_wait(void *blob __attribute__((unused)), long usec) {
    174   return usleep((useconds_t)usec);
    175 }
    176 
    177 uint32_t spidev_transmit(struct EseInterface *ese, const uint8_t *buf,
    178                          uint32_t len, int complete) {
    179   struct NxpState *ns = NXP_PN80T_STATE(ese);
    180   struct Handle *handle = ns->handle;
    181   struct spi_ioc_transfer tr = {
    182       .tx_buf = (unsigned long)buf,
    183       .rx_buf = 0,
    184       .len = (uint32_t)len,
    185       .delay_usecs = 0,
    186       .speed_hz = 0,
    187       .bits_per_word = 0,
    188       .cs_change = !!complete,
    189   };
    190   ssize_t ret = -1;
    191   ALOGV("spidev:%s: called [%d]", __func__, len);
    192   if (len > INT_MAX) {
    193     ese_set_error(ese, kNxpPn80tErrorTransmitSize);
    194     ALOGE("Unexpectedly large transfer attempted: %u", len);
    195     return 0;
    196   }
    197   ret = ioctl(handle->spi_fd, SPI_IOC_MESSAGE(1), &tr);
    198   if (ret < 1) {
    199     ese_set_error(ese, kNxpPn80tErrorTransmit);
    200     ALOGE("%s: failed to write to hw (ret=%zd)", __func__, ret);
    201     return 0;
    202   }
    203   return len;
    204 }
    205 
    206 uint32_t spidev_receive(struct EseInterface *ese, uint8_t *buf, uint32_t len,
    207                         int complete) {
    208   struct NxpState *ns = NXP_PN80T_STATE(ese);
    209   struct Handle *handle = ns->handle;
    210   ssize_t ret = -1;
    211   struct spi_ioc_transfer tr = {
    212       .tx_buf = 0,
    213       .rx_buf = (unsigned long)buf,
    214       .len = (uint32_t)len,
    215       .delay_usecs = 0,
    216       .speed_hz = 0,
    217       .bits_per_word = 0,
    218       .cs_change = !!complete,
    219   };
    220   ALOGV("spidev:%s: called [%d]", __func__, len);
    221   if (len > INT_MAX) {
    222     ese_set_error(ese, kNxpPn80tErrorReceiveSize);
    223     ALOGE("Unexpectedly large receive attempted: %u", len);
    224     return 0;
    225   }
    226   ret = ioctl(handle->spi_fd, SPI_IOC_MESSAGE(1), &tr);
    227   if (ret < 1) {
    228     ALOGE("%s: failed to read from hw (ret=%zd)", __func__, ret);
    229     ese_set_error(ese, kNxpPn80tErrorReceive);
    230     return 0;
    231   }
    232   ALOGV("%s: read bytes: %zd", __func__, len);
    233   return len;
    234 }
    235 
    236 static const struct Pn80tPlatform kPn80tLinuxSpidevPlatform = {
    237     .initialize = &platform_init,
    238     .release = &platform_release,
    239     .toggle_reset = &platform_toggle_reset,
    240     .toggle_ven = &platform_toggle_ven,
    241     .toggle_power_req = &platform_toggle_power_req,
    242     .toggle_bootloader = NULL,
    243     .wait = &platform_wait,
    244 };
    245 
    246 static const struct EseOperations ops = {
    247     .name = "NXP PN80T/PN81A (PN553)",
    248     .open = &nxp_pn80t_open,
    249     .hw_receive = &spidev_receive,
    250     .hw_transmit = &spidev_transmit,
    251     .hw_reset = &nxp_pn80t_reset,
    252     .transceive = &nxp_pn80t_transceive,
    253     .poll = &nxp_pn80t_poll,
    254     .close = &nxp_pn80t_close,
    255     .opts = &kPn80tLinuxSpidevPlatform,
    256     .errors = kNxpPn80tErrorMessages,
    257     .errors_count = kNxpPn80tErrorMax,
    258 };
    259 __attribute__((visibility("default")))
    260 ESE_DEFINE_HW_OPS(ESE_HW_NXP_PN80T_SPIDEV, ops);
    261