C++程序  |  260行  |  6.87 KB

/*
 * Copyright (C) 2017 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
 * Defines the PN80T spidev device and platform wrappers consumed in
 * the common code.
 */

#include <fcntl.h>
#include <limits.h>
#include <linux/spi/spidev.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/ioctl.h>
#include <unistd.h>

#include "../include/ese/hw/nxp/pn80t/common.h"
#include "../include/ese/hw/nxp/spi_board.h"

struct Handle {
  int spi_fd;
  struct NxpSpiBoard *board;
};

int gpio_set(int num, int val) {
  char val_path[256];
  char val_chr = (val ? '1' : '0');
  int fd;
  if (num < 0) {
    return 0;
  }
  if (snprintf(val_path, sizeof(val_path), "/sys/class/gpio/gpio%d/value",
               num) >= (int)sizeof(val_path)) {
    return -1;
  }
  printf("Gpio @ %s\n", val_path);
  fd = open(val_path, O_WRONLY);
  if (fd < 0) {
    return -1;
  }
  if (write(fd, &val_chr, 1) < 0) {
    close(fd);
    return -1;
  }
  close(fd);
  return 0;
}

int platform_toggle_ven(void *blob, int val) {
  struct Handle *handle = blob;
  printf("Toggling VEN: %d\n", val);
  return gpio_set(handle->board->gpios[kBoardGpioNfcVen], val);
}

int platform_toggle_reset(void *blob, int val) {
  struct Handle *handle = blob;
  printf("Toggling RST: %d\n", val);
  return gpio_set(handle->board->gpios[kBoardGpioEseRst], val);
}

int platform_toggle_power_req(void *blob, int val) {
  struct Handle *handle = blob;
  printf("Toggling SVDD_PWR_REQ: %d\n", val);
  return gpio_set(handle->board->gpios[kBoardGpioEseSvddPwrReq], val);
}

int gpio_configure(int num, int out, int val) {
  char dir_path[256];
  char numstr[8];
  char dir[5];
  int fd;
  /* <0 is unmapped. No work to do! */
  if (num < 0) {
    return 0;
  }
  if (snprintf(dir, sizeof(dir), "%s", (out ? "out" : "in")) >=
      (int)sizeof(dir)) {
    return -1;
  }
  if (snprintf(dir_path, sizeof(dir_path), "/sys/class/gpio/gpio%d/direction",
               num) >= (int)sizeof(dir_path)) {
    return -1;
  }
  if (snprintf(numstr, sizeof(numstr), "%d", num) >= (int)sizeof(numstr)) {
    return -1;
  }
  fd = open("/sys/class/gpio/export", O_WRONLY);
  if (fd < 0) {
    return -1;
  }
  /* Exporting can only happen once, so instead of stat()ing, just ignore
   * errors. */
  (void)write(fd, numstr, strlen(numstr));
  close(fd);

  fd = open(dir_path, O_WRONLY);
  if (fd < 0) {
    return -1;
  }
  if (write(fd, dir, strlen(dir)) < 0) {
    close(fd);
    return -1;
  }
  close(fd);
  return gpio_set(num, val);
}

void *platform_init(void *hwopts) {
  struct NxpSpiBoard *board = hwopts;
  struct Handle *handle;
  int gpio = 0;

  handle = malloc(sizeof(*handle));
  if (!handle) {
    return NULL;
  }
  handle->board = board;

  /* Initialize the mapped GPIOs */
  for (; gpio < kBoardGpioMax; ++gpio) {
    if (gpio_configure(board->gpios[gpio], 1, 1) < 0) {
      free(handle);
      return NULL;
    }
  }

  handle->spi_fd = open(board->dev_path, O_RDWR);
  if (handle->spi_fd < 0) {
    free(handle);
    return NULL;
  }
  /* If we need anything fancier, we'll need MODE32 in the headers. */
  if (ioctl(handle->spi_fd, SPI_IOC_WR_MODE, &board->mode) < 0) {
    close(handle->spi_fd);
    free(handle);
    return NULL;
  }
  if (ioctl(handle->spi_fd, SPI_IOC_WR_BITS_PER_WORD, &board->bits) < 0) {
    close(handle->spi_fd);
    free(handle);
    return NULL;
  }
  if (ioctl(handle->spi_fd, SPI_IOC_WR_MAX_SPEED_HZ, &board->speed) < 0) {
    close(handle->spi_fd);
    free(handle);
    return NULL;
  }
  printf("Linux SPIDev initialized\n");
  return (void *)handle;
}

int platform_release(void *blob) {
  struct Handle *handle = blob;
  close(handle->spi_fd);
  free(handle);
  /* Note, we don't unconfigure the GPIOs. */
  return 0;
}

int platform_wait(void *blob __attribute__((unused)), long usec) {
  return usleep((useconds_t)usec);
}

uint32_t spidev_transmit(struct EseInterface *ese, const uint8_t *buf,
                         uint32_t len, int complete) {
  struct NxpState *ns = NXP_PN80T_STATE(ese);
  struct Handle *handle = ns->handle;
  struct spi_ioc_transfer tr = {
      .tx_buf = (unsigned long)buf,
      .rx_buf = 0,
      .len = (uint32_t)len,
      .delay_usecs = 0,
      .speed_hz = 0,
      .bits_per_word = 0,
      .cs_change = !!complete,
  };
  ssize_t ret = -1;
  ALOGV("spidev:%s: called [%d]", __func__, len);
  if (len > INT_MAX) {
    ese_set_error(ese, kNxpPn80tErrorTransmitSize);
    ALOGE("Unexpectedly large transfer attempted: %u", len);
    return 0;
  }
  ret = ioctl(handle->spi_fd, SPI_IOC_MESSAGE(1), &tr);
  if (ret < 1) {
    ese_set_error(ese, kNxpPn80tErrorTransmit);
    ALOGE("%s: failed to write to hw (ret=%zd)", __func__, ret);
    return 0;
  }
  return len;
}

uint32_t spidev_receive(struct EseInterface *ese, uint8_t *buf, uint32_t len,
                        int complete) {
  struct NxpState *ns = NXP_PN80T_STATE(ese);
  struct Handle *handle = ns->handle;
  ssize_t ret = -1;
  struct spi_ioc_transfer tr = {
      .tx_buf = 0,
      .rx_buf = (unsigned long)buf,
      .len = (uint32_t)len,
      .delay_usecs = 0,
      .speed_hz = 0,
      .bits_per_word = 0,
      .cs_change = !!complete,
  };
  ALOGV("spidev:%s: called [%d]", __func__, len);
  if (len > INT_MAX) {
    ese_set_error(ese, kNxpPn80tErrorReceiveSize);
    ALOGE("Unexpectedly large receive attempted: %u", len);
    return 0;
  }
  ret = ioctl(handle->spi_fd, SPI_IOC_MESSAGE(1), &tr);
  if (ret < 1) {
    ALOGE("%s: failed to read from hw (ret=%zd)", __func__, ret);
    ese_set_error(ese, kNxpPn80tErrorReceive);
    return 0;
  }
  ALOGV("%s: read bytes: %zd", __func__, len);
  return len;
}

static const struct Pn80tPlatform kPn80tLinuxSpidevPlatform = {
    .initialize = &platform_init,
    .release = &platform_release,
    .toggle_reset = &platform_toggle_reset,
    .toggle_ven = &platform_toggle_ven,
    .toggle_power_req = &platform_toggle_power_req,
    .wait = &platform_wait,
};

static const struct EseOperations ops = {
    .name = "NXP PN80T/PN81A (PN553)",
    .open = &nxp_pn80t_open,
    .hw_receive = &spidev_receive,
    .hw_transmit = &spidev_transmit,
    .hw_reset = &nxp_pn80t_reset,
    .transceive = &nxp_pn80t_transceive,
    .poll = &nxp_pn80t_poll,
    .close = &nxp_pn80t_close,
    .opts = &kPn80tLinuxSpidevPlatform,
    .errors = kNxpPn80tErrorMessages,
    .errors_count = kNxpPn80tErrorMax,
};
__attribute__((visibility("default")))
ESE_DEFINE_HW_OPS(ESE_HW_NXP_PN80T_SPIDEV, ops);