diff options
author | David S. Miller <davem@davemloft.net> | 2012-09-30 02:30:16 -0400 |
---|---|---|
committer | David S. Miller <davem@davemloft.net> | 2012-09-30 02:30:16 -0400 |
commit | a248afdc1b5916c2bfd007233112333d85aa28f6 (patch) | |
tree | 49d4a0b8fdcf7624ea6b0bdf0af2567d7392210e /net/nfc | |
parent | d379142bc4d9b78cdd9fc5aa696ca1ea083fb7d4 (diff) | |
parent | c487606f835a93a725bac1aefd536be98f22474d (diff) | |
download | blackbird-obmc-linux-a248afdc1b5916c2bfd007233112333d85aa28f6.tar.gz blackbird-obmc-linux-a248afdc1b5916c2bfd007233112333d85aa28f6.zip |
Merge branch 'for-davem' of git://git.kernel.org/pub/scm/linux/kernel/git/linville/wireless-next
John W. Linville says:
====================
Here is another batch of updates intended for 3.7...
Highlights include an hci_connect re-write in Bluetooth, HCI/LLC
layer separation in NFC, removal of the raw pn544 NFC driver, NFC LLCP
raw sockets support, improved IBSS auth frame handling in mac80211,
full-MAC AP mode notification support in mac80211, a lot of attention
paid to brcmfmac, and the usual level of updates to iwlwifi, ath9k,
mwifiex, and rt2x00, and various other updates.
====================
Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'net/nfc')
-rw-r--r-- | net/nfc/core.c | 13 | ||||
-rw-r--r-- | net/nfc/hci/Makefile | 4 | ||||
-rw-r--r-- | net/nfc/hci/command.c | 45 | ||||
-rw-r--r-- | net/nfc/hci/core.c | 336 | ||||
-rw-r--r-- | net/nfc/hci/hci.h | 15 | ||||
-rw-r--r-- | net/nfc/hci/hcp.c | 6 | ||||
-rw-r--r-- | net/nfc/hci/llc.c | 170 | ||||
-rw-r--r-- | net/nfc/hci/llc.h | 69 | ||||
-rw-r--r-- | net/nfc/hci/llc_nop.c | 99 | ||||
-rw-r--r-- | net/nfc/hci/llc_shdlc.c (renamed from net/nfc/hci/shdlc.c) | 544 | ||||
-rw-r--r-- | net/nfc/llcp/commands.c | 2 | ||||
-rw-r--r-- | net/nfc/llcp/llcp.c | 131 | ||||
-rw-r--r-- | net/nfc/llcp/llcp.h | 6 | ||||
-rw-r--r-- | net/nfc/llcp/sock.c | 93 | ||||
-rw-r--r-- | net/nfc/nci/core.c | 91 | ||||
-rw-r--r-- | net/nfc/nci/ntf.c | 52 | ||||
-rw-r--r-- | net/nfc/nci/rsp.c | 14 | ||||
-rw-r--r-- | net/nfc/netlink.c | 46 |
18 files changed, 1157 insertions, 579 deletions
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/Makefile b/net/nfc/hci/Makefile index f9c44b2fb065..c5dbb6891b24 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-$(CONFIG_NFC_SHDLC) += shdlc.o +hci-y := core.o hcp.o command.o llc.o llc_nop.o +hci-$(CONFIG_NFC_SHDLC) += llc_shdlc.o diff --git a/net/nfc/hci/command.c b/net/nfc/hci/command.c index 46362ef979db..71c6a7086b8f 100644 --- a/net/nfc/hci/command.c +++ b/net/nfc/hci/command.c @@ -28,10 +28,29 @@ #include "hci.h" -static void nfc_hci_execute_cb(struct nfc_hci_dev *hdev, int err, - struct sk_buff *skb, void *cb_data) +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) { - struct hcp_exec_waiter *hcp_ew = (struct hcp_exec_waiter *)cb_data; + 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) + * 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 *)context; pr_debug("HCI Cmd completed with result=%d\n", err); @@ -55,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? @@ -133,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) { diff --git a/net/nfc/hci/core.c b/net/nfc/hci/core.c index 1ac7b3fac6c9..d378d93de62e 100644 --- a/net/nfc/hci/core.c +++ b/net/nfc/hci/core.c @@ -26,6 +26,7 @@ #include <net/nfc/nfc.h> #include <net/nfc/hci.h> +#include <net/nfc/llc.h> #include "hci.h" @@ -57,12 +58,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 @@ -78,12 +78,12 @@ 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); if (msg->cb) - msg->cb(hdev, r, NULL, msg->cb_context); + msg->cb(msg->cb_context, NULL, r); kfree(msg); break; } @@ -133,15 +133,15 @@ 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); 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, @@ -398,8 +398,7 @@ disconnect_all: nfc_hci_disconnect_all_gates(hdev); exit: - if (skb) - kfree_skb(skb); + kfree_skb(skb); return r; } @@ -470,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; } @@ -500,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); @@ -539,13 +549,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); @@ -553,40 +587,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, @@ -600,6 +631,93 @@ static int hci_check_presence(struct nfc_dev *nfc_dev, return 0; } +static void nfc_hci_failure(struct nfc_hci_dev *hdev, int err) +{ + mutex_lock(&hdev->msg_tx_mutex); + + if (hdev->cmd_pending_msg == NULL) { + nfc_driver_failure(hdev->ndev, err); + goto exit; + } + + __nfc_hci_cmd_completion(hdev, err, NULL); + +exit: + mutex_unlock(&hdev->msg_tx_mutex); +} + +static void nfc_hci_llc_failure(struct nfc_hci_dev *hdev, int err) +{ + nfc_hci_failure(hdev, err); +} + +static void nfc_hci_recv_from_llc(struct nfc_hci_dev *hdev, struct sk_buff *skb) +{ + struct hcp_packet *packet; + u8 type; + u8 instruction; + struct sk_buff *hcp_skb; + u8 pipe; + struct sk_buff *frag_skb; + int msg_len; + + packet = (struct hcp_packet *)skb->data; + if ((packet->header & ~NFC_HCI_FRAGMENT) == 0) { + skb_queue_tail(&hdev->rx_hcp_frags, skb); + return; + } + + /* it's the last fragment. Does it need re-aggregation? */ + if (skb_queue_len(&hdev->rx_hcp_frags)) { + pipe = packet->header & NFC_HCI_FRAGMENT; + skb_queue_tail(&hdev->rx_hcp_frags, skb); + + msg_len = 0; + skb_queue_walk(&hdev->rx_hcp_frags, frag_skb) { + msg_len += (frag_skb->len - + NFC_HCI_HCP_PACKET_HEADER_LEN); + } + + hcp_skb = nfc_alloc_recv_skb(NFC_HCI_HCP_PACKET_HEADER_LEN + + msg_len, GFP_KERNEL); + if (hcp_skb == NULL) { + nfc_hci_failure(hdev, -ENOMEM); + return; + } + + *skb_put(hcp_skb, NFC_HCI_HCP_PACKET_HEADER_LEN) = pipe; + + skb_queue_walk(&hdev->rx_hcp_frags, frag_skb) { + msg_len = frag_skb->len - NFC_HCI_HCP_PACKET_HEADER_LEN; + memcpy(skb_put(hcp_skb, msg_len), + frag_skb->data + NFC_HCI_HCP_PACKET_HEADER_LEN, + msg_len); + } + + skb_queue_purge(&hdev->rx_hcp_frags); + } else { + packet->header &= NFC_HCI_FRAGMENT; + hcp_skb = skb; + } + + /* if this is a response, dispatch immediately to + * unblock waiting cmd context. Otherwise, enqueue to dispatch + * in separate context where handler can also execute command. + */ + packet = (struct hcp_packet *)hcp_skb->data; + type = HCP_MSG_GET_TYPE(packet->message.header); + if (type == NFC_HCI_HCP_RESPONSE) { + pipe = packet->header; + instruction = HCP_MSG_GET_CMD(packet->message.header); + skb_pull(hcp_skb, NFC_HCI_HCP_PACKET_HEADER_LEN + + NFC_HCI_HCP_MESSAGE_HEADER_LEN); + nfc_hci_hcp_message_rx(hdev, pipe, type, instruction, hcp_skb); + } else { + skb_queue_tail(&hdev->msg_rx_queue, hcp_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, @@ -614,6 +732,7 @@ static struct nfc_ops hci_nfc_ops = { 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) @@ -630,10 +749,19 @@ struct nfc_hci_dev *nfc_hci_allocate_device(struct nfc_hci_ops *ops, 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; } @@ -653,29 +781,18 @@ 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) { - 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 +801,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 +825,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); @@ -743,93 +842,30 @@ void *nfc_hci_get_clientdata(struct nfc_hci_dev *hdev) } EXPORT_SYMBOL(nfc_hci_get_clientdata); -static void nfc_hci_failure(struct nfc_hci_dev *hdev, int err) -{ - mutex_lock(&hdev->msg_tx_mutex); - - if (hdev->cmd_pending_msg == NULL) { - nfc_driver_failure(hdev->ndev, err); - goto exit; - } - - __nfc_hci_cmd_completion(hdev, err, NULL); - -exit: - mutex_unlock(&hdev->msg_tx_mutex); -} - void nfc_hci_driver_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) +void inline nfc_hci_recv_frame(struct nfc_hci_dev *hdev, struct sk_buff *skb) { - struct hcp_packet *packet; - u8 type; - u8 instruction; - struct sk_buff *hcp_skb; - u8 pipe; - struct sk_buff *frag_skb; - int msg_len; - - packet = (struct hcp_packet *)skb->data; - if ((packet->header & ~NFC_HCI_FRAGMENT) == 0) { - skb_queue_tail(&hdev->rx_hcp_frags, skb); - return; - } - - /* it's the last fragment. Does it need re-aggregation? */ - if (skb_queue_len(&hdev->rx_hcp_frags)) { - pipe = packet->header & NFC_HCI_FRAGMENT; - skb_queue_tail(&hdev->rx_hcp_frags, skb); - - msg_len = 0; - skb_queue_walk(&hdev->rx_hcp_frags, frag_skb) { - msg_len += (frag_skb->len - - NFC_HCI_HCP_PACKET_HEADER_LEN); - } - - hcp_skb = nfc_alloc_recv_skb(NFC_HCI_HCP_PACKET_HEADER_LEN + - msg_len, GFP_KERNEL); - if (hcp_skb == NULL) { - nfc_hci_failure(hdev, -ENOMEM); - return; - } - - *skb_put(hcp_skb, NFC_HCI_HCP_PACKET_HEADER_LEN) = pipe; - - skb_queue_walk(&hdev->rx_hcp_frags, frag_skb) { - msg_len = frag_skb->len - NFC_HCI_HCP_PACKET_HEADER_LEN; - memcpy(skb_put(hcp_skb, msg_len), - frag_skb->data + NFC_HCI_HCP_PACKET_HEADER_LEN, - msg_len); - } + nfc_llc_rcv_from_drv(hdev->llc, skb); +} +EXPORT_SYMBOL(nfc_hci_recv_frame); - skb_queue_purge(&hdev->rx_hcp_frags); - } else { - packet->header &= NFC_HCI_FRAGMENT; - hcp_skb = skb; - } +static int __init nfc_hci_init(void) +{ + return nfc_llc_init(); +} - /* if this is a response, dispatch immediately to - * unblock waiting cmd context. Otherwise, enqueue to dispatch - * in separate context where handler can also execute command. - */ - packet = (struct hcp_packet *)hcp_skb->data; - type = HCP_MSG_GET_TYPE(packet->message.header); - if (type == NFC_HCI_HCP_RESPONSE) { - pipe = packet->header; - instruction = HCP_MSG_GET_CMD(packet->message.header); - skb_pull(hcp_skb, NFC_HCI_HCP_PACKET_HEADER_LEN + - NFC_HCI_HCP_MESSAGE_HEADER_LEN); - 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); - } +static void __exit nfc_hci_exit(void) +{ + nfc_llc_exit(); } -EXPORT_SYMBOL(nfc_hci_recv_frame); + +subsys_initcall(nfc_hci_init); +module_exit(nfc_hci_exit); MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("NFC HCI Core"); 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 <net/nfc/hci.h> + 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 f4dad1a89740..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; @@ -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/llc.c b/net/nfc/hci/llc.c new file mode 100644 index 000000000000..ae1205ded87f --- /dev/null +++ b/net/nfc/hci/llc.c @@ -0,0 +1,170 @@ +/* + * 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 <net/nfc/llc.h> + +#include "llc.h" + +static struct list_head llc_engines; + +int nfc_llc_init(void) +{ + int r; + + INIT_LIST_HEAD(&llc_engines); + + 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; +} + +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); + } +} + +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; +} + +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); +} + +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; +} + +void nfc_llc_free(struct nfc_llc *llc) +{ + llc->ops->deinit(llc); + kfree(llc); +} + +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; +} + +inline int nfc_llc_start(struct nfc_llc *llc) +{ + return llc->ops->start(llc); +} + +inline int nfc_llc_stop(struct nfc_llc *llc) +{ + return llc->ops->stop(llc); +} + +inline void nfc_llc_rcv_from_drv(struct nfc_llc *llc, struct sk_buff *skb) +{ + llc->ops->rcv_from_drv(llc, skb); +} + +inline int nfc_llc_xmit_from_hci(struct nfc_llc *llc, struct sk_buff *skb) +{ + return llc->ops->xmit_from_hci(llc, skb); +} + +inline void *nfc_llc_get_data(struct nfc_llc *llc) +{ + return llc->data; +} diff --git a/net/nfc/hci/llc.h b/net/nfc/hci/llc.h new file mode 100644 index 000000000000..7be0b7f3ceb6 --- /dev/null +++ b/net/nfc/hci/llc.h @@ -0,0 +1,69 @@ +/* + * 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 <net/nfc/hci.h> +#include <net/nfc/llc.h> +#include <linux/skbuff.h> + +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); + +int nfc_llc_nop_register(void); + +#if defined(CONFIG_NFC_SHDLC) +int nfc_llc_shdlc_register(void); +#else +static inline int nfc_llc_shdlc_register(void) +{ + return 0; +} +#endif + +#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..87b10291b40f --- /dev/null +++ b/net/nfc/hci/llc_nop.c @@ -0,0 +1,99 @@ +/* + * 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 <linux/types.h> + +#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(void) +{ + return nfc_llc_register(LLC_NOP_NAME, &llc_nop_ops); +} diff --git a/net/nfc/hci/shdlc.c b/net/nfc/hci/llc_shdlc.c index 6f840c18c892..8f69d791dcb3 100644 --- a/net/nfc/hci/shdlc.c +++ b/net/nfc/hci/llc_shdlc.c @@ -1,10 +1,11 @@ /* + * 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 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 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 @@ -19,18 +20,65 @@ #define pr_fmt(fmt) "shdlc: %s: " fmt, __func__ +#include <linux/types.h> #include <linux/sched.h> -#include <linux/export.h> #include <linux/wait.h> -#include <linux/crc-ccitt.h> #include <linux/slab.h> #include <linux/skbuff.h> -#include <net/nfc/hci.h> -#include <net/nfc/shdlc.h> +#include "llc.h" + +enum shdlc_state { + SHDLC_DISCONNECTED = 0, + SHDLC_CONNECTING = 1, + SHDLC_NEGOTIATING = 2, + SHDLC_HALF_CONNECTED = 3, + SHDLC_CONNECTED = 4 +}; + +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_LLC_TAIL_ROOM 2 #define SHDLC_MAX_WINDOW 4 #define SHDLC_SREJ_SUPPORT false @@ -71,7 +119,7 @@ do { \ } while (0) /* checks x < y <= z modulo 8 */ -static bool nfc_shdlc_x_lt_y_lteq_z(int x, int y, int z) +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; @@ -80,7 +128,7 @@ static bool nfc_shdlc_x_lt_y_lteq_z(int x, int y, int z) } /* checks x <= y < z modulo 8 */ -static bool nfc_shdlc_x_lteq_y_lt_z(int x, int y, int z) +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; @@ -88,36 +136,21 @@ static bool nfc_shdlc_x_lteq_y_lt_z(int x, int y, int z) return ((y >= x) || (y < z)) ? true : false; } -static struct sk_buff *nfc_shdlc_alloc_skb(struct nfc_shdlc *shdlc, +static struct sk_buff *llc_shdlc_alloc_skb(struct llc_shdlc *shdlc, int payload_len) { 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); + skb = alloc_skb(shdlc->tx_headroom + SHDLC_LLC_HEAD_ROOM + + shdlc->tx_tailroom + payload_len, GFP_KERNEL); if (skb) - skb_reserve(skb, shdlc->client_headroom + SHDLC_LLC_HEAD_ROOM); + skb_reserve(skb, shdlc->tx_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, +static int llc_shdlc_send_s_frame(struct llc_shdlc *shdlc, enum sframe_type sframe_type, int nr) { int r; @@ -125,15 +158,13 @@ static int nfc_shdlc_send_s_frame(struct nfc_shdlc *shdlc, pr_debug("sframe_type=%d nr=%d\n", sframe_type, nr); - skb = nfc_shdlc_alloc_skb(shdlc, 0); + skb = llc_shdlc_alloc_skb(shdlc, 0); if (skb == NULL) return -ENOMEM; *skb_push(skb, 1) = SHDLC_CONTROL_HEAD_S | (sframe_type << 3) | nr; - nfc_shdlc_add_len_crc(skb); - - r = shdlc->ops->xmit(shdlc, skb); + r = shdlc->xmit_to_drv(shdlc->hdev, skb); kfree_skb(skb); @@ -141,7 +172,7 @@ static int nfc_shdlc_send_s_frame(struct nfc_shdlc *shdlc, } /* immediately sends an U frame. skb may contain optional payload */ -static int nfc_shdlc_send_u_frame(struct nfc_shdlc *shdlc, +static int llc_shdlc_send_u_frame(struct llc_shdlc *shdlc, struct sk_buff *skb, enum uframe_modifier uframe_modifier) { @@ -151,9 +182,7 @@ 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); + r = shdlc->xmit_to_drv(shdlc->hdev, skb); kfree_skb(skb); @@ -164,7 +193,7 @@ static int nfc_shdlc_send_u_frame(struct nfc_shdlc *shdlc, * 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) +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 */ @@ -204,7 +233,7 @@ static void nfc_shdlc_reset_t2(struct nfc_shdlc *shdlc, int y_nr) * 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, +static void llc_shdlc_rcv_i_frame(struct llc_shdlc *shdlc, struct sk_buff *skb, int ns, int nr) { int x_ns = ns; @@ -216,66 +245,64 @@ static void nfc_shdlc_rcv_i_frame(struct nfc_shdlc *shdlc, goto exit; if (x_ns != shdlc->nr) { - nfc_shdlc_send_s_frame(shdlc, S_FRAME_REJ, 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, + 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) { - nfc_hci_recv_frame(shdlc->hdev, skb); + shdlc->rcv_to_hci(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); + 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: - if (skb) - kfree_skb(skb); + kfree_skb(skb); } -static void nfc_shdlc_rcv_ack(struct nfc_shdlc *shdlc, int y_nr) +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 (nfc_shdlc_x_lt_y_lteq_z(shdlc->dnr, y_nr, shdlc->ns)) { - nfc_shdlc_reset_t2(shdlc, 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 nfc_shdlc_requeue_ack_pending(struct nfc_shdlc *shdlc) +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, 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; } -static void nfc_shdlc_rcv_rej(struct nfc_shdlc *shdlc, int y_nr) +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 (nfc_shdlc_x_lteq_y_lt_z(shdlc->dnr, y_nr, shdlc->ns)) { + 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; @@ -289,12 +316,12 @@ static void nfc_shdlc_rcv_rej(struct nfc_shdlc *shdlc, int y_nr) } } - nfc_shdlc_requeue_ack_pending(shdlc); + llc_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, +static void llc_shdlc_rcv_s_frame(struct llc_shdlc *shdlc, enum sframe_type s_frame_type, int nr) { struct sk_buff *skb; @@ -304,21 +331,21 @@ static void nfc_shdlc_rcv_s_frame(struct nfc_shdlc *shdlc, switch (s_frame_type) { case S_FRAME_RR: - nfc_shdlc_rcv_ack(shdlc, nr); + 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 = nfc_shdlc_alloc_skb(shdlc, 0); + skb = llc_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); + llc_shdlc_rcv_rej(shdlc, nr); break; case S_FRAME_RNR: - nfc_shdlc_rcv_ack(shdlc, nr); + llc_shdlc_rcv_ack(shdlc, nr); shdlc->rnr = true; break; default: @@ -326,7 +353,7 @@ static void nfc_shdlc_rcv_s_frame(struct nfc_shdlc *shdlc, } } -static void nfc_shdlc_connect_complete(struct nfc_shdlc *shdlc, int r) +static void llc_shdlc_connect_complete(struct llc_shdlc *shdlc, int r) { pr_debug("result=%d\n", r); @@ -337,7 +364,7 @@ static void nfc_shdlc_connect_complete(struct nfc_shdlc *shdlc, int r) shdlc->nr = 0; shdlc->dnr = 0; - shdlc->state = SHDLC_CONNECTED; + shdlc->state = SHDLC_HALF_CONNECTED; } else { shdlc->state = SHDLC_DISCONNECTED; } @@ -347,36 +374,36 @@ static void nfc_shdlc_connect_complete(struct nfc_shdlc *shdlc, int r) wake_up(shdlc->connect_wq); } -static int nfc_shdlc_connect_initiate(struct nfc_shdlc *shdlc) +static int llc_shdlc_connect_initiate(struct llc_shdlc *shdlc) { struct sk_buff *skb; pr_debug("\n"); - skb = nfc_shdlc_alloc_skb(shdlc, 2); + 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 nfc_shdlc_send_u_frame(shdlc, skb, U_FRAME_RSET); + return llc_shdlc_send_u_frame(shdlc, skb, U_FRAME_RSET); } -static int nfc_shdlc_connect_send_ua(struct nfc_shdlc *shdlc) +static int llc_shdlc_connect_send_ua(struct llc_shdlc *shdlc) { struct sk_buff *skb; pr_debug("\n"); - skb = nfc_shdlc_alloc_skb(shdlc, 0); + skb = llc_shdlc_alloc_skb(shdlc, 0); if (skb == NULL) return -ENOMEM; - return nfc_shdlc_send_u_frame(shdlc, skb, U_FRAME_UA); + return llc_shdlc_send_u_frame(shdlc, skb, U_FRAME_UA); } -static void nfc_shdlc_rcv_u_frame(struct nfc_shdlc *shdlc, +static void llc_shdlc_rcv_u_frame(struct llc_shdlc *shdlc, struct sk_buff *skb, enum uframe_modifier u_frame_modifier) { @@ -388,8 +415,13 @@ static void nfc_shdlc_rcv_u_frame(struct nfc_shdlc *shdlc, switch (u_frame_modifier) { case U_FRAME_RSET: - if (shdlc->state == SHDLC_NEGOCIATING) { - /* we sent RSET, but chip wants to negociate */ + switch (shdlc->state) { + case SHDLC_NEGOTIATING: + 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]; @@ -401,22 +433,34 @@ static void nfc_shdlc_rcv_u_frame(struct nfc_shdlc *shdlc, (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); + 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)) - nfc_shdlc_connect_complete(shdlc, 0); + (shdlc->state == SHDLC_NEGOTIATING)) { + llc_shdlc_connect_complete(shdlc, 0); + shdlc->state = SHDLC_CONNECTED; + } break; default: break; @@ -425,7 +469,7 @@ static void nfc_shdlc_rcv_u_frame(struct nfc_shdlc *shdlc, kfree_skb(skb); } -static void nfc_shdlc_handle_rcv_queue(struct nfc_shdlc *shdlc) +static void llc_shdlc_handle_rcv_queue(struct llc_shdlc *shdlc) { struct sk_buff *skb; u8 control; @@ -443,19 +487,25 @@ static void nfc_shdlc_handle_rcv_queue(struct nfc_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; - nfc_shdlc_rcv_i_frame(shdlc, skb, ns, nr); + 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; - nfc_shdlc_rcv_s_frame(shdlc, s_frame_type, nr); + 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; - nfc_shdlc_rcv_u_frame(shdlc, skb, u_frame_modifier); + llc_shdlc_rcv_u_frame(shdlc, skb, u_frame_modifier); break; default: pr_err("UNKNOWN Control=%d\n", control); @@ -465,7 +515,7 @@ static void nfc_shdlc_handle_rcv_queue(struct nfc_shdlc *shdlc) } } -static int nfc_shdlc_w_used(int ns, int dnr) +static int llc_shdlc_w_used(int ns, int dnr) { int unack_count; @@ -478,7 +528,7 @@ static int nfc_shdlc_w_used(int ns, int dnr) } /* Send frames according to algorithm at spec:10.8.1 */ -static void nfc_shdlc_handle_send_queue(struct nfc_shdlc *shdlc) +static void llc_shdlc_handle_send_queue(struct llc_shdlc *shdlc) { struct sk_buff *skb; int r; @@ -489,7 +539,7 @@ static void nfc_shdlc_handle_send_queue(struct nfc_shdlc *shdlc) ("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->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 && @@ -508,11 +558,9 @@ static void nfc_shdlc_handle_send_queue(struct nfc_shdlc *shdlc) pr_debug("Sending I-Frame %d, waiting to rcv %d\n", shdlc->ns, shdlc->nr); - /* SHDLC_DUMP_SKB("shdlc frame written", skb); */ - - nfc_shdlc_add_len_crc(skb); + SHDLC_DUMP_SKB("shdlc frame written", skb); - r = shdlc->ops->xmit(shdlc, skb); + r = shdlc->xmit_to_drv(shdlc->hdev, skb); if (r < 0) { shdlc->hard_fault = r; break; @@ -534,36 +582,36 @@ static void nfc_shdlc_handle_send_queue(struct nfc_shdlc *shdlc) } } -static void nfc_shdlc_connect_timeout(unsigned long data) +static void llc_shdlc_connect_timeout(unsigned long data) { - struct nfc_shdlc *shdlc = (struct nfc_shdlc *)data; + struct llc_shdlc *shdlc = (struct llc_shdlc *)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) +static void llc_shdlc_t1_timeout(unsigned long data) { - struct nfc_shdlc *shdlc = (struct nfc_shdlc *)data; + struct llc_shdlc *shdlc = (struct llc_shdlc *)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) +static void llc_shdlc_t2_timeout(unsigned long data) { - struct nfc_shdlc *shdlc = (struct nfc_shdlc *)data; + struct llc_shdlc *shdlc = (struct llc_shdlc *)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) +static void llc_shdlc_sm_work(struct work_struct *work) { - struct nfc_shdlc *shdlc = container_of(work, struct nfc_shdlc, sm_work); + struct llc_shdlc *shdlc = container_of(work, struct llc_shdlc, sm_work); int r; pr_debug("\n"); @@ -578,46 +626,47 @@ static void nfc_shdlc_sm_work(struct work_struct *work) break; case SHDLC_CONNECTING: if (shdlc->hard_fault) { - nfc_shdlc_connect_complete(shdlc, shdlc->hard_fault); + llc_shdlc_connect_complete(shdlc, shdlc->hard_fault); break; } if (shdlc->connect_tries++ < 5) - r = nfc_shdlc_connect_initiate(shdlc); + r = llc_shdlc_connect_initiate(shdlc); else r = -ETIME; if (r < 0) - nfc_shdlc_connect_complete(shdlc, r); + llc_shdlc_connect_complete(shdlc, r); else { 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(shdlc->sm_wq, &shdlc->sm_work); + queue_work(system_nrt_wq, &shdlc->sm_work); } - nfc_shdlc_handle_rcv_queue(shdlc); + llc_shdlc_handle_rcv_queue(shdlc); if (shdlc->hard_fault) { - nfc_shdlc_connect_complete(shdlc, shdlc->hard_fault); + llc_shdlc_connect_complete(shdlc, shdlc->hard_fault); break; } break; + case SHDLC_HALF_CONNECTED: case SHDLC_CONNECTED: - nfc_shdlc_handle_rcv_queue(shdlc); - nfc_shdlc_handle_send_queue(shdlc); + 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 = nfc_shdlc_send_s_frame(shdlc, S_FRAME_RR, + r = llc_shdlc_send_s_frame(shdlc, S_FRAME_RR, shdlc->nr); if (r < 0) shdlc->hard_fault = r; @@ -629,12 +678,12 @@ static void nfc_shdlc_sm_work(struct work_struct *work) shdlc->t2_active = false; - nfc_shdlc_requeue_ack_pending(shdlc); - nfc_shdlc_handle_send_queue(shdlc); + llc_shdlc_requeue_ack_pending(shdlc); + llc_shdlc_handle_send_queue(shdlc); } if (shdlc->hard_fault) { - nfc_hci_driver_failure(shdlc->hdev, shdlc->hard_fault); + shdlc->llc_failure(shdlc->hdev, shdlc->hard_fault); } break; default: @@ -647,7 +696,7 @@ static void nfc_shdlc_sm_work(struct work_struct *work) * Called from syscall context to establish shdlc link. Sleeps until * link is ready or failure. */ -static int nfc_shdlc_connect(struct nfc_shdlc *shdlc) +static int llc_shdlc_connect(struct llc_shdlc *shdlc) { DECLARE_WAIT_QUEUE_HEAD_ONSTACK(connect_wq); @@ -662,14 +711,14 @@ 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); return shdlc->connect_result; } -static void nfc_shdlc_disconnect(struct nfc_shdlc *shdlc) +static void llc_shdlc_disconnect(struct llc_shdlc *shdlc) { pr_debug("\n"); @@ -679,7 +728,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); } /* @@ -687,7 +736,7 @@ static void nfc_shdlc_disconnect(struct nfc_shdlc *shdlc) * 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) +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"); @@ -697,176 +746,37 @@ 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); -} -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); + queue_work(system_nrt_wq, &shdlc->sm_work); } -static int nfc_shdlc_hci_ready(struct nfc_hci_dev *hdev) +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 nfc_shdlc *shdlc = nfc_hci_get_clientdata(hdev); - int r = 0; - - pr_debug("\n"); + struct llc_shdlc *shdlc; - 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); + *rx_headroom = SHDLC_LLC_HEAD_ROOM; + *rx_tailroom = 0; - queue_work(shdlc->sm_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, - struct sk_buff **res_skb) -{ - 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 -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; - char name[32]; - - if (ops->xmit == NULL) - return NULL; - - shdlc = kzalloc(sizeof(struct nfc_shdlc), GFP_KERNEL); + shdlc = kzalloc(sizeof(struct llc_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; + shdlc->connect_timer.function = llc_shdlc_connect_timeout; init_timer(&shdlc->t1_timer); shdlc->t1_timer.data = (unsigned long)shdlc; - shdlc->t1_timer.function = nfc_shdlc_t1_timeout; + shdlc->t1_timer.function = llc_shdlc_t1_timeout; init_timer(&shdlc->t2_timer); shdlc->t2_timer.data = (unsigned long)shdlc; - shdlc->t2_timer.function = nfc_shdlc_t2_timeout; + shdlc->t2_timer.function = llc_shdlc_t2_timeout; shdlc->w = SHDLC_MAX_WINDOW; shdlc->srej_support = SHDLC_SREJ_SUPPORT; @@ -875,77 +785,73 @@ struct nfc_shdlc *nfc_shdlc_allocate(struct nfc_shdlc_ops *ops, skb_queue_head_init(&shdlc->send_q); 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; + INIT_WORK(&shdlc->sm_work, llc_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 + SHDLC_LLC_TAIL_ROOM, - 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; + 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; +} -err_regdev: - nfc_hci_free_device(shdlc->hdev); +static void llc_shdlc_deinit(struct nfc_llc *llc) +{ + struct llc_shdlc *shdlc = nfc_llc_get_data(llc); -err_allocdev: - destroy_workqueue(shdlc->sm_wq); + skb_queue_purge(&shdlc->rcv_q); + skb_queue_purge(&shdlc->send_q); + skb_queue_purge(&shdlc->ack_pending_q); -err_allocwq: kfree(shdlc); - - return NULL; } -EXPORT_SYMBOL(nfc_shdlc_allocate); -void nfc_shdlc_free(struct nfc_shdlc *shdlc) +static int llc_shdlc_start(struct nfc_llc *llc) { - pr_debug("\n"); + struct llc_shdlc *shdlc = nfc_llc_get_data(llc); - nfc_hci_unregister_device(shdlc->hdev); - nfc_hci_free_device(shdlc->hdev); + return llc_shdlc_connect(shdlc); +} - destroy_workqueue(shdlc->sm_wq); +static int llc_shdlc_stop(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); + llc_shdlc_disconnect(shdlc); - kfree(shdlc); + return 0; } -EXPORT_SYMBOL(nfc_shdlc_free); -void nfc_shdlc_set_clientdata(struct nfc_shdlc *shdlc, void *clientdata) +static void llc_shdlc_rcv_from_drv(struct nfc_llc *llc, struct sk_buff *skb) { - pr_debug("\n"); + struct llc_shdlc *shdlc = nfc_llc_get_data(llc); - shdlc->clientdata = clientdata; + llc_shdlc_recv_frame(shdlc, skb); } -EXPORT_SYMBOL(nfc_shdlc_set_clientdata); -void *nfc_shdlc_get_clientdata(struct nfc_shdlc *shdlc) +static int llc_shdlc_xmit_from_hci(struct nfc_llc *llc, struct sk_buff *skb) { - return shdlc->clientdata; + 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; } -EXPORT_SYMBOL(nfc_shdlc_get_clientdata); -struct nfc_hci_dev *nfc_shdlc_get_hci_dev(struct nfc_shdlc *shdlc) +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(void) { - return shdlc->hdev; + return nfc_llc_register(LLC_SHDLC_NAME, &llc_shdlc_ops); } -EXPORT_SYMBOL(nfc_shdlc_get_hci_dev); diff --git a/net/nfc/llcp/commands.c b/net/nfc/llcp/commands.c index b982b5b890d7..c45ccd6c094c 100644 --- a/net/nfc/llcp/commands.c +++ b/net/nfc/llcp/commands.c @@ -312,6 +312,8 @@ int nfc_llcp_send_symm(struct nfc_dev *dev) skb = llcp_add_header(skb, 0, 0, LLCP_PDU_SYMM); + nfc_llcp_send_to_raw_sock(local, skb, NFC_LLCP_DIRECTION_TX); + return nfc_data_exchange(dev, local->target_idx, skb, nfc_llcp_recv, local); } diff --git a/net/nfc/llcp/llcp.c b/net/nfc/llcp/llcp.c index 82f0f7588b46..c12c5ef3d036 100644 --- a/net/nfc/llcp/llcp.c +++ b/net/nfc/llcp/llcp.c @@ -56,7 +56,7 @@ static void nfc_llcp_socket_release(struct nfc_llcp_local *local, bool listen) sk_for_each_safe(sk, node, tmp, &local->sockets.head) { llcp_sock = nfc_llcp_sock(sk); - lock_sock(sk); + bh_lock_sock(sk); if (sk->sk_state == LLCP_CONNECTED) nfc_put_device(llcp_sock->dev); @@ -68,26 +68,26 @@ static void nfc_llcp_socket_release(struct nfc_llcp_local *local, bool listen) list_for_each_entry_safe(lsk, n, &llcp_sock->accept_queue, accept_queue) { accept_sk = &lsk->sk; - lock_sock(accept_sk); + bh_lock_sock(accept_sk); nfc_llcp_accept_unlink(accept_sk); accept_sk->sk_state = LLCP_CLOSED; - release_sock(accept_sk); + bh_unlock_sock(accept_sk); sock_orphan(accept_sk); } if (listen == true) { - release_sock(sk); + bh_unlock_sock(sk); continue; } } sk->sk_state = LLCP_CLOSED; - release_sock(sk); + bh_unlock_sock(sk); sock_orphan(sk); @@ -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) @@ -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; + local->gb_len = gb_len; + +out: kfree(version_tlv); kfree(lto_tlv); + kfree(wks_tlv); + kfree(miux_tlv); - local->gb_len = gb_len; - - return 0; + return ret; } u8 *nfc_llcp_general_bytes(struct nfc_dev *dev, size_t *general_bytes_len) @@ -554,6 +558,46 @@ static void nfc_llcp_set_nrns(struct nfc_llcp_sock *sock, struct sk_buff *pdu) sock->recv_ack_n = (sock->recv_n - 1) % 16; } +void nfc_llcp_send_to_raw_sock(struct nfc_llcp_local *local, + struct sk_buff *skb, u8 direction) +{ + struct hlist_node *node; + struct sk_buff *skb_copy = NULL, *nskb; + struct sock *sk; + u8 *data; + + read_lock(&local->raw_sockets.lock); + + sk_for_each(sk, node, &local->raw_sockets.head) { + if (sk->sk_state != LLCP_BOUND) + continue; + + if (skb_copy == NULL) { + skb_copy = __pskb_copy(skb, NFC_LLCP_RAW_HEADER_SIZE, + GFP_ATOMIC); + + if (skb_copy == NULL) + continue; + + data = skb_push(skb_copy, NFC_LLCP_RAW_HEADER_SIZE); + + data[0] = local->dev ? local->dev->idx : 0xFF; + data[1] = direction; + } + + nskb = skb_clone(skb_copy, GFP_ATOMIC); + if (!nskb) + continue; + + if (sock_queue_rcv_skb(sk, nskb)) + kfree_skb(nskb); + } + + read_unlock(&local->raw_sockets.lock); + + kfree_skb(skb_copy); +} + static void nfc_llcp_tx_work(struct work_struct *work) { struct nfc_llcp_local *local = container_of(work, struct nfc_llcp_local, @@ -574,6 +618,9 @@ static void nfc_llcp_tx_work(struct work_struct *work) DUMP_PREFIX_OFFSET, 16, 1, skb->data, skb->len, true); + nfc_llcp_send_to_raw_sock(local, skb, + NFC_LLCP_DIRECTION_TX); + ret = nfc_data_exchange(local->dev, local->target_idx, skb, nfc_llcp_recv, local); @@ -1018,6 +1065,8 @@ static void nfc_llcp_rx_work(struct work_struct *work) print_hex_dump(KERN_DEBUG, "LLCP Rx: ", DUMP_PREFIX_OFFSET, 16, 1, skb->data, skb->len, true); + nfc_llcp_send_to_raw_sock(local, skb, NFC_LLCP_DIRECTION_RX); + switch (ptype) { case LLCP_PDU_SYMM: pr_debug("SYMM\n"); @@ -1052,7 +1101,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 +1120,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 +1135,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 +1170,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 +1179,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,41 +1195,15 @@ 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); + rwlock_init(&local->sockets.lock); + rwlock_init(&local->connecting_sockets.lock); + rwlock_init(&local->raw_sockets.lock); nfc_llcp_build_gb(local); @@ -1193,17 +1213,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; } void nfc_llcp_unregister_device(struct nfc_dev *dev) diff --git a/net/nfc/llcp/llcp.h b/net/nfc/llcp/llcp.h index 83b8bba5a280..fdb2d24e60bd 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; @@ -89,6 +86,7 @@ struct nfc_llcp_local { /* sockets array */ struct llcp_sock_list sockets; struct llcp_sock_list connecting_sockets; + struct llcp_sock_list raw_sockets; }; struct nfc_llcp_sock { @@ -187,6 +185,8 @@ u8 nfc_llcp_get_sdp_ssap(struct nfc_llcp_local *local, u8 nfc_llcp_get_local_ssap(struct nfc_llcp_local *local); void nfc_llcp_put_ssap(struct nfc_llcp_local *local, u8 ssap); int nfc_llcp_queue_i_frames(struct nfc_llcp_sock *sock); +void nfc_llcp_send_to_raw_sock(struct nfc_llcp_local *local, + struct sk_buff *skb, u8 direction); /* Sock API */ struct sock *nfc_llcp_sock_alloc(struct socket *sock, int type, gfp_t gfp); diff --git a/net/nfc/llcp/sock.c b/net/nfc/llcp/sock.c index ddeb9aa398f0..40f056debf9a 100644 --- a/net/nfc/llcp/sock.c +++ b/net/nfc/llcp/sock.c @@ -142,6 +142,60 @@ error: return ret; } +static int llcp_raw_sock_bind(struct socket *sock, struct sockaddr *addr, + int alen) +{ + struct sock *sk = sock->sk; + struct nfc_llcp_sock *llcp_sock = nfc_llcp_sock(sk); + struct nfc_llcp_local *local; + struct nfc_dev *dev; + struct sockaddr_nfc_llcp llcp_addr; + int len, ret = 0; + + if (!addr || addr->sa_family != AF_NFC) + return -EINVAL; + + pr_debug("sk %p addr %p family %d\n", sk, addr, addr->sa_family); + + memset(&llcp_addr, 0, sizeof(llcp_addr)); + len = min_t(unsigned int, sizeof(llcp_addr), alen); + memcpy(&llcp_addr, addr, len); + + lock_sock(sk); + + if (sk->sk_state != LLCP_CLOSED) { + ret = -EBADFD; + goto error; + } + + dev = nfc_get_device(llcp_addr.dev_idx); + if (dev == NULL) { + ret = -ENODEV; + goto error; + } + + local = nfc_llcp_find_local(dev); + if (local == NULL) { + ret = -ENODEV; + goto put_dev; + } + + llcp_sock->dev = dev; + llcp_sock->local = nfc_llcp_local_get(local); + llcp_sock->nfc_protocol = llcp_addr.nfc_protocol; + + nfc_llcp_sock_link(&local->raw_sockets, sk); + + sk->sk_state = LLCP_BOUND; + +put_dev: + nfc_put_device(dev); + +error: + release_sock(sk); + return ret; +} + static int llcp_sock_listen(struct socket *sock, int backlog) { struct sock *sk = sock->sk; @@ -300,9 +354,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); @@ -421,7 +472,10 @@ static int llcp_sock_release(struct socket *sock) release_sock(sk); - nfc_llcp_sock_unlink(&local->sockets, sk); + if (sock->type == SOCK_RAW) + nfc_llcp_sock_unlink(&local->raw_sockets, sk); + else + nfc_llcp_sock_unlink(&local->sockets, sk); out: sock_orphan(sk); @@ -617,7 +671,7 @@ static int llcp_sock_recvmsg(struct kiocb *iocb, struct socket *sock, if (!(flags & MSG_PEEK)) { /* SOCK_STREAM: re-queue skb if it contains unreceived data */ - if (sk->sk_type == SOCK_STREAM) { + if (sk->sk_type == SOCK_STREAM || sk->sk_type == SOCK_RAW) { skb_pull(skb, copied); if (skb->len) { skb_queue_head(&sk->sk_receive_queue, skb); @@ -658,6 +712,26 @@ static const struct proto_ops llcp_sock_ops = { .mmap = sock_no_mmap, }; +static const struct proto_ops llcp_rawsock_ops = { + .family = PF_NFC, + .owner = THIS_MODULE, + .bind = llcp_raw_sock_bind, + .connect = sock_no_connect, + .release = llcp_sock_release, + .socketpair = sock_no_socketpair, + .accept = sock_no_accept, + .getname = llcp_sock_getname, + .poll = llcp_sock_poll, + .ioctl = sock_no_ioctl, + .listen = sock_no_listen, + .shutdown = sock_no_shutdown, + .setsockopt = sock_no_setsockopt, + .getsockopt = sock_no_getsockopt, + .sendmsg = sock_no_sendmsg, + .recvmsg = llcp_sock_recvmsg, + .mmap = sock_no_mmap, +}; + static void llcp_sock_destruct(struct sock *sk) { struct nfc_llcp_sock *llcp_sock = nfc_llcp_sock(sk); @@ -735,10 +809,15 @@ static int llcp_sock_create(struct net *net, struct socket *sock, pr_debug("%p\n", sock); - if (sock->type != SOCK_STREAM && sock->type != SOCK_DGRAM) + if (sock->type != SOCK_STREAM && + sock->type != SOCK_DGRAM && + sock->type != SOCK_RAW) return -ESOCKTNOSUPPORT; - sock->ops = &llcp_sock_ops; + if (sock->type == SOCK_RAW) + sock->ops = &llcp_rawsock_ops; + else + sock->ops = &llcp_sock_ops; sk = nfc_llcp_sock_alloc(sock, sock->type, GFP_ATOMIC); if (sk == NULL) diff --git a/net/nfc/nci/core.c b/net/nfc/nci/core.c index f81efe13985a..acf9abb7d99b 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)); @@ -509,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"); @@ -524,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) @@ -557,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 af7a93b04393..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; @@ -361,6 +363,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 +483,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; @@ -473,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) { 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; diff --git a/net/nfc/netlink.c b/net/nfc/netlink.c index 4bbb70e32d1e..c1b5285cbde7 100644 --- a/net/nfc/netlink.c +++ b/net/nfc/netlink.c @@ -761,31 +761,63 @@ static struct genl_ops nfc_genl_ops[] = { }, }; -static int nfc_genl_rcv_nl_event(struct notifier_block *this, - unsigned long event, void *ptr) + +struct urelease_work { + struct work_struct w; + int portid; +}; + +static void nfc_urelease_event_work(struct work_struct *work) { - struct netlink_notify *n = ptr; + struct urelease_work *w = container_of(work, struct urelease_work, w); struct class_dev_iter iter; struct nfc_dev *dev; - if (event != NETLINK_URELEASE || n->protocol != NETLINK_GENERIC) - goto out; + pr_debug("portid %d\n", w->portid); - pr_debug("NETLINK_URELEASE event from id %d\n", n->portid); + mutex_lock(&nfc_devlist_mutex); nfc_device_iter_init(&iter); dev = nfc_device_iter_next(&iter); while (dev) { - if (dev->genl_data.poll_req_portid == n->portid) { + mutex_lock(&dev->genl_data.genl_data_mutex); + + if (dev->genl_data.poll_req_portid == w->portid) { nfc_stop_poll(dev); dev->genl_data.poll_req_portid = 0; } + + mutex_unlock(&dev->genl_data.genl_data_mutex); + dev = nfc_device_iter_next(&iter); } nfc_device_iter_exit(&iter); + mutex_unlock(&nfc_devlist_mutex); + + kfree(w); +} + +static int nfc_genl_rcv_nl_event(struct notifier_block *this, + unsigned long event, void *ptr) +{ + struct netlink_notify *n = ptr; + struct urelease_work *w; + + if (event != NETLINK_URELEASE || n->protocol != NETLINK_GENERIC) + goto out; + + pr_debug("NETLINK_URELEASE event from id %d\n", n->portid); + + w = kmalloc(sizeof(*w), GFP_ATOMIC); + if (w) { + INIT_WORK((struct work_struct *) w, nfc_urelease_event_work); + w->portid = n->portid; + schedule_work((struct work_struct *) w); + } + out: return NOTIFY_DONE; } |