1 /* 2 * Generic driver for NXP NCI NFC chips 3 * 4 * Copyright (C) 2014 NXP Semiconductors All rights reserved. 5 * 6 * Author: Clément Perrochaud <clement.perrochaud@nxp.com> 7 * 8 * Derived from PN544 device driver: 9 * Copyright (C) 2012 Intel Corporation. All rights reserved. 10 * 11 * This program is free software; you can redistribute it and/or modify it 12 * under the terms and conditions of the GNU General Public License, 13 * version 2, as published by the Free Software Foundation. 14 * 15 * This program is distributed in the hope that it will be useful, 16 * but WITHOUT ANY WARRANTY; without even the implied warranty of 17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 18 * GNU General Public License for more details. 19 * 20 * You should have received a copy of the GNU General Public License 21 * along with this program; if not, see <http://www.gnu.org/licenses/>. 22 */ 23 24 #include <linux/completion.h> 25 #include <linux/firmware.h> 26 #include <linux/nfc.h> 27 #include <asm/unaligned.h> 28 29 #include "nxp-nci.h" 30 31 /* Crypto operations can take up to 30 seconds */ 32 #define NXP_NCI_FW_ANSWER_TIMEOUT msecs_to_jiffies(30000) 33 34 #define NXP_NCI_FW_CMD_RESET 0xF0 35 #define NXP_NCI_FW_CMD_GETVERSION 0xF1 36 #define NXP_NCI_FW_CMD_CHECKINTEGRITY 0xE0 37 #define NXP_NCI_FW_CMD_WRITE 0xC0 38 #define NXP_NCI_FW_CMD_READ 0xA2 39 #define NXP_NCI_FW_CMD_GETSESSIONSTATE 0xF2 40 #define NXP_NCI_FW_CMD_LOG 0xA7 41 #define NXP_NCI_FW_CMD_FORCE 0xD0 42 #define NXP_NCI_FW_CMD_GET_DIE_ID 0xF4 43 44 #define NXP_NCI_FW_CHUNK_FLAG 0x0400 45 46 #define NXP_NCI_FW_RESULT_OK 0x00 47 #define NXP_NCI_FW_RESULT_INVALID_ADDR 0x01 48 #define NXP_NCI_FW_RESULT_GENERIC_ERROR 0x02 49 #define NXP_NCI_FW_RESULT_UNKNOWN_CMD 0x0B 50 #define NXP_NCI_FW_RESULT_ABORTED_CMD 0x0C 51 #define NXP_NCI_FW_RESULT_PLL_ERROR 0x0D 52 #define NXP_NCI_FW_RESULT_ADDR_RANGE_OFL_ERROR 0x1E 53 #define NXP_NCI_FW_RESULT_BUFFER_OFL_ERROR 0x1F 54 #define NXP_NCI_FW_RESULT_MEM_BSY 0x20 55 #define NXP_NCI_FW_RESULT_SIGNATURE_ERROR 0x21 56 #define NXP_NCI_FW_RESULT_FIRMWARE_VERSION_ERROR 0x24 57 #define NXP_NCI_FW_RESULT_PROTOCOL_ERROR 0x28 58 #define NXP_NCI_FW_RESULT_SFWU_DEGRADED 0x2A 59 #define NXP_NCI_FW_RESULT_PH_STATUS_FIRST_CHUNK 0x2D 60 #define NXP_NCI_FW_RESULT_PH_STATUS_NEXT_CHUNK 0x2E 61 #define NXP_NCI_FW_RESULT_PH_STATUS_INTERNAL_ERROR_5 0xC5 62 63 void nxp_nci_fw_work_complete(struct nxp_nci_info *info, int result) 64 { 65 struct nxp_nci_fw_info *fw_info = &info->fw_info; 66 int r; 67 68 if (info->phy_ops->set_mode) { 69 r = info->phy_ops->set_mode(info->phy_id, NXP_NCI_MODE_COLD); 70 if (r < 0 && result == 0) 71 result = -r; 72 } 73 74 info->mode = NXP_NCI_MODE_COLD; 75 76 if (fw_info->fw) { 77 release_firmware(fw_info->fw); 78 fw_info->fw = NULL; 79 } 80 81 nfc_fw_download_done(info->ndev->nfc_dev, fw_info->name, (u32) -result); 82 } 83 84 /* crc_ccitt cannot be used since it is computed MSB first and not LSB first */ 85 static u16 nxp_nci_fw_crc(u8 const *buffer, size_t len) 86 { 87 u16 crc = 0xffff; 88 89 while (len--) { 90 crc = ((crc >> 8) | (crc << 8)) ^ *buffer++; 91 crc ^= (crc & 0xff) >> 4; 92 crc ^= (crc & 0xff) << 12; 93 crc ^= (crc & 0xff) << 5; 94 } 95 96 return crc; 97 } 98 99 static int nxp_nci_fw_send_chunk(struct nxp_nci_info *info) 100 { 101 struct nxp_nci_fw_info *fw_info = &info->fw_info; 102 u16 header, crc; 103 struct sk_buff *skb; 104 size_t chunk_len; 105 size_t remaining_len; 106 int r; 107 108 skb = nci_skb_alloc(info->ndev, info->max_payload, GFP_KERNEL); 109 if (!skb) { 110 r = -ENOMEM; 111 goto chunk_exit; 112 } 113 114 chunk_len = info->max_payload - NXP_NCI_FW_HDR_LEN - NXP_NCI_FW_CRC_LEN; 115 remaining_len = fw_info->frame_size - fw_info->written; 116 117 if (remaining_len > chunk_len) { 118 header = NXP_NCI_FW_CHUNK_FLAG; 119 } else { 120 chunk_len = remaining_len; 121 header = 0x0000; 122 } 123 124 header |= chunk_len & NXP_NCI_FW_FRAME_LEN_MASK; 125 put_unaligned_be16(header, skb_put(skb, NXP_NCI_FW_HDR_LEN)); 126 127 skb_put_data(skb, fw_info->data + fw_info->written, chunk_len); 128 129 crc = nxp_nci_fw_crc(skb->data, chunk_len + NXP_NCI_FW_HDR_LEN); 130 put_unaligned_be16(crc, skb_put(skb, NXP_NCI_FW_CRC_LEN)); 131 132 r = info->phy_ops->write(info->phy_id, skb); 133 if (r >= 0) 134 r = chunk_len; 135 136 kfree_skb(skb); 137 138 chunk_exit: 139 return r; 140 } 141 142 static int nxp_nci_fw_send(struct nxp_nci_info *info) 143 { 144 struct nxp_nci_fw_info *fw_info = &info->fw_info; 145 long completion_rc; 146 int r; 147 148 reinit_completion(&fw_info->cmd_completion); 149 150 if (fw_info->written == 0) { 151 fw_info->frame_size = get_unaligned_be16(fw_info->data) & 152 NXP_NCI_FW_FRAME_LEN_MASK; 153 fw_info->data += NXP_NCI_FW_HDR_LEN; 154 fw_info->size -= NXP_NCI_FW_HDR_LEN; 155 } 156 157 if (fw_info->frame_size > fw_info->size) 158 return -EMSGSIZE; 159 160 r = nxp_nci_fw_send_chunk(info); 161 if (r < 0) 162 return r; 163 164 fw_info->written += r; 165 166 if (*fw_info->data == NXP_NCI_FW_CMD_RESET) { 167 fw_info->cmd_result = 0; 168 if (fw_info->fw) 169 schedule_work(&fw_info->work); 170 } else { 171 completion_rc = wait_for_completion_interruptible_timeout( 172 &fw_info->cmd_completion, NXP_NCI_FW_ANSWER_TIMEOUT); 173 if (completion_rc == 0) 174 return -ETIMEDOUT; 175 } 176 177 return 0; 178 } 179 180 void nxp_nci_fw_work(struct work_struct *work) 181 { 182 struct nxp_nci_info *info; 183 struct nxp_nci_fw_info *fw_info; 184 int r; 185 186 fw_info = container_of(work, struct nxp_nci_fw_info, work); 187 info = container_of(fw_info, struct nxp_nci_info, fw_info); 188 189 mutex_lock(&info->info_lock); 190 191 r = fw_info->cmd_result; 192 if (r < 0) 193 goto exit_work; 194 195 if (fw_info->written == fw_info->frame_size) { 196 fw_info->data += fw_info->frame_size; 197 fw_info->size -= fw_info->frame_size; 198 fw_info->written = 0; 199 } 200 201 if (fw_info->size > 0) 202 r = nxp_nci_fw_send(info); 203 204 exit_work: 205 if (r < 0 || fw_info->size == 0) 206 nxp_nci_fw_work_complete(info, r); 207 mutex_unlock(&info->info_lock); 208 } 209 210 int nxp_nci_fw_download(struct nci_dev *ndev, const char *firmware_name) 211 { 212 struct nxp_nci_info *info = nci_get_drvdata(ndev); 213 struct nxp_nci_fw_info *fw_info = &info->fw_info; 214 int r; 215 216 mutex_lock(&info->info_lock); 217 218 if (!info->phy_ops->set_mode || !info->phy_ops->write) { 219 r = -ENOTSUPP; 220 goto fw_download_exit; 221 } 222 223 if (!firmware_name || firmware_name[0] == '\0') { 224 r = -EINVAL; 225 goto fw_download_exit; 226 } 227 228 strcpy(fw_info->name, firmware_name); 229 230 r = request_firmware(&fw_info->fw, firmware_name, 231 ndev->nfc_dev->dev.parent); 232 if (r < 0) 233 goto fw_download_exit; 234 235 r = info->phy_ops->set_mode(info->phy_id, NXP_NCI_MODE_FW); 236 if (r < 0) { 237 release_firmware(fw_info->fw); 238 goto fw_download_exit; 239 } 240 241 info->mode = NXP_NCI_MODE_FW; 242 243 fw_info->data = fw_info->fw->data; 244 fw_info->size = fw_info->fw->size; 245 fw_info->written = 0; 246 fw_info->frame_size = 0; 247 fw_info->cmd_result = 0; 248 249 schedule_work(&fw_info->work); 250 251 fw_download_exit: 252 mutex_unlock(&info->info_lock); 253 return r; 254 } 255 256 static int nxp_nci_fw_read_status(u8 stat) 257 { 258 switch (stat) { 259 case NXP_NCI_FW_RESULT_OK: 260 return 0; 261 case NXP_NCI_FW_RESULT_INVALID_ADDR: 262 return -EINVAL; 263 case NXP_NCI_FW_RESULT_UNKNOWN_CMD: 264 return -EINVAL; 265 case NXP_NCI_FW_RESULT_ABORTED_CMD: 266 return -EMSGSIZE; 267 case NXP_NCI_FW_RESULT_ADDR_RANGE_OFL_ERROR: 268 return -EADDRNOTAVAIL; 269 case NXP_NCI_FW_RESULT_BUFFER_OFL_ERROR: 270 return -ENOBUFS; 271 case NXP_NCI_FW_RESULT_MEM_BSY: 272 return -ENOKEY; 273 case NXP_NCI_FW_RESULT_SIGNATURE_ERROR: 274 return -EKEYREJECTED; 275 case NXP_NCI_FW_RESULT_FIRMWARE_VERSION_ERROR: 276 return -EALREADY; 277 case NXP_NCI_FW_RESULT_PROTOCOL_ERROR: 278 return -EPROTO; 279 case NXP_NCI_FW_RESULT_SFWU_DEGRADED: 280 return -EHWPOISON; 281 case NXP_NCI_FW_RESULT_PH_STATUS_FIRST_CHUNK: 282 return 0; 283 case NXP_NCI_FW_RESULT_PH_STATUS_NEXT_CHUNK: 284 return 0; 285 case NXP_NCI_FW_RESULT_PH_STATUS_INTERNAL_ERROR_5: 286 return -EINVAL; 287 default: 288 return -EIO; 289 } 290 } 291 292 static u16 nxp_nci_fw_check_crc(struct sk_buff *skb) 293 { 294 u16 crc, frame_crc; 295 size_t len = skb->len - NXP_NCI_FW_CRC_LEN; 296 297 crc = nxp_nci_fw_crc(skb->data, len); 298 frame_crc = get_unaligned_be16(skb->data + len); 299 300 return (crc ^ frame_crc); 301 } 302 303 void nxp_nci_fw_recv_frame(struct nci_dev *ndev, struct sk_buff *skb) 304 { 305 struct nxp_nci_info *info = nci_get_drvdata(ndev); 306 struct nxp_nci_fw_info *fw_info = &info->fw_info; 307 308 complete(&fw_info->cmd_completion); 309 310 if (skb) { 311 if (nxp_nci_fw_check_crc(skb) != 0x00) 312 fw_info->cmd_result = -EBADMSG; 313 else 314 fw_info->cmd_result = nxp_nci_fw_read_status(*(u8 *)skb_pull(skb, NXP_NCI_FW_HDR_LEN)); 315 kfree_skb(skb); 316 } else { 317 fw_info->cmd_result = -EIO; 318 } 319 320 if (fw_info->fw) 321 schedule_work(&fw_info->work); 322 } 323 EXPORT_SYMBOL(nxp_nci_fw_recv_frame); 324