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 58 static const char kDevicePath[] = "/dev/pn81a"; 59 60 struct PlatformHandle { 61 int fd; 62 }; 63 64 int 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 72 int 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 81 void *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 102 int 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 113 int platform_wait(void *UNUSED(blob), long usec) { 114 return usleep((useconds_t)usec); 115 } 116 117 uint32_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 147 uint32_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 183 static 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 193 static 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"))) 207 ESE_DEFINE_HW_OPS(ESE_HW_NXP_PN80T_NQ_NCI, ops); 208