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 
gpio_set(int num,int val)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 
platform_toggle_ven(void * blob,int val)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 
platform_toggle_reset(void * blob,int val)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 
platform_toggle_power_req(void * blob,int val)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 
gpio_configure(int num,int out,int val)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 
platform_init(void * hwopts)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 
platform_release(void * blob)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 
platform_wait(void * blob,long usec)173 int platform_wait(void *blob __attribute__((unused)), long usec) {
174   return usleep((useconds_t)usec);
175 }
176 
spidev_transmit(struct EseInterface * ese,const uint8_t * buf,uint32_t len,int complete)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 
spidev_receive(struct EseInterface * ese,uint8_t * buf,uint32_t len,int complete)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