root/drivers/nfc/nxp-nci/firmware.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. nxp_nci_fw_work_complete
  2. nxp_nci_fw_crc
  3. nxp_nci_fw_send_chunk
  4. nxp_nci_fw_send
  5. nxp_nci_fw_work
  6. nxp_nci_fw_download
  7. nxp_nci_fw_read_status
  8. nxp_nci_fw_check_crc
  9. nxp_nci_fw_recv_frame

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

/* [<][>][^][v][top][bottom][index][help] */