blob: d4ac386206f5b11b872ef828a7bd2bddd9491cb1 [file] [log] [blame]
/*
* 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);