From 5d50b364e61e85eb41938d25770db3aab5e07d82 Mon Sep 17 00:00:00 2001 From: Samuel Ortiz Date: Fri, 17 Aug 2012 23:47:54 +0200 Subject: [PATCH 01/31] NFC: Queue pn533 commands Instead of returning EBUSY when getting a command while another one is running, we queue them. Upon completion of the pending command, the next one is processed. Besides the fact that it simplifies the pn533 locking scheme, it also comes with the nice side effect of fixing the following warning: [ 82.274297] ===================================== [ 82.274297] [ BUG: bad unlock balance detected! ] [ 82.274298] 3.5.0-rc1+ #1 Not tainted [ 82.274299] ------------------------------------- [ 82.274300] kworker/u:1/16 is trying to release lock (&dev->cmd_lock) at: [ 82.274305] [] mutex_unlock+0x9/0xb [ 82.274305] but there are no more locks to release! [ 82.274306] [ 82.274306] other info that might help us debug this: [ 82.274306] 2 locks held by kworker/u:1/16: [ 82.274311] #0: (pn533){.+.+..}, at: [] +process_one_work+0x145/0x2e2 [ 82.274314] #1: ((&dev->cmd_work)){+.+...}, at: [] +process_one_work+0x145/0x2e2 [ 82.274314] [ 82.274314] stack backtrace: [ 82.274315] Pid: 16, comm: kworker/u:1 Not tainted 3.5.0-rc1+ #1 [ 82.274315] Call Trace: [ 82.274317] [] ? mutex_unlock+0x9/0xb [ 82.274321] [] print_unlock_inbalance_bug+0xda/0xe4 [ 82.274323] [] lock_release_non_nested+0xb2/0x232 [ 82.274325] [] ? mark_held_locks+0x6d/0x95 [ 82.274326] [] ? mutex_unlock+0x9/0xb [ 82.274328] [] ? _raw_spin_unlock_irqrestore+0x40/0x5c [ 82.274329] [] ? mutex_unlock+0x9/0xb [ 82.274330] [] lock_release+0x176/0x1ac [ 82.274333] [] ? pn533_send_complete+0xa8/0xa8 [ 82.274334] [] __mutex_unlock_slowpath+0xb0/0x117 [ 82.274336] [] mutex_unlock+0x9/0xb [ 82.274337] [] pn533_wq_cmd_complete+0x51/0x55 [ 82.274338] [] process_one_work+0x1a3/0x2e2 [ 82.274340] [] ? process_one_work+0x145/0x2e2 [ 82.274341] [] worker_thread+0xcf/0x153 [ 82.274343] [] ? manage_workers.isra.22+0x16b/0x16b [ 82.274344] [] ? manage_workers.isra.22+0x16b/0x16b [ 82.274346] [] kthread+0x95/0x9d [ 82.274348] [] kernel_thread_helper+0x4/0x10 [ 82.274351] [] ? finish_task_switch+0x45/0xc3 [ 82.274352] [] ? retint_restore_args+0x13/0x13 [ 82.274353] [] ? __init_kthread_worker+0x55/0x55 [ 82.274354] [] ? gs_change+0x13/0x13 Signed-off-by: Samuel Ortiz --- drivers/nfc/pn533.c | 102 ++++++++++++++++++++++++++++++++++++-------- 1 file changed, 85 insertions(+), 17 deletions(-) diff --git a/drivers/nfc/pn533.c b/drivers/nfc/pn533.c index d606f52fec84..7ceea111439b 100644 --- a/drivers/nfc/pn533.c +++ b/drivers/nfc/pn533.c @@ -356,6 +356,7 @@ struct pn533 { struct workqueue_struct *wq; struct work_struct cmd_work; + struct work_struct cmd_complete_work; struct work_struct poll_work; struct work_struct mi_work; struct work_struct tg_work; @@ -383,6 +384,19 @@ struct pn533 { u8 tgt_mode; u32 device_type; + + struct list_head cmd_queue; + u8 cmd_pending; +}; + +struct pn533_cmd { + struct list_head queue; + struct pn533_frame *out_frame; + struct pn533_frame *in_frame; + int in_frame_len; + pn533_cmd_complete_t cmd_complete; + void *arg; + gfp_t flags; }; struct pn533_frame { @@ -487,7 +501,7 @@ static bool pn533_rx_frame_is_cmd_response(struct pn533_frame *frame, u8 cmd) static void pn533_wq_cmd_complete(struct work_struct *work) { - struct pn533 *dev = container_of(work, struct pn533, cmd_work); + struct pn533 *dev = container_of(work, struct pn533, cmd_complete_work); struct pn533_frame *in_frame; int rc; @@ -502,7 +516,7 @@ static void pn533_wq_cmd_complete(struct work_struct *work) PN533_FRAME_CMD_PARAMS_LEN(in_frame)); if (rc != -EINPROGRESS) - mutex_unlock(&dev->cmd_lock); + queue_work(dev->wq, &dev->cmd_work); } static void pn533_recv_response(struct urb *urb) @@ -550,7 +564,7 @@ static void pn533_recv_response(struct urb *urb) dev->wq_in_frame = in_frame; sched_wq: - queue_work(dev->wq, &dev->cmd_work); + queue_work(dev->wq, &dev->cmd_complete_work); } static int pn533_submit_urb_for_response(struct pn533 *dev, gfp_t flags) @@ -606,7 +620,7 @@ static void pn533_recv_ack(struct urb *urb) sched_wq: dev->wq_in_frame = NULL; - queue_work(dev->wq, &dev->cmd_work); + queue_work(dev->wq, &dev->cmd_complete_work); } static int pn533_submit_urb_for_ack(struct pn533 *dev, gfp_t flags) @@ -669,6 +683,31 @@ error: return rc; } +static void pn533_wq_cmd(struct work_struct *work) +{ + struct pn533 *dev = container_of(work, struct pn533, cmd_work); + struct pn533_cmd *cmd; + + mutex_lock(&dev->cmd_lock); + + if (list_empty(&dev->cmd_queue)) { + dev->cmd_pending = 0; + mutex_unlock(&dev->cmd_lock); + return; + } + + cmd = list_first_entry(&dev->cmd_queue, struct pn533_cmd, queue); + + mutex_unlock(&dev->cmd_lock); + + __pn533_send_cmd_frame_async(dev, cmd->out_frame, cmd->in_frame, + cmd->in_frame_len, cmd->cmd_complete, + cmd->arg, cmd->flags); + + list_del(&cmd->queue); + kfree(cmd); +} + static int pn533_send_cmd_frame_async(struct pn533 *dev, struct pn533_frame *out_frame, struct pn533_frame *in_frame, @@ -676,22 +715,44 @@ static int pn533_send_cmd_frame_async(struct pn533 *dev, pn533_cmd_complete_t cmd_complete, void *arg, gfp_t flags) { + struct pn533_cmd *cmd; int rc; nfc_dev_dbg(&dev->interface->dev, "%s", __func__); - if (!mutex_trylock(&dev->cmd_lock)) - return -EBUSY; + mutex_lock(&dev->cmd_lock); - rc = __pn533_send_cmd_frame_async(dev, out_frame, in_frame, - in_frame_len, cmd_complete, arg, flags); - if (rc) - goto error; + if (!dev->cmd_pending) { + rc = __pn533_send_cmd_frame_async(dev, out_frame, in_frame, + in_frame_len, cmd_complete, + arg, flags); + if (!rc) + dev->cmd_pending = 1; + + mutex_unlock(&dev->cmd_lock); + + return rc; + } + + nfc_dev_dbg(&dev->interface->dev, "%s Queueing command", __func__); + + cmd = kzalloc(sizeof(struct pn533_cmd), flags); + if (!cmd) + return -ENOMEM; + + INIT_LIST_HEAD(&cmd->queue); + cmd->out_frame = out_frame; + cmd->in_frame = in_frame; + cmd->in_frame_len = in_frame_len; + cmd->cmd_complete = cmd_complete; + cmd->arg = arg; + cmd->flags = flags; + + list_add_tail(&cmd->queue, &dev->cmd_queue); + + mutex_unlock(&dev->cmd_lock); return 0; -error: - mutex_unlock(&dev->cmd_lock); - return rc; } struct pn533_sync_cmd_response { @@ -1305,8 +1366,6 @@ static void pn533_listen_mode_timer(unsigned long data) dev->cancel_listen = 1; - mutex_unlock(&dev->cmd_lock); - pn533_poll_next_mod(dev); queue_work(dev->wq, &dev->poll_work); @@ -2131,7 +2190,7 @@ error_cmd: kfree(arg); - mutex_unlock(&dev->cmd_lock); + queue_work(dev->wq, &dev->cmd_work); } static int pn533_set_configuration(struct pn533 *dev, u8 cfgitem, u8 *cfgdata, @@ -2330,7 +2389,8 @@ static int pn533_probe(struct usb_interface *interface, NULL, 0, pn533_send_complete, dev); - INIT_WORK(&dev->cmd_work, pn533_wq_cmd_complete); + INIT_WORK(&dev->cmd_work, pn533_wq_cmd); + INIT_WORK(&dev->cmd_complete_work, pn533_wq_cmd_complete); INIT_WORK(&dev->mi_work, pn533_wq_mi_recv); INIT_WORK(&dev->tg_work, pn533_wq_tg_get_data); INIT_WORK(&dev->poll_work, pn533_wq_poll); @@ -2346,6 +2406,8 @@ static int pn533_probe(struct usb_interface *interface, skb_queue_head_init(&dev->resp_q); + INIT_LIST_HEAD(&dev->cmd_queue); + usb_set_intfdata(interface, dev); pn533_tx_frame_init(dev->out_frame, PN533_CMD_GET_FIRMWARE_VERSION); @@ -2417,6 +2479,7 @@ error: static void pn533_disconnect(struct usb_interface *interface) { struct pn533 *dev; + struct pn533_cmd *cmd, *n; dev = usb_get_intfdata(interface); usb_set_intfdata(interface, NULL); @@ -2433,6 +2496,11 @@ static void pn533_disconnect(struct usb_interface *interface) del_timer(&dev->listen_timer); + list_for_each_entry_safe(cmd, n, &dev->cmd_queue, queue) { + list_del(&cmd->queue); + kfree(cmd); + } + kfree(dev->in_frame); usb_free_urb(dev->in_urb); kfree(dev->out_frame); From 7e0352306f6869b442a574a8e691f126c9fe930a Mon Sep 17 00:00:00 2001 From: Ilan Elias Date: Wed, 15 Aug 2012 11:46:22 +0300 Subject: [PATCH 02/31] NFC: Set local general bytes in nci_start_poll If initiator protocol is NFC-DEP, set the local general bytes in nci_start_poll. Signed-off-by: Ilan Elias Signed-off-by: Samuel Ortiz --- include/net/nfc/nci.h | 23 ++++++++++++++++ include/net/nfc/nci_core.h | 1 + net/nfc/nci/core.c | 55 ++++++++++++++++++++++++++++++++++++++ net/nfc/nci/rsp.c | 14 ++++++++++ 4 files changed, 93 insertions(+) diff --git a/include/net/nfc/nci.h b/include/net/nfc/nci.h index 276094b91d7c..d260ef3629db 100644 --- a/include/net/nfc/nci.h +++ b/include/net/nfc/nci.h @@ -32,6 +32,7 @@ #define NCI_MAX_NUM_MAPPING_CONFIGS 10 #define NCI_MAX_NUM_RF_CONFIGS 10 #define NCI_MAX_NUM_CONN 10 +#define NCI_MAX_PARAM_LEN 251 /* NCI Status Codes */ #define NCI_STATUS_OK 0x00 @@ -102,6 +103,9 @@ #define NCI_RF_INTERFACE_ISO_DEP 0x02 #define NCI_RF_INTERFACE_NFC_DEP 0x03 +/* NCI Configuration Parameter Tags */ +#define NCI_PN_ATR_REQ_GEN_BYTES 0x29 + /* NCI Reset types */ #define NCI_RESET_TYPE_KEEP_CONFIG 0x00 #define NCI_RESET_TYPE_RESET_CONFIG 0x01 @@ -188,6 +192,18 @@ struct nci_core_reset_cmd { #define NCI_OP_CORE_INIT_CMD nci_opcode_pack(NCI_GID_CORE, 0x01) +#define NCI_OP_CORE_SET_CONFIG_CMD nci_opcode_pack(NCI_GID_CORE, 0x02) +struct set_config_param { + __u8 id; + __u8 len; + __u8 val[NCI_MAX_PARAM_LEN]; +} __packed; + +struct nci_core_set_config_cmd { + __u8 num_params; + struct set_config_param param; /* support 1 param per cmd is enough */ +} __packed; + #define NCI_OP_RF_DISCOVER_MAP_CMD nci_opcode_pack(NCI_GID_RF_MGMT, 0x00) struct disc_map_config { __u8 rf_protocol; @@ -252,6 +268,13 @@ struct nci_core_init_rsp_2 { __le32 manufact_specific_info; } __packed; +#define NCI_OP_CORE_SET_CONFIG_RSP nci_opcode_pack(NCI_GID_CORE, 0x02) +struct nci_core_set_config_rsp { + __u8 status; + __u8 num_params; + __u8 params_id[0]; /* variable size array */ +} __packed; + #define NCI_OP_RF_DISCOVER_MAP_RSP nci_opcode_pack(NCI_GID_RF_MGMT, 0x00) #define NCI_OP_RF_DISCOVER_RSP nci_opcode_pack(NCI_GID_RF_MGMT, 0x03) diff --git a/include/net/nfc/nci_core.h b/include/net/nfc/nci_core.h index feba74027ff8..f98674d7baf1 100644 --- a/include/net/nfc/nci_core.h +++ b/include/net/nfc/nci_core.h @@ -54,6 +54,7 @@ enum nci_state { /* NCI timeouts */ #define NCI_RESET_TIMEOUT 5000 #define NCI_INIT_TIMEOUT 5000 +#define NCI_SET_CONFIG_TIMEOUT 5000 #define NCI_RF_DISC_TIMEOUT 5000 #define NCI_RF_DISC_SELECT_TIMEOUT 5000 #define NCI_RF_DEACTIVATE_TIMEOUT 30000 diff --git a/net/nfc/nci/core.c b/net/nfc/nci/core.c index f81efe13985a..f017b781667a 100644 --- a/net/nfc/nci/core.c +++ b/net/nfc/nci/core.c @@ -176,6 +176,27 @@ static void nci_init_complete_req(struct nci_dev *ndev, unsigned long opt) (1 + ((*num) * sizeof(struct disc_map_config))), &cmd); } +struct nci_set_config_param { + __u8 id; + size_t len; + __u8 *val; +}; + +static void nci_set_config_req(struct nci_dev *ndev, unsigned long opt) +{ + struct nci_set_config_param *param = (struct nci_set_config_param *)opt; + struct nci_core_set_config_cmd cmd; + + BUG_ON(param->len > NCI_MAX_PARAM_LEN); + + cmd.num_params = 1; + cmd.param.id = param->id; + cmd.param.len = param->len; + memcpy(cmd.param.val, param->val, param->len); + + nci_send_cmd(ndev, NCI_OP_CORE_SET_CONFIG_CMD, (3 + param->len), &cmd); +} + static void nci_rf_discover_req(struct nci_dev *ndev, unsigned long opt) { struct nci_rf_disc_cmd cmd; @@ -388,6 +409,32 @@ static int nci_dev_down(struct nfc_dev *nfc_dev) return nci_close_device(ndev); } +static int nci_set_local_general_bytes(struct nfc_dev *nfc_dev) +{ + struct nci_dev *ndev = nfc_get_drvdata(nfc_dev); + struct nci_set_config_param param; + __u8 local_gb[NFC_MAX_GT_LEN]; + int i, rc = 0; + + param.val = nfc_get_local_general_bytes(nfc_dev, ¶m.len); + if ((param.val == NULL) || (param.len == 0)) + return rc; + + if (param.len > NCI_MAX_PARAM_LEN) + return -EINVAL; + + for (i = 0; i < param.len; i++) + local_gb[param.len-1-i] = param.val[i]; + + param.id = NCI_PN_ATR_REQ_GEN_BYTES; + param.val = local_gb; + + rc = nci_request(ndev, nci_set_config_req, (unsigned long)¶m, + msecs_to_jiffies(NCI_SET_CONFIG_TIMEOUT)); + + return rc; +} + static int nci_start_poll(struct nfc_dev *nfc_dev, __u32 im_protocols, __u32 tm_protocols) { @@ -415,6 +462,14 @@ static int nci_start_poll(struct nfc_dev *nfc_dev, return -EBUSY; } + if (im_protocols & NFC_PROTO_NFC_DEP_MASK) { + rc = nci_set_local_general_bytes(nfc_dev); + if (rc) { + pr_err("failed to set local general bytes\n"); + return rc; + } + } + rc = nci_request(ndev, nci_rf_discover_req, im_protocols, msecs_to_jiffies(NCI_RF_DISC_TIMEOUT)); diff --git a/net/nfc/nci/rsp.c b/net/nfc/nci/rsp.c index 3003c3390e49..dd072f38ad00 100644 --- a/net/nfc/nci/rsp.c +++ b/net/nfc/nci/rsp.c @@ -119,6 +119,16 @@ exit: nci_req_complete(ndev, rsp_1->status); } +static void nci_core_set_config_rsp_packet(struct nci_dev *ndev, + struct sk_buff *skb) +{ + struct nci_core_set_config_rsp *rsp = (void *) skb->data; + + pr_debug("status 0x%x\n", rsp->status); + + nci_req_complete(ndev, rsp->status); +} + static void nci_rf_disc_map_rsp_packet(struct nci_dev *ndev, struct sk_buff *skb) { @@ -194,6 +204,10 @@ void nci_rsp_packet(struct nci_dev *ndev, struct sk_buff *skb) nci_core_init_rsp_packet(ndev, skb); break; + case NCI_OP_CORE_SET_CONFIG_RSP: + nci_core_set_config_rsp_packet(ndev, skb); + break; + case NCI_OP_RF_DISCOVER_MAP_RSP: nci_rf_disc_map_rsp_packet(ndev, skb); break; From ac206838403411e617dbe0e7df1891ee957f1f9a Mon Sep 17 00:00:00 2001 From: Ilan Elias Date: Wed, 15 Aug 2012 11:46:23 +0300 Subject: [PATCH 03/31] NFC: Parse NCI NFC-DEP activation params Signed-off-by: Ilan Elias Signed-off-by: Samuel Ortiz --- include/net/nfc/nci.h | 6 ++++++ net/nfc/nci/ntf.c | 32 ++++++++++++++++++++++++++++++++ 2 files changed, 38 insertions(+) diff --git a/include/net/nfc/nci.h b/include/net/nfc/nci.h index d260ef3629db..88785e5c6b2c 100644 --- a/include/net/nfc/nci.h +++ b/include/net/nfc/nci.h @@ -351,6 +351,11 @@ struct activation_params_nfcb_poll_iso_dep { __u8 attrib_res[50]; }; +struct activation_params_poll_nfc_dep { + __u8 atr_res_len; + __u8 atr_res[63]; +}; + struct nci_rf_intf_activated_ntf { __u8 rf_discovery_id; __u8 rf_interface; @@ -374,6 +379,7 @@ struct nci_rf_intf_activated_ntf { union { struct activation_params_nfca_poll_iso_dep nfca_poll_iso_dep; struct activation_params_nfcb_poll_iso_dep nfcb_poll_iso_dep; + struct activation_params_poll_nfc_dep poll_nfc_dep; } activation_params; } __packed; diff --git a/net/nfc/nci/ntf.c b/net/nfc/nci/ntf.c index af7a93b04393..6e17661a41a4 100644 --- a/net/nfc/nci/ntf.c +++ b/net/nfc/nci/ntf.c @@ -361,6 +361,33 @@ static int nci_extract_activation_params_iso_dep(struct nci_dev *ndev, return NCI_STATUS_OK; } +static int nci_extract_activation_params_nfc_dep(struct nci_dev *ndev, + struct nci_rf_intf_activated_ntf *ntf, __u8 *data) +{ + struct activation_params_poll_nfc_dep *poll; + int i; + + switch (ntf->activation_rf_tech_and_mode) { + case NCI_NFC_A_PASSIVE_POLL_MODE: + case NCI_NFC_F_PASSIVE_POLL_MODE: + poll = &ntf->activation_params.poll_nfc_dep; + poll->atr_res_len = min_t(__u8, *data++, 63); + pr_debug("atr_res_len %d\n", poll->atr_res_len); + if (poll->atr_res_len > 0) { + for (i = 0; i < poll->atr_res_len; i++) + poll->atr_res[poll->atr_res_len-1-i] = data[i]; + } + break; + + default: + pr_err("unsupported activation_rf_tech_and_mode 0x%x\n", + ntf->activation_rf_tech_and_mode); + return NCI_STATUS_RF_PROTOCOL_ERROR; + } + + return NCI_STATUS_OK; +} + static void nci_target_auto_activated(struct nci_dev *ndev, struct nci_rf_intf_activated_ntf *ntf) { @@ -454,6 +481,11 @@ static void nci_rf_intf_activated_ntf_packet(struct nci_dev *ndev, &ntf, data); break; + case NCI_RF_INTERFACE_NFC_DEP: + err = nci_extract_activation_params_nfc_dep(ndev, + &ntf, data); + break; + case NCI_RF_INTERFACE_FRAME: /* no activation params */ break; From 767f19ae698e535f308663c48245fa951abebe20 Mon Sep 17 00:00:00 2001 From: Ilan Elias Date: Wed, 15 Aug 2012 11:46:24 +0300 Subject: [PATCH 04/31] NFC: Implement NCI dep_link_up and dep_link_down During NFC-DEP target activation, store the remote general bytes to be used later in dep_link_up. When dep_link_up is called, activate the NFC-DEP target, and forward the remote general bytes. When dep_link_down is called, deactivate the target. Signed-off-by: Ilan Elias Signed-off-by: Samuel Ortiz --- include/net/nfc/nci_core.h | 4 ++++ include/net/nfc/nfc.h | 1 + net/nfc/nci/core.c | 36 +++++++++++++++++++++++++++++++++++- net/nfc/nci/ntf.c | 20 ++++++++++++++++++++ 4 files changed, 60 insertions(+), 1 deletion(-) diff --git a/include/net/nfc/nci_core.h b/include/net/nfc/nci_core.h index f98674d7baf1..d705d8674949 100644 --- a/include/net/nfc/nci_core.h +++ b/include/net/nfc/nci_core.h @@ -138,6 +138,10 @@ struct nci_dev { data_exchange_cb_t data_exchange_cb; void *data_exchange_cb_context; struct sk_buff *rx_data_reassembly; + + /* stored during intf_activated_ntf */ + __u8 remote_gb[NFC_MAX_GT_LEN]; + __u8 remote_gb_len; }; /* ----- NCI Devices ----- */ diff --git a/include/net/nfc/nfc.h b/include/net/nfc/nfc.h index 6431f5e39022..7b9f71fa0ea7 100644 --- a/include/net/nfc/nfc.h +++ b/include/net/nfc/nfc.h @@ -72,6 +72,7 @@ struct nfc_ops { #define NFC_TARGET_IDX_ANY -1 #define NFC_MAX_GT_LEN 48 +#define NFC_ATR_RES_GT_OFFSET 15 struct nfc_target { u32 idx; diff --git a/net/nfc/nci/core.c b/net/nfc/nci/core.c index f017b781667a..acf9abb7d99b 100644 --- a/net/nfc/nci/core.c +++ b/net/nfc/nci/core.c @@ -564,7 +564,7 @@ static void nci_deactivate_target(struct nfc_dev *nfc_dev, { struct nci_dev *ndev = nfc_get_drvdata(nfc_dev); - pr_debug("target_idx %d\n", target->idx); + pr_debug("entry\n"); if (!ndev->target_active_prot) { pr_err("unable to deactivate target, no active target\n"); @@ -579,6 +579,38 @@ static void nci_deactivate_target(struct nfc_dev *nfc_dev, } } + +static int nci_dep_link_up(struct nfc_dev *nfc_dev, struct nfc_target *target, + __u8 comm_mode, __u8 *gb, size_t gb_len) +{ + struct nci_dev *ndev = nfc_get_drvdata(nfc_dev); + int rc; + + pr_debug("target_idx %d, comm_mode %d\n", target->idx, comm_mode); + + rc = nci_activate_target(nfc_dev, target, NFC_PROTO_NFC_DEP); + if (rc) + return rc; + + rc = nfc_set_remote_general_bytes(nfc_dev, ndev->remote_gb, + ndev->remote_gb_len); + if (!rc) + rc = nfc_dep_link_is_up(nfc_dev, target->idx, NFC_COMM_PASSIVE, + NFC_RF_INITIATOR); + + return rc; +} + +static int nci_dep_link_down(struct nfc_dev *nfc_dev) +{ + pr_debug("entry\n"); + + nci_deactivate_target(nfc_dev, NULL); + + return 0; +} + + static int nci_transceive(struct nfc_dev *nfc_dev, struct nfc_target *target, struct sk_buff *skb, data_exchange_cb_t cb, void *cb_context) @@ -612,6 +644,8 @@ static struct nfc_ops nci_nfc_ops = { .dev_down = nci_dev_down, .start_poll = nci_start_poll, .stop_poll = nci_stop_poll, + .dep_link_up = nci_dep_link_up, + .dep_link_down = nci_dep_link_down, .activate_target = nci_activate_target, .deactivate_target = nci_deactivate_target, .im_transceive = nci_transceive, diff --git a/net/nfc/nci/ntf.c b/net/nfc/nci/ntf.c index 6e17661a41a4..b2aa98ef0927 100644 --- a/net/nfc/nci/ntf.c +++ b/net/nfc/nci/ntf.c @@ -176,6 +176,8 @@ static int nci_add_new_protocol(struct nci_dev *ndev, protocol = NFC_PROTO_ISO14443_B_MASK; else if (rf_protocol == NCI_RF_PROTOCOL_T3T) protocol = NFC_PROTO_FELICA_MASK; + else if (rf_protocol == NCI_RF_PROTOCOL_NFC_DEP) + protocol = NFC_PROTO_NFC_DEP_MASK; else protocol = 0; @@ -505,6 +507,24 @@ exit: /* set the available credits to initial value */ atomic_set(&ndev->credits_cnt, ndev->initial_num_credits); + + /* store general bytes to be reported later in dep_link_up */ + if (ntf.rf_interface == NCI_RF_INTERFACE_NFC_DEP) { + ndev->remote_gb_len = 0; + + if (ntf.activation_params_len > 0) { + /* ATR_RES general bytes at offset 15 */ + ndev->remote_gb_len = min_t(__u8, + (ntf.activation_params + .poll_nfc_dep.atr_res_len + - NFC_ATR_RES_GT_OFFSET), + NFC_MAX_GT_LEN); + memcpy(ndev->remote_gb, + (ntf.activation_params.poll_nfc_dep + .atr_res + NFC_ATR_RES_GT_OFFSET), + ndev->remote_gb_len); + } + } } if (atomic_read(&ndev->state) == NCI_DISCOVERY) { From 058576ddfb31762bfce70086a3c12678a97c4d75 Mon Sep 17 00:00:00 2001 From: Syam Sidhardhan Date: Wed, 15 Aug 2012 14:04:10 +0530 Subject: [PATCH 05/31] NFC: Use module_platform_driver macro for nfcwilink.c Simplify the code by make use of module_platform_driver macro. Signed-off-by: Syam Sidhardhan Signed-off-by: Samuel Ortiz --- drivers/nfc/nfcwilink.c | 16 +--------------- 1 file changed, 1 insertion(+), 15 deletions(-) diff --git a/drivers/nfc/nfcwilink.c b/drivers/nfc/nfcwilink.c index e7fd4938f9bc..827f039ceaf5 100644 --- a/drivers/nfc/nfcwilink.c +++ b/drivers/nfc/nfcwilink.c @@ -604,21 +604,7 @@ static struct platform_driver nfcwilink_driver = { }, }; -/* ------- Module Init/Exit interfaces ------ */ -static int __init nfcwilink_init(void) -{ - printk(KERN_INFO "NFC Driver for TI WiLink"); - - return platform_driver_register(&nfcwilink_driver); -} - -static void __exit nfcwilink_exit(void) -{ - platform_driver_unregister(&nfcwilink_driver); -} - -module_init(nfcwilink_init); -module_exit(nfcwilink_exit); +module_platform_driver(nfcwilink_driver); /* ------ Module Info ------ */ From 5db327f96daa2401b9afec6cd80cebe6c6475bb1 Mon Sep 17 00:00:00 2001 From: Syam Sidhardhan Date: Thu, 16 Aug 2012 00:55:43 +0530 Subject: [PATCH 06/31] NFC: Remove repeated code for NULL check This patch remove the repeated code for checking llcp_sock & llcp_sock->dev against NULL. Signed-off-by: Syam Sidhardhan Signed-off-by: Samuel Ortiz --- net/nfc/llcp/sock.c | 3 --- 1 file changed, 3 deletions(-) diff --git a/net/nfc/llcp/sock.c b/net/nfc/llcp/sock.c index ddeb9aa398f0..6e188d4020ba 100644 --- a/net/nfc/llcp/sock.c +++ b/net/nfc/llcp/sock.c @@ -300,9 +300,6 @@ static int llcp_sock_getname(struct socket *sock, struct sockaddr *uaddr, pr_debug("%p %d %d %d\n", sk, llcp_sock->target_idx, llcp_sock->dsap, llcp_sock->ssap); - if (llcp_sock == NULL || llcp_sock->dev == NULL) - return -EBADFD; - uaddr->sa_family = AF_NFC; *len = sizeof(struct sockaddr_nfc_llcp); From 474fee3db16c63bc440bfb93b57f72ecfc4246f0 Mon Sep 17 00:00:00 2001 From: Tejun Heo Date: Wed, 22 Aug 2012 16:22:16 -0700 Subject: [PATCH 07/31] NFC: Use system_nrt_wq instead of custom ones NFC is using a number of custom ordered workqueues w/ WQ_MEM_RECLAIM. WQ_MEM_RECLAIM is unnecessary unless NFC is gonna be used as transport for storage device, and all use cases match one work item to one ordered workqueue - IOW, there's no actual ordering going on at all and using system_nrt_wq gives the same behavior. There's nothing to be gained by using custom workqueues. Use system_nrt_wq instead and drop all the custom ones. Signed-off-by: Tejun Heo Signed-off-by: Samuel Ortiz --- include/net/nfc/hci.h | 2 -- include/net/nfc/nfc.h | 1 - include/net/nfc/shdlc.h | 1 - net/nfc/core.c | 13 ++-------- net/nfc/hci/core.c | 42 +++++------------------------- net/nfc/hci/hcp.c | 2 +- net/nfc/hci/shdlc.c | 27 +++++++------------ net/nfc/llcp/llcp.c | 57 ++++++----------------------------------- net/nfc/llcp/llcp.h | 3 --- 9 files changed, 26 insertions(+), 122 deletions(-) diff --git a/include/net/nfc/hci.h b/include/net/nfc/hci.h index f5169b04f082..6f065d563ec1 100644 --- a/include/net/nfc/hci.h +++ b/include/net/nfc/hci.h @@ -74,7 +74,6 @@ struct nfc_hci_dev { struct list_head msg_tx_queue; - struct workqueue_struct *msg_tx_wq; struct work_struct msg_tx_work; struct timer_list cmd_timer; @@ -82,7 +81,6 @@ struct nfc_hci_dev { struct sk_buff_head rx_hcp_frags; - struct workqueue_struct *msg_rx_wq; struct work_struct msg_rx_work; struct sk_buff_head msg_rx_queue; diff --git a/include/net/nfc/nfc.h b/include/net/nfc/nfc.h index 7b9f71fa0ea7..bfbac732d8c6 100644 --- a/include/net/nfc/nfc.h +++ b/include/net/nfc/nfc.h @@ -113,7 +113,6 @@ struct nfc_dev { int tx_tailroom; struct timer_list check_pres_timer; - struct workqueue_struct *check_pres_wq; struct work_struct check_pres_work; struct nfc_ops *ops; diff --git a/include/net/nfc/shdlc.h b/include/net/nfc/shdlc.h index 35e930d2f638..342427362989 100644 --- a/include/net/nfc/shdlc.h +++ b/include/net/nfc/shdlc.h @@ -79,7 +79,6 @@ struct nfc_shdlc { struct sk_buff_head ack_pending_q; - struct workqueue_struct *sm_wq; struct work_struct sm_work; struct nfc_shdlc_ops *ops; diff --git a/net/nfc/core.c b/net/nfc/core.c index ff749794bc5b..c9eacc1f145f 100644 --- a/net/nfc/core.c +++ b/net/nfc/core.c @@ -679,7 +679,7 @@ static void nfc_release(struct device *d) if (dev->ops->check_presence) { del_timer_sync(&dev->check_pres_timer); - destroy_workqueue(dev->check_pres_wq); + cancel_work_sync(&dev->check_pres_work); } nfc_genl_data_exit(&dev->genl_data); @@ -715,7 +715,7 @@ static void nfc_check_pres_timeout(unsigned long data) { struct nfc_dev *dev = (struct nfc_dev *)data; - queue_work(dev->check_pres_wq, &dev->check_pres_work); + queue_work(system_nrt_wq, &dev->check_pres_work); } struct class nfc_class = { @@ -784,20 +784,11 @@ struct nfc_dev *nfc_allocate_device(struct nfc_ops *ops, dev->targets_generation = 1; if (ops->check_presence) { - char name[32]; init_timer(&dev->check_pres_timer); dev->check_pres_timer.data = (unsigned long)dev; dev->check_pres_timer.function = nfc_check_pres_timeout; INIT_WORK(&dev->check_pres_work, nfc_check_pres_work); - snprintf(name, sizeof(name), "nfc%d_check_pres_wq", dev->idx); - dev->check_pres_wq = alloc_workqueue(name, WQ_NON_REENTRANT | - WQ_UNBOUND | - WQ_MEM_RECLAIM, 1); - if (dev->check_pres_wq == NULL) { - kfree(dev); - return NULL; - } } return dev; diff --git a/net/nfc/hci/core.c b/net/nfc/hci/core.c index 1ac7b3fac6c9..03646beb3a73 100644 --- a/net/nfc/hci/core.c +++ b/net/nfc/hci/core.c @@ -141,7 +141,7 @@ static void __nfc_hci_cmd_completion(struct nfc_hci_dev *hdev, int err, kfree(hdev->cmd_pending_msg); hdev->cmd_pending_msg = NULL; - queue_work(hdev->msg_tx_wq, &hdev->msg_tx_work); + queue_work(system_nrt_wq, &hdev->msg_tx_work); } void nfc_hci_resp_received(struct nfc_hci_dev *hdev, u8 result, @@ -326,7 +326,7 @@ static void nfc_hci_cmd_timeout(unsigned long data) { struct nfc_hci_dev *hdev = (struct nfc_hci_dev *)data; - queue_work(hdev->msg_tx_wq, &hdev->msg_tx_work); + queue_work(system_nrt_wq, &hdev->msg_tx_work); } static int hci_dev_connect_gates(struct nfc_hci_dev *hdev, u8 gate_count, @@ -659,23 +659,11 @@ EXPORT_SYMBOL(nfc_hci_free_device); int nfc_hci_register_device(struct nfc_hci_dev *hdev) { - struct device *dev = &hdev->ndev->dev; - const char *devname = dev_name(dev); - char name[32]; - int r = 0; - mutex_init(&hdev->msg_tx_mutex); INIT_LIST_HEAD(&hdev->msg_tx_queue); INIT_WORK(&hdev->msg_tx_work, nfc_hci_msg_tx_work); - snprintf(name, sizeof(name), "%s_hci_msg_tx_wq", devname); - hdev->msg_tx_wq = alloc_workqueue(name, WQ_NON_REENTRANT | WQ_UNBOUND | - WQ_MEM_RECLAIM, 1); - if (hdev->msg_tx_wq == NULL) { - r = -ENOMEM; - goto exit; - } init_timer(&hdev->cmd_timer); hdev->cmd_timer.data = (unsigned long)hdev; @@ -684,27 +672,10 @@ int nfc_hci_register_device(struct nfc_hci_dev *hdev) skb_queue_head_init(&hdev->rx_hcp_frags); INIT_WORK(&hdev->msg_rx_work, nfc_hci_msg_rx_work); - snprintf(name, sizeof(name), "%s_hci_msg_rx_wq", devname); - hdev->msg_rx_wq = alloc_workqueue(name, WQ_NON_REENTRANT | WQ_UNBOUND | - WQ_MEM_RECLAIM, 1); - if (hdev->msg_rx_wq == NULL) { - r = -ENOMEM; - goto exit; - } skb_queue_head_init(&hdev->msg_rx_queue); - r = nfc_register_device(hdev->ndev); - -exit: - if (r < 0) { - if (hdev->msg_tx_wq) - destroy_workqueue(hdev->msg_tx_wq); - if (hdev->msg_rx_wq) - destroy_workqueue(hdev->msg_rx_wq); - } - - return r; + return nfc_register_device(hdev->ndev); } EXPORT_SYMBOL(nfc_hci_register_device); @@ -725,9 +696,8 @@ void nfc_hci_unregister_device(struct nfc_hci_dev *hdev) nfc_unregister_device(hdev->ndev); - destroy_workqueue(hdev->msg_tx_wq); - - destroy_workqueue(hdev->msg_rx_wq); + cancel_work_sync(&hdev->msg_tx_work); + cancel_work_sync(&hdev->msg_rx_work); } EXPORT_SYMBOL(nfc_hci_unregister_device); @@ -827,7 +797,7 @@ void nfc_hci_recv_frame(struct nfc_hci_dev *hdev, struct sk_buff *skb) nfc_hci_hcp_message_rx(hdev, pipe, type, instruction, hcp_skb); } else { skb_queue_tail(&hdev->msg_rx_queue, hcp_skb); - queue_work(hdev->msg_rx_wq, &hdev->msg_rx_work); + queue_work(system_nrt_wq, &hdev->msg_rx_work); } } EXPORT_SYMBOL(nfc_hci_recv_frame); diff --git a/net/nfc/hci/hcp.c b/net/nfc/hci/hcp.c index f4dad1a89740..2372b558abe9 100644 --- a/net/nfc/hci/hcp.c +++ b/net/nfc/hci/hcp.c @@ -108,7 +108,7 @@ int nfc_hci_hcp_message_tx(struct nfc_hci_dev *hdev, u8 pipe, list_add_tail(&cmd->msg_l, &hdev->msg_tx_queue); mutex_unlock(&hdev->msg_tx_mutex); - queue_work(hdev->msg_tx_wq, &hdev->msg_tx_work); + queue_work(system_nrt_wq, &hdev->msg_tx_work); return 0; diff --git a/net/nfc/hci/shdlc.c b/net/nfc/hci/shdlc.c index 6f840c18c892..39b51eacc391 100644 --- a/net/nfc/hci/shdlc.c +++ b/net/nfc/hci/shdlc.c @@ -540,7 +540,7 @@ static void nfc_shdlc_connect_timeout(unsigned long data) pr_debug("\n"); - queue_work(shdlc->sm_wq, &shdlc->sm_work); + queue_work(system_nrt_wq, &shdlc->sm_work); } static void nfc_shdlc_t1_timeout(unsigned long data) @@ -549,7 +549,7 @@ static void nfc_shdlc_t1_timeout(unsigned long data) pr_debug("SoftIRQ: need to send ack\n"); - queue_work(shdlc->sm_wq, &shdlc->sm_work); + queue_work(system_nrt_wq, &shdlc->sm_work); } static void nfc_shdlc_t2_timeout(unsigned long data) @@ -558,7 +558,7 @@ static void nfc_shdlc_t2_timeout(unsigned long data) pr_debug("SoftIRQ: need to retransmit\n"); - queue_work(shdlc->sm_wq, &shdlc->sm_work); + queue_work(system_nrt_wq, &shdlc->sm_work); } static void nfc_shdlc_sm_work(struct work_struct *work) @@ -598,7 +598,7 @@ static void nfc_shdlc_sm_work(struct work_struct *work) case SHDLC_NEGOCIATING: if (timer_pending(&shdlc->connect_timer) == 0) { shdlc->state = SHDLC_CONNECTING; - queue_work(shdlc->sm_wq, &shdlc->sm_work); + queue_work(system_nrt_wq, &shdlc->sm_work); } nfc_shdlc_handle_rcv_queue(shdlc); @@ -662,7 +662,7 @@ static int nfc_shdlc_connect(struct nfc_shdlc *shdlc) mutex_unlock(&shdlc->state_mutex); - queue_work(shdlc->sm_wq, &shdlc->sm_work); + queue_work(system_nrt_wq, &shdlc->sm_work); wait_event(connect_wq, shdlc->connect_result != 1); @@ -679,7 +679,7 @@ static void nfc_shdlc_disconnect(struct nfc_shdlc *shdlc) mutex_unlock(&shdlc->state_mutex); - queue_work(shdlc->sm_wq, &shdlc->sm_work); + queue_work(system_nrt_wq, &shdlc->sm_work); } /* @@ -697,7 +697,7 @@ void nfc_shdlc_recv_frame(struct nfc_shdlc *shdlc, struct sk_buff *skb) skb_queue_tail(&shdlc->rcv_q, skb); } - queue_work(shdlc->sm_wq, &shdlc->sm_work); + queue_work(system_nrt_wq, &shdlc->sm_work); } EXPORT_SYMBOL(nfc_shdlc_recv_frame); @@ -754,7 +754,7 @@ static int nfc_shdlc_xmit(struct nfc_hci_dev *hdev, struct sk_buff *skb) skb_queue_tail(&shdlc->send_q, skb); - queue_work(shdlc->sm_wq, &shdlc->sm_work); + queue_work(system_nrt_wq, &shdlc->sm_work); return 0; } @@ -843,7 +843,6 @@ struct nfc_shdlc *nfc_shdlc_allocate(struct nfc_shdlc_ops *ops, { struct nfc_shdlc *shdlc; int r; - char name[32]; if (ops->xmit == NULL) return NULL; @@ -876,11 +875,6 @@ struct nfc_shdlc *nfc_shdlc_allocate(struct nfc_shdlc_ops *ops, skb_queue_head_init(&shdlc->ack_pending_q); INIT_WORK(&shdlc->sm_work, nfc_shdlc_sm_work); - snprintf(name, sizeof(name), "%s_shdlc_sm_wq", devname); - shdlc->sm_wq = alloc_workqueue(name, WQ_NON_REENTRANT | WQ_UNBOUND | - WQ_MEM_RECLAIM, 1); - if (shdlc->sm_wq == NULL) - goto err_allocwq; shdlc->client_headroom = tx_headroom; shdlc->client_tailroom = tx_tailroom; @@ -904,9 +898,6 @@ err_regdev: nfc_hci_free_device(shdlc->hdev); err_allocdev: - destroy_workqueue(shdlc->sm_wq); - -err_allocwq: kfree(shdlc); return NULL; @@ -920,7 +911,7 @@ void nfc_shdlc_free(struct nfc_shdlc *shdlc) nfc_hci_unregister_device(shdlc->hdev); nfc_hci_free_device(shdlc->hdev); - destroy_workqueue(shdlc->sm_wq); + cancel_work_sync(&shdlc->sm_work); skb_queue_purge(&shdlc->rcv_q); skb_queue_purge(&shdlc->send_q); diff --git a/net/nfc/llcp/llcp.c b/net/nfc/llcp/llcp.c index 82f0f7588b46..6f368412ffd2 100644 --- a/net/nfc/llcp/llcp.c +++ b/net/nfc/llcp/llcp.c @@ -114,9 +114,9 @@ static void local_release(struct kref *ref) nfc_llcp_socket_release(local, false); del_timer_sync(&local->link_timer); skb_queue_purge(&local->tx_queue); - destroy_workqueue(local->tx_wq); - destroy_workqueue(local->rx_wq); - destroy_workqueue(local->timeout_wq); + cancel_work_sync(&local->tx_work); + cancel_work_sync(&local->rx_work); + cancel_work_sync(&local->timeout_work); kfree_skb(local->rx_pending); kfree(local); } @@ -181,7 +181,7 @@ static void nfc_llcp_symm_timer(unsigned long data) pr_err("SYMM timeout\n"); - queue_work(local->timeout_wq, &local->timeout_work); + queue_work(system_nrt_wq, &local->timeout_work); } struct nfc_llcp_local *nfc_llcp_find_local(struct nfc_dev *dev) @@ -1052,7 +1052,7 @@ static void nfc_llcp_rx_work(struct work_struct *work) } - queue_work(local->tx_wq, &local->tx_work); + queue_work(system_nrt_wq, &local->tx_work); kfree_skb(local->rx_pending); local->rx_pending = NULL; @@ -1071,7 +1071,7 @@ void nfc_llcp_recv(void *data, struct sk_buff *skb, int err) local->rx_pending = skb_get(skb); del_timer(&local->link_timer); - queue_work(local->rx_wq, &local->rx_work); + queue_work(system_nrt_wq, &local->rx_work); return; } @@ -1086,7 +1086,7 @@ int nfc_llcp_data_received(struct nfc_dev *dev, struct sk_buff *skb) local->rx_pending = skb_get(skb); del_timer(&local->link_timer); - queue_work(local->rx_wq, &local->rx_work); + queue_work(system_nrt_wq, &local->rx_work); return 0; } @@ -1121,7 +1121,7 @@ void nfc_llcp_mac_is_up(struct nfc_dev *dev, u32 target_idx, if (rf_mode == NFC_RF_INITIATOR) { pr_debug("Queueing Tx work\n"); - queue_work(local->tx_wq, &local->tx_work); + queue_work(system_nrt_wq, &local->tx_work); } else { mod_timer(&local->link_timer, jiffies + msecs_to_jiffies(local->remote_lto)); @@ -1130,10 +1130,7 @@ void nfc_llcp_mac_is_up(struct nfc_dev *dev, u32 target_idx, int nfc_llcp_register_device(struct nfc_dev *ndev) { - struct device *dev = &ndev->dev; struct nfc_llcp_local *local; - char name[32]; - int err; local = kzalloc(sizeof(struct nfc_llcp_local), GFP_KERNEL); if (local == NULL) @@ -1149,38 +1146,11 @@ int nfc_llcp_register_device(struct nfc_dev *ndev) skb_queue_head_init(&local->tx_queue); INIT_WORK(&local->tx_work, nfc_llcp_tx_work); - snprintf(name, sizeof(name), "%s_llcp_tx_wq", dev_name(dev)); - local->tx_wq = - alloc_workqueue(name, - WQ_NON_REENTRANT | WQ_UNBOUND | WQ_MEM_RECLAIM, - 1); - if (local->tx_wq == NULL) { - err = -ENOMEM; - goto err_local; - } local->rx_pending = NULL; INIT_WORK(&local->rx_work, nfc_llcp_rx_work); - snprintf(name, sizeof(name), "%s_llcp_rx_wq", dev_name(dev)); - local->rx_wq = - alloc_workqueue(name, - WQ_NON_REENTRANT | WQ_UNBOUND | WQ_MEM_RECLAIM, - 1); - if (local->rx_wq == NULL) { - err = -ENOMEM; - goto err_tx_wq; - } INIT_WORK(&local->timeout_work, nfc_llcp_timeout_work); - snprintf(name, sizeof(name), "%s_llcp_timeout_wq", dev_name(dev)); - local->timeout_wq = - alloc_workqueue(name, - WQ_NON_REENTRANT | WQ_UNBOUND | WQ_MEM_RECLAIM, - 1); - if (local->timeout_wq == NULL) { - err = -ENOMEM; - goto err_rx_wq; - } local->sockets.lock = __RW_LOCK_UNLOCKED(local->sockets.lock); local->connecting_sockets.lock = __RW_LOCK_UNLOCKED(local->connecting_sockets.lock); @@ -1192,17 +1162,6 @@ int nfc_llcp_register_device(struct nfc_dev *ndev) list_add(&llcp_devices, &local->list); - return 0; - -err_rx_wq: - destroy_workqueue(local->rx_wq); - -err_tx_wq: - destroy_workqueue(local->tx_wq); - -err_local: - kfree(local); - return 0; } diff --git a/net/nfc/llcp/llcp.h b/net/nfc/llcp/llcp.h index 83b8bba5a280..af395c9ceb03 100644 --- a/net/nfc/llcp/llcp.h +++ b/net/nfc/llcp/llcp.h @@ -56,12 +56,9 @@ struct nfc_llcp_local { struct timer_list link_timer; struct sk_buff_head tx_queue; - struct workqueue_struct *tx_wq; struct work_struct tx_work; - struct workqueue_struct *rx_wq; struct work_struct rx_work; struct sk_buff *rx_pending; - struct workqueue_struct *timeout_wq; struct work_struct timeout_work; u32 target_idx; From 58637c9b65300cbdf9fb7c037978daadd136be86 Mon Sep 17 00:00:00 2001 From: Tejun Heo Date: Wed, 22 Aug 2012 16:28:46 -0700 Subject: [PATCH 08/31] NFC: Don't use WQ_MEM_RECLAIM for pn533 NFC driver doesn't sit in memory reclaim path and has no reason to use WQ_MEM_RECLAIM. Drop WQ_MEM_RECLAIM from pn533->wq and use alloc_ordered_workqueue() instead of WQ_UNBOUND w/ max_active == 1. Signed-off-by: Tejun Heo Signed-off-by: Samuel Ortiz --- drivers/nfc/pn533.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/drivers/nfc/pn533.c b/drivers/nfc/pn533.c index 7ceea111439b..d123444404c8 100644 --- a/drivers/nfc/pn533.c +++ b/drivers/nfc/pn533.c @@ -2394,9 +2394,7 @@ static int pn533_probe(struct usb_interface *interface, INIT_WORK(&dev->mi_work, pn533_wq_mi_recv); INIT_WORK(&dev->tg_work, pn533_wq_tg_get_data); INIT_WORK(&dev->poll_work, pn533_wq_poll); - dev->wq = alloc_workqueue("pn533", - WQ_NON_REENTRANT | WQ_UNBOUND | WQ_MEM_RECLAIM, - 1); + dev->wq = alloc_ordered_workqueue("pn533", 0); if (dev->wq == NULL) goto error; From f2ce39828abd556814366c2483191a7213c764f2 Mon Sep 17 00:00:00 2001 From: Samuel Ortiz Date: Sat, 25 Aug 2012 00:40:16 +0200 Subject: [PATCH 09/31] NFC: Set the IRQF_ONESHOT flag from the pn544_hci IRQ handler request As we don't have a primary handler but only a threaded one, __setup_irq() ends up failing if we don't set this flag. Signed-off-by: Samuel Ortiz --- drivers/nfc/pn544_hci.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/nfc/pn544_hci.c b/drivers/nfc/pn544_hci.c index aa71807189ba..9458d53cdb59 100644 --- a/drivers/nfc/pn544_hci.c +++ b/drivers/nfc/pn544_hci.c @@ -848,8 +848,8 @@ static int __devinit pn544_hci_probe(struct i2c_client *client, pn544_hci_platform_init(info); r = request_threaded_irq(client->irq, NULL, pn544_hci_irq_thread_fn, - IRQF_TRIGGER_RISING, PN544_HCI_DRIVER_NAME, - info); + IRQF_TRIGGER_RISING | IRQF_ONESHOT, + PN544_HCI_DRIVER_NAME, info); if (r < 0) { dev_err(&client->dev, "Unable to register IRQ handler\n"); goto err_rti; From 33e5971358c37851137b264f815977507c016fac Mon Sep 17 00:00:00 2001 From: Wei Yongjun Date: Tue, 28 Aug 2012 21:02:40 +0800 Subject: [PATCH 10/31] NFC: Remove pointless conditional before HCI kfree_skb() Signed-off-by: Wei Yongjun Signed-off-by: Samuel Ortiz --- net/nfc/hci/core.c | 3 +-- net/nfc/hci/shdlc.c | 3 +-- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/net/nfc/hci/core.c b/net/nfc/hci/core.c index 03646beb3a73..15744c01bddc 100644 --- a/net/nfc/hci/core.c +++ b/net/nfc/hci/core.c @@ -398,8 +398,7 @@ disconnect_all: nfc_hci_disconnect_all_gates(hdev); exit: - if (skb) - kfree_skb(skb); + kfree_skb(skb); return r; } diff --git a/net/nfc/hci/shdlc.c b/net/nfc/hci/shdlc.c index 39b51eacc391..824fb09384ed 100644 --- a/net/nfc/hci/shdlc.c +++ b/net/nfc/hci/shdlc.c @@ -241,8 +241,7 @@ static void nfc_shdlc_rcv_i_frame(struct nfc_shdlc *shdlc, } exit: - if (skb) - kfree_skb(skb); + kfree_skb(skb); } static void nfc_shdlc_rcv_ack(struct nfc_shdlc *shdlc, int y_nr) From 52da2449e10039d3bb04c598d24cb1a34530b716 Mon Sep 17 00:00:00 2001 From: Wei Yongjun Date: Sun, 2 Sep 2012 21:21:46 +0800 Subject: [PATCH 11/31] NFC: Fix possible LLCP memory leak nfc_llcp_build_tlv() malloced the memory and should be free in nfc_llcp_build_gb() after used, and the same in the error handling case, otherwise it will cause memory leak. spatch with a semantic match is used to found this problem. (http://coccinelle.lip6.fr/) Signed-off-by: Wei Yongjun Signed-off-by: Samuel Ortiz --- net/nfc/llcp/llcp.c | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/net/nfc/llcp/llcp.c b/net/nfc/llcp/llcp.c index 6f368412ffd2..90ef4a176819 100644 --- a/net/nfc/llcp/llcp.c +++ b/net/nfc/llcp/llcp.c @@ -426,6 +426,7 @@ static int nfc_llcp_build_gb(struct nfc_llcp_local *local) u8 *miux_tlv, miux_length; __be16 miux; u8 gb_len = 0; + int ret = 0; version = LLCP_VERSION_11; version_tlv = nfc_llcp_build_tlv(LLCP_TLV_VERSION, &version, @@ -450,8 +451,8 @@ static int nfc_llcp_build_gb(struct nfc_llcp_local *local) gb_len += ARRAY_SIZE(llcp_magic); if (gb_len > NFC_MAX_GT_LEN) { - kfree(version_tlv); - return -EINVAL; + ret = -EINVAL; + goto out; } gb_cur = local->gb; @@ -471,12 +472,15 @@ static int nfc_llcp_build_gb(struct nfc_llcp_local *local) memcpy(gb_cur, miux_tlv, miux_length); gb_cur += miux_length; - kfree(version_tlv); - kfree(lto_tlv); - local->gb_len = gb_len; - return 0; +out: + kfree(version_tlv); + kfree(lto_tlv); + kfree(wks_tlv); + kfree(miux_tlv); + + return ret; } u8 *nfc_llcp_general_bytes(struct nfc_dev *dev, size_t *general_bytes_len) From ade672082dd35aaaf7c8630d16c9f795c30459c4 Mon Sep 17 00:00:00 2001 From: Waldemar Rymarkiewicz Date: Fri, 7 Sep 2012 11:08:29 +0200 Subject: [PATCH 12/31] NFC: Remove crc generation from shdlc layer Checksum is specific for a chip spcification and it varies (in size and type) between different hardware. It should be handled in the driver then. Moreover, shdlc spec doesn't mention crc as a part of the frame. Update pn544_hci driver as well. Signed-off-by: Waldemar Rymarkiewicz Acked-by: Eric Lapuyade Signed-off-by: Samuel Ortiz --- drivers/nfc/pn544_hci.c | 32 ++++++++++++++++++++++++++++++-- net/nfc/hci/shdlc.c | 27 ++------------------------- 2 files changed, 32 insertions(+), 27 deletions(-) diff --git a/drivers/nfc/pn544_hci.c b/drivers/nfc/pn544_hci.c index 9458d53cdb59..d90aecfce739 100644 --- a/drivers/nfc/pn544_hci.c +++ b/drivers/nfc/pn544_hci.c @@ -128,6 +128,8 @@ static struct nfc_hci_gate pn544_gates[] = { /* Largest headroom needed for outgoing custom commands */ #define PN544_CMDS_HEADROOM 2 +#define PN544_FRAME_HEADROOM 1 +#define PN544_FRAME_TAILROOM 2 struct pn544_hci_info { struct i2c_client *i2c_dev; @@ -576,15 +578,40 @@ static int pn544_hci_ready(struct nfc_shdlc *shdlc) return 0; } +static void pn544_hci_add_len_crc(struct sk_buff *skb) +{ + u16 crc; + int len; + + len = skb->len + 2; + *skb_push(skb, 1) = len; + + crc = crc_ccitt(0xffff, skb->data, skb->len); + crc = ~crc; + *skb_put(skb, 1) = crc & 0xff; + *skb_put(skb, 1) = crc >> 8; +} + +static void pn544_hci_remove_len_crc(struct sk_buff *skb) +{ + skb_pull(skb, PN544_FRAME_HEADROOM); + skb_trim(skb, PN544_FRAME_TAILROOM); +} + static int pn544_hci_xmit(struct nfc_shdlc *shdlc, struct sk_buff *skb) { struct pn544_hci_info *info = nfc_shdlc_get_clientdata(shdlc); struct i2c_client *client = info->i2c_dev; + int r; if (info->hard_fault != 0) return info->hard_fault; - return pn544_hci_i2c_write(client, skb->data, skb->len); + pn544_hci_add_len_crc(skb); + r = pn544_hci_i2c_write(client, skb->data, skb->len); + pn544_hci_remove_len_crc(skb); + + return r; } static int pn544_hci_start_poll(struct nfc_shdlc *shdlc, @@ -874,7 +901,8 @@ static int __devinit pn544_hci_probe(struct i2c_client *client, info->shdlc = nfc_shdlc_allocate(&pn544_shdlc_ops, &init_data, protocols, - PN544_CMDS_HEADROOM, 0, + PN544_FRAME_HEADROOM + PN544_CMDS_HEADROOM, + PN544_FRAME_TAILROOM, PN544_HCI_LLC_MAX_PAYLOAD, dev_name(&client->dev)); if (!info->shdlc) { diff --git a/net/nfc/hci/shdlc.c b/net/nfc/hci/shdlc.c index 824fb09384ed..ed8796b78f39 100644 --- a/net/nfc/hci/shdlc.c +++ b/net/nfc/hci/shdlc.c @@ -22,7 +22,6 @@ #include #include #include -#include #include #include @@ -30,7 +29,6 @@ #include #define SHDLC_LLC_HEAD_ROOM 2 -#define SHDLC_LLC_TAIL_ROOM 2 #define SHDLC_MAX_WINDOW 4 #define SHDLC_SREJ_SUPPORT false @@ -94,28 +92,13 @@ static struct sk_buff *nfc_shdlc_alloc_skb(struct nfc_shdlc *shdlc, struct sk_buff *skb; skb = alloc_skb(shdlc->client_headroom + SHDLC_LLC_HEAD_ROOM + - shdlc->client_tailroom + SHDLC_LLC_TAIL_ROOM + - payload_len, GFP_KERNEL); + shdlc->client_tailroom + payload_len, GFP_KERNEL); if (skb) skb_reserve(skb, shdlc->client_headroom + SHDLC_LLC_HEAD_ROOM); return skb; } -static void nfc_shdlc_add_len_crc(struct sk_buff *skb) -{ - u16 crc; - int len; - - len = skb->len + 2; - *skb_push(skb, 1) = len; - - crc = crc_ccitt(0xffff, skb->data, skb->len); - crc = ~crc; - *skb_put(skb, 1) = crc & 0xff; - *skb_put(skb, 1) = crc >> 8; -} - /* immediately sends an S frame. */ static int nfc_shdlc_send_s_frame(struct nfc_shdlc *shdlc, enum sframe_type sframe_type, int nr) @@ -131,8 +114,6 @@ static int nfc_shdlc_send_s_frame(struct nfc_shdlc *shdlc, *skb_push(skb, 1) = SHDLC_CONTROL_HEAD_S | (sframe_type << 3) | nr; - nfc_shdlc_add_len_crc(skb); - r = shdlc->ops->xmit(shdlc, skb); kfree_skb(skb); @@ -151,8 +132,6 @@ static int nfc_shdlc_send_u_frame(struct nfc_shdlc *shdlc, *skb_push(skb, 1) = SHDLC_CONTROL_HEAD_U | uframe_modifier; - nfc_shdlc_add_len_crc(skb); - r = shdlc->ops->xmit(shdlc, skb); kfree_skb(skb); @@ -509,8 +488,6 @@ static void nfc_shdlc_handle_send_queue(struct nfc_shdlc *shdlc) shdlc->nr); /* SHDLC_DUMP_SKB("shdlc frame written", skb); */ - nfc_shdlc_add_len_crc(skb); - r = shdlc->ops->xmit(shdlc, skb); if (r < 0) { shdlc->hard_fault = r; @@ -880,7 +857,7 @@ struct nfc_shdlc *nfc_shdlc_allocate(struct nfc_shdlc_ops *ops, shdlc->hdev = nfc_hci_allocate_device(&shdlc_ops, init_data, protocols, tx_headroom + SHDLC_LLC_HEAD_ROOM, - tx_tailroom + SHDLC_LLC_TAIL_ROOM, + tx_tailroom, max_link_payload); if (shdlc->hdev == NULL) goto err_allocdev; From c1be211727467882e0485ab062e712a3c1fba840 Mon Sep 17 00:00:00 2001 From: Waldemar Rymarkiewicz Date: Fri, 7 Sep 2012 11:08:30 +0200 Subject: [PATCH 13/31] NFC: Correct outgoing frame before requeueing Driver must handle its data added to the frame, so at this point removeing control field of shdlc frame is enough. Signed-off-by: Waldemar Rymarkiewicz Acked-by: Eric Lapuyade Signed-off-by: Samuel Ortiz --- net/nfc/hci/shdlc.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/net/nfc/hci/shdlc.c b/net/nfc/hci/shdlc.c index ed8796b78f39..9357ba7362f6 100644 --- a/net/nfc/hci/shdlc.c +++ b/net/nfc/hci/shdlc.c @@ -240,8 +240,7 @@ static void nfc_shdlc_requeue_ack_pending(struct nfc_shdlc *shdlc) pr_debug("ns reset to %d\n", shdlc->dnr); while ((skb = skb_dequeue_tail(&shdlc->ack_pending_q))) { - skb_pull(skb, 2); /* remove len+control */ - skb_trim(skb, skb->len - 2); /* remove crc */ + skb_pull(skb, 1); /* remove control field */ skb_queue_head(&shdlc->send_q, skb); } shdlc->ns = shdlc->dnr; From b5faa648faf974b58e5a79eafa9a97e1deed7a8a Mon Sep 17 00:00:00 2001 From: Eric Lapuyade Date: Tue, 11 Sep 2012 10:41:41 +0200 Subject: [PATCH 14/31] NFC: Changed the HCI cmd execution callback prototype Make it match the data_exchange_cb_t so that it can be used directly in the implementation of an asynchronous hci_transceive Signed-off-by: Eric Lapuyade Signed-off-by: Samuel Ortiz --- net/nfc/hci/command.c | 11 ++++++++--- net/nfc/hci/core.c | 15 +++++++-------- net/nfc/hci/hci.h | 15 ++++----------- net/nfc/hci/hcp.c | 4 ++-- 4 files changed, 21 insertions(+), 24 deletions(-) diff --git a/net/nfc/hci/command.c b/net/nfc/hci/command.c index 46362ef979db..15e21093c7a5 100644 --- a/net/nfc/hci/command.c +++ b/net/nfc/hci/command.c @@ -28,10 +28,15 @@ #include "hci.h" -static void nfc_hci_execute_cb(struct nfc_hci_dev *hdev, int err, - struct sk_buff *skb, void *cb_data) +/* + * HCI command execution completion callback. + * err will be a standard linux error (may be converted from HCI response) + * skb contains the response data and must be disposed, or may be NULL if + * an error occured + */ +static void nfc_hci_execute_cb(void *context, struct sk_buff *skb, int err) { - struct hcp_exec_waiter *hcp_ew = (struct hcp_exec_waiter *)cb_data; + struct hcp_exec_waiter *hcp_ew = (struct hcp_exec_waiter *)context; pr_debug("HCI Cmd completed with result=%d\n", err); diff --git a/net/nfc/hci/core.c b/net/nfc/hci/core.c index 15744c01bddc..e387c86e0cc7 100644 --- a/net/nfc/hci/core.c +++ b/net/nfc/hci/core.c @@ -57,12 +57,11 @@ static void nfc_hci_msg_tx_work(struct work_struct *work) if (hdev->cmd_pending_msg) { if (timer_pending(&hdev->cmd_timer) == 0) { if (hdev->cmd_pending_msg->cb) - hdev->cmd_pending_msg->cb(hdev, - -ETIME, - NULL, - hdev-> + hdev->cmd_pending_msg->cb(hdev-> cmd_pending_msg-> - cb_context); + cb_context, + NULL, + -ETIME); kfree(hdev->cmd_pending_msg); hdev->cmd_pending_msg = NULL; } else @@ -83,7 +82,7 @@ next_msg: kfree_skb(skb); skb_queue_purge(&msg->msg_frags); if (msg->cb) - msg->cb(hdev, r, NULL, msg->cb_context); + msg->cb(msg->cb_context, NULL, r); kfree(msg); break; } @@ -133,8 +132,8 @@ static void __nfc_hci_cmd_completion(struct nfc_hci_dev *hdev, int err, del_timer_sync(&hdev->cmd_timer); if (hdev->cmd_pending_msg->cb) - hdev->cmd_pending_msg->cb(hdev, err, skb, - hdev->cmd_pending_msg->cb_context); + hdev->cmd_pending_msg->cb(hdev->cmd_pending_msg->cb_context, + skb, err); else kfree_skb(skb); diff --git a/net/nfc/hci/hci.h b/net/nfc/hci/hci.h index fa9a21e92239..b274d12c18ac 100644 --- a/net/nfc/hci/hci.h +++ b/net/nfc/hci/hci.h @@ -20,6 +20,8 @@ #ifndef __LOCAL_HCI_H #define __LOCAL_HCI_H +#include + struct gate_pipe_map { u8 gate; u8 pipe; @@ -35,15 +37,6 @@ struct hcp_packet { struct hcp_message message; } __packed; -/* - * HCI command execution completion callback. - * result will be a standard linux error (may be converted from HCI response) - * skb contains the response data and must be disposed, or may be NULL if - * an error occured - */ -typedef void (*hci_cmd_cb_t) (struct nfc_hci_dev *hdev, int result, - struct sk_buff *skb, void *cb_data); - struct hcp_exec_waiter { wait_queue_head_t *wq; bool exec_complete; @@ -55,7 +48,7 @@ struct hci_msg { struct list_head msg_l; struct sk_buff_head msg_frags; bool wait_response; - hci_cmd_cb_t cb; + data_exchange_cb_t cb; void *cb_context; unsigned long completion_delay; }; @@ -83,7 +76,7 @@ struct hci_create_pipe_resp { int nfc_hci_hcp_message_tx(struct nfc_hci_dev *hdev, u8 pipe, u8 type, u8 instruction, const u8 *payload, size_t payload_len, - hci_cmd_cb_t cb, void *cb_data, + data_exchange_cb_t cb, void *cb_context, unsigned long completion_delay); u8 nfc_hci_pipe2gate(struct nfc_hci_dev *hdev, u8 pipe); diff --git a/net/nfc/hci/hcp.c b/net/nfc/hci/hcp.c index 2372b558abe9..208eedd07ee3 100644 --- a/net/nfc/hci/hcp.c +++ b/net/nfc/hci/hcp.c @@ -35,7 +35,7 @@ int nfc_hci_hcp_message_tx(struct nfc_hci_dev *hdev, u8 pipe, u8 type, u8 instruction, const u8 *payload, size_t payload_len, - hci_cmd_cb_t cb, void *cb_data, + data_exchange_cb_t cb, void *cb_context, unsigned long completion_delay) { struct nfc_dev *ndev = hdev->ndev; @@ -52,7 +52,7 @@ int nfc_hci_hcp_message_tx(struct nfc_hci_dev *hdev, u8 pipe, skb_queue_head_init(&cmd->msg_frags); cmd->wait_response = (type == NFC_HCI_HCP_COMMAND) ? true : false; cmd->cb = cb; - cmd->cb_context = cb_data; + cmd->cb_context = cb_context; cmd->completion_delay = completion_delay; hci_len = payload_len + 1; From e4c4789e55327e5f2bd6cafcccd46f9b6251bbc3 Mon Sep 17 00:00:00 2001 From: Eric Lapuyade Date: Tue, 11 Sep 2012 10:42:54 +0200 Subject: [PATCH 15/31] NFC: Add a public nfc_hci_send_cmd_async method This method initiates execution of an HCI cmd. Result will be delivered through an asynchronous callback. Signed-off-by: Eric Lapuyade Signed-off-by: Samuel Ortiz --- include/net/nfc/hci.h | 3 +++ net/nfc/hci/command.c | 34 +++++++++++++++++++++++++++++++++- 2 files changed, 36 insertions(+), 1 deletion(-) diff --git a/include/net/nfc/hci.h b/include/net/nfc/hci.h index 6f065d563ec1..54ba2caab4a6 100644 --- a/include/net/nfc/hci.h +++ b/include/net/nfc/hci.h @@ -200,6 +200,9 @@ int nfc_hci_set_param(struct nfc_hci_dev *hdev, u8 gate, u8 idx, const u8 *param, size_t param_len); int nfc_hci_send_cmd(struct nfc_hci_dev *hdev, u8 gate, u8 cmd, const u8 *param, size_t param_len, struct sk_buff **skb); +int nfc_hci_send_cmd_async(struct nfc_hci_dev *hdev, u8 gate, u8 cmd, + const u8 *param, size_t param_len, + data_exchange_cb_t cb, void *cb_context); int nfc_hci_send_response(struct nfc_hci_dev *hdev, u8 gate, u8 response, const u8 *param, size_t param_len); int nfc_hci_send_event(struct nfc_hci_dev *hdev, u8 gate, u8 event, diff --git a/net/nfc/hci/command.c b/net/nfc/hci/command.c index 15e21093c7a5..71c6a7086b8f 100644 --- a/net/nfc/hci/command.c +++ b/net/nfc/hci/command.c @@ -28,6 +28,20 @@ #include "hci.h" +static int nfc_hci_execute_cmd_async(struct nfc_hci_dev *hdev, u8 pipe, u8 cmd, + const u8 *param, size_t param_len, + data_exchange_cb_t cb, void *cb_context) +{ + pr_debug("exec cmd async through pipe=%d, cmd=%d, plen=%zd\n", pipe, + cmd, param_len); + + /* TODO: Define hci cmd execution delay. Should it be the same + * for all commands? + */ + return nfc_hci_hcp_message_tx(hdev, pipe, NFC_HCI_HCP_COMMAND, cmd, + param, param_len, cb, cb_context, 3000); +} + /* * HCI command execution completion callback. * err will be a standard linux error (may be converted from HCI response) @@ -60,7 +74,8 @@ static int nfc_hci_execute_cmd(struct nfc_hci_dev *hdev, u8 pipe, u8 cmd, hcp_ew.exec_complete = false; hcp_ew.result_skb = NULL; - pr_debug("through pipe=%d, cmd=%d, plen=%zd\n", pipe, cmd, param_len); + pr_debug("exec cmd sync through pipe=%d, cmd=%d, plen=%zd\n", pipe, + cmd, param_len); /* TODO: Define hci cmd execution delay. Should it be the same * for all commands? @@ -138,6 +153,23 @@ int nfc_hci_send_cmd(struct nfc_hci_dev *hdev, u8 gate, u8 cmd, } EXPORT_SYMBOL(nfc_hci_send_cmd); +int nfc_hci_send_cmd_async(struct nfc_hci_dev *hdev, u8 gate, u8 cmd, + const u8 *param, size_t param_len, + data_exchange_cb_t cb, void *cb_context) +{ + u8 pipe; + + pr_debug("\n"); + + pipe = hdev->gate2pipe[gate]; + if (pipe == NFC_HCI_INVALID_PIPE) + return -EADDRNOTAVAIL; + + return nfc_hci_execute_cmd_async(hdev, pipe, cmd, param, param_len, + cb, cb_context); +} +EXPORT_SYMBOL(nfc_hci_send_cmd_async); + int nfc_hci_set_param(struct nfc_hci_dev *hdev, u8 gate, u8 idx, const u8 *param, size_t param_len) { From f3e8fb552789f4845e60b11c47b676d14b9488e5 Mon Sep 17 00:00:00 2001 From: Eric Lapuyade Date: Tue, 11 Sep 2012 10:43:50 +0200 Subject: [PATCH 16/31] NFC: Modified hci_transceive to become an asynchronous operation This enables the completion callback to be called from a different context, preventing a possible deadlock if the callback resulted in the invocation of a nested call to the currently locked nfc_dev. This is also more in line with the im_transceive nfc_ops for NFC Core or NCI drivers which already behave asynchronously. Signed-off-by: Eric Lapuyade Signed-off-by: Samuel Ortiz --- drivers/nfc/pn544_hci.c | 57 ++++++++++++++++++++++++++++++----------- include/net/nfc/hci.h | 8 ++++-- include/net/nfc/shdlc.h | 4 +-- net/nfc/hci/core.c | 57 ++++++++++++++++++++++++++++------------- net/nfc/hci/shdlc.c | 5 ++-- 5 files changed, 92 insertions(+), 39 deletions(-) diff --git a/drivers/nfc/pn544_hci.c b/drivers/nfc/pn544_hci.c index d90aecfce739..a176d1f7bbf3 100644 --- a/drivers/nfc/pn544_hci.c +++ b/drivers/nfc/pn544_hci.c @@ -148,6 +148,9 @@ struct pn544_hci_info { * < 0 if hardware error occured (e.g. i2c err) * and prevents normal operation. */ + int async_cb_type; + data_exchange_cb_t async_cb; + void *async_cb_context; }; static void pn544_hci_platform_init(struct pn544_hci_info *info) @@ -731,6 +734,26 @@ static int pn544_hci_complete_target_discovered(struct nfc_shdlc *shdlc, return r; } +#define PN544_CB_TYPE_READER_F 1 + +static void pn544_hci_data_exchange_cb(void *context, struct sk_buff *skb, + int err) +{ + struct pn544_hci_info *info = context; + + switch (info->async_cb_type) { + case PN544_CB_TYPE_READER_F: + if (err == 0) + skb_pull(skb, 1); + info->async_cb(info->async_cb_context, skb, err); + break; + default: + if (err == 0) + kfree_skb(skb); + break; + } +} + #define MIFARE_CMD_AUTH_KEY_A 0x60 #define MIFARE_CMD_AUTH_KEY_B 0x61 #define MIFARE_CMD_HEADER 2 @@ -744,11 +767,11 @@ static int pn544_hci_complete_target_discovered(struct nfc_shdlc *shdlc, */ static int pn544_hci_data_exchange(struct nfc_shdlc *shdlc, struct nfc_target *target, - struct sk_buff *skb, - struct sk_buff **res_skb) + struct sk_buff *skb, data_exchange_cb_t cb, + void *cb_context) { + struct pn544_hci_info *info = nfc_shdlc_get_clientdata(shdlc); struct nfc_hci_dev *hdev = nfc_shdlc_get_hci_dev(shdlc); - int r; pr_info(DRIVER_DESC ": %s for gate=%d\n", __func__, target->hci_reader_gate); @@ -773,25 +796,29 @@ static int pn544_hci_data_exchange(struct nfc_shdlc *shdlc, memcpy(data, uid, MIFARE_UID_LEN); } - return nfc_hci_send_cmd(hdev, target->hci_reader_gate, - PN544_MIFARE_CMD, - skb->data, skb->len, res_skb); + return nfc_hci_send_cmd_async(hdev, + target->hci_reader_gate, + PN544_MIFARE_CMD, + skb->data, skb->len, + cb, cb_context); } else return 1; case PN544_RF_READER_F_GATE: *skb_push(skb, 1) = 0; *skb_push(skb, 1) = 0; - r = nfc_hci_send_cmd(hdev, target->hci_reader_gate, - PN544_FELICA_RAW, - skb->data, skb->len, res_skb); - if (r == 0) - skb_pull(*res_skb, 1); - return r; + info->async_cb_type = PN544_CB_TYPE_READER_F; + info->async_cb = cb; + info->async_cb_context = cb_context; + + return nfc_hci_send_cmd_async(hdev, target->hci_reader_gate, + PN544_FELICA_RAW, skb->data, + skb->len, + pn544_hci_data_exchange_cb, info); case PN544_RF_READER_JEWEL_GATE: - return nfc_hci_send_cmd(hdev, target->hci_reader_gate, - PN544_JEWEL_RAW_CMD, - skb->data, skb->len, res_skb); + return nfc_hci_send_cmd_async(hdev, target->hci_reader_gate, + PN544_JEWEL_RAW_CMD, skb->data, + skb->len, cb, cb_context); default: return 1; } diff --git a/include/net/nfc/hci.h b/include/net/nfc/hci.h index 54ba2caab4a6..6cee6e21fc46 100644 --- a/include/net/nfc/hci.h +++ b/include/net/nfc/hci.h @@ -38,8 +38,8 @@ struct nfc_hci_ops { int (*complete_target_discovered) (struct nfc_hci_dev *hdev, u8 gate, struct nfc_target *target); int (*data_exchange) (struct nfc_hci_dev *hdev, - struct nfc_target *target, - struct sk_buff *skb, struct sk_buff **res_skb); + struct nfc_target *target, struct sk_buff *skb, + data_exchange_cb_t cb, void *cb_context); int (*check_presence)(struct nfc_hci_dev *hdev, struct nfc_target *target); }; @@ -103,6 +103,10 @@ struct nfc_hci_dev { u8 hw_mpw; u8 hw_software; u8 hw_bsid; + + int async_cb_type; + data_exchange_cb_t async_cb; + void *async_cb_context; }; /* hci device allocation */ diff --git a/include/net/nfc/shdlc.h b/include/net/nfc/shdlc.h index 342427362989..fe5e19829352 100644 --- a/include/net/nfc/shdlc.h +++ b/include/net/nfc/shdlc.h @@ -34,8 +34,8 @@ struct nfc_shdlc_ops { int (*complete_target_discovered) (struct nfc_shdlc *shdlc, u8 gate, struct nfc_target *target); int (*data_exchange) (struct nfc_shdlc *shdlc, - struct nfc_target *target, - struct sk_buff *skb, struct sk_buff **res_skb); + struct nfc_target *target, struct sk_buff *skb, + data_exchange_cb_t cb, void *cb_context); int (*check_presence)(struct nfc_shdlc *shdlc, struct nfc_target *target); }; diff --git a/net/nfc/hci/core.c b/net/nfc/hci/core.c index e387c86e0cc7..dc57e3dc15a4 100644 --- a/net/nfc/hci/core.c +++ b/net/nfc/hci/core.c @@ -537,13 +537,37 @@ static void hci_deactivate_target(struct nfc_dev *nfc_dev, { } +#define HCI_CB_TYPE_TRANSCEIVE 1 + +static void hci_transceive_cb(void *context, struct sk_buff *skb, int err) +{ + struct nfc_hci_dev *hdev = context; + + switch (hdev->async_cb_type) { + case HCI_CB_TYPE_TRANSCEIVE: + /* + * TODO: Check RF Error indicator to make sure data is valid. + * It seems that HCI cmd can complete without error, but data + * can be invalid if an RF error occured? Ignore for now. + */ + if (err == 0) + skb_trim(skb, skb->len - 1); /* RF Err ind */ + + hdev->async_cb(hdev->async_cb_context, skb, err); + break; + default: + if (err == 0) + kfree_skb(skb); + break; + } +} + static int hci_transceive(struct nfc_dev *nfc_dev, struct nfc_target *target, struct sk_buff *skb, data_exchange_cb_t cb, void *cb_context) { struct nfc_hci_dev *hdev = nfc_get_drvdata(nfc_dev); int r; - struct sk_buff *res_skb = NULL; pr_debug("target_idx=%d\n", target->idx); @@ -551,40 +575,37 @@ static int hci_transceive(struct nfc_dev *nfc_dev, struct nfc_target *target, case NFC_HCI_RF_READER_A_GATE: case NFC_HCI_RF_READER_B_GATE: if (hdev->ops->data_exchange) { - r = hdev->ops->data_exchange(hdev, target, skb, - &res_skb); + r = hdev->ops->data_exchange(hdev, target, skb, cb, + cb_context); if (r <= 0) /* handled */ break; } *skb_push(skb, 1) = 0; /* CTR, see spec:10.2.2.1 */ - r = nfc_hci_send_cmd(hdev, target->hci_reader_gate, - NFC_HCI_WR_XCHG_DATA, - skb->data, skb->len, &res_skb); - /* - * TODO: Check RF Error indicator to make sure data is valid. - * It seems that HCI cmd can complete without error, but data - * can be invalid if an RF error occured? Ignore for now. - */ - if (r == 0) - skb_trim(res_skb, res_skb->len - 1); /* RF Err ind */ + + hdev->async_cb_type = HCI_CB_TYPE_TRANSCEIVE; + hdev->async_cb = cb; + hdev->async_cb_context = cb_context; + + r = nfc_hci_send_cmd_async(hdev, target->hci_reader_gate, + NFC_HCI_WR_XCHG_DATA, skb->data, + skb->len, hci_transceive_cb, hdev); break; default: if (hdev->ops->data_exchange) { - r = hdev->ops->data_exchange(hdev, target, skb, - &res_skb); + r = hdev->ops->data_exchange(hdev, target, skb, cb, + cb_context); if (r == 1) r = -ENOTSUPP; } else r = -ENOTSUPP; + break; } kfree_skb(skb); - cb(cb_context, res_skb, r); - - return 0; + return r; } static int hci_check_presence(struct nfc_dev *nfc_dev, diff --git a/net/nfc/hci/shdlc.c b/net/nfc/hci/shdlc.c index 9357ba7362f6..c63af7d3e859 100644 --- a/net/nfc/hci/shdlc.c +++ b/net/nfc/hci/shdlc.c @@ -777,12 +777,13 @@ static int nfc_shdlc_complete_target_discovered(struct nfc_hci_dev *hdev, static int nfc_shdlc_data_exchange(struct nfc_hci_dev *hdev, struct nfc_target *target, struct sk_buff *skb, - struct sk_buff **res_skb) + data_exchange_cb_t cb, void *cb_context) { struct nfc_shdlc *shdlc = nfc_hci_get_clientdata(hdev); if (shdlc->ops->data_exchange) - return shdlc->ops->data_exchange(shdlc, target, skb, res_skb); + return shdlc->ops->data_exchange(shdlc, target, skb, cb, + cb_context); return -EPERM; } From 67cccfe17d1b3da1ed6c79e643c9be95ebde9642 Mon Sep 17 00:00:00 2001 From: Eric Lapuyade Date: Thu, 13 Sep 2012 17:10:00 +0200 Subject: [PATCH 17/31] NFC: Add an LLC Core layer to HCI The LLC layer manages modules that control the link layer protocol (such as shdlc) between HCI and an HCI driver. The driver must simply specify the required llc when it registers with HCI. Signed-off-by: Eric Lapuyade Signed-off-by: Samuel Ortiz --- include/net/nfc/llc.h | 51 +++++++++++++ net/nfc/hci/Makefile | 2 +- net/nfc/hci/core.c | 14 ++++ net/nfc/hci/llc.c | 168 ++++++++++++++++++++++++++++++++++++++++++ net/nfc/hci/llc.h | 58 +++++++++++++++ 5 files changed, 292 insertions(+), 1 deletion(-) create mode 100644 include/net/nfc/llc.h create mode 100644 net/nfc/hci/llc.c create mode 100644 net/nfc/hci/llc.h diff --git a/include/net/nfc/llc.h b/include/net/nfc/llc.h new file mode 100644 index 000000000000..98df903f8b7d --- /dev/null +++ b/include/net/nfc/llc.h @@ -0,0 +1,51 @@ +/* + * Link Layer Control manager public interface + * + * Copyright (C) 2012 Intel Corporation. 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, write to the + * Free Software Foundation, Inc., + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#ifndef __NFC_LLC_H_ +#define __NFC_LLC_H_ + +#include +#include + +typedef void (*rcv_to_hci_t) (struct nfc_hci_dev *hdev, struct sk_buff *skb); +typedef int (*xmit_to_drv_t) (struct nfc_hci_dev *hdev, struct sk_buff *skb); +typedef void (*llc_failure_t) (struct nfc_hci_dev *hdev, int err); + +struct nfc_llc; + +struct nfc_llc *nfc_llc_allocate(const char *name, struct nfc_hci_dev *hdev, + xmit_to_drv_t xmit_to_drv, + rcv_to_hci_t rcv_to_hci, int tx_headroom, + int tx_tailroom, llc_failure_t llc_failure); +void nfc_llc_free(struct nfc_llc *llc); + +void nfc_llc_get_rx_head_tail_room(struct nfc_llc *llc, int *rx_headroom, + int *rx_tailroom); + + +int nfc_llc_start(struct nfc_llc *llc); +int nfc_llc_stop(struct nfc_llc *llc); +void nfc_llc_rcv_from_drv(struct nfc_llc *llc, struct sk_buff *skb); +int nfc_llc_xmit_from_hci(struct nfc_llc *llc, struct sk_buff *skb); + +int nfc_llc_init(void); +void nfc_llc_exit(void); + +#endif /* __NFC_LLC_H_ */ diff --git a/net/nfc/hci/Makefile b/net/nfc/hci/Makefile index f9c44b2fb065..b44686b581af 100644 --- a/net/nfc/hci/Makefile +++ b/net/nfc/hci/Makefile @@ -4,5 +4,5 @@ obj-$(CONFIG_NFC_HCI) += hci.o -hci-y := core.o hcp.o command.o +hci-y := core.o hcp.o command.o llc.o hci-$(CONFIG_NFC_SHDLC) += shdlc.o diff --git a/net/nfc/hci/core.c b/net/nfc/hci/core.c index dc57e3dc15a4..069e2d6056e5 100644 --- a/net/nfc/hci/core.c +++ b/net/nfc/hci/core.c @@ -26,6 +26,7 @@ #include #include +#include #include "hci.h" @@ -821,4 +822,17 @@ void nfc_hci_recv_frame(struct nfc_hci_dev *hdev, struct sk_buff *skb) } EXPORT_SYMBOL(nfc_hci_recv_frame); +static int __init nfc_hci_init(void) +{ + return nfc_llc_init(); +} + +static void __exit nfc_hci_exit(void) +{ + nfc_llc_exit(); +} + +module_init(nfc_hci_init); +module_exit(nfc_hci_exit); + MODULE_LICENSE("GPL"); diff --git a/net/nfc/hci/llc.c b/net/nfc/hci/llc.c new file mode 100644 index 000000000000..73c42785ce84 --- /dev/null +++ b/net/nfc/hci/llc.c @@ -0,0 +1,168 @@ +/* + * Link Layer Control manager + * + * Copyright (C) 2012 Intel Corporation. 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, write to the + * Free Software Foundation, Inc., + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#include +#include +#include "llc.h" + +static struct list_head llc_engines; + +int nfc_llc_init(void) +{ + INIT_LIST_HEAD(&llc_engines); + + return 0; +} +EXPORT_SYMBOL(nfc_llc_init); + +void nfc_llc_exit(void) +{ + struct nfc_llc_engine *llc_engine, *n; + + list_for_each_entry_safe(llc_engine, n, &llc_engines, entry) { + list_del(&llc_engine->entry); + kfree(llc_engine->name); + kfree(llc_engine); + } +} +EXPORT_SYMBOL(nfc_llc_exit); + +int nfc_llc_register(const char *name, struct nfc_llc_ops *ops) +{ + struct nfc_llc_engine *llc_engine; + + llc_engine = kzalloc(sizeof(struct nfc_llc_engine), GFP_KERNEL); + if (llc_engine == NULL) + return -ENOMEM; + + llc_engine->name = kstrdup(name, GFP_KERNEL); + if (llc_engine->name == NULL) { + kfree(llc_engine); + return -ENOMEM; + } + llc_engine->ops = ops; + + INIT_LIST_HEAD(&llc_engine->entry); + list_add_tail (&llc_engine->entry, &llc_engines); + + return 0; +} +EXPORT_SYMBOL(nfc_llc_register); + +static struct nfc_llc_engine *nfc_llc_name_to_engine(const char *name) +{ + struct nfc_llc_engine *llc_engine; + + list_for_each_entry(llc_engine, &llc_engines, entry) { + if (strcmp(llc_engine->name, name) == 0) + return llc_engine; + } + + return NULL; +} + +void nfc_llc_unregister(const char *name) +{ + struct nfc_llc_engine *llc_engine; + + llc_engine = nfc_llc_name_to_engine(name); + if (llc_engine == NULL) + return; + + list_del(&llc_engine->entry); + kfree(llc_engine->name); + kfree(llc_engine); +} +EXPORT_SYMBOL(nfc_llc_unregister); + +struct nfc_llc *nfc_llc_allocate(const char *name, struct nfc_hci_dev *hdev, + xmit_to_drv_t xmit_to_drv, + rcv_to_hci_t rcv_to_hci, int tx_headroom, + int tx_tailroom, llc_failure_t llc_failure) +{ + struct nfc_llc_engine *llc_engine; + struct nfc_llc *llc; + + llc_engine = nfc_llc_name_to_engine(name); + if (llc_engine == NULL) + return NULL; + + llc = kzalloc(sizeof(struct nfc_llc), GFP_KERNEL); + if (llc == NULL) + return NULL; + + llc->data = llc_engine->ops->init(hdev, xmit_to_drv, rcv_to_hci, + tx_headroom, tx_tailroom, + &llc->rx_headroom, &llc->rx_tailroom, + llc_failure); + if (llc->data == NULL) { + kfree(llc); + return NULL; + } + llc->ops = llc_engine->ops; + + return llc; +} +EXPORT_SYMBOL(nfc_llc_allocate); + +void nfc_llc_free(struct nfc_llc *llc) +{ + llc->ops->deinit(llc); + kfree(llc); +} +EXPORT_SYMBOL(nfc_llc_free); + +inline void nfc_llc_get_rx_head_tail_room(struct nfc_llc *llc, int *rx_headroom, + int *rx_tailroom) +{ + *rx_headroom = llc->rx_headroom; + *rx_tailroom = llc->rx_tailroom; +} +EXPORT_SYMBOL(nfc_llc_get_rx_head_tail_room); + +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) +{ + llc->ops->rcv_from_drv(llc, skb); +} +EXPORT_SYMBOL(nfc_llc_rcv_from_drv); + +inline int nfc_llc_xmit_from_hci(struct nfc_llc *llc, struct sk_buff *skb) +{ + return llc->ops->xmit_from_hci(llc, skb); +} +EXPORT_SYMBOL(nfc_llc_xmit_from_hci); + +inline void *nfc_llc_get_data(struct nfc_llc *llc) +{ + return llc->data; +} +EXPORT_SYMBOL(nfc_llc_get_data); diff --git a/net/nfc/hci/llc.h b/net/nfc/hci/llc.h new file mode 100644 index 000000000000..b2c7285b0309 --- /dev/null +++ b/net/nfc/hci/llc.h @@ -0,0 +1,58 @@ +/* + * Link Layer Control manager + * + * Copyright (C) 2012 Intel Corporation. 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, write to the + * Free Software Foundation, Inc., + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#ifndef __LOCAL_LLC_H_ +#define __LOCAL_LLC_H_ + +#include +#include +#include + +struct nfc_llc_ops { + void *(*init) (struct nfc_hci_dev *hdev, xmit_to_drv_t xmit_to_drv, + rcv_to_hci_t rcv_to_hci, int tx_headroom, + int tx_tailroom, int *rx_headroom, int *rx_tailroom, + llc_failure_t llc_failure); + void (*deinit) (struct nfc_llc *llc); + int (*start) (struct nfc_llc *llc); + int (*stop) (struct nfc_llc *llc); + void (*rcv_from_drv) (struct nfc_llc *llc, struct sk_buff *skb); + int (*xmit_from_hci) (struct nfc_llc *llc, struct sk_buff *skb); +}; + +struct nfc_llc_engine { + const char *name; + struct nfc_llc_ops *ops; + struct list_head entry; +}; + +struct nfc_llc { + void *data; + struct nfc_llc_ops *ops; + int rx_headroom; + int rx_tailroom; +}; + +void *nfc_llc_get_data(struct nfc_llc *llc); + +int nfc_llc_register(const char *name, struct nfc_llc_ops *ops); +void nfc_llc_unregister(const char *name); + +#endif /* __LOCAL_LLC_H_ */ From 8af00d48dc929442644bf68e9cd3d951d9697296 Mon Sep 17 00:00:00 2001 From: Eric Lapuyade Date: Thu, 13 Sep 2012 17:10:48 +0200 Subject: [PATCH 18/31] NFC: Add a nop (passthrough) llc module to llc core This is a passthrough llc. It can be used by HCI drivers that don't need link layer control. HCI will then write directly to the driver, and driver will deliver incoming frames directly to HCI without any processing. Signed-off-by: Eric Lapuyade Signed-off-by: Samuel Ortiz --- include/net/nfc/llc.h | 2 + net/nfc/hci/Makefile | 2 +- net/nfc/hci/llc.c | 2 +- net/nfc/hci/llc.h | 2 + net/nfc/hci/llc_nop.c | 101 ++++++++++++++++++++++++++++++++++++++++++ 5 files changed, 107 insertions(+), 2 deletions(-) create mode 100644 net/nfc/hci/llc_nop.c diff --git a/include/net/nfc/llc.h b/include/net/nfc/llc.h index 98df903f8b7d..146e053f91e7 100644 --- a/include/net/nfc/llc.h +++ b/include/net/nfc/llc.h @@ -24,6 +24,8 @@ #include #include +#define LLC_NOP_NAME "nop" + typedef void (*rcv_to_hci_t) (struct nfc_hci_dev *hdev, struct sk_buff *skb); typedef int (*xmit_to_drv_t) (struct nfc_hci_dev *hdev, struct sk_buff *skb); typedef void (*llc_failure_t) (struct nfc_hci_dev *hdev, int err); diff --git a/net/nfc/hci/Makefile b/net/nfc/hci/Makefile index b44686b581af..2ec4e5876f6b 100644 --- a/net/nfc/hci/Makefile +++ b/net/nfc/hci/Makefile @@ -4,5 +4,5 @@ obj-$(CONFIG_NFC_HCI) += hci.o -hci-y := core.o hcp.o command.o llc.o +hci-y := core.o hcp.o command.o llc.o llc_nop.o hci-$(CONFIG_NFC_SHDLC) += shdlc.o diff --git a/net/nfc/hci/llc.c b/net/nfc/hci/llc.c index 73c42785ce84..32002e5339c0 100644 --- a/net/nfc/hci/llc.c +++ b/net/nfc/hci/llc.c @@ -28,7 +28,7 @@ int nfc_llc_init(void) { INIT_LIST_HEAD(&llc_engines); - return 0; + return nfc_llc_nop_register(); } EXPORT_SYMBOL(nfc_llc_init); diff --git a/net/nfc/hci/llc.h b/net/nfc/hci/llc.h index b2c7285b0309..acdd8d1bbae5 100644 --- a/net/nfc/hci/llc.h +++ b/net/nfc/hci/llc.h @@ -55,4 +55,6 @@ void *nfc_llc_get_data(struct nfc_llc *llc); int nfc_llc_register(const char *name, struct nfc_llc_ops *ops); void nfc_llc_unregister(const char *name); +int nfc_llc_nop_register(void); + #endif /* __LOCAL_LLC_H_ */ diff --git a/net/nfc/hci/llc_nop.c b/net/nfc/hci/llc_nop.c new file mode 100644 index 000000000000..ec627cee12cd --- /dev/null +++ b/net/nfc/hci/llc_nop.c @@ -0,0 +1,101 @@ +/* + * nop (passthrough) Link Layer Control + * + * Copyright (C) 2012 Intel Corporation. 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, write to the + * Free Software Foundation, Inc., + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#include +#include + +#include "llc.h" + +struct llc_nop { + struct nfc_hci_dev *hdev; + xmit_to_drv_t xmit_to_drv; + rcv_to_hci_t rcv_to_hci; + int tx_headroom; + int tx_tailroom; + llc_failure_t llc_failure; +}; + +static void *llc_nop_init(struct nfc_hci_dev *hdev, xmit_to_drv_t xmit_to_drv, + rcv_to_hci_t rcv_to_hci, int tx_headroom, + int tx_tailroom, int *rx_headroom, int *rx_tailroom, + llc_failure_t llc_failure) +{ + struct llc_nop *llc_nop; + + *rx_headroom = 0; + *rx_tailroom = 0; + + llc_nop = kzalloc(sizeof(struct llc_nop), GFP_KERNEL); + if (llc_nop == NULL) + return NULL; + + llc_nop->hdev = hdev; + llc_nop->xmit_to_drv = xmit_to_drv; + llc_nop->rcv_to_hci = rcv_to_hci; + llc_nop->tx_headroom = tx_headroom; + llc_nop->tx_tailroom = tx_tailroom; + llc_nop->llc_failure = llc_failure; + + return llc_nop; +} + +static void llc_nop_deinit(struct nfc_llc *llc) +{ + kfree(nfc_llc_get_data(llc)); +} + +static int llc_nop_start(struct nfc_llc *llc) +{ + return 0; +} + +static int llc_nop_stop(struct nfc_llc *llc) +{ + return 0; +} + +static void llc_nop_rcv_from_drv(struct nfc_llc *llc, struct sk_buff *skb) +{ + struct llc_nop *llc_nop = nfc_llc_get_data(llc); + + llc_nop->rcv_to_hci(llc_nop->hdev, skb); +} + +static int llc_nop_xmit_from_hci(struct nfc_llc *llc, struct sk_buff *skb) +{ + struct llc_nop *llc_nop = nfc_llc_get_data(llc); + + return llc_nop->xmit_to_drv(llc_nop->hdev, skb); +} + +static struct nfc_llc_ops llc_nop_ops = { + .init = llc_nop_init, + .deinit = llc_nop_deinit, + .start = llc_nop_start, + .stop = llc_nop_stop, + .rcv_from_drv = llc_nop_rcv_from_drv, + .xmit_from_hci = llc_nop_xmit_from_hci, +}; + +int nfc_llc_nop_register() +{ + return nfc_llc_register(LLC_NOP_NAME, &llc_nop_ops); +} +EXPORT_SYMBOL(nfc_llc_nop_register); From 4a61cd6687fc6348d08724676d34e38160d6cf9b Mon Sep 17 00:00:00 2001 From: Eric Lapuyade Date: Thu, 13 Sep 2012 17:11:37 +0200 Subject: [PATCH 19/31] NFC: Add an shdlc llc module to llc core This is used by HCI drivers such as the one for the pn544 which require communications between HCI and the chip to use shdlc. Signed-off-by: Eric Lapuyade Signed-off-by: Samuel Ortiz --- include/net/nfc/llc.h | 1 + net/nfc/hci/Makefile | 2 +- net/nfc/hci/llc.c | 16 +- net/nfc/hci/llc.h | 1 + net/nfc/hci/llc_shdlc.c | 834 ++++++++++++++++++++++++++++++++++++++++ 5 files changed, 852 insertions(+), 2 deletions(-) create mode 100644 net/nfc/hci/llc_shdlc.c diff --git a/include/net/nfc/llc.h b/include/net/nfc/llc.h index 146e053f91e7..400ab7ae749d 100644 --- a/include/net/nfc/llc.h +++ b/include/net/nfc/llc.h @@ -25,6 +25,7 @@ #include #define LLC_NOP_NAME "nop" +#define LLC_SHDLC_NAME "shdlc" typedef void (*rcv_to_hci_t) (struct nfc_hci_dev *hdev, struct sk_buff *skb); typedef int (*xmit_to_drv_t) (struct nfc_hci_dev *hdev, struct sk_buff *skb); diff --git a/net/nfc/hci/Makefile b/net/nfc/hci/Makefile index 2ec4e5876f6b..c4d65479629b 100644 --- a/net/nfc/hci/Makefile +++ b/net/nfc/hci/Makefile @@ -5,4 +5,4 @@ obj-$(CONFIG_NFC_HCI) += hci.o hci-y := core.o hcp.o command.o llc.o llc_nop.o -hci-$(CONFIG_NFC_SHDLC) += shdlc.o +hci-$(CONFIG_NFC_SHDLC) += shdlc.o llc_shdlc.o diff --git a/net/nfc/hci/llc.c b/net/nfc/hci/llc.c index 32002e5339c0..bd11b0f7658a 100644 --- a/net/nfc/hci/llc.c +++ b/net/nfc/hci/llc.c @@ -26,9 +26,23 @@ static struct list_head llc_engines; int nfc_llc_init(void) { + int r; + INIT_LIST_HEAD(&llc_engines); - return nfc_llc_nop_register(); + r = nfc_llc_nop_register(); + if (r) + goto exit; + + r = nfc_llc_shdlc_register(); + if (r) + goto exit; + + return 0; + +exit: + nfc_llc_exit(); + return r; } EXPORT_SYMBOL(nfc_llc_init); diff --git a/net/nfc/hci/llc.h b/net/nfc/hci/llc.h index acdd8d1bbae5..c7014fdfc8c9 100644 --- a/net/nfc/hci/llc.h +++ b/net/nfc/hci/llc.h @@ -56,5 +56,6 @@ int nfc_llc_register(const char *name, struct nfc_llc_ops *ops); void nfc_llc_unregister(const char *name); int nfc_llc_nop_register(void); +int nfc_llc_shdlc_register(void); #endif /* __LOCAL_LLC_H_ */ diff --git a/net/nfc/hci/llc_shdlc.c b/net/nfc/hci/llc_shdlc.c new file mode 100644 index 000000000000..bb191100ee96 --- /dev/null +++ b/net/nfc/hci/llc_shdlc.c @@ -0,0 +1,834 @@ +/* + * shdlc Link Layer Control + * + * Copyright (C) 2012 Intel Corporation. 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, write to the + * Free Software Foundation, Inc., + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#define pr_fmt(fmt) "shdlc: %s: " fmt, __func__ + +#include +#include +#include +#include +#include +#include + +#include "llc.h" + +enum shdlc_state { + SHDLC_DISCONNECTED = 0, + SHDLC_CONNECTING = 1, + SHDLC_NEGOCIATING = 2, + SHDLC_CONNECTED = 3 +}; + +struct llc_shdlc { + struct nfc_hci_dev *hdev; + xmit_to_drv_t xmit_to_drv; + rcv_to_hci_t rcv_to_hci; + + struct mutex state_mutex; + enum shdlc_state state; + int hard_fault; + + wait_queue_head_t *connect_wq; + int connect_tries; + int connect_result; + struct timer_list connect_timer;/* aka T3 in spec 10.6.1 */ + + u8 w; /* window size */ + bool srej_support; + + struct timer_list t1_timer; /* send ack timeout */ + bool t1_active; + + struct timer_list t2_timer; /* guard/retransmit timeout */ + bool t2_active; + + int ns; /* next seq num for send */ + int nr; /* next expected seq num for receive */ + int dnr; /* oldest sent unacked seq num */ + + struct sk_buff_head rcv_q; + + struct sk_buff_head send_q; + bool rnr; /* other side is not ready to receive */ + + struct sk_buff_head ack_pending_q; + + struct work_struct sm_work; + + int tx_headroom; + int tx_tailroom; + + llc_failure_t llc_failure; +}; + +#define SHDLC_LLC_HEAD_ROOM 2 + +#define SHDLC_MAX_WINDOW 4 +#define SHDLC_SREJ_SUPPORT false + +#define SHDLC_CONTROL_HEAD_MASK 0xe0 +#define SHDLC_CONTROL_HEAD_I 0x80 +#define SHDLC_CONTROL_HEAD_I2 0xa0 +#define SHDLC_CONTROL_HEAD_S 0xc0 +#define SHDLC_CONTROL_HEAD_U 0xe0 + +#define SHDLC_CONTROL_NS_MASK 0x38 +#define SHDLC_CONTROL_NR_MASK 0x07 +#define SHDLC_CONTROL_TYPE_MASK 0x18 + +#define SHDLC_CONTROL_M_MASK 0x1f + +enum sframe_type { + S_FRAME_RR = 0x00, + S_FRAME_REJ = 0x01, + S_FRAME_RNR = 0x02, + S_FRAME_SREJ = 0x03 +}; + +enum uframe_modifier { + U_FRAME_UA = 0x06, + U_FRAME_RSET = 0x19 +}; + +#define SHDLC_CONNECT_VALUE_MS 5 +#define SHDLC_T1_VALUE_MS(w) ((5 * w) / 4) +#define SHDLC_T2_VALUE_MS 300 + +#define SHDLC_DUMP_SKB(info, skb) \ +do { \ + pr_debug("%s:\n", info); \ + print_hex_dump(KERN_DEBUG, "shdlc: ", DUMP_PREFIX_OFFSET, \ + 16, 1, skb->data, skb->len, 0); \ +} while (0) + +/* checks x < y <= z modulo 8 */ +static bool llc_shdlc_x_lt_y_lteq_z(int x, int y, int z) +{ + if (x < z) + return ((x < y) && (y <= z)) ? true : false; + else + return ((y > x) || (y <= z)) ? true : false; +} + +/* checks x <= y < z modulo 8 */ +static bool llc_shdlc_x_lteq_y_lt_z(int x, int y, int z) +{ + if (x <= z) + return ((x <= y) && (y < z)) ? true : false; + else /* x > z -> z+8 > x */ + return ((y >= x) || (y < z)) ? true : false; +} + +static struct sk_buff *llc_shdlc_alloc_skb(struct llc_shdlc *shdlc, + int payload_len) +{ + struct sk_buff *skb; + + skb = alloc_skb(shdlc->tx_headroom + SHDLC_LLC_HEAD_ROOM + + shdlc->tx_tailroom + payload_len, GFP_KERNEL); + if (skb) + skb_reserve(skb, shdlc->tx_headroom + SHDLC_LLC_HEAD_ROOM); + + return skb; +} + +/* immediately sends an S frame. */ +static int llc_shdlc_send_s_frame(struct llc_shdlc *shdlc, + enum sframe_type sframe_type, int nr) +{ + int r; + struct sk_buff *skb; + + pr_debug("sframe_type=%d nr=%d\n", sframe_type, nr); + + skb = llc_shdlc_alloc_skb(shdlc, 0); + if (skb == NULL) + return -ENOMEM; + + *skb_push(skb, 1) = SHDLC_CONTROL_HEAD_S | (sframe_type << 3) | nr; + + r = shdlc->xmit_to_drv(shdlc->hdev, skb); + + kfree_skb(skb); + + return r; +} + +/* immediately sends an U frame. skb may contain optional payload */ +static int llc_shdlc_send_u_frame(struct llc_shdlc *shdlc, + struct sk_buff *skb, + enum uframe_modifier uframe_modifier) +{ + int r; + + pr_debug("uframe_modifier=%d\n", uframe_modifier); + + *skb_push(skb, 1) = SHDLC_CONTROL_HEAD_U | uframe_modifier; + + r = shdlc->xmit_to_drv(shdlc->hdev, skb); + + kfree_skb(skb); + + return r; +} + +/* + * Free ack_pending frames until y_nr - 1, and reset t2 according to + * the remaining oldest ack_pending frame sent time + */ +static void llc_shdlc_reset_t2(struct llc_shdlc *shdlc, int y_nr) +{ + struct sk_buff *skb; + int dnr = shdlc->dnr; /* MUST initially be < y_nr */ + + pr_debug("release ack pending up to frame %d excluded\n", y_nr); + + while (dnr != y_nr) { + pr_debug("release ack pending frame %d\n", dnr); + + skb = skb_dequeue(&shdlc->ack_pending_q); + kfree_skb(skb); + + dnr = (dnr + 1) % 8; + } + + if (skb_queue_empty(&shdlc->ack_pending_q)) { + if (shdlc->t2_active) { + del_timer_sync(&shdlc->t2_timer); + shdlc->t2_active = false; + + pr_debug + ("All sent frames acked. Stopped T2(retransmit)\n"); + } + } else { + skb = skb_peek(&shdlc->ack_pending_q); + + mod_timer(&shdlc->t2_timer, *(unsigned long *)skb->cb + + msecs_to_jiffies(SHDLC_T2_VALUE_MS)); + shdlc->t2_active = true; + + pr_debug + ("Start T2(retransmit) for remaining unacked sent frames\n"); + } +} + +/* + * Receive validated frames from lower layer. skb contains HCI payload only. + * Handle according to algorithm at spec:10.8.2 + */ +static void llc_shdlc_rcv_i_frame(struct llc_shdlc *shdlc, + struct sk_buff *skb, int ns, int nr) +{ + int x_ns = ns; + int y_nr = nr; + + pr_debug("recvd I-frame %d, remote waiting frame %d\n", ns, nr); + + if (shdlc->state != SHDLC_CONNECTED) + goto exit; + + if (x_ns != shdlc->nr) { + llc_shdlc_send_s_frame(shdlc, S_FRAME_REJ, shdlc->nr); + goto exit; + } + + if (shdlc->t1_active == false) { + shdlc->t1_active = true; + mod_timer(&shdlc->t1_timer, jiffies + + msecs_to_jiffies(SHDLC_T1_VALUE_MS(shdlc->w))); + pr_debug("(re)Start T1(send ack)\n"); + } + + if (skb->len) { + shdlc->rcv_to_hci(shdlc->hdev, skb); + skb = NULL; + } + + shdlc->nr = (shdlc->nr + 1) % 8; + + if (llc_shdlc_x_lt_y_lteq_z(shdlc->dnr, y_nr, shdlc->ns)) { + llc_shdlc_reset_t2(shdlc, y_nr); + + shdlc->dnr = y_nr; + } + +exit: + kfree_skb(skb); +} + +static void llc_shdlc_rcv_ack(struct llc_shdlc *shdlc, int y_nr) +{ + pr_debug("remote acked up to frame %d excluded\n", y_nr); + + if (llc_shdlc_x_lt_y_lteq_z(shdlc->dnr, y_nr, shdlc->ns)) { + llc_shdlc_reset_t2(shdlc, y_nr); + shdlc->dnr = y_nr; + } +} + +static void llc_shdlc_requeue_ack_pending(struct llc_shdlc *shdlc) +{ + struct sk_buff *skb; + + pr_debug("ns reset to %d\n", shdlc->dnr); + + while ((skb = skb_dequeue_tail(&shdlc->ack_pending_q))) { + skb_pull(skb, 1); /* remove control field */ + skb_queue_head(&shdlc->send_q, skb); + } + shdlc->ns = shdlc->dnr; +} + +static void llc_shdlc_rcv_rej(struct llc_shdlc *shdlc, int y_nr) +{ + struct sk_buff *skb; + + pr_debug("remote asks retransmition from frame %d\n", y_nr); + + if (llc_shdlc_x_lteq_y_lt_z(shdlc->dnr, y_nr, shdlc->ns)) { + if (shdlc->t2_active) { + del_timer_sync(&shdlc->t2_timer); + shdlc->t2_active = false; + pr_debug("Stopped T2(retransmit)\n"); + } + + if (shdlc->dnr != y_nr) { + while ((shdlc->dnr = ((shdlc->dnr + 1) % 8)) != y_nr) { + skb = skb_dequeue(&shdlc->ack_pending_q); + kfree_skb(skb); + } + } + + llc_shdlc_requeue_ack_pending(shdlc); + } +} + +/* See spec RR:10.8.3 REJ:10.8.4 */ +static void llc_shdlc_rcv_s_frame(struct llc_shdlc *shdlc, + enum sframe_type s_frame_type, int nr) +{ + struct sk_buff *skb; + + if (shdlc->state != SHDLC_CONNECTED) + return; + + switch (s_frame_type) { + case S_FRAME_RR: + llc_shdlc_rcv_ack(shdlc, nr); + if (shdlc->rnr == true) { /* see SHDLC 10.7.7 */ + shdlc->rnr = false; + if (shdlc->send_q.qlen == 0) { + skb = llc_shdlc_alloc_skb(shdlc, 0); + if (skb) + skb_queue_tail(&shdlc->send_q, skb); + } + } + break; + case S_FRAME_REJ: + llc_shdlc_rcv_rej(shdlc, nr); + break; + case S_FRAME_RNR: + llc_shdlc_rcv_ack(shdlc, nr); + shdlc->rnr = true; + break; + default: + break; + } +} + +static void llc_shdlc_connect_complete(struct llc_shdlc *shdlc, int r) +{ + pr_debug("result=%d\n", r); + + del_timer_sync(&shdlc->connect_timer); + + if (r == 0) { + shdlc->ns = 0; + shdlc->nr = 0; + shdlc->dnr = 0; + + shdlc->state = SHDLC_CONNECTED; + } else { + shdlc->state = SHDLC_DISCONNECTED; + } + + shdlc->connect_result = r; + + wake_up(shdlc->connect_wq); +} + +static int llc_shdlc_connect_initiate(struct llc_shdlc *shdlc) +{ + struct sk_buff *skb; + + pr_debug("\n"); + + skb = llc_shdlc_alloc_skb(shdlc, 2); + if (skb == NULL) + return -ENOMEM; + + *skb_put(skb, 1) = SHDLC_MAX_WINDOW; + *skb_put(skb, 1) = SHDLC_SREJ_SUPPORT ? 1 : 0; + + return llc_shdlc_send_u_frame(shdlc, skb, U_FRAME_RSET); +} + +static int llc_shdlc_connect_send_ua(struct llc_shdlc *shdlc) +{ + struct sk_buff *skb; + + pr_debug("\n"); + + skb = llc_shdlc_alloc_skb(shdlc, 0); + if (skb == NULL) + return -ENOMEM; + + return llc_shdlc_send_u_frame(shdlc, skb, U_FRAME_UA); +} + +static void llc_shdlc_rcv_u_frame(struct llc_shdlc *shdlc, + struct sk_buff *skb, + enum uframe_modifier u_frame_modifier) +{ + u8 w = SHDLC_MAX_WINDOW; + bool srej_support = SHDLC_SREJ_SUPPORT; + int r; + + pr_debug("u_frame_modifier=%d\n", u_frame_modifier); + + switch (u_frame_modifier) { + case U_FRAME_RSET: + if (shdlc->state == SHDLC_NEGOCIATING) { + /* we sent RSET, but chip wants to negociate */ + if (skb->len > 0) + w = skb->data[0]; + + if (skb->len > 1) + srej_support = skb->data[1] & 0x01 ? true : + false; + + if ((w <= SHDLC_MAX_WINDOW) && + (SHDLC_SREJ_SUPPORT || (srej_support == false))) { + shdlc->w = w; + shdlc->srej_support = srej_support; + r = llc_shdlc_connect_send_ua(shdlc); + llc_shdlc_connect_complete(shdlc, r); + } + } else if (shdlc->state == SHDLC_CONNECTED) { + /* + * Chip wants to reset link. This is unexpected and + * unsupported. + */ + shdlc->hard_fault = -ECONNRESET; + } + break; + case U_FRAME_UA: + if ((shdlc->state == SHDLC_CONNECTING && + shdlc->connect_tries > 0) || + (shdlc->state == SHDLC_NEGOCIATING)) + llc_shdlc_connect_complete(shdlc, 0); + break; + default: + break; + } + + kfree_skb(skb); +} + +static void llc_shdlc_handle_rcv_queue(struct llc_shdlc *shdlc) +{ + struct sk_buff *skb; + u8 control; + int nr; + int ns; + enum sframe_type s_frame_type; + enum uframe_modifier u_frame_modifier; + + if (shdlc->rcv_q.qlen) + pr_debug("rcvQlen=%d\n", shdlc->rcv_q.qlen); + + while ((skb = skb_dequeue(&shdlc->rcv_q)) != NULL) { + control = skb->data[0]; + skb_pull(skb, 1); + switch (control & SHDLC_CONTROL_HEAD_MASK) { + case SHDLC_CONTROL_HEAD_I: + case SHDLC_CONTROL_HEAD_I2: + ns = (control & SHDLC_CONTROL_NS_MASK) >> 3; + nr = control & SHDLC_CONTROL_NR_MASK; + llc_shdlc_rcv_i_frame(shdlc, skb, ns, nr); + break; + case SHDLC_CONTROL_HEAD_S: + s_frame_type = (control & SHDLC_CONTROL_TYPE_MASK) >> 3; + nr = control & SHDLC_CONTROL_NR_MASK; + llc_shdlc_rcv_s_frame(shdlc, s_frame_type, nr); + kfree_skb(skb); + break; + case SHDLC_CONTROL_HEAD_U: + u_frame_modifier = control & SHDLC_CONTROL_M_MASK; + llc_shdlc_rcv_u_frame(shdlc, skb, u_frame_modifier); + break; + default: + pr_err("UNKNOWN Control=%d\n", control); + kfree_skb(skb); + break; + } + } +} + +static int llc_shdlc_w_used(int ns, int dnr) +{ + int unack_count; + + if (dnr <= ns) + unack_count = ns - dnr; + else + unack_count = 8 - dnr + ns; + + return unack_count; +} + +/* Send frames according to algorithm at spec:10.8.1 */ +static void llc_shdlc_handle_send_queue(struct llc_shdlc *shdlc) +{ + struct sk_buff *skb; + int r; + unsigned long time_sent; + + if (shdlc->send_q.qlen) + pr_debug + ("sendQlen=%d ns=%d dnr=%d rnr=%s w_room=%d unackQlen=%d\n", + shdlc->send_q.qlen, shdlc->ns, shdlc->dnr, + shdlc->rnr == false ? "false" : "true", + shdlc->w - llc_shdlc_w_used(shdlc->ns, shdlc->dnr), + shdlc->ack_pending_q.qlen); + + while (shdlc->send_q.qlen && shdlc->ack_pending_q.qlen < shdlc->w && + (shdlc->rnr == false)) { + + if (shdlc->t1_active) { + del_timer_sync(&shdlc->t1_timer); + shdlc->t1_active = false; + pr_debug("Stopped T1(send ack)\n"); + } + + skb = skb_dequeue(&shdlc->send_q); + + *skb_push(skb, 1) = SHDLC_CONTROL_HEAD_I | (shdlc->ns << 3) | + shdlc->nr; + + pr_debug("Sending I-Frame %d, waiting to rcv %d\n", shdlc->ns, + shdlc->nr); + /* SHDLC_DUMP_SKB("shdlc frame written", skb); */ + + r = shdlc->xmit_to_drv(shdlc->hdev, skb); + if (r < 0) { + shdlc->hard_fault = r; + break; + } + + shdlc->ns = (shdlc->ns + 1) % 8; + + time_sent = jiffies; + *(unsigned long *)skb->cb = time_sent; + + skb_queue_tail(&shdlc->ack_pending_q, skb); + + if (shdlc->t2_active == false) { + shdlc->t2_active = true; + mod_timer(&shdlc->t2_timer, time_sent + + msecs_to_jiffies(SHDLC_T2_VALUE_MS)); + pr_debug("Started T2 (retransmit)\n"); + } + } +} + +static void llc_shdlc_connect_timeout(unsigned long data) +{ + struct llc_shdlc *shdlc = (struct llc_shdlc *)data; + + pr_debug("\n"); + + queue_work(system_nrt_wq, &shdlc->sm_work); +} + +static void llc_shdlc_t1_timeout(unsigned long data) +{ + struct llc_shdlc *shdlc = (struct llc_shdlc *)data; + + pr_debug("SoftIRQ: need to send ack\n"); + + queue_work(system_nrt_wq, &shdlc->sm_work); +} + +static void llc_shdlc_t2_timeout(unsigned long data) +{ + struct llc_shdlc *shdlc = (struct llc_shdlc *)data; + + pr_debug("SoftIRQ: need to retransmit\n"); + + queue_work(system_nrt_wq, &shdlc->sm_work); +} + +static void llc_shdlc_sm_work(struct work_struct *work) +{ + struct llc_shdlc *shdlc = container_of(work, struct llc_shdlc, sm_work); + int r; + + pr_debug("\n"); + + mutex_lock(&shdlc->state_mutex); + + switch (shdlc->state) { + case SHDLC_DISCONNECTED: + skb_queue_purge(&shdlc->rcv_q); + skb_queue_purge(&shdlc->send_q); + skb_queue_purge(&shdlc->ack_pending_q); + break; + case SHDLC_CONNECTING: + if (shdlc->hard_fault) { + llc_shdlc_connect_complete(shdlc, shdlc->hard_fault); + break; + } + + if (shdlc->connect_tries++ < 5) + r = llc_shdlc_connect_initiate(shdlc); + else + r = -ETIME; + if (r < 0) + llc_shdlc_connect_complete(shdlc, r); + else { + mod_timer(&shdlc->connect_timer, jiffies + + msecs_to_jiffies(SHDLC_CONNECT_VALUE_MS)); + + shdlc->state = SHDLC_NEGOCIATING; + } + break; + case SHDLC_NEGOCIATING: + if (timer_pending(&shdlc->connect_timer) == 0) { + shdlc->state = SHDLC_CONNECTING; + queue_work(system_nrt_wq, &shdlc->sm_work); + } + + llc_shdlc_handle_rcv_queue(shdlc); + + if (shdlc->hard_fault) { + llc_shdlc_connect_complete(shdlc, shdlc->hard_fault); + break; + } + break; + case SHDLC_CONNECTED: + llc_shdlc_handle_rcv_queue(shdlc); + llc_shdlc_handle_send_queue(shdlc); + + if (shdlc->t1_active && timer_pending(&shdlc->t1_timer) == 0) { + pr_debug + ("Handle T1(send ack) elapsed (T1 now inactive)\n"); + + shdlc->t1_active = false; + r = llc_shdlc_send_s_frame(shdlc, S_FRAME_RR, + shdlc->nr); + if (r < 0) + shdlc->hard_fault = r; + } + + if (shdlc->t2_active && timer_pending(&shdlc->t2_timer) == 0) { + pr_debug + ("Handle T2(retransmit) elapsed (T2 inactive)\n"); + + shdlc->t2_active = false; + + llc_shdlc_requeue_ack_pending(shdlc); + llc_shdlc_handle_send_queue(shdlc); + } + + if (shdlc->hard_fault) { + shdlc->llc_failure(shdlc->hdev, shdlc->hard_fault); + } + break; + default: + break; + } + mutex_unlock(&shdlc->state_mutex); +} + +/* + * Called from syscall context to establish shdlc link. Sleeps until + * link is ready or failure. + */ +static int llc_shdlc_connect(struct llc_shdlc *shdlc) +{ + DECLARE_WAIT_QUEUE_HEAD_ONSTACK(connect_wq); + + pr_debug("\n"); + + mutex_lock(&shdlc->state_mutex); + + shdlc->state = SHDLC_CONNECTING; + shdlc->connect_wq = &connect_wq; + shdlc->connect_tries = 0; + shdlc->connect_result = 1; + + mutex_unlock(&shdlc->state_mutex); + + queue_work(system_nrt_wq, &shdlc->sm_work); + + wait_event(connect_wq, shdlc->connect_result != 1); + + return shdlc->connect_result; +} + +static void llc_shdlc_disconnect(struct llc_shdlc *shdlc) +{ + pr_debug("\n"); + + mutex_lock(&shdlc->state_mutex); + + shdlc->state = SHDLC_DISCONNECTED; + + mutex_unlock(&shdlc->state_mutex); + + queue_work(system_nrt_wq, &shdlc->sm_work); +} + +/* + * Receive an incoming shdlc frame. Frame has already been crc-validated. + * skb contains only LLC header and payload. + * If skb == NULL, it is a notification that the link below is dead. + */ +static void llc_shdlc_recv_frame(struct llc_shdlc *shdlc, struct sk_buff *skb) +{ + if (skb == NULL) { + pr_err("NULL Frame -> link is dead\n"); + shdlc->hard_fault = -EREMOTEIO; + } else { + SHDLC_DUMP_SKB("incoming frame", skb); + skb_queue_tail(&shdlc->rcv_q, skb); + } + + queue_work(system_nrt_wq, &shdlc->sm_work); +} + +static void *llc_shdlc_init(struct nfc_hci_dev *hdev, xmit_to_drv_t xmit_to_drv, + rcv_to_hci_t rcv_to_hci, int tx_headroom, + int tx_tailroom, int *rx_headroom, int *rx_tailroom, + llc_failure_t llc_failure) +{ + struct llc_shdlc *shdlc; + + *rx_headroom = SHDLC_LLC_HEAD_ROOM; + *rx_tailroom = 0; + + shdlc = kzalloc(sizeof(struct llc_shdlc), GFP_KERNEL); + if (shdlc == NULL) + return NULL; + + mutex_init(&shdlc->state_mutex); + shdlc->state = SHDLC_DISCONNECTED; + + init_timer(&shdlc->connect_timer); + shdlc->connect_timer.data = (unsigned long)shdlc; + shdlc->connect_timer.function = llc_shdlc_connect_timeout; + + init_timer(&shdlc->t1_timer); + shdlc->t1_timer.data = (unsigned long)shdlc; + shdlc->t1_timer.function = llc_shdlc_t1_timeout; + + init_timer(&shdlc->t2_timer); + shdlc->t2_timer.data = (unsigned long)shdlc; + shdlc->t2_timer.function = llc_shdlc_t2_timeout; + + shdlc->w = SHDLC_MAX_WINDOW; + shdlc->srej_support = SHDLC_SREJ_SUPPORT; + + skb_queue_head_init(&shdlc->rcv_q); + skb_queue_head_init(&shdlc->send_q); + skb_queue_head_init(&shdlc->ack_pending_q); + + INIT_WORK(&shdlc->sm_work, llc_shdlc_sm_work); + + shdlc->hdev = hdev; + shdlc->xmit_to_drv = xmit_to_drv; + shdlc->rcv_to_hci = rcv_to_hci; + shdlc->tx_headroom = tx_headroom; + shdlc->tx_tailroom = tx_tailroom; + shdlc->llc_failure = llc_failure; + + return shdlc; +} + +static void llc_shdlc_deinit(struct nfc_llc *llc) +{ + struct llc_shdlc *shdlc = nfc_llc_get_data(llc); + + skb_queue_purge(&shdlc->rcv_q); + skb_queue_purge(&shdlc->send_q); + skb_queue_purge(&shdlc->ack_pending_q); + + kfree(shdlc); +} + +static int llc_shdlc_start(struct nfc_llc *llc) +{ + struct llc_shdlc *shdlc = nfc_llc_get_data(llc); + + return llc_shdlc_connect(shdlc); +} + +static int llc_shdlc_stop(struct nfc_llc *llc) +{ + struct llc_shdlc *shdlc = nfc_llc_get_data(llc); + + llc_shdlc_disconnect(shdlc); + + return 0; +} + +static void llc_shdlc_rcv_from_drv(struct nfc_llc *llc, struct sk_buff *skb) +{ + struct llc_shdlc *shdlc = nfc_llc_get_data(llc); + + llc_shdlc_recv_frame(shdlc, skb); +} + +static int llc_shdlc_xmit_from_hci(struct nfc_llc *llc, struct sk_buff *skb) +{ + struct llc_shdlc *shdlc = nfc_llc_get_data(llc); + + skb_queue_tail(&shdlc->send_q, skb); + + queue_work(system_nrt_wq, &shdlc->sm_work); + + return 0; +} + +static struct nfc_llc_ops llc_shdlc_ops = { + .init = llc_shdlc_init, + .deinit = llc_shdlc_deinit, + .start = llc_shdlc_start, + .stop = llc_shdlc_stop, + .rcv_from_drv = llc_shdlc_rcv_from_drv, + .xmit_from_hci = llc_shdlc_xmit_from_hci, +}; + +int nfc_llc_shdlc_register() +{ + return nfc_llc_register(LLC_SHDLC_NAME, &llc_shdlc_ops); +} +EXPORT_SYMBOL(nfc_llc_shdlc_register); From 412fda538f4b1317ecd0fbe6e5bc9124792bea88 Mon Sep 17 00:00:00 2001 From: Eric Lapuyade Date: Tue, 18 Sep 2012 19:45:48 +0200 Subject: [PATCH 20/31] NFC: Changed HCI and PN544 HCI driver to use the new HCI LLC Core The previous shdlc HCI driver and its header are removed from the tree. PN544 now registers directly with HCI and passes the name of the llc it requires (shdlc). HCI instantiation now allocates the required llc instance. The llc is started when the HCI device is brought up. Signed-off-by: Eric Lapuyade Signed-off-by: Samuel Ortiz --- drivers/nfc/Kconfig | 2 +- drivers/nfc/pn544_hci.c | 73 ++-- include/net/nfc/hci.h | 3 + include/net/nfc/shdlc.h | 106 ----- net/nfc/hci/Makefile | 2 +- net/nfc/hci/core.c | 284 +++++++------ net/nfc/hci/llc_shdlc.c | 2 +- net/nfc/hci/shdlc.c | 918 ---------------------------------------- 8 files changed, 201 insertions(+), 1189 deletions(-) delete mode 100644 include/net/nfc/shdlc.h delete mode 100644 net/nfc/hci/shdlc.c diff --git a/drivers/nfc/Kconfig b/drivers/nfc/Kconfig index 3b20b73ee649..89c57d1a7cad 100644 --- a/drivers/nfc/Kconfig +++ b/drivers/nfc/Kconfig @@ -19,7 +19,7 @@ config PN544_NFC config PN544_HCI_NFC tristate "HCI PN544 NFC driver" - depends on I2C && NFC_SHDLC + depends on I2C && NFC_HCI && NFC_SHDLC select CRC_CCITT default n ---help--- diff --git a/drivers/nfc/pn544_hci.c b/drivers/nfc/pn544_hci.c index a176d1f7bbf3..e681da2e3413 100644 --- a/drivers/nfc/pn544_hci.c +++ b/drivers/nfc/pn544_hci.c @@ -29,7 +29,7 @@ #include #include -#include +#include #include @@ -133,7 +133,7 @@ static struct nfc_hci_gate pn544_gates[] = { struct pn544_hci_info { struct i2c_client *i2c_dev; - struct nfc_shdlc *shdlc; + struct nfc_hci_dev *hdev; enum pn544_state state; @@ -362,21 +362,21 @@ static irqreturn_t pn544_hci_irq_thread_fn(int irq, void *dev_id) if (r == -EREMOTEIO) { info->hard_fault = r; - nfc_shdlc_recv_frame(info->shdlc, NULL); + nfc_hci_recv_frame(info->hdev, NULL); return IRQ_HANDLED; } else if ((r == -ENOMEM) || (r == -EBADMSG)) { return IRQ_HANDLED; } - nfc_shdlc_recv_frame(info->shdlc, skb); + nfc_hci_recv_frame(info->hdev, skb); return IRQ_HANDLED; } -static int pn544_hci_open(struct nfc_shdlc *shdlc) +static int pn544_hci_open(struct nfc_hci_dev *hdev) { - struct pn544_hci_info *info = nfc_shdlc_get_clientdata(shdlc); + struct pn544_hci_info *info = nfc_hci_get_clientdata(hdev); int r = 0; mutex_lock(&info->info_lock); @@ -396,9 +396,9 @@ out: return r; } -static void pn544_hci_close(struct nfc_shdlc *shdlc) +static void pn544_hci_close(struct nfc_hci_dev *hdev) { - struct pn544_hci_info *info = nfc_shdlc_get_clientdata(shdlc); + struct pn544_hci_info *info = nfc_hci_get_clientdata(hdev); mutex_lock(&info->info_lock); @@ -413,9 +413,8 @@ out: mutex_unlock(&info->info_lock); } -static int pn544_hci_ready(struct nfc_shdlc *shdlc) +static int pn544_hci_ready(struct nfc_hci_dev *hdev) { - struct nfc_hci_dev *hdev = nfc_shdlc_get_hci_dev(shdlc); struct sk_buff *skb; static struct hw_config { u8 adr[2]; @@ -601,9 +600,9 @@ static void pn544_hci_remove_len_crc(struct sk_buff *skb) skb_trim(skb, PN544_FRAME_TAILROOM); } -static int pn544_hci_xmit(struct nfc_shdlc *shdlc, struct sk_buff *skb) +static int pn544_hci_xmit(struct nfc_hci_dev *hdev, struct sk_buff *skb) { - struct pn544_hci_info *info = nfc_shdlc_get_clientdata(shdlc); + struct pn544_hci_info *info = nfc_hci_get_clientdata(hdev); struct i2c_client *client = info->i2c_dev; int r; @@ -617,10 +616,9 @@ static int pn544_hci_xmit(struct nfc_shdlc *shdlc, struct sk_buff *skb) return r; } -static int pn544_hci_start_poll(struct nfc_shdlc *shdlc, +static int pn544_hci_start_poll(struct nfc_hci_dev *hdev, u32 im_protocols, u32 tm_protocols) { - struct nfc_hci_dev *hdev = nfc_shdlc_get_hci_dev(shdlc); u8 phases = 0; int r; u8 duration[2]; @@ -671,7 +669,7 @@ static int pn544_hci_start_poll(struct nfc_shdlc *shdlc, return r; } -static int pn544_hci_target_from_gate(struct nfc_shdlc *shdlc, u8 gate, +static int pn544_hci_target_from_gate(struct nfc_hci_dev *hdev, u8 gate, struct nfc_target *target) { switch (gate) { @@ -689,11 +687,10 @@ static int pn544_hci_target_from_gate(struct nfc_shdlc *shdlc, u8 gate, return 0; } -static int pn544_hci_complete_target_discovered(struct nfc_shdlc *shdlc, +static int pn544_hci_complete_target_discovered(struct nfc_hci_dev *hdev, u8 gate, struct nfc_target *target) { - struct nfc_hci_dev *hdev = nfc_shdlc_get_hci_dev(shdlc); struct sk_buff *uid_skb; int r = 0; @@ -765,13 +762,12 @@ static void pn544_hci_data_exchange_cb(void *context, struct sk_buff *skb, * <= 0: driver handled the data exchange * 1: driver doesn't especially handle, please do standard processing */ -static int pn544_hci_data_exchange(struct nfc_shdlc *shdlc, +static int pn544_hci_data_exchange(struct nfc_hci_dev *hdev, struct nfc_target *target, struct sk_buff *skb, data_exchange_cb_t cb, void *cb_context) { - struct pn544_hci_info *info = nfc_shdlc_get_clientdata(shdlc); - struct nfc_hci_dev *hdev = nfc_shdlc_get_hci_dev(shdlc); + struct pn544_hci_info *info = nfc_hci_get_clientdata(hdev); pr_info(DRIVER_DESC ": %s for gate=%d\n", __func__, target->hci_reader_gate); @@ -824,17 +820,15 @@ static int pn544_hci_data_exchange(struct nfc_shdlc *shdlc, } } -static int pn544_hci_check_presence(struct nfc_shdlc *shdlc, +static int pn544_hci_check_presence(struct nfc_hci_dev *hdev, struct nfc_target *target) { - struct nfc_hci_dev *hdev = nfc_shdlc_get_hci_dev(shdlc); - return nfc_hci_send_cmd(hdev, target->hci_reader_gate, PN544_RF_READER_CMD_PRESENCE_CHECK, NULL, 0, NULL); } -static struct nfc_shdlc_ops pn544_shdlc_ops = { +static struct nfc_hci_ops pn544_hci_ops = { .open = pn544_hci_open, .close = pn544_hci_close, .hci_ready = pn544_hci_ready, @@ -926,23 +920,30 @@ static int __devinit pn544_hci_probe(struct i2c_client *client, NFC_PROTO_ISO14443_B_MASK | NFC_PROTO_NFC_DEP_MASK; - info->shdlc = nfc_shdlc_allocate(&pn544_shdlc_ops, - &init_data, protocols, - PN544_FRAME_HEADROOM + PN544_CMDS_HEADROOM, - PN544_FRAME_TAILROOM, - PN544_HCI_LLC_MAX_PAYLOAD, - dev_name(&client->dev)); - if (!info->shdlc) { - dev_err(&client->dev, "Cannot allocate nfc shdlc.\n"); + info->hdev = nfc_hci_allocate_device(&pn544_hci_ops, &init_data, + protocols, LLC_SHDLC_NAME, + PN544_FRAME_HEADROOM + + PN544_CMDS_HEADROOM, + PN544_FRAME_TAILROOM, + PN544_HCI_LLC_MAX_PAYLOAD); + if (!info->hdev) { + dev_err(&client->dev, "Cannot allocate nfc hdev.\n"); r = -ENOMEM; - goto err_allocshdlc; + goto err_alloc_hdev; } - nfc_shdlc_set_clientdata(info->shdlc, info); + nfc_hci_set_clientdata(info->hdev, info); + + r = nfc_hci_register_device(info->hdev); + if (r) + goto err_regdev; return 0; -err_allocshdlc: +err_regdev: + nfc_hci_free_device(info->hdev); + +err_alloc_hdev: free_irq(client->irq, info); err_rti: @@ -963,7 +964,7 @@ static __devexit int pn544_hci_remove(struct i2c_client *client) dev_dbg(&client->dev, "%s\n", __func__); - nfc_shdlc_free(info->shdlc); + nfc_hci_free_device(info->hdev); if (info->state != PN544_ST_COLD) { if (pdata->disable) diff --git a/include/net/nfc/hci.h b/include/net/nfc/hci.h index 6cee6e21fc46..9b5ed2d94d60 100644 --- a/include/net/nfc/hci.h +++ b/include/net/nfc/hci.h @@ -87,6 +87,8 @@ struct nfc_hci_dev { struct nfc_hci_ops *ops; + struct nfc_llc *llc; + struct nfc_hci_init_data init_data; void *clientdata; @@ -113,6 +115,7 @@ struct nfc_hci_dev { struct nfc_hci_dev *nfc_hci_allocate_device(struct nfc_hci_ops *ops, struct nfc_hci_init_data *init_data, u32 protocols, + const char *llc_name, int tx_headroom, int tx_tailroom, int max_link_payload); diff --git a/include/net/nfc/shdlc.h b/include/net/nfc/shdlc.h deleted file mode 100644 index fe5e19829352..000000000000 --- a/include/net/nfc/shdlc.h +++ /dev/null @@ -1,106 +0,0 @@ -/* - * Copyright (C) 2012 Intel Corporation. All rights reserved. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * 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, write to the - * Free Software Foundation, Inc., - * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - */ - -#ifndef __NFC_SHDLC_H -#define __NFC_SHDLC_H - -struct nfc_shdlc; - -struct nfc_shdlc_ops { - int (*open) (struct nfc_shdlc *shdlc); - void (*close) (struct nfc_shdlc *shdlc); - int (*hci_ready) (struct nfc_shdlc *shdlc); - int (*xmit) (struct nfc_shdlc *shdlc, struct sk_buff *skb); - int (*start_poll) (struct nfc_shdlc *shdlc, - u32 im_protocols, u32 tm_protocols); - int (*target_from_gate) (struct nfc_shdlc *shdlc, u8 gate, - struct nfc_target *target); - int (*complete_target_discovered) (struct nfc_shdlc *shdlc, u8 gate, - struct nfc_target *target); - int (*data_exchange) (struct nfc_shdlc *shdlc, - struct nfc_target *target, struct sk_buff *skb, - data_exchange_cb_t cb, void *cb_context); - int (*check_presence)(struct nfc_shdlc *shdlc, - struct nfc_target *target); -}; - -enum shdlc_state { - SHDLC_DISCONNECTED = 0, - SHDLC_CONNECTING = 1, - SHDLC_NEGOCIATING = 2, - SHDLC_CONNECTED = 3 -}; - -struct nfc_shdlc { - struct mutex state_mutex; - enum shdlc_state state; - int hard_fault; - - struct nfc_hci_dev *hdev; - - wait_queue_head_t *connect_wq; - int connect_tries; - int connect_result; - struct timer_list connect_timer;/* aka T3 in spec 10.6.1 */ - - u8 w; /* window size */ - bool srej_support; - - struct timer_list t1_timer; /* send ack timeout */ - bool t1_active; - - struct timer_list t2_timer; /* guard/retransmit timeout */ - bool t2_active; - - int ns; /* next seq num for send */ - int nr; /* next expected seq num for receive */ - int dnr; /* oldest sent unacked seq num */ - - struct sk_buff_head rcv_q; - - struct sk_buff_head send_q; - bool rnr; /* other side is not ready to receive */ - - struct sk_buff_head ack_pending_q; - - struct work_struct sm_work; - - struct nfc_shdlc_ops *ops; - - int client_headroom; - int client_tailroom; - - void *clientdata; -}; - -void nfc_shdlc_recv_frame(struct nfc_shdlc *shdlc, struct sk_buff *skb); - -struct nfc_shdlc *nfc_shdlc_allocate(struct nfc_shdlc_ops *ops, - struct nfc_hci_init_data *init_data, - u32 protocols, - int tx_headroom, int tx_tailroom, - int max_link_payload, const char *devname); - -void nfc_shdlc_free(struct nfc_shdlc *shdlc); - -void nfc_shdlc_set_clientdata(struct nfc_shdlc *shdlc, void *clientdata); -void *nfc_shdlc_get_clientdata(struct nfc_shdlc *shdlc); -struct nfc_hci_dev *nfc_shdlc_get_hci_dev(struct nfc_shdlc *shdlc); - -#endif /* __NFC_SHDLC_H */ diff --git a/net/nfc/hci/Makefile b/net/nfc/hci/Makefile index c4d65479629b..c5dbb6891b24 100644 --- a/net/nfc/hci/Makefile +++ b/net/nfc/hci/Makefile @@ -5,4 +5,4 @@ obj-$(CONFIG_NFC_HCI) += hci.o hci-y := core.o hcp.o command.o llc.o llc_nop.o -hci-$(CONFIG_NFC_SHDLC) += shdlc.o llc_shdlc.o +hci-$(CONFIG_NFC_SHDLC) += llc_shdlc.o diff --git a/net/nfc/hci/core.c b/net/nfc/hci/core.c index 069e2d6056e5..c1129c22d835 100644 --- a/net/nfc/hci/core.c +++ b/net/nfc/hci/core.c @@ -78,7 +78,7 @@ next_msg: pr_debug("msg_tx_queue has a cmd to send\n"); while ((skb = skb_dequeue(&msg->msg_frags)) != NULL) { - r = hdev->ops->xmit(hdev, skb); + r = nfc_llc_xmit_from_hci(hdev->llc, skb); if (r < 0) { kfree_skb(skb); skb_queue_purge(&msg->msg_frags); @@ -469,29 +469,38 @@ static int hci_dev_up(struct nfc_dev *nfc_dev) return r; } + r = nfc_llc_start(hdev->llc); + if (r < 0) + goto exit_close; + r = hci_dev_session_init(hdev); if (r < 0) - goto exit; + goto exit_llc; r = nfc_hci_send_event(hdev, NFC_HCI_RF_READER_A_GATE, NFC_HCI_EVT_END_OPERATION, NULL, 0); if (r < 0) - goto exit; + goto exit_llc; if (hdev->ops->hci_ready) { r = hdev->ops->hci_ready(hdev); if (r < 0) - goto exit; + goto exit_llc; } r = hci_dev_version(hdev); if (r < 0) - goto exit; + goto exit_llc; + + return 0; + +exit_llc: + nfc_llc_stop(hdev->llc); + +exit_close: + if (hdev->ops->close) + hdev->ops->close(hdev); -exit: - if (r < 0) - if (hdev->ops->close) - hdev->ops->close(hdev); return r; } @@ -499,6 +508,8 @@ static int hci_dev_down(struct nfc_dev *nfc_dev) { struct nfc_hci_dev *hdev = nfc_get_drvdata(nfc_dev); + nfc_llc_stop(hdev->llc); + if (hdev->ops->close) hdev->ops->close(hdev); @@ -620,119 +631,6 @@ static int hci_check_presence(struct nfc_dev *nfc_dev, return 0; } -static struct nfc_ops hci_nfc_ops = { - .dev_up = hci_dev_up, - .dev_down = hci_dev_down, - .start_poll = hci_start_poll, - .stop_poll = hci_stop_poll, - .activate_target = hci_activate_target, - .deactivate_target = hci_deactivate_target, - .im_transceive = hci_transceive, - .check_presence = hci_check_presence, -}; - -struct nfc_hci_dev *nfc_hci_allocate_device(struct nfc_hci_ops *ops, - struct nfc_hci_init_data *init_data, - u32 protocols, - int tx_headroom, - int tx_tailroom, - int max_link_payload) -{ - struct nfc_hci_dev *hdev; - - if (ops->xmit == NULL) - return NULL; - - if (protocols == 0) - return NULL; - - hdev = kzalloc(sizeof(struct nfc_hci_dev), GFP_KERNEL); - if (hdev == NULL) - return NULL; - - hdev->ndev = nfc_allocate_device(&hci_nfc_ops, protocols, - tx_headroom + HCI_CMDS_HEADROOM, - tx_tailroom); - if (!hdev->ndev) { - kfree(hdev); - return NULL; - } - - hdev->ops = ops; - hdev->max_data_link_payload = max_link_payload; - hdev->init_data = *init_data; - - nfc_set_drvdata(hdev->ndev, hdev); - - memset(hdev->gate2pipe, NFC_HCI_INVALID_PIPE, sizeof(hdev->gate2pipe)); - - return hdev; -} -EXPORT_SYMBOL(nfc_hci_allocate_device); - -void nfc_hci_free_device(struct nfc_hci_dev *hdev) -{ - nfc_free_device(hdev->ndev); - kfree(hdev); -} -EXPORT_SYMBOL(nfc_hci_free_device); - -int nfc_hci_register_device(struct nfc_hci_dev *hdev) -{ - mutex_init(&hdev->msg_tx_mutex); - - INIT_LIST_HEAD(&hdev->msg_tx_queue); - - INIT_WORK(&hdev->msg_tx_work, nfc_hci_msg_tx_work); - - init_timer(&hdev->cmd_timer); - hdev->cmd_timer.data = (unsigned long)hdev; - hdev->cmd_timer.function = nfc_hci_cmd_timeout; - - skb_queue_head_init(&hdev->rx_hcp_frags); - - INIT_WORK(&hdev->msg_rx_work, nfc_hci_msg_rx_work); - - skb_queue_head_init(&hdev->msg_rx_queue); - - return nfc_register_device(hdev->ndev); -} -EXPORT_SYMBOL(nfc_hci_register_device); - -void nfc_hci_unregister_device(struct nfc_hci_dev *hdev) -{ - struct hci_msg *msg, *n; - - skb_queue_purge(&hdev->rx_hcp_frags); - skb_queue_purge(&hdev->msg_rx_queue); - - list_for_each_entry_safe(msg, n, &hdev->msg_tx_queue, msg_l) { - list_del(&msg->msg_l); - skb_queue_purge(&msg->msg_frags); - kfree(msg); - } - - del_timer_sync(&hdev->cmd_timer); - - nfc_unregister_device(hdev->ndev); - - cancel_work_sync(&hdev->msg_tx_work); - cancel_work_sync(&hdev->msg_rx_work); -} -EXPORT_SYMBOL(nfc_hci_unregister_device); - -void nfc_hci_set_clientdata(struct nfc_hci_dev *hdev, void *clientdata) -{ - hdev->clientdata = clientdata; -} -EXPORT_SYMBOL(nfc_hci_set_clientdata); - -void *nfc_hci_get_clientdata(struct nfc_hci_dev *hdev) -{ - return hdev->clientdata; -} -EXPORT_SYMBOL(nfc_hci_get_clientdata); - static void nfc_hci_failure(struct nfc_hci_dev *hdev, int err) { mutex_lock(&hdev->msg_tx_mutex); @@ -748,13 +646,12 @@ exit: mutex_unlock(&hdev->msg_tx_mutex); } -void nfc_hci_driver_failure(struct nfc_hci_dev *hdev, int err) +static void nfc_hci_llc_failure(struct nfc_hci_dev *hdev, int err) { nfc_hci_failure(hdev, err); } -EXPORT_SYMBOL(nfc_hci_driver_failure); -void nfc_hci_recv_frame(struct nfc_hci_dev *hdev, struct sk_buff *skb) +static void nfc_hci_recv_from_llc(struct nfc_hci_dev *hdev, struct sk_buff *skb) { struct hcp_packet *packet; u8 type; @@ -820,6 +717,141 @@ void nfc_hci_recv_frame(struct nfc_hci_dev *hdev, struct sk_buff *skb) queue_work(system_nrt_wq, &hdev->msg_rx_work); } } + +static struct nfc_ops hci_nfc_ops = { + .dev_up = hci_dev_up, + .dev_down = hci_dev_down, + .start_poll = hci_start_poll, + .stop_poll = hci_stop_poll, + .activate_target = hci_activate_target, + .deactivate_target = hci_deactivate_target, + .im_transceive = hci_transceive, + .check_presence = hci_check_presence, +}; + +struct nfc_hci_dev *nfc_hci_allocate_device(struct nfc_hci_ops *ops, + struct nfc_hci_init_data *init_data, + u32 protocols, + const char *llc_name, + int tx_headroom, + int tx_tailroom, + int max_link_payload) +{ + struct nfc_hci_dev *hdev; + + if (ops->xmit == NULL) + return NULL; + + if (protocols == 0) + return NULL; + + hdev = kzalloc(sizeof(struct nfc_hci_dev), GFP_KERNEL); + if (hdev == NULL) + return NULL; + + hdev->llc = nfc_llc_allocate(llc_name, hdev, ops->xmit, + nfc_hci_recv_from_llc, tx_headroom, + tx_tailroom, nfc_hci_llc_failure); + if (hdev->llc == NULL) { + kfree(hdev); + return NULL; + } + + hdev->ndev = nfc_allocate_device(&hci_nfc_ops, protocols, + tx_headroom + HCI_CMDS_HEADROOM, + tx_tailroom); + if (!hdev->ndev) { + nfc_llc_free(hdev->llc); + kfree(hdev); + return NULL; + } + + hdev->ops = ops; + hdev->max_data_link_payload = max_link_payload; + hdev->init_data = *init_data; + + nfc_set_drvdata(hdev->ndev, hdev); + + memset(hdev->gate2pipe, NFC_HCI_INVALID_PIPE, sizeof(hdev->gate2pipe)); + + return hdev; +} +EXPORT_SYMBOL(nfc_hci_allocate_device); + +void nfc_hci_free_device(struct nfc_hci_dev *hdev) +{ + nfc_free_device(hdev->ndev); + nfc_llc_free(hdev->llc); + kfree(hdev); +} +EXPORT_SYMBOL(nfc_hci_free_device); + +int nfc_hci_register_device(struct nfc_hci_dev *hdev) +{ + mutex_init(&hdev->msg_tx_mutex); + + INIT_LIST_HEAD(&hdev->msg_tx_queue); + + INIT_WORK(&hdev->msg_tx_work, nfc_hci_msg_tx_work); + + init_timer(&hdev->cmd_timer); + hdev->cmd_timer.data = (unsigned long)hdev; + hdev->cmd_timer.function = nfc_hci_cmd_timeout; + + skb_queue_head_init(&hdev->rx_hcp_frags); + + INIT_WORK(&hdev->msg_rx_work, nfc_hci_msg_rx_work); + + skb_queue_head_init(&hdev->msg_rx_queue); + + return nfc_register_device(hdev->ndev); +} +EXPORT_SYMBOL(nfc_hci_register_device); + +void nfc_hci_unregister_device(struct nfc_hci_dev *hdev) +{ + struct hci_msg *msg, *n; + + skb_queue_purge(&hdev->rx_hcp_frags); + skb_queue_purge(&hdev->msg_rx_queue); + + list_for_each_entry_safe(msg, n, &hdev->msg_tx_queue, msg_l) { + list_del(&msg->msg_l); + skb_queue_purge(&msg->msg_frags); + kfree(msg); + } + + del_timer_sync(&hdev->cmd_timer); + + nfc_unregister_device(hdev->ndev); + + cancel_work_sync(&hdev->msg_tx_work); + cancel_work_sync(&hdev->msg_rx_work); +} +EXPORT_SYMBOL(nfc_hci_unregister_device); + +void nfc_hci_set_clientdata(struct nfc_hci_dev *hdev, void *clientdata) +{ + hdev->clientdata = clientdata; +} +EXPORT_SYMBOL(nfc_hci_set_clientdata); + +void *nfc_hci_get_clientdata(struct nfc_hci_dev *hdev) +{ + return hdev->clientdata; +} +EXPORT_SYMBOL(nfc_hci_get_clientdata); + +void nfc_hci_driver_failure(struct nfc_hci_dev *hdev, int err) +{ + nfc_hci_failure(hdev, err); +} +EXPORT_SYMBOL(nfc_hci_driver_failure); + +void inline nfc_hci_recv_frame(struct nfc_hci_dev *hdev, struct sk_buff *skb) +{ + nfc_llc_rcv_from_drv(hdev->llc, skb); +} EXPORT_SYMBOL(nfc_hci_recv_frame); static int __init nfc_hci_init(void) @@ -832,7 +864,7 @@ static void __exit nfc_hci_exit(void) nfc_llc_exit(); } -module_init(nfc_hci_init); +subsys_initcall(nfc_hci_init); module_exit(nfc_hci_exit); MODULE_LICENSE("GPL"); diff --git a/net/nfc/hci/llc_shdlc.c b/net/nfc/hci/llc_shdlc.c index bb191100ee96..fad6cd18d613 100644 --- a/net/nfc/hci/llc_shdlc.c +++ b/net/nfc/hci/llc_shdlc.c @@ -535,7 +535,7 @@ static void llc_shdlc_handle_send_queue(struct llc_shdlc *shdlc) pr_debug("Sending I-Frame %d, waiting to rcv %d\n", shdlc->ns, shdlc->nr); - /* SHDLC_DUMP_SKB("shdlc frame written", skb); */ + SHDLC_DUMP_SKB("shdlc frame written", skb); r = shdlc->xmit_to_drv(shdlc->hdev, skb); if (r < 0) { diff --git a/net/nfc/hci/shdlc.c b/net/nfc/hci/shdlc.c deleted file mode 100644 index c63af7d3e859..000000000000 --- a/net/nfc/hci/shdlc.c +++ /dev/null @@ -1,918 +0,0 @@ -/* - * Copyright (C) 2012 Intel Corporation. All rights reserved. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * 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, write to the - * Free Software Foundation, Inc., - * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - */ - -#define pr_fmt(fmt) "shdlc: %s: " fmt, __func__ - -#include -#include -#include -#include -#include - -#include -#include - -#define SHDLC_LLC_HEAD_ROOM 2 - -#define SHDLC_MAX_WINDOW 4 -#define SHDLC_SREJ_SUPPORT false - -#define SHDLC_CONTROL_HEAD_MASK 0xe0 -#define SHDLC_CONTROL_HEAD_I 0x80 -#define SHDLC_CONTROL_HEAD_I2 0xa0 -#define SHDLC_CONTROL_HEAD_S 0xc0 -#define SHDLC_CONTROL_HEAD_U 0xe0 - -#define SHDLC_CONTROL_NS_MASK 0x38 -#define SHDLC_CONTROL_NR_MASK 0x07 -#define SHDLC_CONTROL_TYPE_MASK 0x18 - -#define SHDLC_CONTROL_M_MASK 0x1f - -enum sframe_type { - S_FRAME_RR = 0x00, - S_FRAME_REJ = 0x01, - S_FRAME_RNR = 0x02, - S_FRAME_SREJ = 0x03 -}; - -enum uframe_modifier { - U_FRAME_UA = 0x06, - U_FRAME_RSET = 0x19 -}; - -#define SHDLC_CONNECT_VALUE_MS 5 -#define SHDLC_T1_VALUE_MS(w) ((5 * w) / 4) -#define SHDLC_T2_VALUE_MS 300 - -#define SHDLC_DUMP_SKB(info, skb) \ -do { \ - pr_debug("%s:\n", info); \ - print_hex_dump(KERN_DEBUG, "shdlc: ", DUMP_PREFIX_OFFSET, \ - 16, 1, skb->data, skb->len, 0); \ -} while (0) - -/* checks x < y <= z modulo 8 */ -static bool nfc_shdlc_x_lt_y_lteq_z(int x, int y, int z) -{ - if (x < z) - return ((x < y) && (y <= z)) ? true : false; - else - return ((y > x) || (y <= z)) ? true : false; -} - -/* checks x <= y < z modulo 8 */ -static bool nfc_shdlc_x_lteq_y_lt_z(int x, int y, int z) -{ - if (x <= z) - return ((x <= y) && (y < z)) ? true : false; - else /* x > z -> z+8 > x */ - return ((y >= x) || (y < z)) ? true : false; -} - -static struct sk_buff *nfc_shdlc_alloc_skb(struct nfc_shdlc *shdlc, - int payload_len) -{ - struct sk_buff *skb; - - skb = alloc_skb(shdlc->client_headroom + SHDLC_LLC_HEAD_ROOM + - shdlc->client_tailroom + payload_len, GFP_KERNEL); - if (skb) - skb_reserve(skb, shdlc->client_headroom + SHDLC_LLC_HEAD_ROOM); - - return skb; -} - -/* immediately sends an S frame. */ -static int nfc_shdlc_send_s_frame(struct nfc_shdlc *shdlc, - enum sframe_type sframe_type, int nr) -{ - int r; - struct sk_buff *skb; - - pr_debug("sframe_type=%d nr=%d\n", sframe_type, nr); - - skb = nfc_shdlc_alloc_skb(shdlc, 0); - if (skb == NULL) - return -ENOMEM; - - *skb_push(skb, 1) = SHDLC_CONTROL_HEAD_S | (sframe_type << 3) | nr; - - r = shdlc->ops->xmit(shdlc, skb); - - kfree_skb(skb); - - return r; -} - -/* immediately sends an U frame. skb may contain optional payload */ -static int nfc_shdlc_send_u_frame(struct nfc_shdlc *shdlc, - struct sk_buff *skb, - enum uframe_modifier uframe_modifier) -{ - int r; - - pr_debug("uframe_modifier=%d\n", uframe_modifier); - - *skb_push(skb, 1) = SHDLC_CONTROL_HEAD_U | uframe_modifier; - - r = shdlc->ops->xmit(shdlc, skb); - - kfree_skb(skb); - - return r; -} - -/* - * Free ack_pending frames until y_nr - 1, and reset t2 according to - * the remaining oldest ack_pending frame sent time - */ -static void nfc_shdlc_reset_t2(struct nfc_shdlc *shdlc, int y_nr) -{ - struct sk_buff *skb; - int dnr = shdlc->dnr; /* MUST initially be < y_nr */ - - pr_debug("release ack pending up to frame %d excluded\n", y_nr); - - while (dnr != y_nr) { - pr_debug("release ack pending frame %d\n", dnr); - - skb = skb_dequeue(&shdlc->ack_pending_q); - kfree_skb(skb); - - dnr = (dnr + 1) % 8; - } - - if (skb_queue_empty(&shdlc->ack_pending_q)) { - if (shdlc->t2_active) { - del_timer_sync(&shdlc->t2_timer); - shdlc->t2_active = false; - - pr_debug - ("All sent frames acked. Stopped T2(retransmit)\n"); - } - } else { - skb = skb_peek(&shdlc->ack_pending_q); - - mod_timer(&shdlc->t2_timer, *(unsigned long *)skb->cb + - msecs_to_jiffies(SHDLC_T2_VALUE_MS)); - shdlc->t2_active = true; - - pr_debug - ("Start T2(retransmit) for remaining unacked sent frames\n"); - } -} - -/* - * Receive validated frames from lower layer. skb contains HCI payload only. - * Handle according to algorithm at spec:10.8.2 - */ -static void nfc_shdlc_rcv_i_frame(struct nfc_shdlc *shdlc, - struct sk_buff *skb, int ns, int nr) -{ - int x_ns = ns; - int y_nr = nr; - - pr_debug("recvd I-frame %d, remote waiting frame %d\n", ns, nr); - - if (shdlc->state != SHDLC_CONNECTED) - goto exit; - - if (x_ns != shdlc->nr) { - nfc_shdlc_send_s_frame(shdlc, S_FRAME_REJ, shdlc->nr); - goto exit; - } - - if (shdlc->t1_active == false) { - shdlc->t1_active = true; - mod_timer(&shdlc->t1_timer, - msecs_to_jiffies(SHDLC_T1_VALUE_MS(shdlc->w))); - pr_debug("(re)Start T1(send ack)\n"); - } - - if (skb->len) { - nfc_hci_recv_frame(shdlc->hdev, skb); - skb = NULL; - } - - shdlc->nr = (shdlc->nr + 1) % 8; - - if (nfc_shdlc_x_lt_y_lteq_z(shdlc->dnr, y_nr, shdlc->ns)) { - nfc_shdlc_reset_t2(shdlc, y_nr); - - shdlc->dnr = y_nr; - } - -exit: - kfree_skb(skb); -} - -static void nfc_shdlc_rcv_ack(struct nfc_shdlc *shdlc, int y_nr) -{ - pr_debug("remote acked up to frame %d excluded\n", y_nr); - - if (nfc_shdlc_x_lt_y_lteq_z(shdlc->dnr, y_nr, shdlc->ns)) { - nfc_shdlc_reset_t2(shdlc, y_nr); - shdlc->dnr = y_nr; - } -} - -static void nfc_shdlc_requeue_ack_pending(struct nfc_shdlc *shdlc) -{ - struct sk_buff *skb; - - pr_debug("ns reset to %d\n", shdlc->dnr); - - while ((skb = skb_dequeue_tail(&shdlc->ack_pending_q))) { - skb_pull(skb, 1); /* remove control field */ - skb_queue_head(&shdlc->send_q, skb); - } - shdlc->ns = shdlc->dnr; -} - -static void nfc_shdlc_rcv_rej(struct nfc_shdlc *shdlc, int y_nr) -{ - struct sk_buff *skb; - - pr_debug("remote asks retransmition from frame %d\n", y_nr); - - if (nfc_shdlc_x_lteq_y_lt_z(shdlc->dnr, y_nr, shdlc->ns)) { - if (shdlc->t2_active) { - del_timer_sync(&shdlc->t2_timer); - shdlc->t2_active = false; - pr_debug("Stopped T2(retransmit)\n"); - } - - if (shdlc->dnr != y_nr) { - while ((shdlc->dnr = ((shdlc->dnr + 1) % 8)) != y_nr) { - skb = skb_dequeue(&shdlc->ack_pending_q); - kfree_skb(skb); - } - } - - nfc_shdlc_requeue_ack_pending(shdlc); - } -} - -/* See spec RR:10.8.3 REJ:10.8.4 */ -static void nfc_shdlc_rcv_s_frame(struct nfc_shdlc *shdlc, - enum sframe_type s_frame_type, int nr) -{ - struct sk_buff *skb; - - if (shdlc->state != SHDLC_CONNECTED) - return; - - switch (s_frame_type) { - case S_FRAME_RR: - nfc_shdlc_rcv_ack(shdlc, nr); - if (shdlc->rnr == true) { /* see SHDLC 10.7.7 */ - shdlc->rnr = false; - if (shdlc->send_q.qlen == 0) { - skb = nfc_shdlc_alloc_skb(shdlc, 0); - if (skb) - skb_queue_tail(&shdlc->send_q, skb); - } - } - break; - case S_FRAME_REJ: - nfc_shdlc_rcv_rej(shdlc, nr); - break; - case S_FRAME_RNR: - nfc_shdlc_rcv_ack(shdlc, nr); - shdlc->rnr = true; - break; - default: - break; - } -} - -static void nfc_shdlc_connect_complete(struct nfc_shdlc *shdlc, int r) -{ - pr_debug("result=%d\n", r); - - del_timer_sync(&shdlc->connect_timer); - - if (r == 0) { - shdlc->ns = 0; - shdlc->nr = 0; - shdlc->dnr = 0; - - shdlc->state = SHDLC_CONNECTED; - } else { - shdlc->state = SHDLC_DISCONNECTED; - } - - shdlc->connect_result = r; - - wake_up(shdlc->connect_wq); -} - -static int nfc_shdlc_connect_initiate(struct nfc_shdlc *shdlc) -{ - struct sk_buff *skb; - - pr_debug("\n"); - - skb = nfc_shdlc_alloc_skb(shdlc, 2); - if (skb == NULL) - return -ENOMEM; - - *skb_put(skb, 1) = SHDLC_MAX_WINDOW; - *skb_put(skb, 1) = SHDLC_SREJ_SUPPORT ? 1 : 0; - - return nfc_shdlc_send_u_frame(shdlc, skb, U_FRAME_RSET); -} - -static int nfc_shdlc_connect_send_ua(struct nfc_shdlc *shdlc) -{ - struct sk_buff *skb; - - pr_debug("\n"); - - skb = nfc_shdlc_alloc_skb(shdlc, 0); - if (skb == NULL) - return -ENOMEM; - - return nfc_shdlc_send_u_frame(shdlc, skb, U_FRAME_UA); -} - -static void nfc_shdlc_rcv_u_frame(struct nfc_shdlc *shdlc, - struct sk_buff *skb, - enum uframe_modifier u_frame_modifier) -{ - u8 w = SHDLC_MAX_WINDOW; - bool srej_support = SHDLC_SREJ_SUPPORT; - int r; - - pr_debug("u_frame_modifier=%d\n", u_frame_modifier); - - switch (u_frame_modifier) { - case U_FRAME_RSET: - if (shdlc->state == SHDLC_NEGOCIATING) { - /* we sent RSET, but chip wants to negociate */ - if (skb->len > 0) - w = skb->data[0]; - - if (skb->len > 1) - srej_support = skb->data[1] & 0x01 ? true : - false; - - if ((w <= SHDLC_MAX_WINDOW) && - (SHDLC_SREJ_SUPPORT || (srej_support == false))) { - shdlc->w = w; - shdlc->srej_support = srej_support; - r = nfc_shdlc_connect_send_ua(shdlc); - nfc_shdlc_connect_complete(shdlc, r); - } - } else if (shdlc->state == SHDLC_CONNECTED) { - /* - * Chip wants to reset link. This is unexpected and - * unsupported. - */ - shdlc->hard_fault = -ECONNRESET; - } - break; - case U_FRAME_UA: - if ((shdlc->state == SHDLC_CONNECTING && - shdlc->connect_tries > 0) || - (shdlc->state == SHDLC_NEGOCIATING)) - nfc_shdlc_connect_complete(shdlc, 0); - break; - default: - break; - } - - kfree_skb(skb); -} - -static void nfc_shdlc_handle_rcv_queue(struct nfc_shdlc *shdlc) -{ - struct sk_buff *skb; - u8 control; - int nr; - int ns; - enum sframe_type s_frame_type; - enum uframe_modifier u_frame_modifier; - - if (shdlc->rcv_q.qlen) - pr_debug("rcvQlen=%d\n", shdlc->rcv_q.qlen); - - while ((skb = skb_dequeue(&shdlc->rcv_q)) != NULL) { - control = skb->data[0]; - skb_pull(skb, 1); - switch (control & SHDLC_CONTROL_HEAD_MASK) { - case SHDLC_CONTROL_HEAD_I: - case SHDLC_CONTROL_HEAD_I2: - ns = (control & SHDLC_CONTROL_NS_MASK) >> 3; - nr = control & SHDLC_CONTROL_NR_MASK; - nfc_shdlc_rcv_i_frame(shdlc, skb, ns, nr); - break; - case SHDLC_CONTROL_HEAD_S: - s_frame_type = (control & SHDLC_CONTROL_TYPE_MASK) >> 3; - nr = control & SHDLC_CONTROL_NR_MASK; - nfc_shdlc_rcv_s_frame(shdlc, s_frame_type, nr); - kfree_skb(skb); - break; - case SHDLC_CONTROL_HEAD_U: - u_frame_modifier = control & SHDLC_CONTROL_M_MASK; - nfc_shdlc_rcv_u_frame(shdlc, skb, u_frame_modifier); - break; - default: - pr_err("UNKNOWN Control=%d\n", control); - kfree_skb(skb); - break; - } - } -} - -static int nfc_shdlc_w_used(int ns, int dnr) -{ - int unack_count; - - if (dnr <= ns) - unack_count = ns - dnr; - else - unack_count = 8 - dnr + ns; - - return unack_count; -} - -/* Send frames according to algorithm at spec:10.8.1 */ -static void nfc_shdlc_handle_send_queue(struct nfc_shdlc *shdlc) -{ - struct sk_buff *skb; - int r; - unsigned long time_sent; - - if (shdlc->send_q.qlen) - pr_debug - ("sendQlen=%d ns=%d dnr=%d rnr=%s w_room=%d unackQlen=%d\n", - shdlc->send_q.qlen, shdlc->ns, shdlc->dnr, - shdlc->rnr == false ? "false" : "true", - shdlc->w - nfc_shdlc_w_used(shdlc->ns, shdlc->dnr), - shdlc->ack_pending_q.qlen); - - while (shdlc->send_q.qlen && shdlc->ack_pending_q.qlen < shdlc->w && - (shdlc->rnr == false)) { - - if (shdlc->t1_active) { - del_timer_sync(&shdlc->t1_timer); - shdlc->t1_active = false; - pr_debug("Stopped T1(send ack)\n"); - } - - skb = skb_dequeue(&shdlc->send_q); - - *skb_push(skb, 1) = SHDLC_CONTROL_HEAD_I | (shdlc->ns << 3) | - shdlc->nr; - - pr_debug("Sending I-Frame %d, waiting to rcv %d\n", shdlc->ns, - shdlc->nr); - /* SHDLC_DUMP_SKB("shdlc frame written", skb); */ - - r = shdlc->ops->xmit(shdlc, skb); - if (r < 0) { - shdlc->hard_fault = r; - break; - } - - shdlc->ns = (shdlc->ns + 1) % 8; - - time_sent = jiffies; - *(unsigned long *)skb->cb = time_sent; - - skb_queue_tail(&shdlc->ack_pending_q, skb); - - if (shdlc->t2_active == false) { - shdlc->t2_active = true; - mod_timer(&shdlc->t2_timer, time_sent + - msecs_to_jiffies(SHDLC_T2_VALUE_MS)); - pr_debug("Started T2 (retransmit)\n"); - } - } -} - -static void nfc_shdlc_connect_timeout(unsigned long data) -{ - struct nfc_shdlc *shdlc = (struct nfc_shdlc *)data; - - pr_debug("\n"); - - queue_work(system_nrt_wq, &shdlc->sm_work); -} - -static void nfc_shdlc_t1_timeout(unsigned long data) -{ - struct nfc_shdlc *shdlc = (struct nfc_shdlc *)data; - - pr_debug("SoftIRQ: need to send ack\n"); - - queue_work(system_nrt_wq, &shdlc->sm_work); -} - -static void nfc_shdlc_t2_timeout(unsigned long data) -{ - struct nfc_shdlc *shdlc = (struct nfc_shdlc *)data; - - pr_debug("SoftIRQ: need to retransmit\n"); - - queue_work(system_nrt_wq, &shdlc->sm_work); -} - -static void nfc_shdlc_sm_work(struct work_struct *work) -{ - struct nfc_shdlc *shdlc = container_of(work, struct nfc_shdlc, sm_work); - int r; - - pr_debug("\n"); - - mutex_lock(&shdlc->state_mutex); - - switch (shdlc->state) { - case SHDLC_DISCONNECTED: - skb_queue_purge(&shdlc->rcv_q); - skb_queue_purge(&shdlc->send_q); - skb_queue_purge(&shdlc->ack_pending_q); - break; - case SHDLC_CONNECTING: - if (shdlc->hard_fault) { - nfc_shdlc_connect_complete(shdlc, shdlc->hard_fault); - break; - } - - if (shdlc->connect_tries++ < 5) - r = nfc_shdlc_connect_initiate(shdlc); - else - r = -ETIME; - if (r < 0) - nfc_shdlc_connect_complete(shdlc, r); - else { - mod_timer(&shdlc->connect_timer, jiffies + - msecs_to_jiffies(SHDLC_CONNECT_VALUE_MS)); - - shdlc->state = SHDLC_NEGOCIATING; - } - break; - case SHDLC_NEGOCIATING: - if (timer_pending(&shdlc->connect_timer) == 0) { - shdlc->state = SHDLC_CONNECTING; - queue_work(system_nrt_wq, &shdlc->sm_work); - } - - nfc_shdlc_handle_rcv_queue(shdlc); - - if (shdlc->hard_fault) { - nfc_shdlc_connect_complete(shdlc, shdlc->hard_fault); - break; - } - break; - case SHDLC_CONNECTED: - nfc_shdlc_handle_rcv_queue(shdlc); - nfc_shdlc_handle_send_queue(shdlc); - - if (shdlc->t1_active && timer_pending(&shdlc->t1_timer) == 0) { - pr_debug - ("Handle T1(send ack) elapsed (T1 now inactive)\n"); - - shdlc->t1_active = false; - r = nfc_shdlc_send_s_frame(shdlc, S_FRAME_RR, - shdlc->nr); - if (r < 0) - shdlc->hard_fault = r; - } - - if (shdlc->t2_active && timer_pending(&shdlc->t2_timer) == 0) { - pr_debug - ("Handle T2(retransmit) elapsed (T2 inactive)\n"); - - shdlc->t2_active = false; - - nfc_shdlc_requeue_ack_pending(shdlc); - nfc_shdlc_handle_send_queue(shdlc); - } - - if (shdlc->hard_fault) { - nfc_hci_driver_failure(shdlc->hdev, shdlc->hard_fault); - } - break; - default: - break; - } - mutex_unlock(&shdlc->state_mutex); -} - -/* - * Called from syscall context to establish shdlc link. Sleeps until - * link is ready or failure. - */ -static int nfc_shdlc_connect(struct nfc_shdlc *shdlc) -{ - DECLARE_WAIT_QUEUE_HEAD_ONSTACK(connect_wq); - - pr_debug("\n"); - - mutex_lock(&shdlc->state_mutex); - - shdlc->state = SHDLC_CONNECTING; - shdlc->connect_wq = &connect_wq; - shdlc->connect_tries = 0; - shdlc->connect_result = 1; - - mutex_unlock(&shdlc->state_mutex); - - queue_work(system_nrt_wq, &shdlc->sm_work); - - wait_event(connect_wq, shdlc->connect_result != 1); - - return shdlc->connect_result; -} - -static void nfc_shdlc_disconnect(struct nfc_shdlc *shdlc) -{ - pr_debug("\n"); - - mutex_lock(&shdlc->state_mutex); - - shdlc->state = SHDLC_DISCONNECTED; - - mutex_unlock(&shdlc->state_mutex); - - queue_work(system_nrt_wq, &shdlc->sm_work); -} - -/* - * Receive an incoming shdlc frame. Frame has already been crc-validated. - * skb contains only LLC header and payload. - * If skb == NULL, it is a notification that the link below is dead. - */ -void nfc_shdlc_recv_frame(struct nfc_shdlc *shdlc, struct sk_buff *skb) -{ - if (skb == NULL) { - pr_err("NULL Frame -> link is dead\n"); - shdlc->hard_fault = -EREMOTEIO; - } else { - SHDLC_DUMP_SKB("incoming frame", skb); - skb_queue_tail(&shdlc->rcv_q, skb); - } - - queue_work(system_nrt_wq, &shdlc->sm_work); -} -EXPORT_SYMBOL(nfc_shdlc_recv_frame); - -static int nfc_shdlc_open(struct nfc_hci_dev *hdev) -{ - struct nfc_shdlc *shdlc = nfc_hci_get_clientdata(hdev); - int r; - - pr_debug("\n"); - - if (shdlc->ops->open) { - r = shdlc->ops->open(shdlc); - if (r < 0) - return r; - } - - r = nfc_shdlc_connect(shdlc); - if (r < 0 && shdlc->ops->close) - shdlc->ops->close(shdlc); - - return r; -} - -static void nfc_shdlc_close(struct nfc_hci_dev *hdev) -{ - struct nfc_shdlc *shdlc = nfc_hci_get_clientdata(hdev); - - pr_debug("\n"); - - nfc_shdlc_disconnect(shdlc); - - if (shdlc->ops->close) - shdlc->ops->close(shdlc); -} - -static int nfc_shdlc_hci_ready(struct nfc_hci_dev *hdev) -{ - struct nfc_shdlc *shdlc = nfc_hci_get_clientdata(hdev); - int r = 0; - - pr_debug("\n"); - - if (shdlc->ops->hci_ready) - r = shdlc->ops->hci_ready(shdlc); - - return r; -} - -static int nfc_shdlc_xmit(struct nfc_hci_dev *hdev, struct sk_buff *skb) -{ - struct nfc_shdlc *shdlc = nfc_hci_get_clientdata(hdev); - - SHDLC_DUMP_SKB("queuing HCP packet to shdlc", skb); - - skb_queue_tail(&shdlc->send_q, skb); - - queue_work(system_nrt_wq, &shdlc->sm_work); - - return 0; -} - -static int nfc_shdlc_start_poll(struct nfc_hci_dev *hdev, - u32 im_protocols, u32 tm_protocols) -{ - struct nfc_shdlc *shdlc = nfc_hci_get_clientdata(hdev); - - pr_debug("\n"); - - if (shdlc->ops->start_poll) - return shdlc->ops->start_poll(shdlc, - im_protocols, tm_protocols); - - return 0; -} - -static int nfc_shdlc_target_from_gate(struct nfc_hci_dev *hdev, u8 gate, - struct nfc_target *target) -{ - struct nfc_shdlc *shdlc = nfc_hci_get_clientdata(hdev); - - if (shdlc->ops->target_from_gate) - return shdlc->ops->target_from_gate(shdlc, gate, target); - - return -EPERM; -} - -static int nfc_shdlc_complete_target_discovered(struct nfc_hci_dev *hdev, - u8 gate, - struct nfc_target *target) -{ - struct nfc_shdlc *shdlc = nfc_hci_get_clientdata(hdev); - - pr_debug("\n"); - - if (shdlc->ops->complete_target_discovered) - return shdlc->ops->complete_target_discovered(shdlc, gate, - target); - - return 0; -} - -static int nfc_shdlc_data_exchange(struct nfc_hci_dev *hdev, - struct nfc_target *target, - struct sk_buff *skb, - data_exchange_cb_t cb, void *cb_context) -{ - struct nfc_shdlc *shdlc = nfc_hci_get_clientdata(hdev); - - if (shdlc->ops->data_exchange) - return shdlc->ops->data_exchange(shdlc, target, skb, cb, - cb_context); - - return -EPERM; -} - -static int nfc_shdlc_check_presence(struct nfc_hci_dev *hdev, - struct nfc_target *target) -{ - struct nfc_shdlc *shdlc = nfc_hci_get_clientdata(hdev); - - if (shdlc->ops->check_presence) - return shdlc->ops->check_presence(shdlc, target); - - return 0; -} - -static struct nfc_hci_ops shdlc_ops = { - .open = nfc_shdlc_open, - .close = nfc_shdlc_close, - .hci_ready = nfc_shdlc_hci_ready, - .xmit = nfc_shdlc_xmit, - .start_poll = nfc_shdlc_start_poll, - .target_from_gate = nfc_shdlc_target_from_gate, - .complete_target_discovered = nfc_shdlc_complete_target_discovered, - .data_exchange = nfc_shdlc_data_exchange, - .check_presence = nfc_shdlc_check_presence, -}; - -struct nfc_shdlc *nfc_shdlc_allocate(struct nfc_shdlc_ops *ops, - struct nfc_hci_init_data *init_data, - u32 protocols, - int tx_headroom, int tx_tailroom, - int max_link_payload, const char *devname) -{ - struct nfc_shdlc *shdlc; - int r; - - if (ops->xmit == NULL) - return NULL; - - shdlc = kzalloc(sizeof(struct nfc_shdlc), GFP_KERNEL); - if (shdlc == NULL) - return NULL; - - mutex_init(&shdlc->state_mutex); - shdlc->ops = ops; - shdlc->state = SHDLC_DISCONNECTED; - - init_timer(&shdlc->connect_timer); - shdlc->connect_timer.data = (unsigned long)shdlc; - shdlc->connect_timer.function = nfc_shdlc_connect_timeout; - - init_timer(&shdlc->t1_timer); - shdlc->t1_timer.data = (unsigned long)shdlc; - shdlc->t1_timer.function = nfc_shdlc_t1_timeout; - - init_timer(&shdlc->t2_timer); - shdlc->t2_timer.data = (unsigned long)shdlc; - shdlc->t2_timer.function = nfc_shdlc_t2_timeout; - - shdlc->w = SHDLC_MAX_WINDOW; - shdlc->srej_support = SHDLC_SREJ_SUPPORT; - - skb_queue_head_init(&shdlc->rcv_q); - skb_queue_head_init(&shdlc->send_q); - skb_queue_head_init(&shdlc->ack_pending_q); - - INIT_WORK(&shdlc->sm_work, nfc_shdlc_sm_work); - - shdlc->client_headroom = tx_headroom; - shdlc->client_tailroom = tx_tailroom; - - shdlc->hdev = nfc_hci_allocate_device(&shdlc_ops, init_data, protocols, - tx_headroom + SHDLC_LLC_HEAD_ROOM, - tx_tailroom, - max_link_payload); - if (shdlc->hdev == NULL) - goto err_allocdev; - - nfc_hci_set_clientdata(shdlc->hdev, shdlc); - - r = nfc_hci_register_device(shdlc->hdev); - if (r < 0) - goto err_regdev; - - return shdlc; - -err_regdev: - nfc_hci_free_device(shdlc->hdev); - -err_allocdev: - kfree(shdlc); - - return NULL; -} -EXPORT_SYMBOL(nfc_shdlc_allocate); - -void nfc_shdlc_free(struct nfc_shdlc *shdlc) -{ - pr_debug("\n"); - - nfc_hci_unregister_device(shdlc->hdev); - nfc_hci_free_device(shdlc->hdev); - - cancel_work_sync(&shdlc->sm_work); - - skb_queue_purge(&shdlc->rcv_q); - skb_queue_purge(&shdlc->send_q); - skb_queue_purge(&shdlc->ack_pending_q); - - kfree(shdlc); -} -EXPORT_SYMBOL(nfc_shdlc_free); - -void nfc_shdlc_set_clientdata(struct nfc_shdlc *shdlc, void *clientdata) -{ - pr_debug("\n"); - - shdlc->clientdata = clientdata; -} -EXPORT_SYMBOL(nfc_shdlc_set_clientdata); - -void *nfc_shdlc_get_clientdata(struct nfc_shdlc *shdlc) -{ - return shdlc->clientdata; -} -EXPORT_SYMBOL(nfc_shdlc_get_clientdata); - -struct nfc_hci_dev *nfc_shdlc_get_hci_dev(struct nfc_shdlc *shdlc) -{ - return shdlc->hdev; -} -EXPORT_SYMBOL(nfc_shdlc_get_hci_dev); From f4f20d0650e9cd13f65b5e7f93af126b526af721 Mon Sep 17 00:00:00 2001 From: Samuel Ortiz Date: Tue, 18 Sep 2012 19:17:33 +0200 Subject: [PATCH 21/31] NFC: Remove unneeded LLC symbols export After fixing the LLC Makefile, we no longer need those exports. Signed-off-by: Samuel Ortiz --- net/nfc/hci/llc.c | 14 +------------- net/nfc/hci/llc_nop.c | 2 -- net/nfc/hci/llc_shdlc.c | 2 -- 3 files changed, 1 insertion(+), 17 deletions(-) diff --git a/net/nfc/hci/llc.c b/net/nfc/hci/llc.c index bd11b0f7658a..ae1205ded87f 100644 --- a/net/nfc/hci/llc.c +++ b/net/nfc/hci/llc.c @@ -18,8 +18,8 @@ * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ -#include #include + #include "llc.h" static struct list_head llc_engines; @@ -44,7 +44,6 @@ exit: nfc_llc_exit(); return r; } -EXPORT_SYMBOL(nfc_llc_init); void nfc_llc_exit(void) { @@ -56,7 +55,6 @@ void nfc_llc_exit(void) kfree(llc_engine); } } -EXPORT_SYMBOL(nfc_llc_exit); int nfc_llc_register(const char *name, struct nfc_llc_ops *ops) { @@ -78,7 +76,6 @@ int nfc_llc_register(const char *name, struct nfc_llc_ops *ops) return 0; } -EXPORT_SYMBOL(nfc_llc_register); static struct nfc_llc_engine *nfc_llc_name_to_engine(const char *name) { @@ -104,7 +101,6 @@ void nfc_llc_unregister(const char *name) kfree(llc_engine->name); kfree(llc_engine); } -EXPORT_SYMBOL(nfc_llc_unregister); struct nfc_llc *nfc_llc_allocate(const char *name, struct nfc_hci_dev *hdev, xmit_to_drv_t xmit_to_drv, @@ -134,14 +130,12 @@ struct nfc_llc *nfc_llc_allocate(const char *name, struct nfc_hci_dev *hdev, return llc; } -EXPORT_SYMBOL(nfc_llc_allocate); void nfc_llc_free(struct nfc_llc *llc) { llc->ops->deinit(llc); kfree(llc); } -EXPORT_SYMBOL(nfc_llc_free); inline void nfc_llc_get_rx_head_tail_room(struct nfc_llc *llc, int *rx_headroom, int *rx_tailroom) @@ -149,34 +143,28 @@ inline void nfc_llc_get_rx_head_tail_room(struct nfc_llc *llc, int *rx_headroom, *rx_headroom = llc->rx_headroom; *rx_tailroom = llc->rx_tailroom; } -EXPORT_SYMBOL(nfc_llc_get_rx_head_tail_room); 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) { llc->ops->rcv_from_drv(llc, skb); } -EXPORT_SYMBOL(nfc_llc_rcv_from_drv); inline int nfc_llc_xmit_from_hci(struct nfc_llc *llc, struct sk_buff *skb) { return llc->ops->xmit_from_hci(llc, skb); } -EXPORT_SYMBOL(nfc_llc_xmit_from_hci); inline void *nfc_llc_get_data(struct nfc_llc *llc) { return llc->data; } -EXPORT_SYMBOL(nfc_llc_get_data); diff --git a/net/nfc/hci/llc_nop.c b/net/nfc/hci/llc_nop.c index ec627cee12cd..d91dd66a082d 100644 --- a/net/nfc/hci/llc_nop.c +++ b/net/nfc/hci/llc_nop.c @@ -19,7 +19,6 @@ */ #include -#include #include "llc.h" @@ -98,4 +97,3 @@ int nfc_llc_nop_register() { return nfc_llc_register(LLC_NOP_NAME, &llc_nop_ops); } -EXPORT_SYMBOL(nfc_llc_nop_register); diff --git a/net/nfc/hci/llc_shdlc.c b/net/nfc/hci/llc_shdlc.c index fad6cd18d613..3761c7490a61 100644 --- a/net/nfc/hci/llc_shdlc.c +++ b/net/nfc/hci/llc_shdlc.c @@ -22,7 +22,6 @@ #include #include -#include #include #include #include @@ -831,4 +830,3 @@ int nfc_llc_shdlc_register() { return nfc_llc_register(LLC_SHDLC_NAME, &llc_shdlc_ops); } -EXPORT_SYMBOL(nfc_llc_shdlc_register); From d6650a2ccfdcf536841c2f880a1a6f19fce2e3a1 Mon Sep 17 00:00:00 2001 From: Wei Yongjun Date: Sat, 8 Sep 2012 09:53:22 +0800 Subject: [PATCH 22/31] NFC: Move the nfcwilink dereference below the NULL test spatch with a semantic match is used to found this. (http://coccinelle.lip6.fr/) Signed-off-by: Wei Yongjun Signed-off-by: Samuel Ortiz --- drivers/nfc/nfcwilink.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/nfc/nfcwilink.c b/drivers/nfc/nfcwilink.c index 827f039ceaf5..50b1ee41afc6 100644 --- a/drivers/nfc/nfcwilink.c +++ b/drivers/nfc/nfcwilink.c @@ -352,8 +352,6 @@ static long nfcwilink_receive(void *priv_data, struct sk_buff *skb) struct nfcwilink *drv = priv_data; int rc; - nfc_dev_dbg(&drv->pdev->dev, "receive entry, len %d", skb->len); - if (!skb) return -EFAULT; @@ -362,6 +360,8 @@ static long nfcwilink_receive(void *priv_data, struct sk_buff *skb) return -EFAULT; } + nfc_dev_dbg(&drv->pdev->dev, "receive entry, len %d", skb->len); + /* strip the ST header (apart for the chnl byte, which is not received in the hdr) */ skb_pull(skb, (NFCWILINK_HDR_LEN-1)); From c51ad34e542606854d0545533329e916689599ba Mon Sep 17 00:00:00 2001 From: Wei Yongjun Date: Sat, 8 Sep 2012 21:19:44 +0800 Subject: [PATCH 23/31] NFC: Move the pn544_hci dereference below the NULL test The dereference should be moved below the NULL test. spatch with a semantic match is used to found this. (http://coccinelle.lip6.fr/) Signed-off-by: Wei Yongjun Signed-off-by: Samuel Ortiz --- drivers/nfc/pn544_hci.c | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/drivers/nfc/pn544_hci.c b/drivers/nfc/pn544_hci.c index e681da2e3413..7b0c2176e89b 100644 --- a/drivers/nfc/pn544_hci.c +++ b/drivers/nfc/pn544_hci.c @@ -346,13 +346,16 @@ flush: static irqreturn_t pn544_hci_irq_thread_fn(int irq, void *dev_id) { struct pn544_hci_info *info = dev_id; - struct i2c_client *client = info->i2c_dev; + struct i2c_client *client; struct sk_buff *skb = NULL; int r; - BUG_ON(!info); - BUG_ON(irq != info->i2c_dev->irq); + if (!info || irq != info->i2c_dev->irq) { + WARN_ON_ONCE(1); + return IRQ_NONE; + } + client = info->i2c_dev; dev_dbg(&client->dev, "IRQ\n"); if (info->hard_fault != 0) From a7d0281bbf7e1b0eff2de3c7ed6a5ffce6b62dac Mon Sep 17 00:00:00 2001 From: Eric Lapuyade Date: Tue, 18 Sep 2012 19:24:37 +0200 Subject: [PATCH 24/31] NFC: Fix LLC registration definitions for ANSI compliance Signed-off-by: Eric Lapuyade Signed-off-by: Samuel Ortiz --- net/nfc/hci/llc_nop.c | 2 +- net/nfc/hci/llc_shdlc.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/net/nfc/hci/llc_nop.c b/net/nfc/hci/llc_nop.c index d91dd66a082d..87b10291b40f 100644 --- a/net/nfc/hci/llc_nop.c +++ b/net/nfc/hci/llc_nop.c @@ -93,7 +93,7 @@ static struct nfc_llc_ops llc_nop_ops = { .xmit_from_hci = llc_nop_xmit_from_hci, }; -int nfc_llc_nop_register() +int nfc_llc_nop_register(void) { return nfc_llc_register(LLC_NOP_NAME, &llc_nop_ops); } diff --git a/net/nfc/hci/llc_shdlc.c b/net/nfc/hci/llc_shdlc.c index 3761c7490a61..d54bfc51c693 100644 --- a/net/nfc/hci/llc_shdlc.c +++ b/net/nfc/hci/llc_shdlc.c @@ -826,7 +826,7 @@ static struct nfc_llc_ops llc_shdlc_ops = { .xmit_from_hci = llc_shdlc_xmit_from_hci, }; -int nfc_llc_shdlc_register() +int nfc_llc_shdlc_register(void) { return nfc_llc_register(LLC_SHDLC_NAME, &llc_shdlc_ops); } From 80faa59847108d48928fac41b4817ef21ca108a9 Mon Sep 17 00:00:00 2001 From: Eric Lapuyade Date: Tue, 18 Sep 2012 19:25:38 +0200 Subject: [PATCH 25/31] NFC: Add HCI module description Signed-off-by: Eric Lapuyade Signed-off-by: Samuel Ortiz --- net/nfc/hci/core.c | 1 + 1 file changed, 1 insertion(+) diff --git a/net/nfc/hci/core.c b/net/nfc/hci/core.c index c1129c22d835..d378d93de62e 100644 --- a/net/nfc/hci/core.c +++ b/net/nfc/hci/core.c @@ -868,3 +868,4 @@ subsys_initcall(nfc_hci_init); module_exit(nfc_hci_exit); MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("NFC HCI Core"); From 7b55279f6a454771d06e8ddf4a7114d17ae9a741 Mon Sep 17 00:00:00 2001 From: Cong Wang Date: Tue, 18 Sep 2012 19:31:58 +0200 Subject: [PATCH 26/31] NFC: Remove the pn544 raw driver This was scheduled for 3.6, we're late. Signed-off-by: Cong Wang Acked-by: Marcel Holtmann Signed-off-by: Samuel Ortiz --- Documentation/feature-removal-schedule.txt | 12 - drivers/nfc/Kconfig | 12 - drivers/nfc/Makefile | 1 - drivers/nfc/pn544.c | 893 --------------------- 4 files changed, 918 deletions(-) delete mode 100644 drivers/nfc/pn544.c diff --git a/Documentation/feature-removal-schedule.txt b/Documentation/feature-removal-schedule.txt index afaff312bf41..2cbbac8019c8 100644 --- a/Documentation/feature-removal-schedule.txt +++ b/Documentation/feature-removal-schedule.txt @@ -516,18 +516,6 @@ Who: Kees Cook ---------------------------- -What: Removing the pn544 raw driver. -When: 3.6 -Why: With the introduction of the NFC HCI and SHDL kernel layers, pn544.c - is being replaced by pn544_hci.c which is accessible through the netlink - and socket NFC APIs. Moreover, pn544.c is outdated and does not seem to - work properly with the latest Android stacks. - Having 2 drivers for the same hardware is confusing and as such we - should only keep the one following the kernel NFC APIs. -Who: Samuel Ortiz - ----------------------------- - What: setitimer accepts user NULL pointer (value) When: 3.6 Why: setitimer is not returning -EFAULT if user pointer is NULL. This diff --git a/drivers/nfc/Kconfig b/drivers/nfc/Kconfig index 89c57d1a7cad..ec857676c39f 100644 --- a/drivers/nfc/Kconfig +++ b/drivers/nfc/Kconfig @@ -5,18 +5,6 @@ menu "Near Field Communication (NFC) devices" depends on NFC -config PN544_NFC - tristate "PN544 NFC driver" - depends on I2C - select CRC_CCITT - default n - ---help--- - Say yes if you want PN544 Near Field Communication driver. - This is for i2c connected version. If unsure, say N here. - - To compile this driver as a module, choose m here. The module will - be called pn544. - config PN544_HCI_NFC tristate "HCI PN544 NFC driver" depends on I2C && NFC_HCI && NFC_SHDLC diff --git a/drivers/nfc/Makefile b/drivers/nfc/Makefile index 473e44cef612..bf05831fdf09 100644 --- a/drivers/nfc/Makefile +++ b/drivers/nfc/Makefile @@ -2,7 +2,6 @@ # Makefile for nfc devices # -obj-$(CONFIG_PN544_NFC) += pn544.o obj-$(CONFIG_PN544_HCI_NFC) += pn544_hci.o obj-$(CONFIG_NFC_PN533) += pn533.o obj-$(CONFIG_NFC_WILINK) += nfcwilink.o diff --git a/drivers/nfc/pn544.c b/drivers/nfc/pn544.c deleted file mode 100644 index 724f65d8f9e4..000000000000 --- a/drivers/nfc/pn544.c +++ /dev/null @@ -1,893 +0,0 @@ -/* - * Driver for the PN544 NFC chip. - * - * Copyright (C) Nokia Corporation - * - * Author: Jari Vanhala - * Contact: Matti Aaltonen - * - * This program is free software; you can redistribute it and/or - * modify it under the terms 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, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include /* for TCGETS */ -#include - -#define DRIVER_CARD "PN544 NFC" -#define DRIVER_DESC "NFC driver for PN544" - -static struct i2c_device_id pn544_id_table[] = { - { PN544_DRIVER_NAME, 0 }, - { } -}; -MODULE_DEVICE_TABLE(i2c, pn544_id_table); - -#define HCI_MODE 0 -#define FW_MODE 1 - -enum pn544_state { - PN544_ST_COLD, - PN544_ST_FW_READY, - PN544_ST_READY, -}; - -enum pn544_irq { - PN544_NONE, - PN544_INT, -}; - -struct pn544_info { - struct miscdevice miscdev; - struct i2c_client *i2c_dev; - struct regulator_bulk_data regs[3]; - - enum pn544_state state; - wait_queue_head_t read_wait; - loff_t read_offset; - enum pn544_irq read_irq; - struct mutex read_mutex; /* Serialize read_irq access */ - struct mutex mutex; /* Serialize info struct access */ - u8 *buf; - size_t buflen; -}; - -static const char reg_vdd_io[] = "Vdd_IO"; -static const char reg_vbat[] = "VBat"; -static const char reg_vsim[] = "VSim"; - -/* sysfs interface */ -static ssize_t pn544_test(struct device *dev, - struct device_attribute *attr, char *buf) -{ - struct pn544_info *info = dev_get_drvdata(dev); - struct i2c_client *client = info->i2c_dev; - struct pn544_nfc_platform_data *pdata = client->dev.platform_data; - - return snprintf(buf, PAGE_SIZE, "%d\n", pdata->test()); -} - -static int pn544_enable(struct pn544_info *info, int mode) -{ - struct pn544_nfc_platform_data *pdata; - struct i2c_client *client = info->i2c_dev; - - int r; - - r = regulator_bulk_enable(ARRAY_SIZE(info->regs), info->regs); - if (r < 0) - return r; - - pdata = client->dev.platform_data; - info->read_irq = PN544_NONE; - if (pdata->enable) - pdata->enable(mode); - - if (mode) { - info->state = PN544_ST_FW_READY; - dev_dbg(&client->dev, "now in FW-mode\n"); - } else { - info->state = PN544_ST_READY; - dev_dbg(&client->dev, "now in HCI-mode\n"); - } - - usleep_range(10000, 15000); - - return 0; -} - -static void pn544_disable(struct pn544_info *info) -{ - struct pn544_nfc_platform_data *pdata; - struct i2c_client *client = info->i2c_dev; - - pdata = client->dev.platform_data; - if (pdata->disable) - pdata->disable(); - - info->state = PN544_ST_COLD; - - dev_dbg(&client->dev, "Now in OFF-mode\n"); - - msleep(PN544_RESETVEN_TIME); - - info->read_irq = PN544_NONE; - regulator_bulk_disable(ARRAY_SIZE(info->regs), info->regs); -} - -static int check_crc(u8 *buf, int buflen) -{ - u8 len; - u16 crc; - - len = buf[0] + 1; - if (len < 4 || len != buflen || len > PN544_MSG_MAX_SIZE) { - pr_err(PN544_DRIVER_NAME - ": CRC; corrupt packet len %u (%d)\n", len, buflen); - print_hex_dump(KERN_DEBUG, "crc: ", DUMP_PREFIX_NONE, - 16, 2, buf, buflen, false); - return -EPERM; - } - crc = crc_ccitt(0xffff, buf, len - 2); - crc = ~crc; - - if (buf[len-2] != (crc & 0xff) || buf[len-1] != (crc >> 8)) { - pr_err(PN544_DRIVER_NAME ": CRC error 0x%x != 0x%x 0x%x\n", - crc, buf[len-1], buf[len-2]); - - print_hex_dump(KERN_DEBUG, "crc: ", DUMP_PREFIX_NONE, - 16, 2, buf, buflen, false); - return -EPERM; - } - return 0; -} - -static int pn544_i2c_write(struct i2c_client *client, u8 *buf, int len) -{ - int r; - - if (len < 4 || len != (buf[0] + 1)) { - dev_err(&client->dev, "%s: Illegal message length: %d\n", - __func__, len); - return -EINVAL; - } - - if (check_crc(buf, len)) - return -EINVAL; - - usleep_range(3000, 6000); - - r = i2c_master_send(client, buf, len); - dev_dbg(&client->dev, "send: %d\n", r); - - if (r == -EREMOTEIO) { /* Retry, chip was in standby */ - usleep_range(6000, 10000); - r = i2c_master_send(client, buf, len); - dev_dbg(&client->dev, "send2: %d\n", r); - } - - if (r != len) - return -EREMOTEIO; - - return r; -} - -static int pn544_i2c_read(struct i2c_client *client, u8 *buf, int buflen) -{ - int r; - u8 len; - - /* - * You could read a packet in one go, but then you'd need to read - * max size and rest would be 0xff fill, so we do split reads. - */ - r = i2c_master_recv(client, &len, 1); - dev_dbg(&client->dev, "recv1: %d\n", r); - - if (r != 1) - return -EREMOTEIO; - - if (len < PN544_LLC_HCI_OVERHEAD) - len = PN544_LLC_HCI_OVERHEAD; - else if (len > (PN544_MSG_MAX_SIZE - 1)) - len = PN544_MSG_MAX_SIZE - 1; - - if (1 + len > buflen) /* len+(data+crc16) */ - return -EMSGSIZE; - - buf[0] = len; - - r = i2c_master_recv(client, buf + 1, len); - dev_dbg(&client->dev, "recv2: %d\n", r); - - if (r != len) - return -EREMOTEIO; - - usleep_range(3000, 6000); - - return r + 1; -} - -static int pn544_fw_write(struct i2c_client *client, u8 *buf, int len) -{ - int r; - - dev_dbg(&client->dev, "%s\n", __func__); - - if (len < PN544_FW_HEADER_SIZE || - (PN544_FW_HEADER_SIZE + (buf[1] << 8) + buf[2]) != len) - return -EINVAL; - - r = i2c_master_send(client, buf, len); - dev_dbg(&client->dev, "fw send: %d\n", r); - - if (r == -EREMOTEIO) { /* Retry, chip was in standby */ - usleep_range(6000, 10000); - r = i2c_master_send(client, buf, len); - dev_dbg(&client->dev, "fw send2: %d\n", r); - } - - if (r != len) - return -EREMOTEIO; - - return r; -} - -static int pn544_fw_read(struct i2c_client *client, u8 *buf, int buflen) -{ - int r, len; - - if (buflen < PN544_FW_HEADER_SIZE) - return -EINVAL; - - r = i2c_master_recv(client, buf, PN544_FW_HEADER_SIZE); - dev_dbg(&client->dev, "FW recv1: %d\n", r); - - if (r < 0) - return r; - - if (r < PN544_FW_HEADER_SIZE) - return -EINVAL; - - len = (buf[1] << 8) + buf[2]; - if (len == 0) /* just header, no additional data */ - return r; - - if (len > buflen - PN544_FW_HEADER_SIZE) - return -EMSGSIZE; - - r = i2c_master_recv(client, buf + PN544_FW_HEADER_SIZE, len); - dev_dbg(&client->dev, "fw recv2: %d\n", r); - - if (r != len) - return -EINVAL; - - return r + PN544_FW_HEADER_SIZE; -} - -static irqreturn_t pn544_irq_thread_fn(int irq, void *dev_id) -{ - struct pn544_info *info = dev_id; - struct i2c_client *client = info->i2c_dev; - - BUG_ON(!info); - BUG_ON(irq != info->i2c_dev->irq); - - dev_dbg(&client->dev, "IRQ\n"); - - mutex_lock(&info->read_mutex); - info->read_irq = PN544_INT; - mutex_unlock(&info->read_mutex); - - wake_up_interruptible(&info->read_wait); - - return IRQ_HANDLED; -} - -static enum pn544_irq pn544_irq_state(struct pn544_info *info) -{ - enum pn544_irq irq; - - mutex_lock(&info->read_mutex); - irq = info->read_irq; - mutex_unlock(&info->read_mutex); - /* - * XXX: should we check GPIO-line status directly? - * return pdata->irq_status() ? PN544_INT : PN544_NONE; - */ - - return irq; -} - -static ssize_t pn544_read(struct file *file, char __user *buf, - size_t count, loff_t *offset) -{ - struct pn544_info *info = container_of(file->private_data, - struct pn544_info, miscdev); - struct i2c_client *client = info->i2c_dev; - enum pn544_irq irq; - size_t len; - int r = 0; - - dev_dbg(&client->dev, "%s: info: %p, count: %zu\n", __func__, - info, count); - - mutex_lock(&info->mutex); - - if (info->state == PN544_ST_COLD) { - r = -ENODEV; - goto out; - } - - irq = pn544_irq_state(info); - if (irq == PN544_NONE) { - if (file->f_flags & O_NONBLOCK) { - r = -EAGAIN; - goto out; - } - - if (wait_event_interruptible(info->read_wait, - (info->read_irq == PN544_INT))) { - r = -ERESTARTSYS; - goto out; - } - } - - if (info->state == PN544_ST_FW_READY) { - len = min(count, info->buflen); - - mutex_lock(&info->read_mutex); - r = pn544_fw_read(info->i2c_dev, info->buf, len); - info->read_irq = PN544_NONE; - mutex_unlock(&info->read_mutex); - - if (r < 0) { - dev_err(&info->i2c_dev->dev, "FW read failed: %d\n", r); - goto out; - } - - print_hex_dump(KERN_DEBUG, "FW read: ", DUMP_PREFIX_NONE, - 16, 2, info->buf, r, false); - - *offset += r; - if (copy_to_user(buf, info->buf, r)) { - r = -EFAULT; - goto out; - } - } else { - len = min(count, info->buflen); - - mutex_lock(&info->read_mutex); - r = pn544_i2c_read(info->i2c_dev, info->buf, len); - info->read_irq = PN544_NONE; - mutex_unlock(&info->read_mutex); - - if (r < 0) { - dev_err(&info->i2c_dev->dev, "read failed (%d)\n", r); - goto out; - } - print_hex_dump(KERN_DEBUG, "read: ", DUMP_PREFIX_NONE, - 16, 2, info->buf, r, false); - - *offset += r; - if (copy_to_user(buf, info->buf, r)) { - r = -EFAULT; - goto out; - } - } - -out: - mutex_unlock(&info->mutex); - - return r; -} - -static unsigned int pn544_poll(struct file *file, poll_table *wait) -{ - struct pn544_info *info = container_of(file->private_data, - struct pn544_info, miscdev); - struct i2c_client *client = info->i2c_dev; - int r = 0; - - dev_dbg(&client->dev, "%s: info: %p\n", __func__, info); - - mutex_lock(&info->mutex); - - if (info->state == PN544_ST_COLD) { - r = -ENODEV; - goto out; - } - - poll_wait(file, &info->read_wait, wait); - - if (pn544_irq_state(info) == PN544_INT) { - r = POLLIN | POLLRDNORM; - goto out; - } -out: - mutex_unlock(&info->mutex); - - return r; -} - -static ssize_t pn544_write(struct file *file, const char __user *buf, - size_t count, loff_t *ppos) -{ - struct pn544_info *info = container_of(file->private_data, - struct pn544_info, miscdev); - struct i2c_client *client = info->i2c_dev; - ssize_t len; - int r; - - dev_dbg(&client->dev, "%s: info: %p, count %zu\n", __func__, - info, count); - - mutex_lock(&info->mutex); - - if (info->state == PN544_ST_COLD) { - r = -ENODEV; - goto out; - } - - /* - * XXX: should we detect rset-writes and clean possible - * read_irq state - */ - if (info->state == PN544_ST_FW_READY) { - size_t fw_len; - - if (count < PN544_FW_HEADER_SIZE) { - r = -EINVAL; - goto out; - } - - len = min(count, info->buflen); - if (copy_from_user(info->buf, buf, len)) { - r = -EFAULT; - goto out; - } - - print_hex_dump(KERN_DEBUG, "FW write: ", DUMP_PREFIX_NONE, - 16, 2, info->buf, len, false); - - fw_len = PN544_FW_HEADER_SIZE + (info->buf[1] << 8) + - info->buf[2]; - - if (len > fw_len) /* 1 msg at a time */ - len = fw_len; - - r = pn544_fw_write(info->i2c_dev, info->buf, len); - } else { - if (count < PN544_LLC_MIN_SIZE) { - r = -EINVAL; - goto out; - } - - len = min(count, info->buflen); - if (copy_from_user(info->buf, buf, len)) { - r = -EFAULT; - goto out; - } - - print_hex_dump(KERN_DEBUG, "write: ", DUMP_PREFIX_NONE, - 16, 2, info->buf, len, false); - - if (len > (info->buf[0] + 1)) /* 1 msg at a time */ - len = info->buf[0] + 1; - - r = pn544_i2c_write(info->i2c_dev, info->buf, len); - } -out: - mutex_unlock(&info->mutex); - - return r; - -} - -static long pn544_ioctl(struct file *file, unsigned int cmd, unsigned long arg) -{ - struct pn544_info *info = container_of(file->private_data, - struct pn544_info, miscdev); - struct i2c_client *client = info->i2c_dev; - struct pn544_nfc_platform_data *pdata; - unsigned int val; - int r = 0; - - dev_dbg(&client->dev, "%s: info: %p, cmd: 0x%x\n", __func__, info, cmd); - - mutex_lock(&info->mutex); - - if (info->state == PN544_ST_COLD) { - r = -ENODEV; - goto out; - } - - pdata = info->i2c_dev->dev.platform_data; - switch (cmd) { - case PN544_GET_FW_MODE: - dev_dbg(&client->dev, "%s: PN544_GET_FW_MODE\n", __func__); - - val = (info->state == PN544_ST_FW_READY); - if (copy_to_user((void __user *)arg, &val, sizeof(val))) { - r = -EFAULT; - goto out; - } - - break; - - case PN544_SET_FW_MODE: - dev_dbg(&client->dev, "%s: PN544_SET_FW_MODE\n", __func__); - - if (copy_from_user(&val, (void __user *)arg, sizeof(val))) { - r = -EFAULT; - goto out; - } - - if (val) { - if (info->state == PN544_ST_FW_READY) - break; - - pn544_disable(info); - r = pn544_enable(info, FW_MODE); - if (r < 0) - goto out; - } else { - if (info->state == PN544_ST_READY) - break; - pn544_disable(info); - r = pn544_enable(info, HCI_MODE); - if (r < 0) - goto out; - } - file->f_pos = info->read_offset; - break; - - case TCGETS: - dev_dbg(&client->dev, "%s: TCGETS\n", __func__); - - r = -ENOIOCTLCMD; - break; - - default: - dev_err(&client->dev, "Unknown ioctl 0x%x\n", cmd); - r = -ENOIOCTLCMD; - break; - } - -out: - mutex_unlock(&info->mutex); - - return r; -} - -static int pn544_open(struct inode *inode, struct file *file) -{ - struct pn544_info *info = container_of(file->private_data, - struct pn544_info, miscdev); - struct i2c_client *client = info->i2c_dev; - int r = 0; - - dev_dbg(&client->dev, "%s: info: %p, client %p\n", __func__, - info, info->i2c_dev); - - mutex_lock(&info->mutex); - - /* - * Only 1 at a time. - * XXX: maybe user (counter) would work better - */ - if (info->state != PN544_ST_COLD) { - r = -EBUSY; - goto out; - } - - file->f_pos = info->read_offset; - r = pn544_enable(info, HCI_MODE); - -out: - mutex_unlock(&info->mutex); - return r; -} - -static int pn544_close(struct inode *inode, struct file *file) -{ - struct pn544_info *info = container_of(file->private_data, - struct pn544_info, miscdev); - struct i2c_client *client = info->i2c_dev; - - dev_dbg(&client->dev, "%s: info: %p, client %p\n", - __func__, info, info->i2c_dev); - - mutex_lock(&info->mutex); - pn544_disable(info); - mutex_unlock(&info->mutex); - - return 0; -} - -static const struct file_operations pn544_fops = { - .owner = THIS_MODULE, - .llseek = no_llseek, - .read = pn544_read, - .write = pn544_write, - .poll = pn544_poll, - .open = pn544_open, - .release = pn544_close, - .unlocked_ioctl = pn544_ioctl, -}; - -#ifdef CONFIG_PM -static int pn544_suspend(struct device *dev) -{ - struct i2c_client *client = to_i2c_client(dev); - struct pn544_info *info; - int r = 0; - - dev_info(&client->dev, "***\n%s: client %p\n***\n", __func__, client); - - info = i2c_get_clientdata(client); - dev_info(&client->dev, "%s: info: %p, client %p\n", __func__, - info, client); - - mutex_lock(&info->mutex); - - switch (info->state) { - case PN544_ST_FW_READY: - /* Do not suspend while upgrading FW, please! */ - r = -EPERM; - break; - - case PN544_ST_READY: - /* - * CHECK: Device should be in standby-mode. No way to check? - * Allowing low power mode for the regulator is potentially - * dangerous if pn544 does not go to suspension. - */ - break; - - case PN544_ST_COLD: - break; - }; - - mutex_unlock(&info->mutex); - return r; -} - -static int pn544_resume(struct device *dev) -{ - struct i2c_client *client = to_i2c_client(dev); - struct pn544_info *info = i2c_get_clientdata(client); - int r = 0; - - dev_dbg(&client->dev, "%s: info: %p, client %p\n", __func__, - info, client); - - mutex_lock(&info->mutex); - - switch (info->state) { - case PN544_ST_READY: - /* - * CHECK: If regulator low power mode is allowed in - * pn544_suspend, we should go back to normal mode - * here. - */ - break; - - case PN544_ST_COLD: - break; - - case PN544_ST_FW_READY: - break; - }; - - mutex_unlock(&info->mutex); - - return r; -} - -static SIMPLE_DEV_PM_OPS(pn544_pm_ops, pn544_suspend, pn544_resume); -#endif - -static struct device_attribute pn544_attr = - __ATTR(nfc_test, S_IRUGO, pn544_test, NULL); - -static int __devinit pn544_probe(struct i2c_client *client, - const struct i2c_device_id *id) -{ - struct pn544_info *info; - struct pn544_nfc_platform_data *pdata; - int r = 0; - - dev_dbg(&client->dev, "%s\n", __func__); - dev_dbg(&client->dev, "IRQ: %d\n", client->irq); - - /* private data allocation */ - info = kzalloc(sizeof(struct pn544_info), GFP_KERNEL); - if (!info) { - dev_err(&client->dev, - "Cannot allocate memory for pn544_info.\n"); - r = -ENOMEM; - goto err_info_alloc; - } - - info->buflen = max(PN544_MSG_MAX_SIZE, PN544_MAX_I2C_TRANSFER); - info->buf = kzalloc(info->buflen, GFP_KERNEL); - if (!info->buf) { - dev_err(&client->dev, - "Cannot allocate memory for pn544_info->buf.\n"); - r = -ENOMEM; - goto err_buf_alloc; - } - - info->regs[0].supply = reg_vdd_io; - info->regs[1].supply = reg_vbat; - info->regs[2].supply = reg_vsim; - r = regulator_bulk_get(&client->dev, ARRAY_SIZE(info->regs), - info->regs); - if (r < 0) - goto err_kmalloc; - - info->i2c_dev = client; - info->state = PN544_ST_COLD; - info->read_irq = PN544_NONE; - mutex_init(&info->read_mutex); - mutex_init(&info->mutex); - init_waitqueue_head(&info->read_wait); - i2c_set_clientdata(client, info); - pdata = client->dev.platform_data; - if (!pdata) { - dev_err(&client->dev, "No platform data\n"); - r = -EINVAL; - goto err_reg; - } - - if (!pdata->request_resources) { - dev_err(&client->dev, "request_resources() missing\n"); - r = -EINVAL; - goto err_reg; - } - - r = pdata->request_resources(client); - if (r) { - dev_err(&client->dev, "Cannot get platform resources\n"); - goto err_reg; - } - - r = request_threaded_irq(client->irq, NULL, pn544_irq_thread_fn, - IRQF_TRIGGER_RISING, PN544_DRIVER_NAME, - info); - if (r < 0) { - dev_err(&client->dev, "Unable to register IRQ handler\n"); - goto err_res; - } - - /* If we don't have the test we don't need the sysfs file */ - if (pdata->test) { - r = device_create_file(&client->dev, &pn544_attr); - if (r) { - dev_err(&client->dev, - "sysfs registration failed, error %d\n", r); - goto err_irq; - } - } - - info->miscdev.minor = MISC_DYNAMIC_MINOR; - info->miscdev.name = PN544_DRIVER_NAME; - info->miscdev.fops = &pn544_fops; - info->miscdev.parent = &client->dev; - r = misc_register(&info->miscdev); - if (r < 0) { - dev_err(&client->dev, "Device registration failed\n"); - goto err_sysfs; - } - - dev_dbg(&client->dev, "%s: info: %p, pdata %p, client %p\n", - __func__, info, pdata, client); - - return 0; - -err_sysfs: - if (pdata->test) - device_remove_file(&client->dev, &pn544_attr); -err_irq: - free_irq(client->irq, info); -err_res: - if (pdata->free_resources) - pdata->free_resources(); -err_reg: - regulator_bulk_free(ARRAY_SIZE(info->regs), info->regs); -err_kmalloc: - kfree(info->buf); -err_buf_alloc: - kfree(info); -err_info_alloc: - return r; -} - -static __devexit int pn544_remove(struct i2c_client *client) -{ - struct pn544_info *info = i2c_get_clientdata(client); - struct pn544_nfc_platform_data *pdata = client->dev.platform_data; - - dev_dbg(&client->dev, "%s\n", __func__); - - misc_deregister(&info->miscdev); - if (pdata->test) - device_remove_file(&client->dev, &pn544_attr); - - if (info->state != PN544_ST_COLD) { - if (pdata->disable) - pdata->disable(); - - info->read_irq = PN544_NONE; - } - - free_irq(client->irq, info); - if (pdata->free_resources) - pdata->free_resources(); - - regulator_bulk_free(ARRAY_SIZE(info->regs), info->regs); - kfree(info->buf); - kfree(info); - - return 0; -} - -static struct i2c_driver pn544_driver = { - .driver = { - .name = PN544_DRIVER_NAME, -#ifdef CONFIG_PM - .pm = &pn544_pm_ops, -#endif - }, - .probe = pn544_probe, - .id_table = pn544_id_table, - .remove = __devexit_p(pn544_remove), -}; - -static int __init pn544_init(void) -{ - int r; - - pr_debug(DRIVER_DESC ": %s\n", __func__); - - r = i2c_add_driver(&pn544_driver); - if (r) { - pr_err(PN544_DRIVER_NAME ": driver registration failed\n"); - return r; - } - - return 0; -} - -static void __exit pn544_exit(void) -{ - i2c_del_driver(&pn544_driver); - pr_info(DRIVER_DESC ", Exiting.\n"); -} - -module_init(pn544_init); -module_exit(pn544_exit); - -MODULE_LICENSE("GPL"); -MODULE_DESCRIPTION(DRIVER_DESC); From 5adf54de97270256f6ec0e368ddde68ac516b692 Mon Sep 17 00:00:00 2001 From: Samuel Ortiz Date: Wed, 18 Jul 2012 12:58:51 +0200 Subject: [PATCH 27/31] MAINTAINERS: Add NFC specific mailing list linux-nfc@lists.01.org is where all the Linux NFC related discussions take place, and one can also send kernel patches there. Signed-off-by: Samuel Ortiz --- MAINTAINERS | 1 + 1 file changed, 1 insertion(+) diff --git a/MAINTAINERS b/MAINTAINERS index 94b823f71e94..006ebb39376f 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -4793,6 +4793,7 @@ M: Lauro Ramos Venancio M: Aloisio Almeida Jr M: Samuel Ortiz L: linux-wireless@vger.kernel.org +L: linux-nfc@lists.01.org (moderated for non-subscribers) S: Maintained F: net/nfc/ F: include/linux/nfc.h From 96e324024b421b3753eb142d5d92fbe4ac5e7519 Mon Sep 17 00:00:00 2001 From: Waldemar Rymarkiewicz Date: Thu, 20 Sep 2012 08:59:10 +0200 Subject: [PATCH 28/31] NFC: xmit from hci ops must return 0 or negative xmit callback provided by a driver encapsulates upper layers data and sends it to the hardware. So, HCI does not know the exact amount of data being sent and thus can't handle partially sent frames properly. Therefore, the driver must return 0 for completely sent frame or negative for failure. Signed-off-by: Waldemar Rymarkiewicz Acked-by: Eric Lapuyade Signed-off-by: Samuel Ortiz --- drivers/nfc/pn544_hci.c | 8 ++++++-- include/net/nfc/hci.h | 5 +++++ 2 files changed, 11 insertions(+), 2 deletions(-) diff --git a/drivers/nfc/pn544_hci.c b/drivers/nfc/pn544_hci.c index 7b0c2176e89b..c9c8570273ab 100644 --- a/drivers/nfc/pn544_hci.c +++ b/drivers/nfc/pn544_hci.c @@ -235,8 +235,12 @@ static int pn544_hci_i2c_write(struct i2c_client *client, u8 *buf, int len) r = i2c_master_send(client, buf, len); } - if (r >= 0 && r != len) - r = -EREMOTEIO; + if (r >= 0) { + if (r != len) + return -EREMOTEIO; + else + return 0; + } return r; } diff --git a/include/net/nfc/hci.h b/include/net/nfc/hci.h index 9b5ed2d94d60..e900072950cb 100644 --- a/include/net/nfc/hci.h +++ b/include/net/nfc/hci.h @@ -30,6 +30,11 @@ struct nfc_hci_ops { int (*open) (struct nfc_hci_dev *hdev); void (*close) (struct nfc_hci_dev *hdev); int (*hci_ready) (struct nfc_hci_dev *hdev); + /* + * xmit must always send the complete buffer before + * returning. Returned result must be 0 for success + * or negative for failure. + */ int (*xmit) (struct nfc_hci_dev *hdev, struct sk_buff *skb); int (*start_poll) (struct nfc_hci_dev *hdev, u32 im_protocols, u32 tm_protocols); From 9010e39f508ee57dc9a7675073659cb9d019a802 Mon Sep 17 00:00:00 2001 From: Waldemar Rymarkiewicz Date: Thu, 20 Sep 2012 08:59:11 +0200 Subject: [PATCH 29/31] NFC: Handle RSET in SHDLC_CONNECTING state As queue_work() does not guarantee immediate execution of sm_work it can happen in crossover RSET usecase that connect timer will constantly change the shdlc state from NEGOTIATING to CONNECTING before shdlc has chance to handle incoming frame. Signed-off-by: Waldemar Rymarkiewicz Acked-by: Eric Lapuyade Signed-off-by: Samuel Ortiz --- net/nfc/hci/llc_shdlc.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/net/nfc/hci/llc_shdlc.c b/net/nfc/hci/llc_shdlc.c index d54bfc51c693..a7931c7c57f2 100644 --- a/net/nfc/hci/llc_shdlc.c +++ b/net/nfc/hci/llc_shdlc.c @@ -414,7 +414,8 @@ static void llc_shdlc_rcv_u_frame(struct llc_shdlc *shdlc, switch (u_frame_modifier) { case U_FRAME_RSET: - if (shdlc->state == SHDLC_NEGOCIATING) { + if ((shdlc->state == SHDLC_NEGOCIATING) || + (shdlc->state == SHDLC_CONNECTING)) { /* we sent RSET, but chip wants to negociate */ if (skb->len > 0) w = skb->data[0]; From 12bfd1e8906637757a0bc42970f0fc11a9918019 Mon Sep 17 00:00:00 2001 From: Waldemar Rymarkiewicz Date: Fri, 21 Sep 2012 14:02:46 +0200 Subject: [PATCH 30/31] NFC: Don't handle consequent RSET frames after UA During processing incoming RSET frame chip, possibly due to its internal timout, can retrnasmit an another RSET which is next queued for processing in shdlc layer. In case when we accept processed RSET skip those remaining on the rcv queue until chip will send it's first S or I frame. This will mean the chip completed connection as well. Signed-off-by: Waldemar Rymarkiewicz Signed-off-by: Samuel Ortiz --- net/nfc/hci/llc_shdlc.c | 38 +++++++++++++++++++++++++++++++------- 1 file changed, 31 insertions(+), 7 deletions(-) diff --git a/net/nfc/hci/llc_shdlc.c b/net/nfc/hci/llc_shdlc.c index a7931c7c57f2..3afde1ecd925 100644 --- a/net/nfc/hci/llc_shdlc.c +++ b/net/nfc/hci/llc_shdlc.c @@ -32,7 +32,8 @@ enum shdlc_state { SHDLC_DISCONNECTED = 0, SHDLC_CONNECTING = 1, SHDLC_NEGOCIATING = 2, - SHDLC_CONNECTED = 3 + SHDLC_HALF_CONNECTED = 3, + SHDLC_CONNECTED = 4 }; struct llc_shdlc { @@ -363,7 +364,7 @@ static void llc_shdlc_connect_complete(struct llc_shdlc *shdlc, int r) shdlc->nr = 0; shdlc->dnr = 0; - shdlc->state = SHDLC_CONNECTED; + shdlc->state = SHDLC_HALF_CONNECTED; } else { shdlc->state = SHDLC_DISCONNECTED; } @@ -414,9 +415,13 @@ static void llc_shdlc_rcv_u_frame(struct llc_shdlc *shdlc, switch (u_frame_modifier) { case U_FRAME_RSET: - if ((shdlc->state == SHDLC_NEGOCIATING) || - (shdlc->state == SHDLC_CONNECTING)) { - /* we sent RSET, but chip wants to negociate */ + switch (shdlc->state) { + case SHDLC_NEGOCIATING: + case SHDLC_CONNECTING: + /* + * We sent RSET, but chip wants to negociate or we + * got RSET before we managed to send out our. + */ if (skb->len > 0) w = skb->data[0]; @@ -431,19 +436,31 @@ static void llc_shdlc_rcv_u_frame(struct llc_shdlc *shdlc, r = llc_shdlc_connect_send_ua(shdlc); llc_shdlc_connect_complete(shdlc, r); } - } else if (shdlc->state == SHDLC_CONNECTED) { + break; + case SHDLC_HALF_CONNECTED: + /* + * Chip resent RSET due to its timeout - Ignote it + * as we already sent UA. + */ + break; + case SHDLC_CONNECTED: /* * Chip wants to reset link. This is unexpected and * unsupported. */ shdlc->hard_fault = -ECONNRESET; + break; + default: + break; } break; case U_FRAME_UA: if ((shdlc->state == SHDLC_CONNECTING && shdlc->connect_tries > 0) || - (shdlc->state == SHDLC_NEGOCIATING)) + (shdlc->state == SHDLC_NEGOCIATING)) { llc_shdlc_connect_complete(shdlc, 0); + shdlc->state = SHDLC_CONNECTED; + } break; default: break; @@ -470,11 +487,17 @@ static void llc_shdlc_handle_rcv_queue(struct llc_shdlc *shdlc) switch (control & SHDLC_CONTROL_HEAD_MASK) { case SHDLC_CONTROL_HEAD_I: case SHDLC_CONTROL_HEAD_I2: + if (shdlc->state == SHDLC_HALF_CONNECTED) + shdlc->state = SHDLC_CONNECTED; + ns = (control & SHDLC_CONTROL_NS_MASK) >> 3; nr = control & SHDLC_CONTROL_NR_MASK; llc_shdlc_rcv_i_frame(shdlc, skb, ns, nr); break; case SHDLC_CONTROL_HEAD_S: + if (shdlc->state == SHDLC_HALF_CONNECTED) + shdlc->state = SHDLC_CONNECTED; + s_frame_type = (control & SHDLC_CONTROL_TYPE_MASK) >> 3; nr = control & SHDLC_CONTROL_NR_MASK; llc_shdlc_rcv_s_frame(shdlc, s_frame_type, nr); @@ -633,6 +656,7 @@ static void llc_shdlc_sm_work(struct work_struct *work) break; } break; + case SHDLC_HALF_CONNECTED: case SHDLC_CONNECTED: llc_shdlc_handle_rcv_queue(shdlc); llc_shdlc_handle_send_queue(shdlc); From 4c0ba9ac4bf5f20ada774f5d181d03044e0147e7 Mon Sep 17 00:00:00 2001 From: Waldemar Rymarkiewicz Date: Mon, 24 Sep 2012 08:07:07 +0200 Subject: [PATCH 31/31] NFC: Fix typo negociating -> negotiating Signed-off-by: Waldemar Rymarkiewicz Signed-off-by: Samuel Ortiz --- net/nfc/hci/llc_shdlc.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/net/nfc/hci/llc_shdlc.c b/net/nfc/hci/llc_shdlc.c index 3afde1ecd925..8f69d791dcb3 100644 --- a/net/nfc/hci/llc_shdlc.c +++ b/net/nfc/hci/llc_shdlc.c @@ -31,7 +31,7 @@ enum shdlc_state { SHDLC_DISCONNECTED = 0, SHDLC_CONNECTING = 1, - SHDLC_NEGOCIATING = 2, + SHDLC_NEGOTIATING = 2, SHDLC_HALF_CONNECTED = 3, SHDLC_CONNECTED = 4 }; @@ -416,7 +416,7 @@ static void llc_shdlc_rcv_u_frame(struct llc_shdlc *shdlc, switch (u_frame_modifier) { case U_FRAME_RSET: switch (shdlc->state) { - case SHDLC_NEGOCIATING: + case SHDLC_NEGOTIATING: case SHDLC_CONNECTING: /* * We sent RSET, but chip wants to negociate or we @@ -457,7 +457,7 @@ static void llc_shdlc_rcv_u_frame(struct llc_shdlc *shdlc, case U_FRAME_UA: if ((shdlc->state == SHDLC_CONNECTING && shdlc->connect_tries > 0) || - (shdlc->state == SHDLC_NEGOCIATING)) { + (shdlc->state == SHDLC_NEGOTIATING)) { llc_shdlc_connect_complete(shdlc, 0); shdlc->state = SHDLC_CONNECTED; } @@ -640,10 +640,10 @@ static void llc_shdlc_sm_work(struct work_struct *work) mod_timer(&shdlc->connect_timer, jiffies + msecs_to_jiffies(SHDLC_CONNECT_VALUE_MS)); - shdlc->state = SHDLC_NEGOCIATING; + shdlc->state = SHDLC_NEGOTIATING; } break; - case SHDLC_NEGOCIATING: + case SHDLC_NEGOTIATING: if (timer_pending(&shdlc->connect_timer) == 0) { shdlc->state = SHDLC_CONNECTING; queue_work(system_nrt_wq, &shdlc->sm_work);