diff options
author | Dmitry Torokhov <dmitry.torokhov@gmail.com> | 2012-05-24 12:13:01 +0400 |
---|---|---|
committer | Dmitry Torokhov <dmitry.torokhov@gmail.com> | 2012-05-24 12:13:01 +0400 |
commit | e644dae645e167d154c0526358940986682a72b0 (patch) | |
tree | 972993c6568085b8d407fc7e13de10f4b93c651d /drivers/nfc | |
parent | 899c612d74d4a242158a4db20367388d6299c028 (diff) | |
parent | 86809173ce32ef03bd4d0389dfc72df0c805e9c4 (diff) | |
download | linux-e644dae645e167d154c0526358940986682a72b0.tar.xz |
Merge branch 'next' into for-linus
Diffstat (limited to 'drivers/nfc')
-rw-r--r-- | drivers/nfc/nfcwilink.c | 305 | ||||
-rw-r--r-- | drivers/nfc/pn533.c | 31 |
2 files changed, 310 insertions, 26 deletions
diff --git a/drivers/nfc/nfcwilink.c b/drivers/nfc/nfcwilink.c index 06c3642e5bdb..1f74a77d040d 100644 --- a/drivers/nfc/nfcwilink.c +++ b/drivers/nfc/nfcwilink.c @@ -28,6 +28,8 @@ */ #include <linux/platform_device.h> #include <linux/module.h> +#include <linux/types.h> +#include <linux/firmware.h> #include <linux/nfc.h> #include <net/nfc/nci.h> #include <net/nfc/nci_core.h> @@ -40,11 +42,52 @@ #define NFCWILINK_OFFSET_LEN_IN_HDR 1 #define NFCWILINK_LEN_SIZE 2 #define NFCWILINK_REGISTER_TIMEOUT 8000 /* 8 sec */ +#define NFCWILINK_CMD_TIMEOUT 5000 /* 5 sec */ + +#define BTS_FILE_NAME_MAX_SIZE 40 +#define BTS_FILE_HDR_MAGIC 0x42535442 +#define BTS_FILE_CMD_MAX_LEN 0xff +#define BTS_FILE_ACTION_TYPE_SEND_CMD 1 + +#define NCI_VS_NFCC_INFO_CMD_GID 0x2f +#define NCI_VS_NFCC_INFO_CMD_OID 0x12 +#define NCI_VS_NFCC_INFO_RSP_GID 0x4f +#define NCI_VS_NFCC_INFO_RSP_OID 0x12 struct nfcwilink_hdr { - u8 chnl; - u8 opcode; - u16 len; + __u8 chnl; + __u8 opcode; + __le16 len; +} __packed; + +struct nci_vs_nfcc_info_cmd { + __u8 gid; + __u8 oid; + __u8 plen; +} __packed; + +struct nci_vs_nfcc_info_rsp { + __u8 gid; + __u8 oid; + __u8 plen; + __u8 status; + __u8 hw_id; + __u8 sw_ver_x; + __u8 sw_ver_z; + __u8 patch_id; +} __packed; + +struct bts_file_hdr { + __le32 magic; + __le32 ver; + __u8 rfu[24]; + __u8 actions[0]; +} __packed; + +struct bts_file_action { + __le16 type; + __le16 len; + __u8 data[0]; } __packed; struct nfcwilink { @@ -54,14 +97,241 @@ struct nfcwilink { char st_register_cb_status; long (*st_write) (struct sk_buff *); - struct completion st_register_completed; + + struct completion completed; + + struct nci_vs_nfcc_info_rsp nfcc_info; }; /* NFCWILINK driver flags */ enum { NFCWILINK_RUNNING, + NFCWILINK_FW_DOWNLOAD, }; +static int nfcwilink_send(struct sk_buff *skb); + +static inline struct sk_buff *nfcwilink_skb_alloc(unsigned int len, gfp_t how) +{ + struct sk_buff *skb; + + skb = alloc_skb(len + NFCWILINK_HDR_LEN, how); + if (skb) + skb_reserve(skb, NFCWILINK_HDR_LEN); + + return skb; +} + +static void nfcwilink_fw_download_receive(struct nfcwilink *drv, + struct sk_buff *skb) +{ + struct nci_vs_nfcc_info_rsp *rsp = (void *)skb->data; + + /* Detect NCI_VS_NFCC_INFO_RSP and store the result */ + if ((skb->len > 3) && (rsp->gid == NCI_VS_NFCC_INFO_RSP_GID) && + (rsp->oid == NCI_VS_NFCC_INFO_RSP_OID)) { + memcpy(&drv->nfcc_info, rsp, + sizeof(struct nci_vs_nfcc_info_rsp)); + } + + kfree_skb(skb); + + complete(&drv->completed); +} + +static int nfcwilink_get_bts_file_name(struct nfcwilink *drv, char *file_name) +{ + struct nci_vs_nfcc_info_cmd *cmd; + struct sk_buff *skb; + unsigned long comp_ret; + int rc; + + nfc_dev_dbg(&drv->pdev->dev, "get_bts_file_name entry"); + + skb = nfcwilink_skb_alloc(sizeof(struct nci_vs_nfcc_info_cmd), + GFP_KERNEL); + if (!skb) { + nfc_dev_err(&drv->pdev->dev, + "no memory for nci_vs_nfcc_info_cmd"); + return -ENOMEM; + } + + skb->dev = (void *)drv->ndev; + + cmd = (struct nci_vs_nfcc_info_cmd *) + skb_put(skb, sizeof(struct nci_vs_nfcc_info_cmd)); + cmd->gid = NCI_VS_NFCC_INFO_CMD_GID; + cmd->oid = NCI_VS_NFCC_INFO_CMD_OID; + cmd->plen = 0; + + drv->nfcc_info.plen = 0; + + rc = nfcwilink_send(skb); + if (rc) + return rc; + + comp_ret = wait_for_completion_timeout(&drv->completed, + msecs_to_jiffies(NFCWILINK_CMD_TIMEOUT)); + nfc_dev_dbg(&drv->pdev->dev, "wait_for_completion_timeout returned %ld", + comp_ret); + if (comp_ret == 0) { + nfc_dev_err(&drv->pdev->dev, + "timeout on wait_for_completion_timeout"); + return -ETIMEDOUT; + } + + nfc_dev_dbg(&drv->pdev->dev, "nci_vs_nfcc_info_rsp: plen %d, status %d", + drv->nfcc_info.plen, + drv->nfcc_info.status); + + if ((drv->nfcc_info.plen != 5) || (drv->nfcc_info.status != 0)) { + nfc_dev_err(&drv->pdev->dev, + "invalid nci_vs_nfcc_info_rsp"); + return -EINVAL; + } + + snprintf(file_name, BTS_FILE_NAME_MAX_SIZE, + "TINfcInit_%d.%d.%d.%d.bts", + drv->nfcc_info.hw_id, + drv->nfcc_info.sw_ver_x, + drv->nfcc_info.sw_ver_z, + drv->nfcc_info.patch_id); + + nfc_dev_info(&drv->pdev->dev, "nfcwilink FW file name: %s", file_name); + + return 0; +} + +static int nfcwilink_send_bts_cmd(struct nfcwilink *drv, __u8 *data, int len) +{ + struct nfcwilink_hdr *hdr = (struct nfcwilink_hdr *)data; + struct sk_buff *skb; + unsigned long comp_ret; + int rc; + + nfc_dev_dbg(&drv->pdev->dev, "send_bts_cmd entry"); + + /* verify valid cmd for the NFC channel */ + if ((len <= sizeof(struct nfcwilink_hdr)) || + (len > BTS_FILE_CMD_MAX_LEN) || + (hdr->chnl != NFCWILINK_CHNL) || + (hdr->opcode != NFCWILINK_OPCODE)) { + nfc_dev_err(&drv->pdev->dev, + "ignoring invalid bts cmd, len %d, chnl %d, opcode %d", + len, hdr->chnl, hdr->opcode); + return 0; + } + + /* remove the ST header */ + len -= sizeof(struct nfcwilink_hdr); + data += sizeof(struct nfcwilink_hdr); + + skb = nfcwilink_skb_alloc(len, GFP_KERNEL); + if (!skb) { + nfc_dev_err(&drv->pdev->dev, "no memory for bts cmd"); + return -ENOMEM; + } + + skb->dev = (void *)drv->ndev; + + memcpy(skb_put(skb, len), data, len); + + rc = nfcwilink_send(skb); + if (rc) + return rc; + + comp_ret = wait_for_completion_timeout(&drv->completed, + msecs_to_jiffies(NFCWILINK_CMD_TIMEOUT)); + nfc_dev_dbg(&drv->pdev->dev, "wait_for_completion_timeout returned %ld", + comp_ret); + if (comp_ret == 0) { + nfc_dev_err(&drv->pdev->dev, + "timeout on wait_for_completion_timeout"); + return -ETIMEDOUT; + } + + return 0; +} + +static int nfcwilink_download_fw(struct nfcwilink *drv) +{ + unsigned char file_name[BTS_FILE_NAME_MAX_SIZE]; + const struct firmware *fw; + __u16 action_type, action_len; + __u8 *ptr; + int len, rc; + + nfc_dev_dbg(&drv->pdev->dev, "download_fw entry"); + + set_bit(NFCWILINK_FW_DOWNLOAD, &drv->flags); + + rc = nfcwilink_get_bts_file_name(drv, file_name); + if (rc) + goto exit; + + rc = request_firmware(&fw, file_name, &drv->pdev->dev); + if (rc) { + nfc_dev_err(&drv->pdev->dev, "request_firmware failed %d", rc); + + /* if the file is not found, don't exit with failure */ + if (rc == -ENOENT) + rc = 0; + + goto exit; + } + + len = fw->size; + ptr = (__u8 *)fw->data; + + if ((len == 0) || (ptr == NULL)) { + nfc_dev_dbg(&drv->pdev->dev, + "request_firmware returned size %d", len); + goto release_fw; + } + + if (__le32_to_cpu(((struct bts_file_hdr *)ptr)->magic) != + BTS_FILE_HDR_MAGIC) { + nfc_dev_err(&drv->pdev->dev, "wrong bts magic number"); + rc = -EINVAL; + goto release_fw; + } + + /* remove the BTS header */ + len -= sizeof(struct bts_file_hdr); + ptr += sizeof(struct bts_file_hdr); + + while (len > 0) { + action_type = + __le16_to_cpu(((struct bts_file_action *)ptr)->type); + action_len = + __le16_to_cpu(((struct bts_file_action *)ptr)->len); + + nfc_dev_dbg(&drv->pdev->dev, "bts_file_action type %d, len %d", + action_type, action_len); + + switch (action_type) { + case BTS_FILE_ACTION_TYPE_SEND_CMD: + rc = nfcwilink_send_bts_cmd(drv, + ((struct bts_file_action *)ptr)->data, + action_len); + if (rc) + goto release_fw; + break; + } + + /* advance to the next action */ + len -= (sizeof(struct bts_file_action) + action_len); + ptr += (sizeof(struct bts_file_action) + action_len); + } + +release_fw: + release_firmware(fw); + +exit: + clear_bit(NFCWILINK_FW_DOWNLOAD, &drv->flags); + return rc; +} + /* Called by ST when registration is complete */ static void nfcwilink_register_complete(void *priv_data, char data) { @@ -73,7 +343,7 @@ static void nfcwilink_register_complete(void *priv_data, char data) drv->st_register_cb_status = data; /* complete the wait in nfc_st_open() */ - complete(&drv->st_register_completed); + complete(&drv->completed); } /* Called by ST when receive data is available */ @@ -96,6 +366,11 @@ static long nfcwilink_receive(void *priv_data, struct sk_buff *skb) (apart for the chnl byte, which is not received in the hdr) */ skb_pull(skb, (NFCWILINK_HDR_LEN-1)); + if (test_bit(NFCWILINK_FW_DOWNLOAD, &drv->flags)) { + nfcwilink_fw_download_receive(drv, skb); + return 0; + } + skb->dev = (void *) drv->ndev; /* Forward skb to NCI core layer */ @@ -136,14 +411,14 @@ static int nfcwilink_open(struct nci_dev *ndev) nfcwilink_proto.priv_data = drv; - init_completion(&drv->st_register_completed); + init_completion(&drv->completed); drv->st_register_cb_status = -EINPROGRESS; rc = st_register(&nfcwilink_proto); if (rc < 0) { if (rc == -EINPROGRESS) { comp_ret = wait_for_completion_timeout( - &drv->st_register_completed, + &drv->completed, msecs_to_jiffies(NFCWILINK_REGISTER_TIMEOUT)); nfc_dev_dbg(&drv->pdev->dev, @@ -171,6 +446,12 @@ static int nfcwilink_open(struct nci_dev *ndev) BUG_ON(nfcwilink_proto.write == NULL); drv->st_write = nfcwilink_proto.write; + if (nfcwilink_download_fw(drv)) { + nfc_dev_err(&drv->pdev->dev, "nfcwilink_download_fw failed %d", + rc); + /* open should succeed, even if the FW download failed */ + } + goto exit; clear_exit: @@ -208,11 +489,13 @@ static int nfcwilink_send(struct sk_buff *skb) nfc_dev_dbg(&drv->pdev->dev, "send entry, len %d", skb->len); - if (!test_bit(NFCWILINK_RUNNING, &drv->flags)) - return -EBUSY; + if (!test_bit(NFCWILINK_RUNNING, &drv->flags)) { + kfree_skb(skb); + return -EINVAL; + } /* add the ST hdr to the start of the buffer */ - hdr.len = skb->len; + hdr.len = cpu_to_le16(skb->len); memcpy(skb_push(skb, NFCWILINK_HDR_LEN), &hdr, NFCWILINK_HDR_LEN); /* Insert skb to shared transport layer's transmit queue. @@ -239,7 +522,7 @@ static int nfcwilink_probe(struct platform_device *pdev) { static struct nfcwilink *drv; int rc; - u32 protocols; + __u32 protocols; nfc_dev_dbg(&pdev->dev, "probe entry"); diff --git a/drivers/nfc/pn533.c b/drivers/nfc/pn533.c index 1a1500bc8452..cb6204f78300 100644 --- a/drivers/nfc/pn533.c +++ b/drivers/nfc/pn533.c @@ -736,6 +736,8 @@ static int pn533_target_found_type_a(struct nfc_target *nfc_tgt, u8 *tgt_data, nfc_tgt->sens_res = be16_to_cpu(tgt_type_a->sens_res); nfc_tgt->sel_res = tgt_type_a->sel_res; + nfc_tgt->nfcid1_len = tgt_type_a->nfcid_len; + memcpy(nfc_tgt->nfcid1, tgt_type_a->nfcid_data, nfc_tgt->nfcid1_len); return 0; } @@ -781,6 +783,9 @@ static int pn533_target_found_felica(struct nfc_target *nfc_tgt, u8 *tgt_data, else nfc_tgt->supported_protocols = NFC_PROTO_FELICA_MASK; + memcpy(nfc_tgt->sensf_res, &tgt_felica->opcode, 9); + nfc_tgt->sensf_res_len = 9; + return 0; } @@ -823,6 +828,8 @@ static int pn533_target_found_jewel(struct nfc_target *nfc_tgt, u8 *tgt_data, nfc_tgt->supported_protocols = NFC_PROTO_JEWEL_MASK; nfc_tgt->sens_res = be16_to_cpu(tgt_jewel->sens_res); + nfc_tgt->nfcid1_len = 4; + memcpy(nfc_tgt->nfcid1, tgt_jewel->jewelid, nfc_tgt->nfcid1_len); return 0; } @@ -902,6 +909,8 @@ static int pn533_target_found(struct pn533 *dev, if (resp->tg != 1) return -EPROTO; + memset(&nfc_tgt, 0, sizeof(struct nfc_target)); + target_data_len = resp_len - sizeof(struct pn533_poll_response); switch (dev->poll_mod_curr) { @@ -1307,6 +1316,8 @@ static int pn533_in_dep_link_up_complete(struct pn533 *dev, void *arg, nfc_dev_dbg(&dev->interface->dev, "Creating new target"); nfc_target.supported_protocols = NFC_PROTO_NFC_DEP_MASK; + nfc_target.nfcid1_len = 10; + memcpy(nfc_target.nfcid1, resp->nfcid3t, nfc_target.nfcid1_len); rc = nfc_targets_found(dev->nfc_dev, &nfc_target, 1); if (rc) return 0; @@ -1329,21 +1340,15 @@ static int pn533_in_dep_link_up_complete(struct pn533 *dev, void *arg, } static int pn533_dep_link_up(struct nfc_dev *nfc_dev, int target_idx, - u8 comm_mode, u8 rf_mode) + u8 comm_mode, u8* gb, size_t gb_len) { struct pn533 *dev = nfc_get_drvdata(nfc_dev); struct pn533_cmd_jump_dep *cmd; - u8 cmd_len, local_gt_len, *local_gt; + u8 cmd_len; int rc; nfc_dev_dbg(&dev->interface->dev, "%s", __func__); - if (rf_mode == NFC_RF_TARGET) { - nfc_dev_err(&dev->interface->dev, "Target mode not supported"); - return -EOPNOTSUPP; - } - - if (dev->poll_mod_count) { nfc_dev_err(&dev->interface->dev, "Cannot bring the DEP link up while polling"); @@ -1356,11 +1361,7 @@ static int pn533_dep_link_up(struct nfc_dev *nfc_dev, int target_idx, return -EBUSY; } - local_gt = nfc_get_local_general_bytes(dev->nfc_dev, &local_gt_len); - if (local_gt_len > NFC_MAX_GT_LEN) - return -EINVAL; - - cmd_len = sizeof(struct pn533_cmd_jump_dep) + local_gt_len; + cmd_len = sizeof(struct pn533_cmd_jump_dep) + gb_len; cmd = kzalloc(cmd_len, GFP_KERNEL); if (cmd == NULL) return -ENOMEM; @@ -1369,9 +1370,9 @@ static int pn533_dep_link_up(struct nfc_dev *nfc_dev, int target_idx, cmd->active = !comm_mode; cmd->baud = 0; - if (local_gt != NULL) { + if (gb != NULL && gb_len > 0) { cmd->next = 4; /* We have some Gi */ - memcpy(cmd->gt, local_gt, local_gt_len); + memcpy(cmd->gt, gb, gb_len); } else { cmd->next = 0; } |