From 21d19f87d483fbac66ca24863b8c8d52a7ab539d Mon Sep 17 00:00:00 2001 From: Samuel Ortiz Date: Sat, 3 Oct 2015 05:02:28 +0200 Subject: NFC: nci: Use __nci_request for exported routines Since we do not know in which context drivers will call these routines, they should use the unlocked version of nci_request, i.e. __nci_request. It is up to drivers to know/decide if they need to take the req_lock mutex before calling those routines. When being called from the NCI setup routine there is no need to do so as ops->setup is called under req_lock. Signed-off-by: Samuel Ortiz --- net/nfc/nci/core.c | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) (limited to 'net') diff --git a/net/nfc/nci/core.c b/net/nfc/nci/core.c index 943889b87a34..090972033a15 100644 --- a/net/nfc/nci/core.c +++ b/net/nfc/nci/core.c @@ -540,7 +540,7 @@ static void nci_nfcee_discover_req(struct nci_dev *ndev, unsigned long opt) int nci_nfcee_discover(struct nci_dev *ndev, u8 action) { - return nci_request(ndev, nci_nfcee_discover_req, action, + return __nci_request(ndev, nci_nfcee_discover_req, action, msecs_to_jiffies(NCI_CMD_TIMEOUT)); } EXPORT_SYMBOL(nci_nfcee_discover); @@ -561,8 +561,9 @@ int nci_nfcee_mode_set(struct nci_dev *ndev, u8 nfcee_id, u8 nfcee_mode) cmd.nfcee_id = nfcee_id; cmd.nfcee_mode = nfcee_mode; - return nci_request(ndev, nci_nfcee_mode_set_req, (unsigned long)&cmd, - msecs_to_jiffies(NCI_CMD_TIMEOUT)); + return __nci_request(ndev, nci_nfcee_mode_set_req, + (unsigned long)&cmd, + msecs_to_jiffies(NCI_CMD_TIMEOUT)); } EXPORT_SYMBOL(nci_nfcee_mode_set); @@ -612,8 +613,8 @@ static void nci_core_conn_close_req(struct nci_dev *ndev, unsigned long opt) int nci_core_conn_close(struct nci_dev *ndev, u8 conn_id) { - return nci_request(ndev, nci_core_conn_close_req, conn_id, - msecs_to_jiffies(NCI_CMD_TIMEOUT)); + return __nci_request(ndev, nci_core_conn_close_req, conn_id, + msecs_to_jiffies(NCI_CMD_TIMEOUT)); } EXPORT_SYMBOL(nci_core_conn_close); -- cgit v1.2.3-59-g8ed1b From 9abebb8a10ce93db45327bd3a4d06801e6db92f2 Mon Sep 17 00:00:00 2001 From: Julia Lawall Date: Sat, 17 Oct 2015 11:32:19 +0200 Subject: NFC: delete null dereference The exit label performs device_unlock(&dev->dev);, which will fail when dev is NULL, and nfc_put_device(dev);, which is not useful when dev is NULL, so just exit the function immediately. Problem found using scripts/coccinelle/null/deref_null.cocci Signed-off-by: Julia Lawall Signed-off-by: Samuel Ortiz --- net/nfc/netlink.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) (limited to 'net') diff --git a/net/nfc/netlink.c b/net/nfc/netlink.c index 853172c27f68..f04053295ff1 100644 --- a/net/nfc/netlink.c +++ b/net/nfc/netlink.c @@ -1109,10 +1109,8 @@ static int nfc_genl_llc_sdreq(struct sk_buff *skb, struct genl_info *info) idx = nla_get_u32(info->attrs[NFC_ATTR_DEVICE_INDEX]); dev = nfc_get_device(idx); - if (!dev) { - rc = -ENODEV; - goto exit; - } + if (!dev) + return -ENODEV; device_lock(&dev->dev); -- cgit v1.2.3-59-g8ed1b From ea785c094d38bc5eae8011330759d68f45236559 Mon Sep 17 00:00:00 2001 From: Robert Dolca Date: Thu, 22 Oct 2015 12:11:33 +0300 Subject: NFC: nci: Export nci data send API For the firmware update the driver may use nci_send_data. Signed-off-by: Robert Dolca Signed-off-by: Samuel Ortiz --- net/nfc/nci/data.c | 1 + 1 file changed, 1 insertion(+) (limited to 'net') diff --git a/net/nfc/nci/data.c b/net/nfc/nci/data.c index 566466d90048..83acd189c13a 100644 --- a/net/nfc/nci/data.c +++ b/net/nfc/nci/data.c @@ -203,6 +203,7 @@ free_exit: exit: return rc; } +EXPORT_SYMBOL(nci_send_data); /* ----------------- NCI RX Data ----------------- */ -- cgit v1.2.3-59-g8ed1b From 2663589ce67e5429bac89544bb0e102bb1fcf617 Mon Sep 17 00:00:00 2001 From: Robert Dolca Date: Thu, 22 Oct 2015 12:11:34 +0300 Subject: NFC: nci: Add function to get max packet size for conn FDP driver needs to send the firmware as regular packets (not fragmented). The driver should have a way to get the max packet size for a given connection. Signed-off-by: Robert Dolca Signed-off-by: Samuel Ortiz --- include/net/nfc/nci_core.h | 1 + net/nfc/nci/data.c | 12 ++++++++++++ 2 files changed, 13 insertions(+) (limited to 'net') diff --git a/include/net/nfc/nci_core.h b/include/net/nfc/nci_core.h index d0d0f1e53bb9..aaada20bf78e 100644 --- a/include/net/nfc/nci_core.h +++ b/include/net/nfc/nci_core.h @@ -351,6 +351,7 @@ int nci_prop_ntf_packet(struct nci_dev *ndev, __u16 opcode, void nci_rx_data_packet(struct nci_dev *ndev, struct sk_buff *skb); int nci_send_cmd(struct nci_dev *ndev, __u16 opcode, __u8 plen, void *payload); int nci_send_data(struct nci_dev *ndev, __u8 conn_id, struct sk_buff *skb); +int nci_conn_max_data_pkt_payload_size(struct nci_dev *ndev, __u8 conn_id); void nci_data_exchange_complete(struct nci_dev *ndev, struct sk_buff *skb, __u8 conn_id, int err); void nci_hci_data_received_cb(void *context, struct sk_buff *skb, int err); diff --git a/net/nfc/nci/data.c b/net/nfc/nci/data.c index 83acd189c13a..dbd24254412a 100644 --- a/net/nfc/nci/data.c +++ b/net/nfc/nci/data.c @@ -90,6 +90,18 @@ static inline void nci_push_data_hdr(struct nci_dev *ndev, nci_pbf_set((__u8 *)hdr, pbf); } +int nci_conn_max_data_pkt_payload_size(struct nci_dev *ndev, __u8 conn_id) +{ + struct nci_conn_info *conn_info; + + conn_info = nci_get_conn_info_by_conn_id(ndev, conn_id); + if (!conn_info) + return -EPROTO; + + return conn_info->max_pkt_payload_len; +} +EXPORT_SYMBOL(nci_conn_max_data_pkt_payload_size); + static int nci_queue_tx_data_frags(struct nci_dev *ndev, __u8 conn_id, struct sk_buff *skb) { -- cgit v1.2.3-59-g8ed1b From e4dbd62528931951aa9d3b313ee7d536df5069fc Mon Sep 17 00:00:00 2001 From: Robert Dolca Date: Thu, 22 Oct 2015 12:11:36 +0300 Subject: NFC: nci: Do not call post_setup when setup fails The driver should know that it can continue with post setup where setup left off. Being able to execute post_setup when setup fails may force the developer to keep this state in the driver. Signed-off-by: Robert Dolca Signed-off-by: Samuel Ortiz --- net/nfc/nci/core.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'net') diff --git a/net/nfc/nci/core.c b/net/nfc/nci/core.c index 090972033a15..5362d8f543e7 100644 --- a/net/nfc/nci/core.c +++ b/net/nfc/nci/core.c @@ -402,9 +402,8 @@ static int nci_open_device(struct nci_dev *ndev) msecs_to_jiffies(NCI_INIT_TIMEOUT)); } - if (ndev->ops->post_setup) { + if (!rc && ndev->ops->post_setup) rc = ndev->ops->post_setup(ndev); - } if (!rc) { rc = __nci_request(ndev, nci_init_complete_req, 0, -- cgit v1.2.3-59-g8ed1b From 7bc4824ed5cf9feb0173b90a6bec28f694a5f7ce Mon Sep 17 00:00:00 2001 From: Robert Dolca Date: Thu, 22 Oct 2015 12:11:37 +0300 Subject: NFC: nci: Introduce nci_core_cmd This allows sending core commands from the driver. The driver should be able to send NCI core commands like CORE_GET_CONFIG_CMD. Signed-off-by: Robert Dolca Signed-off-by: Samuel Ortiz --- include/net/nfc/nci_core.h | 1 + net/nfc/nci/core.c | 24 +++++++++++++++++++----- 2 files changed, 20 insertions(+), 5 deletions(-) (limited to 'net') diff --git a/include/net/nfc/nci_core.h b/include/net/nfc/nci_core.h index aaada20bf78e..4ca1b6d1684b 100644 --- a/include/net/nfc/nci_core.h +++ b/include/net/nfc/nci_core.h @@ -278,6 +278,7 @@ int nci_request(struct nci_dev *ndev, unsigned long opt), unsigned long opt, __u32 timeout); int nci_prop_cmd(struct nci_dev *ndev, __u8 oid, size_t len, __u8 *payload); +int nci_core_cmd(struct nci_dev *ndev, __u16 opcode, size_t len, __u8 *payload); int nci_core_reset(struct nci_dev *ndev); int nci_core_init(struct nci_dev *ndev); diff --git a/net/nfc/nci/core.c b/net/nfc/nci/core.c index 5362d8f543e7..5b4f48a827d9 100644 --- a/net/nfc/nci/core.c +++ b/net/nfc/nci/core.c @@ -325,32 +325,46 @@ static void nci_rf_deactivate_req(struct nci_dev *ndev, unsigned long opt) sizeof(struct nci_rf_deactivate_cmd), &cmd); } -struct nci_prop_cmd_param { +struct nci_cmd_param { __u16 opcode; size_t len; __u8 *payload; }; -static void nci_prop_cmd_req(struct nci_dev *ndev, unsigned long opt) +static void nci_generic_req(struct nci_dev *ndev, unsigned long opt) { - struct nci_prop_cmd_param *param = (struct nci_prop_cmd_param *)opt; + struct nci_cmd_param *param = + (struct nci_cmd_param *)opt; nci_send_cmd(ndev, param->opcode, param->len, param->payload); } int nci_prop_cmd(struct nci_dev *ndev, __u8 oid, size_t len, __u8 *payload) { - struct nci_prop_cmd_param param; + struct nci_cmd_param param; param.opcode = nci_opcode_pack(NCI_GID_PROPRIETARY, oid); param.len = len; param.payload = payload; - return __nci_request(ndev, nci_prop_cmd_req, (unsigned long)¶m, + return __nci_request(ndev, nci_generic_req, (unsigned long)¶m, msecs_to_jiffies(NCI_CMD_TIMEOUT)); } EXPORT_SYMBOL(nci_prop_cmd); +int nci_core_cmd(struct nci_dev *ndev, __u16 opcode, size_t len, __u8 *payload) +{ + struct nci_cmd_param param; + + param.opcode = opcode; + param.len = len; + param.payload = payload; + + return __nci_request(ndev, nci_generic_req, (unsigned long)¶m, + msecs_to_jiffies(NCI_CMD_TIMEOUT)); +} +EXPORT_SYMBOL(nci_core_cmd); + int nci_core_reset(struct nci_dev *ndev) { return __nci_request(ndev, nci_reset_req, 0, -- cgit v1.2.3-59-g8ed1b From 0a97a3cba298fd989802bf34541c94b6488c3834 Mon Sep 17 00:00:00 2001 From: Robert Dolca Date: Thu, 22 Oct 2015 12:11:38 +0300 Subject: NFC: nci: Allow the driver to set handler for core nci ops The driver may be required to act when some responses or notifications arrive. For example the NCI core does not have a handler for NCI_OP_CORE_GET_CONFIG_RSP. The NFCC can send a config response that has to be read by the driver and the packet may contain vendor specific data. The Fields Peak driver needs to take certain actions when a reset notification arrives (packet also not handled by the nfc core). The driver handlers do not interfere with the core and they are called after the core processes the packet. Signed-off-by: Robert Dolca Signed-off-by: Samuel Ortiz --- include/net/nfc/nci_core.h | 11 +++++-- net/nfc/nci/core.c | 71 +++++++++++++++++++++++++++++++++------------- net/nfc/nci/ntf.c | 3 +- net/nfc/nci/rsp.c | 1 + 4 files changed, 63 insertions(+), 23 deletions(-) (limited to 'net') diff --git a/include/net/nfc/nci_core.h b/include/net/nfc/nci_core.h index 4ca1b6d1684b..d5a1caae9fd9 100644 --- a/include/net/nfc/nci_core.h +++ b/include/net/nfc/nci_core.h @@ -96,6 +96,9 @@ struct nci_ops { struct nci_prop_ops *prop_ops; size_t n_prop_ops; + + struct nci_prop_ops *core_ops; + size_t n_core_ops; }; #define NCI_MAX_SUPPORTED_RF_INTERFACES 4 @@ -345,9 +348,13 @@ static inline int nci_set_vendor_cmds(struct nci_dev *ndev, void nci_rsp_packet(struct nci_dev *ndev, struct sk_buff *skb); void nci_ntf_packet(struct nci_dev *ndev, struct sk_buff *skb); -int nci_prop_rsp_packet(struct nci_dev *ndev, __u16 opcode, +inline int nci_prop_rsp_packet(struct nci_dev *ndev, __u16 opcode, + struct sk_buff *skb); +inline int nci_prop_ntf_packet(struct nci_dev *ndev, __u16 opcode, + struct sk_buff *skb); +inline int nci_core_rsp_packet(struct nci_dev *ndev, __u16 opcode, struct sk_buff *skb); -int nci_prop_ntf_packet(struct nci_dev *ndev, __u16 opcode, +inline int nci_core_ntf_packet(struct nci_dev *ndev, __u16 opcode, struct sk_buff *skb); void nci_rx_data_packet(struct nci_dev *ndev, struct sk_buff *skb); int nci_send_cmd(struct nci_dev *ndev, __u16 opcode, __u8 plen, void *payload); diff --git a/net/nfc/nci/core.c b/net/nfc/nci/core.c index 5b4f48a827d9..30c270862884 100644 --- a/net/nfc/nci/core.c +++ b/net/nfc/nci/core.c @@ -1242,46 +1242,77 @@ int nci_send_cmd(struct nci_dev *ndev, __u16 opcode, __u8 plen, void *payload) } /* Proprietary commands API */ -static struct nci_prop_ops *prop_cmd_lookup(struct nci_dev *ndev, - __u16 opcode) +static struct nci_prop_ops *ops_cmd_lookup(struct nci_prop_ops *ops, + size_t n_ops, + __u16 opcode) { size_t i; - struct nci_prop_ops *prop_op; + struct nci_prop_ops *op; - if (!ndev->ops->prop_ops || !ndev->ops->n_prop_ops) + if (!ops || !n_ops) return NULL; - for (i = 0; i < ndev->ops->n_prop_ops; i++) { - prop_op = &ndev->ops->prop_ops[i]; - if (prop_op->opcode == opcode) - return prop_op; + for (i = 0; i < n_ops; i++) { + op = &ops[i]; + if (op->opcode == opcode) + return op; } return NULL; } -int nci_prop_rsp_packet(struct nci_dev *ndev, __u16 rsp_opcode, - struct sk_buff *skb) +static int nci_op_rsp_packet(struct nci_dev *ndev, __u16 rsp_opcode, + struct sk_buff *skb, struct nci_prop_ops *ops, + size_t n_ops) { - struct nci_prop_ops *prop_op; + struct nci_prop_ops *op; - prop_op = prop_cmd_lookup(ndev, rsp_opcode); - if (!prop_op || !prop_op->rsp) + op = ops_cmd_lookup(ops, n_ops, rsp_opcode); + if (!op || !op->rsp) return -ENOTSUPP; - return prop_op->rsp(ndev, skb); + return op->rsp(ndev, skb); } -int nci_prop_ntf_packet(struct nci_dev *ndev, __u16 ntf_opcode, - struct sk_buff *skb) +static int nci_op_ntf_packet(struct nci_dev *ndev, __u16 ntf_opcode, + struct sk_buff *skb, struct nci_prop_ops *ops, + size_t n_ops) { - struct nci_prop_ops *prop_op; + struct nci_prop_ops *op; - prop_op = prop_cmd_lookup(ndev, ntf_opcode); - if (!prop_op || !prop_op->ntf) + op = ops_cmd_lookup(ops, n_ops, ntf_opcode); + if (!op || !op->ntf) return -ENOTSUPP; - return prop_op->ntf(ndev, skb); + return op->ntf(ndev, skb); +} + +inline int nci_prop_rsp_packet(struct nci_dev *ndev, __u16 opcode, + struct sk_buff *skb) +{ + return nci_op_rsp_packet(ndev, opcode, skb, ndev->ops->prop_ops, + ndev->ops->n_prop_ops); +} + +inline int nci_prop_ntf_packet(struct nci_dev *ndev, __u16 opcode, + struct sk_buff *skb) +{ + return nci_op_ntf_packet(ndev, opcode, skb, ndev->ops->prop_ops, + ndev->ops->n_prop_ops); +} + +inline int nci_core_rsp_packet(struct nci_dev *ndev, __u16 opcode, + struct sk_buff *skb) +{ + return nci_op_rsp_packet(ndev, opcode, skb, ndev->ops->core_ops, + ndev->ops->n_core_ops); +} + +inline int nci_core_ntf_packet(struct nci_dev *ndev, __u16 opcode, + struct sk_buff *skb) +{ + return nci_op_ntf_packet(ndev, opcode, skb, ndev->ops->core_ops, + ndev->ops->n_core_ops); } /* ---- NCI TX Data worker thread ---- */ diff --git a/net/nfc/nci/ntf.c b/net/nfc/nci/ntf.c index 5d1c2e391c56..2ada2b39e355 100644 --- a/net/nfc/nci/ntf.c +++ b/net/nfc/nci/ntf.c @@ -759,7 +759,7 @@ void nci_ntf_packet(struct nci_dev *ndev, struct sk_buff *skb) skb_pull(skb, NCI_CTRL_HDR_SIZE); if (nci_opcode_gid(ntf_opcode) == NCI_GID_PROPRIETARY) { - if (nci_prop_ntf_packet(ndev, ntf_opcode, skb)) { + if (nci_prop_ntf_packet(ndev, ntf_opcode, skb) == -ENOTSUPP) { pr_err("unsupported ntf opcode 0x%x\n", ntf_opcode); } @@ -805,6 +805,7 @@ void nci_ntf_packet(struct nci_dev *ndev, struct sk_buff *skb) break; } + nci_core_ntf_packet(ndev, ntf_opcode, skb); end: kfree_skb(skb); } diff --git a/net/nfc/nci/rsp.c b/net/nfc/nci/rsp.c index 408bd8f857ab..9b6eb913d801 100644 --- a/net/nfc/nci/rsp.c +++ b/net/nfc/nci/rsp.c @@ -355,6 +355,7 @@ void nci_rsp_packet(struct nci_dev *ndev, struct sk_buff *skb) break; } + nci_core_rsp_packet(ndev, rsp_opcode, skb); end: kfree_skb(skb); -- cgit v1.2.3-59-g8ed1b From 22e4bd09c401905671f3787a8392d269a0ebfa0d Mon Sep 17 00:00:00 2001 From: Robert Dolca Date: Thu, 22 Oct 2015 12:11:39 +0300 Subject: NFC: nci: rename nci_prop_ops to nci_driver_ops Initially it was used to create hooks in the driver for proprietary operations. Currently it is being used for hooks for both proprietary and generic operations. Signed-off-by: Robert Dolca Signed-off-by: Samuel Ortiz --- drivers/nfc/s3fwrn5/nci.c | 4 ++-- drivers/nfc/s3fwrn5/nci.h | 2 +- drivers/nfc/st-nci/core.c | 2 +- include/net/nfc/nci_core.h | 6 +++--- net/nfc/nci/core.c | 16 ++++++++-------- 5 files changed, 15 insertions(+), 15 deletions(-) (limited to 'net') diff --git a/drivers/nfc/s3fwrn5/nci.c b/drivers/nfc/s3fwrn5/nci.c index ace0071c5339..075e4e877b33 100644 --- a/drivers/nfc/s3fwrn5/nci.c +++ b/drivers/nfc/s3fwrn5/nci.c @@ -31,7 +31,7 @@ static int s3fwrn5_nci_prop_rsp(struct nci_dev *ndev, struct sk_buff *skb) return 0; } -static struct nci_prop_ops s3fwrn5_nci_prop_ops[] = { +static struct nci_driver_ops s3fwrn5_nci_prop_ops[] = { { .opcode = nci_opcode_pack(NCI_GID_PROPRIETARY, NCI_PROP_AGAIN), @@ -79,7 +79,7 @@ static struct nci_prop_ops s3fwrn5_nci_prop_ops[] = { }, }; -void s3fwrn5_nci_get_prop_ops(struct nci_prop_ops **ops, size_t *n) +void s3fwrn5_nci_get_prop_ops(struct nci_driver_ops **ops, size_t *n) { *ops = s3fwrn5_nci_prop_ops; *n = ARRAY_SIZE(s3fwrn5_nci_prop_ops); diff --git a/drivers/nfc/s3fwrn5/nci.h b/drivers/nfc/s3fwrn5/nci.h index 0e68d439dde6..60c7fb575b66 100644 --- a/drivers/nfc/s3fwrn5/nci.h +++ b/drivers/nfc/s3fwrn5/nci.h @@ -83,7 +83,7 @@ struct nci_prop_fw_cfg_rsp { #define NCI_PROP_WR_RESET 0x2f -void s3fwrn5_nci_get_prop_ops(struct nci_prop_ops **ops, size_t *n); +void s3fwrn5_nci_get_prop_ops(struct nci_driver_ops **ops, size_t *n); int s3fwrn5_nci_rf_configure(struct s3fwrn5_info *info, const char *fw_name); #endif /* __LOCAL_S3FWRN5_NCI_H_ */ diff --git a/drivers/nfc/st-nci/core.c b/drivers/nfc/st-nci/core.c index 8d1fdd2ee498..5443caa0d55e 100644 --- a/drivers/nfc/st-nci/core.c +++ b/drivers/nfc/st-nci/core.c @@ -98,7 +98,7 @@ static int st_nci_prop_rsp_packet(struct nci_dev *ndev, return 0; } -static struct nci_prop_ops st_nci_prop_ops[] = { +static struct nci_driver_ops st_nci_prop_ops[] = { { .opcode = nci_opcode_pack(NCI_GID_PROPRIETARY, ST_NCI_CORE_PROP), diff --git a/include/net/nfc/nci_core.h b/include/net/nfc/nci_core.h index d5a1caae9fd9..5daf0042261e 100644 --- a/include/net/nfc/nci_core.h +++ b/include/net/nfc/nci_core.h @@ -67,7 +67,7 @@ enum nci_state { struct nci_dev; -struct nci_prop_ops { +struct nci_driver_ops { __u16 opcode; int (*rsp)(struct nci_dev *dev, struct sk_buff *skb); int (*ntf)(struct nci_dev *dev, struct sk_buff *skb); @@ -94,10 +94,10 @@ struct nci_ops { void (*hci_cmd_received)(struct nci_dev *ndev, u8 pipe, u8 cmd, struct sk_buff *skb); - struct nci_prop_ops *prop_ops; + struct nci_driver_ops *prop_ops; size_t n_prop_ops; - struct nci_prop_ops *core_ops; + struct nci_driver_ops *core_ops; size_t n_core_ops; }; diff --git a/net/nfc/nci/core.c b/net/nfc/nci/core.c index 30c270862884..f66a5da85ddb 100644 --- a/net/nfc/nci/core.c +++ b/net/nfc/nci/core.c @@ -1242,12 +1242,12 @@ int nci_send_cmd(struct nci_dev *ndev, __u16 opcode, __u8 plen, void *payload) } /* Proprietary commands API */ -static struct nci_prop_ops *ops_cmd_lookup(struct nci_prop_ops *ops, - size_t n_ops, - __u16 opcode) +static struct nci_driver_ops *ops_cmd_lookup(struct nci_driver_ops *ops, + size_t n_ops, + __u16 opcode) { size_t i; - struct nci_prop_ops *op; + struct nci_driver_ops *op; if (!ops || !n_ops) return NULL; @@ -1262,10 +1262,10 @@ static struct nci_prop_ops *ops_cmd_lookup(struct nci_prop_ops *ops, } static int nci_op_rsp_packet(struct nci_dev *ndev, __u16 rsp_opcode, - struct sk_buff *skb, struct nci_prop_ops *ops, + struct sk_buff *skb, struct nci_driver_ops *ops, size_t n_ops) { - struct nci_prop_ops *op; + struct nci_driver_ops *op; op = ops_cmd_lookup(ops, n_ops, rsp_opcode); if (!op || !op->rsp) @@ -1275,10 +1275,10 @@ static int nci_op_rsp_packet(struct nci_dev *ndev, __u16 rsp_opcode, } static int nci_op_ntf_packet(struct nci_dev *ndev, __u16 ntf_opcode, - struct sk_buff *skb, struct nci_prop_ops *ops, + struct sk_buff *skb, struct nci_driver_ops *ops, size_t n_ops) { - struct nci_prop_ops *op; + struct nci_driver_ops *op; op = ops_cmd_lookup(ops, n_ops, ntf_opcode); if (!op || !op->ntf) -- cgit v1.2.3-59-g8ed1b From caa575a86ec1f177730cafa089d69ab4e424860c Mon Sep 17 00:00:00 2001 From: Robert Dolca Date: Thu, 22 Oct 2015 12:11:40 +0300 Subject: NFC: nci: fix possible crash in nci_core_conn_create If the number of destination speific parameters supplied is 0 the call will fail. If the first destination specific parameter does not have a value, curr_id will be set to 0. Signed-off-by: Robert Dolca Signed-off-by: Samuel Ortiz --- net/nfc/nci/core.c | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) (limited to 'net') diff --git a/net/nfc/nci/core.c b/net/nfc/nci/core.c index f66a5da85ddb..9d5f7a2b1d03 100644 --- a/net/nfc/nci/core.c +++ b/net/nfc/nci/core.c @@ -602,12 +602,19 @@ int nci_core_conn_create(struct nci_dev *ndev, u8 destination_type, if (!cmd) return -ENOMEM; + if (!number_destination_params) + return -EINVAL; + cmd->destination_type = destination_type; cmd->number_destination_params = number_destination_params; memcpy(cmd->params, params, params_len); data.cmd = cmd; - ndev->cur_id = params->value[DEST_SPEC_PARAMS_ID_INDEX]; + + if (params->length > 0) + ndev->cur_id = params->value[DEST_SPEC_PARAMS_ID_INDEX]; + else + ndev->cur_id = 0; r = __nci_request(ndev, nci_core_conn_create_req, (unsigned long)&data, -- cgit v1.2.3-59-g8ed1b From 85b9ce9a21b119a8163f20d60e7f0ce58fffbeef Mon Sep 17 00:00:00 2001 From: Robert Dolca Date: Thu, 22 Oct 2015 12:11:41 +0300 Subject: NFC: nci: add nci_get_conn_info_by_id function This functin takes as a parameter a pointer to the nci_dev struct and the first byte from the values of the first domain specific parameter that was used for the connection creation. Signed-off-by: Robert Dolca Signed-off-by: Samuel Ortiz --- include/net/nfc/nci_core.h | 1 + net/nfc/nci/core.c | 13 +++++++++++++ 2 files changed, 14 insertions(+) (limited to 'net') diff --git a/include/net/nfc/nci_core.h b/include/net/nfc/nci_core.h index 5daf0042261e..0569cb251135 100644 --- a/include/net/nfc/nci_core.h +++ b/include/net/nfc/nci_core.h @@ -374,6 +374,7 @@ void nci_clear_target_list(struct nci_dev *ndev); void nci_req_complete(struct nci_dev *ndev, int result); struct nci_conn_info *nci_get_conn_info_by_conn_id(struct nci_dev *ndev, int conn_id); +int nci_get_conn_info_by_id(struct nci_dev *ndev, u8 id); /* ----- NCI status code ----- */ int nci_to_errno(__u8 code); diff --git a/net/nfc/nci/core.c b/net/nfc/nci/core.c index 9d5f7a2b1d03..75bda34fd8e4 100644 --- a/net/nfc/nci/core.c +++ b/net/nfc/nci/core.c @@ -64,6 +64,19 @@ struct nci_conn_info *nci_get_conn_info_by_conn_id(struct nci_dev *ndev, return NULL; } +int nci_get_conn_info_by_id(struct nci_dev *ndev, u8 id) +{ + struct nci_conn_info *conn_info; + + list_for_each_entry(conn_info, &ndev->conn_info_list, list) { + if (conn_info->id == id) + return conn_info->conn_id; + } + + return -EINVAL; +} +EXPORT_SYMBOL(nci_get_conn_info_by_id); + /* ---- NCI requests ---- */ void nci_req_complete(struct nci_dev *ndev, int result) -- cgit v1.2.3-59-g8ed1b From 500c4ef02277eaadbfe20537f963b6221f6ac007 Mon Sep 17 00:00:00 2001 From: Christophe Ricard Date: Sun, 25 Oct 2015 22:54:20 +0100 Subject: NFC: nci: Fix incorrect data chaining when sending data When sending HCI data over NCI, cmd information should be present only on the first packet. Each packet shall be specifically allocated and sent to the NCI layer. Cc: stable@vger.kernel.org Signed-off-by: Christophe Ricard Signed-off-by: Samuel Ortiz --- net/nfc/nci/hci.c | 20 +++++++++++++------- 1 file changed, 13 insertions(+), 7 deletions(-) (limited to 'net') diff --git a/net/nfc/nci/hci.c b/net/nfc/nci/hci.c index 609f92283d1b..321889ee2107 100644 --- a/net/nfc/nci/hci.c +++ b/net/nfc/nci/hci.c @@ -146,18 +146,18 @@ static int nci_hci_send_data(struct nci_dev *ndev, u8 pipe, if (!conn_info) return -EPROTO; - skb = nci_skb_alloc(ndev, 2 + conn_info->max_pkt_payload_len + + i = 0; + skb = nci_skb_alloc(ndev, conn_info->max_pkt_payload_len + NCI_DATA_HDR_SIZE, GFP_KERNEL); if (!skb) return -ENOMEM; - skb_reserve(skb, 2 + NCI_DATA_HDR_SIZE); + skb_reserve(skb, NCI_DATA_HDR_SIZE + 2); *skb_push(skb, 1) = data_type; - i = 0; - len = conn_info->max_pkt_payload_len; - do { + len = conn_info->max_pkt_payload_len; + /* If last packet add NCI_HFP_NO_CHAINING */ if (i + conn_info->max_pkt_payload_len - (skb->len + 1) >= data_len) { @@ -177,9 +177,15 @@ static int nci_hci_send_data(struct nci_dev *ndev, u8 pipe, return r; i += len; + if (i < data_len) { - skb_trim(skb, 0); - skb_pull(skb, len); + skb = nci_skb_alloc(ndev, + conn_info->max_pkt_payload_len + + NCI_DATA_HDR_SIZE, GFP_KERNEL); + if (!skb) + return -ENOMEM; + + skb_reserve(skb, NCI_DATA_HDR_SIZE + 1); } } while (i < data_len); -- cgit v1.2.3-59-g8ed1b From d8cd37ed2fc871c66b4c79c59f651dc2cdf7091c Mon Sep 17 00:00:00 2001 From: Christophe Ricard Date: Sun, 25 Oct 2015 22:54:21 +0100 Subject: NFC: nci: Fix improper management of HCI return code When sending HCI data over NCI, HCI return code is part of the NCI data. In order to get correctly the HCI return code, we assume the NCI communication is successful and extract the return code for the nci_hci functions return code. This is done because nci_to_errno does not match hci return code value. Cc: stable@vger.kernel.org Signed-off-by: Christophe Ricard Signed-off-by: Samuel Ortiz --- net/nfc/nci/hci.c | 66 +++++++++++++++++++++++++++++++++++++++++-------------- 1 file changed, 49 insertions(+), 17 deletions(-) (limited to 'net') diff --git a/net/nfc/nci/hci.c b/net/nfc/nci/hci.c index 321889ee2107..b07092f4111b 100644 --- a/net/nfc/nci/hci.c +++ b/net/nfc/nci/hci.c @@ -101,6 +101,20 @@ struct nci_hcp_packet { #define NCI_HCP_MSG_GET_CMD(header) (header & 0x3f) #define NCI_HCP_MSG_GET_PIPE(header) (header & 0x7f) +static int nci_hci_result_to_errno(u8 result) +{ + switch (result) { + case NCI_HCI_ANY_OK: + return 0; + case NCI_HCI_ANY_E_REG_PAR_UNKNOWN: + return -EOPNOTSUPP; + case NCI_HCI_ANY_E_TIMEOUT: + return -ETIME; + default: + return -1; + } +} + /* HCI core */ static void nci_hci_reset_pipes(struct nci_hci_dev *hdev) { @@ -218,7 +232,8 @@ int nci_hci_send_cmd(struct nci_dev *ndev, u8 gate, u8 cmd, const u8 *param, size_t param_len, struct sk_buff **skb) { - struct nci_conn_info *conn_info; + struct nci_hcp_message *message; + struct nci_conn_info *conn_info; struct nci_data data; int r; u8 pipe = ndev->hci_dev->gate2pipe[gate]; @@ -238,9 +253,15 @@ int nci_hci_send_cmd(struct nci_dev *ndev, u8 gate, u8 cmd, r = nci_request(ndev, nci_hci_send_data_req, (unsigned long)&data, msecs_to_jiffies(NCI_DATA_TIMEOUT)); - - if (r == NCI_STATUS_OK && skb) - *skb = conn_info->rx_skb; + if (r == NCI_STATUS_OK) { + message = (struct nci_hcp_message *)conn_info->rx_skb->data; + r = nci_hci_result_to_errno( + NCI_HCP_MSG_GET_CMD(message->header)); + skb_pull(conn_info->rx_skb, NCI_HCI_HCP_MESSAGE_HEADER_LEN); + + if (!r && skb) + *skb = conn_info->rx_skb; + } return r; } @@ -334,9 +355,6 @@ static void nci_hci_resp_received(struct nci_dev *ndev, u8 pipe, struct nci_conn_info *conn_info; u8 status = result; - if (result != NCI_HCI_ANY_OK) - goto exit; - conn_info = ndev->hci_dev->conn_info; if (!conn_info) { status = NCI_STATUS_REJECTED; @@ -346,7 +364,7 @@ static void nci_hci_resp_received(struct nci_dev *ndev, u8 pipe, conn_info->rx_skb = skb; exit: - nci_req_complete(ndev, status); + nci_req_complete(ndev, NCI_STATUS_OK); } /* Receive hcp message for pipe, with type and cmd. @@ -401,7 +419,7 @@ void nci_hci_data_received_cb(void *context, { struct nci_dev *ndev = (struct nci_dev *)context; struct nci_hcp_packet *packet; - u8 pipe, type, instruction; + u8 pipe, type; struct sk_buff *hcp_skb; struct sk_buff *frag_skb; int msg_len; @@ -440,7 +458,7 @@ void nci_hci_data_received_cb(void *context, *skb_put(hcp_skb, NCI_HCI_HCP_PACKET_HEADER_LEN) = pipe; skb_queue_walk(&ndev->hci_dev->rx_hcp_frags, frag_skb) { - msg_len = frag_skb->len - NCI_HCI_HCP_PACKET_HEADER_LEN; + msg_len = frag_skb->len - NCI_HCI_HCP_PACKET_HEADER_LEN; memcpy(skb_put(hcp_skb, msg_len), frag_skb->data + NCI_HCI_HCP_PACKET_HEADER_LEN, msg_len); } @@ -458,11 +476,10 @@ void nci_hci_data_received_cb(void *context, packet = (struct nci_hcp_packet *)hcp_skb->data; type = NCI_HCP_MSG_GET_TYPE(packet->message.header); if (type == NCI_HCI_HCP_RESPONSE) { - pipe = packet->header; - instruction = NCI_HCP_MSG_GET_CMD(packet->message.header); - skb_pull(hcp_skb, NCI_HCI_HCP_PACKET_HEADER_LEN + - NCI_HCI_HCP_MESSAGE_HEADER_LEN); - nci_hci_hcp_message_rx(ndev, pipe, type, instruction, hcp_skb); + pipe = NCI_HCP_MSG_GET_PIPE(packet->header); + skb_pull(hcp_skb, NCI_HCI_HCP_PACKET_HEADER_LEN); + nci_hci_hcp_message_rx(ndev, pipe, type, + NCI_STATUS_OK, hcp_skb); } else { skb_queue_tail(&ndev->hci_dev->msg_rx_queue, hcp_skb); schedule_work(&ndev->hci_dev->msg_rx_work); @@ -494,6 +511,7 @@ EXPORT_SYMBOL(nci_hci_open_pipe); int nci_hci_set_param(struct nci_dev *ndev, u8 gate, u8 idx, const u8 *param, size_t param_len) { + struct nci_hcp_message *message; struct nci_conn_info *conn_info; struct nci_data data; int r; @@ -526,6 +544,12 @@ int nci_hci_set_param(struct nci_dev *ndev, u8 gate, u8 idx, r = nci_request(ndev, nci_hci_send_data_req, (unsigned long)&data, msecs_to_jiffies(NCI_DATA_TIMEOUT)); + if (r == NCI_STATUS_OK) { + message = (struct nci_hcp_message *)conn_info->rx_skb->data; + r = nci_hci_result_to_errno( + NCI_HCP_MSG_GET_CMD(message->header)); + skb_pull(conn_info->rx_skb, NCI_HCI_HCP_MESSAGE_HEADER_LEN); + } kfree(tmp); return r; @@ -535,6 +559,7 @@ EXPORT_SYMBOL(nci_hci_set_param); int nci_hci_get_param(struct nci_dev *ndev, u8 gate, u8 idx, struct sk_buff **skb) { + struct nci_hcp_message *message; struct nci_conn_info *conn_info; struct nci_data data; int r; @@ -559,8 +584,15 @@ int nci_hci_get_param(struct nci_dev *ndev, u8 gate, u8 idx, r = nci_request(ndev, nci_hci_send_data_req, (unsigned long)&data, msecs_to_jiffies(NCI_DATA_TIMEOUT)); - if (r == NCI_STATUS_OK) - *skb = conn_info->rx_skb; + if (r == NCI_STATUS_OK) { + message = (struct nci_hcp_message *)conn_info->rx_skb->data; + r = nci_hci_result_to_errno( + NCI_HCP_MSG_GET_CMD(message->header)); + skb_pull(conn_info->rx_skb, NCI_HCI_HCP_MESSAGE_HEADER_LEN); + + if (!r && skb) + *skb = conn_info->rx_skb; + } return r; } -- cgit v1.2.3-59-g8ed1b From e65917b6d54f8b47d8293ea96adfa604fd46cf0d Mon Sep 17 00:00:00 2001 From: Christophe Ricard Date: Sun, 25 Oct 2015 22:54:22 +0100 Subject: NFC: nci: extract pipe value using NCI_HCP_MSG_GET_PIPE When receiving data in nci_hci_msg_rx_work, extract pipe value using NCI_HCP_MSG_GET_PIPE macro. Cc: stable@vger.kernel.org Signed-off-by: Christophe Ricard Signed-off-by: Samuel Ortiz --- net/nfc/nci/hci.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'net') diff --git a/net/nfc/nci/hci.c b/net/nfc/nci/hci.c index b07092f4111b..30b09f04c142 100644 --- a/net/nfc/nci/hci.c +++ b/net/nfc/nci/hci.c @@ -402,7 +402,7 @@ static void nci_hci_msg_rx_work(struct work_struct *work) u8 pipe, type, instruction; while ((skb = skb_dequeue(&hdev->msg_rx_queue)) != NULL) { - pipe = skb->data[0]; + pipe = NCI_HCP_MSG_GET_PIPE(skb->data[0]); skb_pull(skb, NCI_HCI_HCP_PACKET_HEADER_LEN); message = (struct nci_hcp_message *)skb->data; type = NCI_HCP_MSG_GET_TYPE(message->header); @@ -439,7 +439,7 @@ void nci_hci_data_received_cb(void *context, /* it's the last fragment. Does it need re-aggregation? */ if (skb_queue_len(&ndev->hci_dev->rx_hcp_frags)) { - pipe = packet->header & NCI_HCI_FRAGMENT; + pipe = NCI_HCP_MSG_GET_PIPE(packet->header); skb_queue_tail(&ndev->hci_dev->rx_hcp_frags, skb); msg_len = 0; -- cgit v1.2.3-59-g8ed1b From fa6fbadea5d3618339fae8f9d5136a470bad4fa7 Mon Sep 17 00:00:00 2001 From: Christophe Ricard Date: Sun, 25 Oct 2015 22:54:23 +0100 Subject: NFC: nci: add nci_hci_clear_all_pipes functions nci_hci_clear_all_pipes might be use full in some cases for example after a firmware update. Signed-off-by: Christophe Ricard Signed-off-by: Samuel Ortiz --- include/net/nfc/nci_core.h | 1 + net/nfc/nci/hci.c | 15 +++++++++++++++ 2 files changed, 16 insertions(+) (limited to 'net') diff --git a/include/net/nfc/nci_core.h b/include/net/nfc/nci_core.h index 0569cb251135..65e40df05df2 100644 --- a/include/net/nfc/nci_core.h +++ b/include/net/nfc/nci_core.h @@ -309,6 +309,7 @@ int nci_hci_set_param(struct nci_dev *ndev, u8 gate, u8 idx, const u8 *param, size_t param_len); int nci_hci_get_param(struct nci_dev *ndev, u8 gate, u8 idx, struct sk_buff **skb); +int nci_hci_clear_all_pipes(struct nci_dev *ndev); int nci_hci_dev_session_init(struct nci_dev *ndev); static inline struct sk_buff *nci_skb_alloc(struct nci_dev *ndev, diff --git a/net/nfc/nci/hci.c b/net/nfc/nci/hci.c index 30b09f04c142..6f477d21adfd 100644 --- a/net/nfc/nci/hci.c +++ b/net/nfc/nci/hci.c @@ -70,6 +70,7 @@ struct nci_hcp_packet { #define NCI_HCI_ANY_SET_PARAMETER 0x01 #define NCI_HCI_ANY_GET_PARAMETER 0x02 #define NCI_HCI_ANY_CLOSE_PIPE 0x04 +#define NCI_HCI_ADM_CLEAR_ALL_PIPE 0x14 #define NCI_HFP_NO_CHAINING 0x80 @@ -267,6 +268,20 @@ int nci_hci_send_cmd(struct nci_dev *ndev, u8 gate, u8 cmd, } EXPORT_SYMBOL(nci_hci_send_cmd); +int nci_hci_clear_all_pipes(struct nci_dev *ndev) +{ + int r; + + r = nci_hci_send_cmd(ndev, NCI_HCI_ADMIN_GATE, + NCI_HCI_ADM_CLEAR_ALL_PIPE, NULL, 0, NULL); + if (r < 0) + return r; + + nci_hci_reset_pipes(ndev->hci_dev); + return r; +} +EXPORT_SYMBOL(nci_hci_clear_all_pipes); + static void nci_hci_event_received(struct nci_dev *ndev, u8 pipe, u8 event, struct sk_buff *skb) { -- cgit v1.2.3-59-g8ed1b From 8a49943f5bc5ff4f835d50451ecf2380eab44d2e Mon Sep 17 00:00:00 2001 From: Christophe Ricard Date: Sun, 25 Oct 2015 22:54:24 +0100 Subject: NFC: nci: Call nci_hci_clear_all_pipes at HCI initial activation. When session_id is filled to 0xff, the pipe configuration is probably incorrect and needs to be cleared. Signed-off-by: Christophe Ricard Signed-off-by: Samuel Ortiz --- net/nfc/nci/hci.c | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'net') diff --git a/net/nfc/nci/hci.c b/net/nfc/nci/hci.c index 6f477d21adfd..af401fff72d9 100644 --- a/net/nfc/nci/hci.c +++ b/net/nfc/nci/hci.c @@ -706,6 +706,10 @@ int nci_hci_dev_session_init(struct nci_dev *ndev) /* Restore gate<->pipe table from some proprietary location. */ r = ndev->ops->hci_load_session(ndev); } else { + r = nci_hci_clear_all_pipes(ndev); + if (r < 0) + goto exit; + r = nci_hci_dev_connect_gates(ndev, ndev->hci_dev->init_data.gate_count, ndev->hci_dev->init_data.gates); -- cgit v1.2.3-59-g8ed1b From a1b0b9415817c14d207921582f269d03f848b69f Mon Sep 17 00:00:00 2001 From: Christophe Ricard Date: Sun, 25 Oct 2015 22:54:25 +0100 Subject: NFC: nci: Create pipe on specific gate in nci_hci_connect_gate Some gates might need to have their pipes explicitly created. Add a call to nci_hci_create_pipe in nci_hci_connect_gate for every gate that is different than NCI_HCI_LINK_MGMT_GATE or NCI_HCI_ADMIN_GATE. In case of an error when opening a pipe, like in hci layer, delete the pipe if it was created. Signed-off-by: Christophe Ricard Signed-off-by: Samuel Ortiz --- net/nfc/nci/hci.c | 56 ++++++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 55 insertions(+), 1 deletion(-) (limited to 'net') diff --git a/net/nfc/nci/hci.c b/net/nfc/nci/hci.c index af401fff72d9..c96830421097 100644 --- a/net/nfc/nci/hci.c +++ b/net/nfc/nci/hci.c @@ -79,6 +79,8 @@ struct nci_hcp_packet { #define NCI_EVT_HOT_PLUG 0x03 #define NCI_HCI_ADMIN_PARAM_SESSION_IDENTITY 0x01 +#define NCI_HCI_ADM_CREATE_PIPE 0x10 +#define NCI_HCI_ADM_DELETE_PIPE 0x11 /* HCP headers */ #define NCI_HCI_HCP_PACKET_HEADER_LEN 1 @@ -523,6 +525,43 @@ int nci_hci_open_pipe(struct nci_dev *ndev, u8 pipe) } EXPORT_SYMBOL(nci_hci_open_pipe); +static u8 nci_hci_create_pipe(struct nci_dev *ndev, u8 dest_host, + u8 dest_gate, int *result) +{ + u8 pipe; + struct sk_buff *skb; + struct nci_hci_create_pipe_params params; + struct nci_hci_create_pipe_resp *resp; + + pr_debug("gate=%d\n", dest_gate); + + params.src_gate = NCI_HCI_ADMIN_GATE; + params.dest_host = dest_host; + params.dest_gate = dest_gate; + + *result = nci_hci_send_cmd(ndev, NCI_HCI_ADMIN_GATE, + NCI_HCI_ADM_CREATE_PIPE, + (u8 *)¶ms, sizeof(params), &skb); + if (*result < 0) + return NCI_HCI_INVALID_PIPE; + + resp = (struct nci_hci_create_pipe_resp *)skb->data; + pipe = resp->pipe; + kfree_skb(skb); + + pr_debug("pipe created=%d\n", pipe); + + return pipe; +} + +static int nci_hci_delete_pipe(struct nci_dev *ndev, u8 pipe) +{ + pr_debug("\n"); + + return nci_hci_send_cmd(ndev, NCI_HCI_ADMIN_GATE, + NCI_HCI_ADM_DELETE_PIPE, &pipe, 1, NULL); +} + int nci_hci_set_param(struct nci_dev *ndev, u8 gate, u8 idx, const u8 *param, size_t param_len) { @@ -616,6 +655,7 @@ EXPORT_SYMBOL(nci_hci_get_param); int nci_hci_connect_gate(struct nci_dev *ndev, u8 dest_host, u8 dest_gate, u8 pipe) { + bool pipe_created = false; int r; if (pipe == NCI_HCI_DO_NOT_OPEN_PIPE) @@ -634,12 +674,26 @@ int nci_hci_connect_gate(struct nci_dev *ndev, case NCI_HCI_ADMIN_GATE: pipe = NCI_HCI_ADMIN_PIPE; break; + default: + pipe = nci_hci_create_pipe(ndev, dest_host, dest_gate, &r); + if (pipe < 0) + return r; + pipe_created = true; + break; } open_pipe: r = nci_hci_open_pipe(ndev, pipe); - if (r < 0) + if (r < 0) { + if (pipe_created) { + if (nci_hci_delete_pipe(ndev, pipe) < 0) { + /* TODO: Cannot clean by deleting pipe... + * -> inconsistent state + */ + } + } return r; + } ndev->hci_dev->pipes[pipe].gate = dest_gate; ndev->hci_dev->pipes[pipe].host = dest_host; -- cgit v1.2.3-59-g8ed1b From 96d4581f0b3712221d6ed4a765bb6b4ea40f2b2f Mon Sep 17 00:00:00 2001 From: Christophe Ricard Date: Sun, 25 Oct 2015 22:54:43 +0100 Subject: NFC: netlink: Add mode parameter to deactivate_target functions In order to manage in a better way the nci poll mode state machine, add mode parameter to deactivate_target functions. This way we can manage different target state. mode parameter make sense only in nci core. Signed-off-by: Christophe Ricard Signed-off-by: Samuel Ortiz --- drivers/nfc/nfcsim.c | 2 +- drivers/nfc/pn533.c | 2 +- include/net/nfc/nfc.h | 2 +- net/nfc/core.c | 4 ++-- net/nfc/digital_core.c | 3 ++- net/nfc/hci/core.c | 3 ++- net/nfc/nci/core.c | 15 +++++++++++---- net/nfc/netlink.c | 2 +- net/nfc/nfc.h | 5 ++++- net/nfc/rawsock.c | 3 ++- 10 files changed, 27 insertions(+), 14 deletions(-) (limited to 'net') diff --git a/drivers/nfc/nfcsim.c b/drivers/nfc/nfcsim.c index 93111fa8d282..26ac9e5fa1ab 100644 --- a/drivers/nfc/nfcsim.c +++ b/drivers/nfc/nfcsim.c @@ -246,7 +246,7 @@ static int nfcsim_activate_target(struct nfc_dev *nfc_dev, } static void nfcsim_deactivate_target(struct nfc_dev *nfc_dev, - struct nfc_target *target) + struct nfc_target *target, u8 mode) { struct nfcsim *dev = nfc_get_drvdata(nfc_dev); diff --git a/drivers/nfc/pn533.c b/drivers/nfc/pn533.c index a03e4eb5fe29..bb3d5ea9869c 100644 --- a/drivers/nfc/pn533.c +++ b/drivers/nfc/pn533.c @@ -2263,7 +2263,7 @@ static int pn533_activate_target(struct nfc_dev *nfc_dev, } static void pn533_deactivate_target(struct nfc_dev *nfc_dev, - struct nfc_target *target) + struct nfc_target *target, u8 mode) { struct pn533 *dev = nfc_get_drvdata(nfc_dev); struct sk_buff *skb; diff --git a/include/net/nfc/nfc.h b/include/net/nfc/nfc.h index 30afc9a6718c..dcfcfc9c00bf 100644 --- a/include/net/nfc/nfc.h +++ b/include/net/nfc/nfc.h @@ -68,7 +68,7 @@ struct nfc_ops { int (*activate_target)(struct nfc_dev *dev, struct nfc_target *target, u32 protocol); void (*deactivate_target)(struct nfc_dev *dev, - struct nfc_target *target); + struct nfc_target *target, u8 mode); int (*im_transceive)(struct nfc_dev *dev, struct nfc_target *target, struct sk_buff *skb, data_exchange_cb_t cb, void *cb_context); diff --git a/net/nfc/core.c b/net/nfc/core.c index cff3f1614ad4..1fe3d3b362c0 100644 --- a/net/nfc/core.c +++ b/net/nfc/core.c @@ -449,7 +449,7 @@ error: * @dev: The nfc device that found the target * @target_idx: index of the target that must be deactivated */ -int nfc_deactivate_target(struct nfc_dev *dev, u32 target_idx) +int nfc_deactivate_target(struct nfc_dev *dev, u32 target_idx, u8 mode) { int rc = 0; @@ -476,7 +476,7 @@ int nfc_deactivate_target(struct nfc_dev *dev, u32 target_idx) if (dev->ops->check_presence) del_timer_sync(&dev->check_pres_timer); - dev->ops->deactivate_target(dev, dev->active_target); + dev->ops->deactivate_target(dev, dev->active_target, mode); dev->active_target = NULL; error: diff --git a/net/nfc/digital_core.c b/net/nfc/digital_core.c index 009bcf317101..23c2a118ac9f 100644 --- a/net/nfc/digital_core.c +++ b/net/nfc/digital_core.c @@ -631,7 +631,8 @@ static int digital_activate_target(struct nfc_dev *nfc_dev, } static void digital_deactivate_target(struct nfc_dev *nfc_dev, - struct nfc_target *target) + struct nfc_target *target, + u8 mode) { struct nfc_digital_dev *ddev = nfc_get_drvdata(nfc_dev); diff --git a/net/nfc/hci/core.c b/net/nfc/hci/core.c index 6e061da2258a..2b0f0ac498d2 100644 --- a/net/nfc/hci/core.c +++ b/net/nfc/hci/core.c @@ -678,7 +678,8 @@ static int hci_activate_target(struct nfc_dev *nfc_dev, } static void hci_deactivate_target(struct nfc_dev *nfc_dev, - struct nfc_target *target) + struct nfc_target *target, + u8 mode) { } diff --git a/net/nfc/nci/core.c b/net/nfc/nci/core.c index 75bda34fd8e4..ecf420d43a6d 100644 --- a/net/nfc/nci/core.c +++ b/net/nfc/nci/core.c @@ -835,9 +835,11 @@ static int nci_activate_target(struct nfc_dev *nfc_dev, } static void nci_deactivate_target(struct nfc_dev *nfc_dev, - struct nfc_target *target) + struct nfc_target *target, + __u8 mode) { struct nci_dev *ndev = nfc_get_drvdata(nfc_dev); + u8 nci_mode = NCI_DEACTIVATE_TYPE_IDLE_MODE; pr_debug("entry\n"); @@ -848,9 +850,14 @@ static void nci_deactivate_target(struct nfc_dev *nfc_dev, ndev->target_active_prot = 0; + switch (mode) { + case NFC_TARGET_MODE_SLEEP: + nci_mode = NCI_DEACTIVATE_TYPE_SLEEP_MODE; + break; + } + if (atomic_read(&ndev->state) == NCI_POLL_ACTIVE) { - nci_request(ndev, nci_rf_deactivate_req, - NCI_DEACTIVATE_TYPE_IDLE_MODE, + nci_request(ndev, nci_rf_deactivate_req, nci_mode, msecs_to_jiffies(NCI_RF_DEACTIVATE_TIMEOUT)); } } @@ -884,7 +891,7 @@ static int nci_dep_link_down(struct nfc_dev *nfc_dev) pr_debug("entry\n"); if (nfc_dev->rf_mode == NFC_RF_INITIATOR) { - nci_deactivate_target(nfc_dev, NULL); + nci_deactivate_target(nfc_dev, NULL, NCI_DEACTIVATE_TYPE_IDLE_MODE); } else { if (atomic_read(&ndev->state) == NCI_LISTEN_ACTIVE || atomic_read(&ndev->state) == NCI_DISCOVERY) { diff --git a/net/nfc/netlink.c b/net/nfc/netlink.c index f04053295ff1..f58c1fba1026 100644 --- a/net/nfc/netlink.c +++ b/net/nfc/netlink.c @@ -885,7 +885,7 @@ static int nfc_genl_activate_target(struct sk_buff *skb, struct genl_info *info) target_idx = nla_get_u32(info->attrs[NFC_ATTR_TARGET_INDEX]); protocol = nla_get_u32(info->attrs[NFC_ATTR_PROTOCOLS]); - nfc_deactivate_target(dev, target_idx); + nfc_deactivate_target(dev, target_idx, NFC_TARGET_MODE_SLEEP); rc = nfc_activate_target(dev, target_idx, protocol); nfc_put_device(dev); diff --git a/net/nfc/nfc.h b/net/nfc/nfc.h index 5c93e8412a26..c20b784ad720 100644 --- a/net/nfc/nfc.h +++ b/net/nfc/nfc.h @@ -25,6 +25,9 @@ #include #include +#define NFC_TARGET_MODE_IDLE 0 +#define NFC_TARGET_MODE_SLEEP 1 + struct nfc_protocol { int id; struct proto *proto; @@ -147,7 +150,7 @@ int nfc_dep_link_down(struct nfc_dev *dev); int nfc_activate_target(struct nfc_dev *dev, u32 target_idx, u32 protocol); -int nfc_deactivate_target(struct nfc_dev *dev, u32 target_idx); +int nfc_deactivate_target(struct nfc_dev *dev, u32 target_idx, u8 mode); int nfc_data_exchange(struct nfc_dev *dev, u32 target_idx, struct sk_buff *skb, data_exchange_cb_t cb, void *cb_context); diff --git a/net/nfc/rawsock.c b/net/nfc/rawsock.c index e9a91488fe3d..e386e6c90b17 100644 --- a/net/nfc/rawsock.c +++ b/net/nfc/rawsock.c @@ -321,7 +321,8 @@ static void rawsock_destruct(struct sock *sk) if (sk->sk_state == TCP_ESTABLISHED) { nfc_deactivate_target(nfc_rawsock(sk)->dev, - nfc_rawsock(sk)->target_idx); + nfc_rawsock(sk)->target_idx, + NFC_TARGET_MODE_IDLE); nfc_put_device(nfc_rawsock(sk)->dev); } -- cgit v1.2.3-59-g8ed1b From 064d00479697907730a4aff9edf879c0ca4743f0 Mon Sep 17 00:00:00 2001 From: Christophe Ricard Date: Sun, 25 Oct 2015 22:54:44 +0100 Subject: NFC: st-nci: Add few code style fixes Add some few code style fixes. Signed-off-by: Christophe Ricard Signed-off-by: Samuel Ortiz --- drivers/nfc/st-nci/i2c.c | 2 +- drivers/nfc/st-nci/se.c | 2 +- net/nfc/nci/hci.c | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) (limited to 'net') diff --git a/drivers/nfc/st-nci/i2c.c b/drivers/nfc/st-nci/i2c.c index 172cbc34cc9f..2a96f1a415e4 100644 --- a/drivers/nfc/st-nci/i2c.c +++ b/drivers/nfc/st-nci/i2c.c @@ -32,7 +32,7 @@ #define DRIVER_DESC "NCI NFC driver for ST_NCI" /* ndlc header */ -#define ST_NCI_FRAME_HEADROOM 1 +#define ST_NCI_FRAME_HEADROOM 1 #define ST_NCI_FRAME_TAILROOM 0 #define ST_NCI_I2C_MIN_SIZE 4 /* PCB(1) + NCI Packet header(3) */ diff --git a/drivers/nfc/st-nci/se.c b/drivers/nfc/st-nci/se.c index 1cc24b6623e0..dbab722a0654 100644 --- a/drivers/nfc/st-nci/se.c +++ b/drivers/nfc/st-nci/se.c @@ -62,7 +62,7 @@ struct st_nci_pipe_info { #define ST_NCI_EVT_SE_HARD_RESET 0x20 #define ST_NCI_EVT_TRANSMIT_DATA 0x10 -#define ST_NCI_EVT_WTX_REQUEST 0x11 +#define ST_NCI_EVT_WTX_REQUEST 0x11 #define ST_NCI_EVT_SE_SOFT_RESET 0x11 #define ST_NCI_EVT_SE_END_OF_APDU_TRANSFER 0x21 #define ST_NCI_EVT_HOT_PLUG 0x03 diff --git a/net/nfc/nci/hci.c b/net/nfc/nci/hci.c index c96830421097..2aedac15cb59 100644 --- a/net/nfc/nci/hci.c +++ b/net/nfc/nci/hci.c @@ -407,7 +407,7 @@ static void nci_hci_hcp_message_rx(struct nci_dev *ndev, u8 pipe, break; } - nci_req_complete(ndev, 0); + nci_req_complete(ndev, NCI_STATUS_OK); } static void nci_hci_msg_rx_work(struct work_struct *work) -- cgit v1.2.3-59-g8ed1b From 15d17170b4e44c5768c3d32b093cd393c4dbc158 Mon Sep 17 00:00:00 2001 From: Christophe Ricard Date: Mon, 26 Oct 2015 07:50:11 +0100 Subject: NFC: st21nfca: Add support for proprietary commands Add support for proprietary commands useful mainly for factory testings. Here is a list: - FACTORY_MODE: Allow to set the driver into a mode where no secure element are activated. It does not consider any NFC_ATTR_VENDOR_DATA. - HCI_CLEAR_ALL_PIPES: Allow to execute a HCI clear all pipes command. It does not consider any NFC_ATTR_VENDOR_DATA. - HCI_DM_PUT_DATA: Allow to configure specific CLF registry as for example RF trimmings or low level drivers configurations (I2C, SPI, SWP). - HCI_DM_UPDATE_AID: Allow to configure an AID routing into the CLF routing table following RF technology, CLF mode or protocol. - HCI_DM_GET_INFO: Allow to retrieve CLF information. - HCI_DM_GET_DATA: Allow to retrieve CLF configurable data such as low level drivers configurations or RF trimmings. - HCI_DM_LOAD: Allow to load a firmware into the CLF. A complete packet can be more than 8KB. - HCI_DM_RESET: Allow to run a CLF reset in order to "commit" CLF configuration changes without CLF power off. - HCI_GET_PARAM: Allow to retrieve an HCI CLF parameter (for example the white list). - HCI_DM_FIELD_GENERATOR: Allow to generate different kind of RF technology. When using this command to anti-collision is done. - HCI_LOOPBACK: Allow to echo a command and test the Dh to CLF connectivity. Signed-off-by: Christophe Ricard Signed-off-by: Samuel Ortiz --- drivers/nfc/st21nfca/Makefile | 2 +- drivers/nfc/st21nfca/core.c | 3 + drivers/nfc/st21nfca/se.c | 4 +- drivers/nfc/st21nfca/st21nfca.h | 54 +++++- drivers/nfc/st21nfca/vendor_cmds.c | 375 +++++++++++++++++++++++++++++++++++++ net/nfc/hci/llc.c | 2 + 6 files changed, 437 insertions(+), 3 deletions(-) create mode 100644 drivers/nfc/st21nfca/vendor_cmds.c (limited to 'net') diff --git a/drivers/nfc/st21nfca/Makefile b/drivers/nfc/st21nfca/Makefile index 82434c3a7c9b..ded6489c3eeb 100644 --- a/drivers/nfc/st21nfca/Makefile +++ b/drivers/nfc/st21nfca/Makefile @@ -2,7 +2,7 @@ # Makefile for ST21NFCA HCI based NFC driver # -st21nfca_hci-objs = core.o dep.o se.o +st21nfca_hci-objs = core.o dep.o se.o vendor_cmds.o obj-$(CONFIG_NFC_ST21NFCA) += st21nfca_hci.o st21nfca_i2c-objs = i2c.o diff --git a/drivers/nfc/st21nfca/core.c b/drivers/nfc/st21nfca/core.c index 496ee7cc1f41..dd8b150fbffa 100644 --- a/drivers/nfc/st21nfca/core.c +++ b/drivers/nfc/st21nfca/core.c @@ -923,6 +923,8 @@ static int st21nfca_hci_event_received(struct nfc_hci_dev *hdev, u8 pipe, event, skb); case ST21NFCA_APDU_READER_GATE: return st21nfca_apdu_reader_event_received(hdev, event, skb); + case NFC_HCI_LOOPBACK_GATE: + return st21nfca_hci_loopback_event_received(hdev, event, skb); default: return 1; } @@ -1024,6 +1026,7 @@ int st21nfca_hci_probe(void *phy_id, struct nfc_phy_ops *phy_ops, *hdev = info->hdev; st21nfca_dep_init(info->hdev); st21nfca_se_init(info->hdev); + st21nfca_vendor_cmds_init(info->hdev); return 0; diff --git a/drivers/nfc/st21nfca/se.c b/drivers/nfc/st21nfca/se.c index dd3e15c17353..c79d99b24c96 100644 --- a/drivers/nfc/st21nfca/se.c +++ b/drivers/nfc/st21nfca/se.c @@ -167,6 +167,9 @@ int st21nfca_hci_discover_se(struct nfc_hci_dev *hdev) struct st21nfca_hci_info *info = nfc_hci_get_clientdata(hdev); int se_count = 0; + if (test_bit(ST21NFCA_FACTORY_MODE, &hdev->quirks)) + return 0; + if (info->se_status->is_uicc_present) { nfc_add_se(hdev->ndev, NFC_HCI_UICC_HOST_ID, NFC_SE_UICC); se_count++; @@ -191,7 +194,6 @@ int st21nfca_hci_enable_se(struct nfc_hci_dev *hdev, u32 se_idx) * Same for eSE. */ r = st21nfca_hci_control_se(hdev, se_idx, ST21NFCA_SE_MODE_ON); - if (r == ST21NFCA_ESE_HOST_ID) { st21nfca_se_get_atr(hdev); r = nfc_hci_send_event(hdev, ST21NFCA_APDU_READER_GATE, diff --git a/drivers/nfc/st21nfca/st21nfca.h b/drivers/nfc/st21nfca/st21nfca.h index 70e2d43b4ec3..94ffb0501e87 100644 --- a/drivers/nfc/st21nfca/st21nfca.h +++ b/drivers/nfc/st21nfca/st21nfca.h @@ -66,6 +66,9 @@ #define ST21NFCA_HCI_MODE 0 #define ST21NFCA_NUM_DEVICES 256 +#define ST21NFCA_VENDOR_OUI 0x0080E1 /* STMicroelectronics */ +#define ST21NFCA_FACTORY_MODE 2 + struct st21nfca_se_status { bool is_ese_present; bool is_uicc_present; @@ -76,6 +79,50 @@ enum st21nfca_state { ST21NFCA_ST_READY, }; +/** + * enum nfc_vendor_cmds - supported nfc vendor commands + * + * @FACTORY_MODE: Allow to set the driver into a mode where no secure element + * are activated. It does not consider any NFC_ATTR_VENDOR_DATA. + * @HCI_CLEAR_ALL_PIPES: Allow to execute a HCI clear all pipes command. + * It does not consider any NFC_ATTR_VENDOR_DATA. + * @HCI_DM_PUT_DATA: Allow to configure specific CLF registry as for example + * RF trimmings or low level drivers configurations (I2C, SPI, SWP). + * @HCI_DM_UPDATE_AID: Allow to configure an AID routing into the CLF routing + * table following RF technology, CLF mode or protocol. + * @HCI_DM_GET_INFO: Allow to retrieve CLF information. + * @HCI_DM_GET_DATA: Allow to retrieve CLF configurable data such as low + * level drivers configurations or RF trimmings. + * @HCI_DM_LOAD: Allow to load a firmware into the CLF. A complete + * packet can be more than 8KB. + * @HCI_DM_RESET: Allow to run a CLF reset in order to "commit" CLF + * configuration changes without CLF power off. + * @HCI_GET_PARAM: Allow to retrieve an HCI CLF parameter (for example the + * white list). + * @HCI_DM_FIELD_GENERATOR: Allow to generate different kind of RF + * technology. When using this command to anti-collision is done. + * @HCI_LOOPBACK: Allow to echo a command and test the Dh to CLF + * connectivity. + */ +enum nfc_vendor_cmds { + FACTORY_MODE, + HCI_CLEAR_ALL_PIPES, + HCI_DM_PUT_DATA, + HCI_DM_UPDATE_AID, + HCI_DM_GET_INFO, + HCI_DM_GET_DATA, + HCI_DM_LOAD, + HCI_DM_RESET, + HCI_GET_PARAM, + HCI_DM_FIELD_GENERATOR, + HCI_LOOPBACK, +}; + +struct st21nfca_vendor_info { + struct completion req_completion; + struct sk_buff *rx_skb; +}; + struct st21nfca_dep_info { struct sk_buff *tx_pending; struct work_struct tx_work; @@ -124,12 +171,13 @@ struct st21nfca_hci_info { struct st21nfca_dep_info dep_info; struct st21nfca_se_info se_info; + struct st21nfca_vendor_info vendor_info; }; int st21nfca_hci_probe(void *phy_id, struct nfc_phy_ops *phy_ops, char *llc_name, int phy_headroom, int phy_tailroom, int phy_payload, struct nfc_hci_dev **hdev, - struct st21nfca_se_status *se_status); + struct st21nfca_se_status *se_status); void st21nfca_hci_remove(struct nfc_hci_dev *hdev); int st21nfca_dep_event_received(struct nfc_hci_dev *hdev, @@ -156,4 +204,8 @@ int st21nfca_hci_se_io(struct nfc_hci_dev *hdev, u32 se_idx, void st21nfca_se_init(struct nfc_hci_dev *hdev); void st21nfca_se_deinit(struct nfc_hci_dev *hdev); +int st21nfca_hci_loopback_event_received(struct nfc_hci_dev *ndev, u8 event, + struct sk_buff *skb); +int st21nfca_vendor_cmds_init(struct nfc_hci_dev *ndev); + #endif /* __LOCAL_ST21NFCA_H_ */ diff --git a/drivers/nfc/st21nfca/vendor_cmds.c b/drivers/nfc/st21nfca/vendor_cmds.c new file mode 100644 index 000000000000..ab765e5478c0 --- /dev/null +++ b/drivers/nfc/st21nfca/vendor_cmds.c @@ -0,0 +1,375 @@ +/* + * Proprietary commands extension for STMicroelectronics NFC Chip + * + * Copyright (C) 2014-2015 STMicroelectronics SAS. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see . + */ + +#include +#include +#include +#include +#include + +#include "st21nfca.h" + +#define ST21NFCA_HCI_DM_GETDATA 0x10 +#define ST21NFCA_HCI_DM_PUTDATA 0x11 +#define ST21NFCA_HCI_DM_LOAD 0x12 +#define ST21NFCA_HCI_DM_GETINFO 0x13 +#define ST21NFCA_HCI_DM_UPDATE_AID 0x20 +#define ST21NFCA_HCI_DM_RESET 0x3e + +#define ST21NFCA_HCI_DM_FIELD_GENERATOR 0x32 + +#define ST21NFCA_FACTORY_MODE_ON 1 +#define ST21NFCA_FACTORY_MODE_OFF 0 + +#define ST21NFCA_EVT_POST_DATA 0x02 + +struct get_param_data { + u8 gate; + u8 data; +} __packed; + +static int st21nfca_factory_mode(struct nfc_dev *dev, void *data, + size_t data_len) +{ + struct nfc_hci_dev *hdev = nfc_get_drvdata(dev); + + if (data_len != 1) + return -EINVAL; + + pr_debug("factory mode: %x\n", ((u8 *)data)[0]); + + switch (((u8 *)data)[0]) { + case ST21NFCA_FACTORY_MODE_ON: + test_and_set_bit(ST21NFCA_FACTORY_MODE, &hdev->quirks); + break; + case ST21NFCA_FACTORY_MODE_OFF: + clear_bit(ST21NFCA_FACTORY_MODE, &hdev->quirks); + break; + default: + return -EINVAL; + } + + return 0; +} + +static int st21nfca_hci_clear_all_pipes(struct nfc_dev *dev, void *data, + size_t data_len) +{ + struct nfc_hci_dev *hdev = nfc_get_drvdata(dev); + + return nfc_hci_disconnect_all_gates(hdev); +} + +static int st21nfca_hci_dm_put_data(struct nfc_dev *dev, void *data, + size_t data_len) +{ + struct nfc_hci_dev *hdev = nfc_get_drvdata(dev); + + return nfc_hci_send_cmd(hdev, ST21NFCA_DEVICE_MGNT_GATE, + ST21NFCA_HCI_DM_PUTDATA, data, + data_len, NULL); +} + +static int st21nfca_hci_dm_update_aid(struct nfc_dev *dev, void *data, + size_t data_len) +{ + struct nfc_hci_dev *hdev = nfc_get_drvdata(dev); + + return nfc_hci_send_cmd(hdev, ST21NFCA_DEVICE_MGNT_GATE, + ST21NFCA_HCI_DM_UPDATE_AID, data, data_len, NULL); +} + +static int st21nfca_hci_dm_get_info(struct nfc_dev *dev, void *data, + size_t data_len) +{ + int r; + struct sk_buff *msg, *skb; + struct nfc_hci_dev *hdev = nfc_get_drvdata(dev); + + r = nfc_hci_send_cmd(hdev, + ST21NFCA_DEVICE_MGNT_GATE, + ST21NFCA_HCI_DM_GETINFO, + data, data_len, &skb); + if (r) + goto exit; + + msg = nfc_vendor_cmd_alloc_reply_skb(dev, ST21NFCA_VENDOR_OUI, + HCI_DM_GET_INFO, skb->len); + if (!msg) { + r = -ENOMEM; + goto free_skb; + } + + if (nla_put(msg, NFC_ATTR_VENDOR_DATA, skb->len, skb->data)) { + kfree_skb(msg); + r = -ENOBUFS; + goto free_skb; + } + + r = nfc_vendor_cmd_reply(msg); + +free_skb: + kfree_skb(skb); +exit: + return r; +} + +static int st21nfca_hci_dm_get_data(struct nfc_dev *dev, void *data, + size_t data_len) +{ + int r; + struct sk_buff *msg, *skb; + struct nfc_hci_dev *hdev = nfc_get_drvdata(dev); + + r = nfc_hci_send_cmd(hdev, + ST21NFCA_DEVICE_MGNT_GATE, + ST21NFCA_HCI_DM_GETDATA, + data, data_len, &skb); + if (r) + goto exit; + + msg = nfc_vendor_cmd_alloc_reply_skb(dev, ST21NFCA_VENDOR_OUI, + HCI_DM_GET_DATA, skb->len); + if (!msg) { + r = -ENOMEM; + goto free_skb; + } + + if (nla_put(msg, NFC_ATTR_VENDOR_DATA, skb->len, skb->data)) { + kfree_skb(msg); + r = -ENOBUFS; + goto free_skb; + } + + r = nfc_vendor_cmd_reply(msg); + +free_skb: + kfree_skb(skb); +exit: + return r; +} + +static int st21nfca_hci_dm_load(struct nfc_dev *dev, void *data, + size_t data_len) +{ + struct nfc_hci_dev *hdev = nfc_get_drvdata(dev); + + return nfc_hci_send_cmd(hdev, ST21NFCA_DEVICE_MGNT_GATE, + ST21NFCA_HCI_DM_LOAD, data, data_len, NULL); +} + +static int st21nfca_hci_dm_reset(struct nfc_dev *dev, void *data, + size_t data_len) +{ + int r; + struct nfc_hci_dev *hdev = nfc_get_drvdata(dev); + + r = nfc_hci_send_cmd_async(hdev, ST21NFCA_DEVICE_MGNT_GATE, + ST21NFCA_HCI_DM_RESET, data, data_len, NULL, NULL); + if (r < 0) + return r; + + r = nfc_llc_stop(hdev->llc); + if (r < 0) + return r; + + return nfc_llc_start(hdev->llc); +} + +static int st21nfca_hci_get_param(struct nfc_dev *dev, void *data, + size_t data_len) +{ + int r; + struct sk_buff *msg, *skb; + struct nfc_hci_dev *hdev = nfc_get_drvdata(dev); + struct get_param_data *param = (struct get_param_data *)data; + + if (data_len < sizeof(struct get_param_data)) + return -EPROTO; + + r = nfc_hci_get_param(hdev, param->gate, param->data, &skb); + if (r) + goto exit; + + msg = nfc_vendor_cmd_alloc_reply_skb(dev, ST21NFCA_VENDOR_OUI, + HCI_GET_PARAM, skb->len); + if (!msg) { + r = -ENOMEM; + goto free_skb; + } + + if (nla_put(msg, NFC_ATTR_VENDOR_DATA, skb->len, skb->data)) { + kfree_skb(msg); + r = -ENOBUFS; + goto free_skb; + } + + r = nfc_vendor_cmd_reply(msg); + +free_skb: + kfree_skb(skb); +exit: + return r; +} + +static int st21nfca_hci_dm_field_generator(struct nfc_dev *dev, void *data, + size_t data_len) +{ + struct nfc_hci_dev *hdev = nfc_get_drvdata(dev); + + return nfc_hci_send_cmd(hdev, + ST21NFCA_DEVICE_MGNT_GATE, + ST21NFCA_HCI_DM_FIELD_GENERATOR, + data, data_len, NULL); +} + +int st21nfca_hci_loopback_event_received(struct nfc_hci_dev *hdev, u8 event, + struct sk_buff *skb) +{ + struct st21nfca_hci_info *info = nfc_hci_get_clientdata(hdev); + + switch (event) { + case ST21NFCA_EVT_POST_DATA: + info->vendor_info.rx_skb = skb; + break; + default: + nfc_err(&hdev->ndev->dev, "Unexpected event on loopback gate\n"); + } + complete(&info->vendor_info.req_completion); + return 0; +} +EXPORT_SYMBOL(st21nfca_hci_loopback_event_received); + +static int st21nfca_hci_loopback(struct nfc_dev *dev, void *data, + size_t data_len) +{ + int r; + struct sk_buff *msg; + struct nfc_hci_dev *hdev = nfc_get_drvdata(dev); + struct st21nfca_hci_info *info = nfc_hci_get_clientdata(hdev); + + if (data_len <= 0) + return -EPROTO; + + reinit_completion(&info->vendor_info.req_completion); + info->vendor_info.rx_skb = NULL; + + r = nfc_hci_send_event(hdev, NFC_HCI_LOOPBACK_GATE, + ST21NFCA_EVT_POST_DATA, data, data_len); + if (r < 0) { + r = -EPROTO; + goto exit; + } + + wait_for_completion_interruptible(&info->vendor_info.req_completion); + if (!info->vendor_info.rx_skb || + info->vendor_info.rx_skb->len != data_len) { + r = -EPROTO; + goto exit; + } + + msg = nfc_vendor_cmd_alloc_reply_skb(hdev->ndev, + ST21NFCA_VENDOR_OUI, + HCI_LOOPBACK, + info->vendor_info.rx_skb->len); + if (!msg) { + r = -ENOMEM; + goto free_skb; + } + + if (nla_put(msg, NFC_ATTR_VENDOR_DATA, info->vendor_info.rx_skb->len, + info->vendor_info.rx_skb->data)) { + kfree_skb(msg); + r = -ENOBUFS; + goto free_skb; + } + + r = nfc_vendor_cmd_reply(msg); +free_skb: + kfree_skb(info->vendor_info.rx_skb); +exit: + return r; +} + +static struct nfc_vendor_cmd st21nfca_vendor_cmds[] = { + { + .vendor_id = ST21NFCA_VENDOR_OUI, + .subcmd = FACTORY_MODE, + .doit = st21nfca_factory_mode, + }, + { + .vendor_id = ST21NFCA_VENDOR_OUI, + .subcmd = HCI_CLEAR_ALL_PIPES, + .doit = st21nfca_hci_clear_all_pipes, + }, + { + .vendor_id = ST21NFCA_VENDOR_OUI, + .subcmd = HCI_DM_PUT_DATA, + .doit = st21nfca_hci_dm_put_data, + }, + { + .vendor_id = ST21NFCA_VENDOR_OUI, + .subcmd = HCI_DM_UPDATE_AID, + .doit = st21nfca_hci_dm_update_aid, + }, + { + .vendor_id = ST21NFCA_VENDOR_OUI, + .subcmd = HCI_DM_GET_INFO, + .doit = st21nfca_hci_dm_get_info, + }, + { + .vendor_id = ST21NFCA_VENDOR_OUI, + .subcmd = HCI_DM_GET_DATA, + .doit = st21nfca_hci_dm_get_data, + }, + { + .vendor_id = ST21NFCA_VENDOR_OUI, + .subcmd = HCI_DM_LOAD, + .doit = st21nfca_hci_dm_load, + }, + { + .vendor_id = ST21NFCA_VENDOR_OUI, + .subcmd = HCI_DM_RESET, + .doit = st21nfca_hci_dm_reset, + }, + { + .vendor_id = ST21NFCA_VENDOR_OUI, + .subcmd = HCI_GET_PARAM, + .doit = st21nfca_hci_get_param, + }, + { + .vendor_id = ST21NFCA_VENDOR_OUI, + .subcmd = HCI_DM_FIELD_GENERATOR, + .doit = st21nfca_hci_dm_field_generator, + }, + { + .vendor_id = ST21NFCA_VENDOR_OUI, + .subcmd = HCI_LOOPBACK, + .doit = st21nfca_hci_loopback, + }, +}; + +int st21nfca_vendor_cmds_init(struct nfc_hci_dev *hdev) +{ + struct st21nfca_hci_info *info = nfc_hci_get_clientdata(hdev); + + init_completion(&info->vendor_info.req_completion); + return nfc_set_vendor_cmds(hdev->ndev, st21nfca_vendor_cmds, + sizeof(st21nfca_vendor_cmds)); +} +EXPORT_SYMBOL(st21nfca_vendor_cmds_init); diff --git a/net/nfc/hci/llc.c b/net/nfc/hci/llc.c index 1b90c0531852..1399a03fa6e6 100644 --- a/net/nfc/hci/llc.c +++ b/net/nfc/hci/llc.c @@ -144,11 +144,13 @@ inline int nfc_llc_start(struct nfc_llc *llc) { return llc->ops->start(llc); } +EXPORT_SYMBOL(nfc_llc_start); inline int nfc_llc_stop(struct nfc_llc *llc) { return llc->ops->stop(llc); } +EXPORT_SYMBOL(nfc_llc_stop); inline void nfc_llc_rcv_from_drv(struct nfc_llc *llc, struct sk_buff *skb) { -- cgit v1.2.3-59-g8ed1b From e5629d29470134af1954d2bbe45c4f2b73f68ee9 Mon Sep 17 00:00:00 2001 From: Vincent Cuissard Date: Mon, 26 Oct 2015 10:27:38 +0100 Subject: NFC: NCI: export nci_send_frame and nci_send_cmd function Export nci_send_frame and nci_send_cmd symbols to allow drivers to use it. This is needed for example if NCI is used during firmware download phase. Signed-off-by: Vincent Cuissard Signed-off-by: Samuel Ortiz --- include/net/nfc/nci_core.h | 1 + net/nfc/nci/core.c | 4 +++- 2 files changed, 4 insertions(+), 1 deletion(-) (limited to 'net') diff --git a/include/net/nfc/nci_core.h b/include/net/nfc/nci_core.h index 530df66f9484..77880bdf93a8 100644 --- a/include/net/nfc/nci_core.h +++ b/include/net/nfc/nci_core.h @@ -288,6 +288,7 @@ int nci_core_reset(struct nci_dev *ndev); int nci_core_init(struct nci_dev *ndev); int nci_recv_frame(struct nci_dev *ndev, struct sk_buff *skb); +int nci_send_frame(struct nci_dev *ndev, struct sk_buff *skb); int nci_set_config(struct nci_dev *ndev, __u8 id, size_t len, __u8 *val); int nci_nfcee_discover(struct nci_dev *ndev, u8 action); diff --git a/net/nfc/nci/core.c b/net/nfc/nci/core.c index ecf420d43a6d..2da03c08dad2 100644 --- a/net/nfc/nci/core.c +++ b/net/nfc/nci/core.c @@ -1218,7 +1218,7 @@ int nci_recv_frame(struct nci_dev *ndev, struct sk_buff *skb) } EXPORT_SYMBOL(nci_recv_frame); -static int nci_send_frame(struct nci_dev *ndev, struct sk_buff *skb) +int nci_send_frame(struct nci_dev *ndev, struct sk_buff *skb) { pr_debug("len %d\n", skb->len); @@ -1236,6 +1236,7 @@ static int nci_send_frame(struct nci_dev *ndev, struct sk_buff *skb) return ndev->ops->send(ndev, skb); } +EXPORT_SYMBOL(nci_send_frame); /* Send NCI command */ int nci_send_cmd(struct nci_dev *ndev, __u16 opcode, __u8 plen, void *payload) @@ -1267,6 +1268,7 @@ int nci_send_cmd(struct nci_dev *ndev, __u16 opcode, __u8 plen, void *payload) return 0; } +EXPORT_SYMBOL(nci_send_cmd); /* Proprietary commands API */ static struct nci_driver_ops *ops_cmd_lookup(struct nci_driver_ops *ops, -- cgit v1.2.3-59-g8ed1b From fcd9d046fd8998cc984f2ff80e76f246ebca15b6 Mon Sep 17 00:00:00 2001 From: Vincent Cuissard Date: Mon, 26 Oct 2015 10:27:42 +0100 Subject: NFC: NCI: move generic spi driver to a module SPI driver should be a module. Signed-off-by: Vincent Cuissard Signed-off-by: Samuel Ortiz --- net/nfc/nci/Kconfig | 2 +- net/nfc/nci/Makefile | 3 ++- net/nfc/nci/spi.c | 4 ++++ 3 files changed, 7 insertions(+), 2 deletions(-) (limited to 'net') diff --git a/net/nfc/nci/Kconfig b/net/nfc/nci/Kconfig index 901c1ddba841..85d4819ab657 100644 --- a/net/nfc/nci/Kconfig +++ b/net/nfc/nci/Kconfig @@ -12,7 +12,7 @@ config NFC_NCI config NFC_NCI_SPI depends on NFC_NCI && SPI select CRC_CCITT - bool "NCI over SPI protocol support" + tristate "NCI over SPI protocol support" default n help NCI (NFC Controller Interface) is a communication protocol between diff --git a/net/nfc/nci/Makefile b/net/nfc/nci/Makefile index b4b85b82e988..0ca31d9bf741 100644 --- a/net/nfc/nci/Makefile +++ b/net/nfc/nci/Makefile @@ -6,7 +6,8 @@ obj-$(CONFIG_NFC_NCI) += nci.o nci-objs := core.o data.o lib.o ntf.o rsp.o hci.o -nci-$(CONFIG_NFC_NCI_SPI) += spi.o +nci_spi-y += spi.o +obj-$(CONFIG_NFC_NCI_SPI) += nci_spi.o nci_uart-y += uart.o obj-$(CONFIG_NFC_NCI_UART) += nci_uart.o diff --git a/net/nfc/nci/spi.c b/net/nfc/nci/spi.c index ec250e77763a..25153d0e33cf 100644 --- a/net/nfc/nci/spi.c +++ b/net/nfc/nci/spi.c @@ -18,6 +18,8 @@ #define pr_fmt(fmt) "nci_spi: %s: " fmt, __func__ +#include + #include #include #include @@ -320,3 +322,5 @@ done: return skb; } EXPORT_SYMBOL_GPL(nci_spi_read); + +MODULE_LICENSE("GPL"); -- cgit v1.2.3-59-g8ed1b From 2bd832459a0827b8dcf13b345380b66f92089d74 Mon Sep 17 00:00:00 2001 From: Vincent Cuissard Date: Mon, 26 Oct 2015 10:27:43 +0100 Subject: NFC: NCI: allow spi driver to choose transfer clock In some cases low level drivers might want to update the SPI transfer clock (e.g. during firmware download). This patch adds this support. Without any modification the driver will use the default SPI clock (from pdata or device tree). Signed-off-by: Vincent Cuissard Signed-off-by: Samuel Ortiz --- include/net/nfc/nci_core.h | 6 ++++++ net/nfc/nci/spi.c | 7 ++++++- 2 files changed, 12 insertions(+), 1 deletion(-) (limited to 'net') diff --git a/include/net/nfc/nci_core.h b/include/net/nfc/nci_core.h index 77880bdf93a8..5a1572a34c49 100644 --- a/include/net/nfc/nci_core.h +++ b/include/net/nfc/nci_core.h @@ -394,6 +394,12 @@ struct nci_spi { unsigned int xfer_udelay; /* microseconds delay between transactions */ + + unsigned int xfer_speed_hz; /* + * SPI clock frequency + * 0 => default clock + */ + u8 acknowledge_mode; struct completion req_completion; diff --git a/net/nfc/nci/spi.c b/net/nfc/nci/spi.c index 25153d0e33cf..d904cd2f1442 100644 --- a/net/nfc/nci/spi.c +++ b/net/nfc/nci/spi.c @@ -58,6 +58,7 @@ static int __nci_spi_send(struct nci_spi *nspi, struct sk_buff *skb, } t.cs_change = cs_change; t.delay_usecs = nspi->xfer_udelay; + t.speed_hz = nspi->xfer_speed_hz; spi_message_init(&m); spi_message_add_tail(&t, &m); @@ -144,7 +145,8 @@ struct nci_spi *nci_spi_allocate_spi(struct spi_device *spi, nspi->acknowledge_mode = acknowledge_mode; nspi->xfer_udelay = delay; - + /* Use controller max SPI speed by default */ + nspi->xfer_speed_hz = 0; nspi->spi = spi; nspi->ndev = ndev; init_completion(&nspi->req_completion); @@ -197,12 +199,14 @@ static struct sk_buff *__nci_spi_read(struct nci_spi *nspi) tx.tx_buf = req; tx.len = 2; tx.cs_change = 0; + tx.speed_hz = nspi->xfer_speed_hz; spi_message_add_tail(&tx, &m); memset(&rx, 0, sizeof(struct spi_transfer)); rx.rx_buf = resp_hdr; rx.len = 2; rx.cs_change = 1; + rx.speed_hz = nspi->xfer_speed_hz; spi_message_add_tail(&rx, &m); ret = spi_sync(nspi->spi, &m); @@ -226,6 +230,7 @@ static struct sk_buff *__nci_spi_read(struct nci_spi *nspi) rx.len = rx_len; rx.cs_change = 0; rx.delay_usecs = nspi->xfer_udelay; + rx.speed_hz = nspi->xfer_speed_hz; spi_message_add_tail(&rx, &m); ret = spi_sync(nspi->spi, &m); -- cgit v1.2.3-59-g8ed1b From f11631748ee6973f85238109a3fa8ab8e760e5a4 Mon Sep 17 00:00:00 2001 From: Robert Dolca Date: Mon, 26 Oct 2015 13:58:54 +0200 Subject: NFC: nci: non-static functions can not be inline MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This fixes a build error that seems to be toochain dependent (Not seen with gcc v5.1): In file included from net/nfc/nci/rsp.c:36:0: net/nfc/nci/rsp.c: In function ‘nci_rsp_packet’: include/net/nfc/nci_core.h:355:12: error: inlining failed in call to always_inline ‘nci_prop_rsp_packet’: function body not available inline int nci_prop_rsp_packet(struct nci_dev *ndev, __u16 opcode, Signed-off-by: Robert Dolca Signed-off-by: Samuel Ortiz --- include/net/nfc/nci_core.h | 8 ++++---- net/nfc/nci/core.c | 16 ++++++++-------- 2 files changed, 12 insertions(+), 12 deletions(-) (limited to 'net') diff --git a/include/net/nfc/nci_core.h b/include/net/nfc/nci_core.h index 5a1572a34c49..57ce24fb0047 100644 --- a/include/net/nfc/nci_core.h +++ b/include/net/nfc/nci_core.h @@ -352,13 +352,13 @@ static inline int nci_set_vendor_cmds(struct nci_dev *ndev, void nci_rsp_packet(struct nci_dev *ndev, struct sk_buff *skb); void nci_ntf_packet(struct nci_dev *ndev, struct sk_buff *skb); -inline int nci_prop_rsp_packet(struct nci_dev *ndev, __u16 opcode, +int nci_prop_rsp_packet(struct nci_dev *ndev, __u16 opcode, struct sk_buff *skb); -inline int nci_prop_ntf_packet(struct nci_dev *ndev, __u16 opcode, +int nci_prop_ntf_packet(struct nci_dev *ndev, __u16 opcode, struct sk_buff *skb); -inline int nci_core_rsp_packet(struct nci_dev *ndev, __u16 opcode, +int nci_core_rsp_packet(struct nci_dev *ndev, __u16 opcode, struct sk_buff *skb); -inline int nci_core_ntf_packet(struct nci_dev *ndev, __u16 opcode, +int nci_core_ntf_packet(struct nci_dev *ndev, __u16 opcode, struct sk_buff *skb); void nci_rx_data_packet(struct nci_dev *ndev, struct sk_buff *skb); int nci_send_cmd(struct nci_dev *ndev, __u16 opcode, __u8 plen, void *payload); diff --git a/net/nfc/nci/core.c b/net/nfc/nci/core.c index 2da03c08dad2..10c99a578421 100644 --- a/net/nfc/nci/core.c +++ b/net/nfc/nci/core.c @@ -1316,29 +1316,29 @@ static int nci_op_ntf_packet(struct nci_dev *ndev, __u16 ntf_opcode, return op->ntf(ndev, skb); } -inline int nci_prop_rsp_packet(struct nci_dev *ndev, __u16 opcode, - struct sk_buff *skb) +int nci_prop_rsp_packet(struct nci_dev *ndev, __u16 opcode, + struct sk_buff *skb) { return nci_op_rsp_packet(ndev, opcode, skb, ndev->ops->prop_ops, ndev->ops->n_prop_ops); } -inline int nci_prop_ntf_packet(struct nci_dev *ndev, __u16 opcode, - struct sk_buff *skb) +int nci_prop_ntf_packet(struct nci_dev *ndev, __u16 opcode, + struct sk_buff *skb) { return nci_op_ntf_packet(ndev, opcode, skb, ndev->ops->prop_ops, ndev->ops->n_prop_ops); } -inline int nci_core_rsp_packet(struct nci_dev *ndev, __u16 opcode, - struct sk_buff *skb) +int nci_core_rsp_packet(struct nci_dev *ndev, __u16 opcode, + struct sk_buff *skb) { return nci_op_rsp_packet(ndev, opcode, skb, ndev->ops->core_ops, ndev->ops->n_core_ops); } -inline int nci_core_ntf_packet(struct nci_dev *ndev, __u16 opcode, - struct sk_buff *skb) +int nci_core_ntf_packet(struct nci_dev *ndev, __u16 opcode, + struct sk_buff *skb) { return nci_op_ntf_packet(ndev, opcode, skb, ndev->ops->core_ops, ndev->ops->n_core_ops); -- cgit v1.2.3-59-g8ed1b