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 * Platform implementation for a nq-nci extension driver.
17 *
18 * The driver presents the following interface on a miscdev:
19 * - ioctl():
20 *   - for setting and getting power.
21 *     This handles SVDD_PWR_REQ and NFC_VEN muxing.
22 *     (ESE_RST is not connected in this case.)
23 * - read():
24 *   - For reading arbitrary amounts of data.
25 *     CS is asserted and deasserted on each call, but the clock
26 *     also appears to do the same which keeps the ese data available
27 *     as far as I can tell.
28 * - write():
29 *   - For writing arbitrary amounts of data.
30 *     CS is asserted as with read() calls, so the less fragmented
31 *     the better.
32 *
33 * All GPIO toggling and chip select requirements are handled behind this
34 * interface.
35 *
36 */
37
38#include <errno.h>
39#include <fcntl.h>
40#include <limits.h>
41#include <stdint.h>
42#include <stdlib.h>
43#include <string.h>
44#include <sys/ioctl.h>
45#include <unistd.h>
46
47#include "../include/ese/hw/nxp/pn80t/common.h"
48
49#ifndef UNUSED
50#define UNUSED(x) x __attribute__((unused))
51#endif
52
53/* From kernel/drivers/nfc/nq-nci.h */
54#define ESE_SET_PWR _IOW(0xE9, 0x02, unsigned int)
55#define ESE_GET_PWR _IOR(0xE9, 0x03, unsigned int)
56#define ESE_CLEAR_GPIO _IOW(0xE9, 0x11, unsigned int)
57
58static const char kDevicePath[] = "/dev/pn81a";
59
60struct PlatformHandle {
61  int fd;
62};
63
64int platform_toggle_bootloader(void *blob, int val) {
65  const struct PlatformHandle *handle = blob;
66  if (!handle) {
67    return -1;
68  }
69  return ioctl(handle->fd, ESE_CLEAR_GPIO, val);
70}
71
72int platform_toggle_reset(void *blob, int val) {
73  const struct PlatformHandle *handle = blob;
74  if (!handle) {
75    return -1;
76  }
77  /* 0=power and 1=no power in the kernel. */
78  return ioctl(handle->fd, ESE_SET_PWR, !val);
79}
80
81void *platform_init(void *hwopts) {
82  /* TODO(wad): It may make sense to pass in the dev path here. */
83  if (hwopts != NULL) {
84    return NULL;
85  }
86
87  struct PlatformHandle *handle = calloc(1, sizeof(*handle));
88  if (!handle) {
89    ALOGE("%s: unable to allocate memory for handle", __func__);
90    return NULL;
91  }
92  handle->fd = open(kDevicePath, O_RDWR);
93  if (handle->fd < 0) {
94    ALOGE("%s: opening '%s' failed: %s", __func__, kDevicePath,
95          strerror(errno));
96    free(handle);
97    return NULL;
98  }
99  return handle;
100}
101
102int platform_release(void *blob) {
103  struct PlatformHandle *handle = blob;
104  if (!handle) {
105    return -1;
106  }
107  /* Power off and cooldown should've happened via common code. */
108  close(handle->fd);
109  free(handle);
110  return 0;
111}
112
113int platform_wait(void *UNUSED(blob), long usec) {
114  return usleep((useconds_t)usec);
115}
116
117uint32_t nq_transmit(struct EseInterface *ese, const uint8_t *buf, uint32_t len,
118                     int UNUSED(complete)) {
119  struct NxpState *ns = NXP_PN80T_STATE(ese);
120  const struct Pn80tPlatform *platform = ese->ops->opts;
121  uint32_t bytes = 0;
122  ALOGV("nq_nci:%s: called [%d]", __func__, len);
123  if (len > INT_MAX) {
124    ese_set_error(ese, kNxpPn80tErrorTransmitSize);
125    ALOGE("Unexpectedly large transfer attempted: %u", len);
126    return 0;
127  }
128  if (len == 0)
129    return len;
130  const struct PlatformHandle *handle = ns->handle;
131  while (bytes < len) {
132    ssize_t ret = write(handle->fd, (void *)(buf + bytes), len - bytes);
133    if (ret < 0) {
134      if (errno == EAGAIN || errno == EINTR) {
135        continue;
136      }
137      ese_set_error(ese, kNxpPn80tErrorTransmit);
138      ALOGE("%s: failed to write to hw (ret=%zd, errno=%d)", __func__, ret,
139            errno);
140      return 0;
141    }
142    bytes += ret;
143  }
144  return len;
145}
146
147uint32_t nq_receive(struct EseInterface *ese, uint8_t *buf, uint32_t len,
148                    int UNUSED(complete)) {
149  const struct Pn80tPlatform *platform = ese->ops->opts;
150  struct NxpState *ns = NXP_PN80T_STATE(ese);
151  ALOGV("nq_nci:%s: called [%d]", __func__, len);
152  if (!ns) {
153    ALOGE("NxpState was NULL");
154    return 0;
155  }
156  if (len > INT_MAX) {
157    ese_set_error(ese, kNxpPn80tErrorReceiveSize);
158    ALOGE("Unexpectedly large receive attempted: %u", len);
159    return 0;
160  }
161  const struct PlatformHandle *handle = ns->handle;
162  if (len == 0) {
163    return 0;
164  }
165  uint32_t bytes = 0;
166  while (bytes < len) {
167    ssize_t ret = read(handle->fd, (void *)(buf + bytes), len - bytes);
168    if (ret < 0) {
169      if (errno == EAGAIN || errno == EINTR) {
170        continue;
171      }
172      ALOGE("%s: failed to read from hw (ret=%zd, errno=%d)", __func__, ret,
173            errno);
174      ese_set_error(ese, kNxpPn80tErrorReceive);
175      return 0;
176    }
177    bytes += ret;
178  }
179  ALOGV("%s: read bytes: %u", __func__, bytes);
180  return len;
181}
182
183static const struct Pn80tPlatform kPn80tNqNciPlatform = {
184    .initialize = &platform_init,
185    .release = &platform_release,
186    .toggle_reset = &platform_toggle_reset,
187    .toggle_ven = NULL,
188    .toggle_power_req = NULL,
189    .toggle_bootloader = &platform_toggle_bootloader,
190    .wait = &platform_wait,
191};
192
193static const struct EseOperations ops = {
194    .name = "NXP PN80T/PN81A (NQ-NCI:PN553)",
195    .open = &nxp_pn80t_open,
196    .hw_receive = &nq_receive,
197    .hw_transmit = &nq_transmit,
198    .hw_reset = &nxp_pn80t_reset,
199    .transceive = &nxp_pn80t_transceive,
200    .poll = &nxp_pn80t_poll,
201    .close = &nxp_pn80t_close,
202    .opts = &kPn80tNqNciPlatform,
203    .errors = kNxpPn80tErrorMessages,
204    .errors_count = kNxpPn80tErrorMax,
205};
206__attribute__((visibility("default")))
207ESE_DEFINE_HW_OPS(ESE_HW_NXP_PN80T_NQ_NCI, ops);
208