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 .wait = &platform_wait, 243 }; 244 245 static const struct EseOperations ops = { 246 .name = "NXP PN80T/PN81A (PN553)", 247 .open = &nxp_pn80t_open, 248 .hw_receive = &spidev_receive, 249 .hw_transmit = &spidev_transmit, 250 .hw_reset = &nxp_pn80t_reset, 251 .transceive = &nxp_pn80t_transceive, 252 .poll = &nxp_pn80t_poll, 253 .close = &nxp_pn80t_close, 254 .opts = &kPn80tLinuxSpidevPlatform, 255 .errors = kNxpPn80tErrorMessages, 256 .errors_count = kNxpPn80tErrorMax, 257 }; 258 __attribute__((visibility("default"))) 259 ESE_DEFINE_HW_OPS(ESE_HW_NXP_PN80T_SPIDEV, ops); 260