summaryrefslogtreecommitdiff
path: root/net/nfc
diff options
context:
space:
mode:
Diffstat (limited to 'net/nfc')
-rw-r--r--net/nfc/core.c50
-rw-r--r--net/nfc/hci/command.c7
-rw-r--r--net/nfc/hci/core.c57
-rw-r--r--net/nfc/hci/hcp.c7
-rw-r--r--net/nfc/llcp/commands.c2
-rw-r--r--net/nfc/llcp/llcp.c49
-rw-r--r--net/nfc/llcp/llcp.h1
-rw-r--r--net/nfc/llcp/sock.c22
-rw-r--r--net/nfc/nci/core.c2
-rw-r--r--net/nfc/netlink.c1
10 files changed, 129 insertions, 69 deletions
diff --git a/net/nfc/core.c b/net/nfc/core.c
index 0f4a6de6f16..6ceee8e181c 100644
--- a/net/nfc/core.c
+++ b/net/nfc/core.c
@@ -338,7 +338,7 @@ int nfc_activate_target(struct nfc_dev *dev, u32 target_idx, u32 protocol)
dev->active_target = target;
dev->rf_mode = NFC_RF_INITIATOR;
- if (dev->ops->check_presence)
+ if (dev->ops->check_presence && !dev->shutting_down)
mod_timer(&dev->check_pres_timer, jiffies +
msecs_to_jiffies(NFC_CHECK_PRES_FREQ_MS));
}
@@ -429,7 +429,7 @@ int nfc_data_exchange(struct nfc_dev *dev, u32 target_idx, struct sk_buff *skb,
rc = dev->ops->im_transceive(dev, dev->active_target, skb, cb,
cb_context);
- if (!rc && dev->ops->check_presence)
+ if (!rc && dev->ops->check_presence && !dev->shutting_down)
mod_timer(&dev->check_pres_timer, jiffies +
msecs_to_jiffies(NFC_CHECK_PRES_FREQ_MS));
} else if (dev->rf_mode == NFC_RF_TARGET && dev->ops->tm_send != NULL) {
@@ -684,11 +684,6 @@ static void nfc_release(struct device *d)
pr_debug("dev_name=%s\n", dev_name(&dev->dev));
- if (dev->ops->check_presence) {
- del_timer_sync(&dev->check_pres_timer);
- cancel_work_sync(&dev->check_pres_work);
- }
-
nfc_genl_data_exit(&dev->genl_data);
kfree(dev->targets);
kfree(dev);
@@ -706,15 +701,16 @@ static void nfc_check_pres_work(struct work_struct *work)
rc = dev->ops->check_presence(dev, dev->active_target);
if (rc == -EOPNOTSUPP)
goto exit;
- if (!rc) {
- mod_timer(&dev->check_pres_timer, jiffies +
- msecs_to_jiffies(NFC_CHECK_PRES_FREQ_MS));
- } else {
+ if (rc) {
u32 active_target_idx = dev->active_target->idx;
device_unlock(&dev->dev);
nfc_target_lost(dev, active_target_idx);
return;
}
+
+ if (!dev->shutting_down)
+ mod_timer(&dev->check_pres_timer, jiffies +
+ msecs_to_jiffies(NFC_CHECK_PRES_FREQ_MS));
}
exit:
@@ -761,6 +757,7 @@ struct nfc_dev *nfc_get_device(unsigned int idx)
*/
struct nfc_dev *nfc_allocate_device(struct nfc_ops *ops,
u32 supported_protocols,
+ u32 supported_se,
int tx_headroom, int tx_tailroom)
{
struct nfc_dev *dev;
@@ -778,6 +775,8 @@ struct nfc_dev *nfc_allocate_device(struct nfc_ops *ops,
dev->ops = ops;
dev->supported_protocols = supported_protocols;
+ dev->supported_se = supported_se;
+ dev->active_se = NFC_SE_NONE;
dev->tx_headroom = tx_headroom;
dev->tx_tailroom = tx_tailroom;
@@ -853,26 +852,27 @@ void nfc_unregister_device(struct nfc_dev *dev)
id = dev->idx;
- mutex_lock(&nfc_devlist_mutex);
- nfc_devlist_generation++;
-
- /* lock to avoid unregistering a device while an operation
- is in progress */
- device_lock(&dev->dev);
- device_del(&dev->dev);
- device_unlock(&dev->dev);
+ if (dev->ops->check_presence) {
+ device_lock(&dev->dev);
+ dev->shutting_down = true;
+ device_unlock(&dev->dev);
+ del_timer_sync(&dev->check_pres_timer);
+ cancel_work_sync(&dev->check_pres_work);
+ }
- mutex_unlock(&nfc_devlist_mutex);
+ rc = nfc_genl_device_removed(dev);
+ if (rc)
+ pr_debug("The userspace won't be notified that the device %s "
+ "was removed\n", dev_name(&dev->dev));
nfc_llcp_unregister_device(dev);
- rc = nfc_genl_device_removed(dev);
- if (rc)
- pr_debug("The userspace won't be notified that the device %s was removed\n",
- dev_name(&dev->dev));
+ mutex_lock(&nfc_devlist_mutex);
+ nfc_devlist_generation++;
+ device_del(&dev->dev);
+ mutex_unlock(&nfc_devlist_mutex);
ida_simple_remove(&nfc_index_ida, id);
-
}
EXPORT_SYMBOL(nfc_unregister_device);
diff --git a/net/nfc/hci/command.c b/net/nfc/hci/command.c
index 7d99410e6c1..64f922be928 100644
--- a/net/nfc/hci/command.c
+++ b/net/nfc/hci/command.c
@@ -280,14 +280,19 @@ static int nfc_hci_delete_pipe(struct nfc_hci_dev *hdev, u8 pipe)
static int nfc_hci_clear_all_pipes(struct nfc_hci_dev *hdev)
{
u8 param[2];
+ size_t param_len = 2;
/* TODO: Find out what the identity reference data is
* and fill param with it. HCI spec 6.1.3.5 */
pr_debug("\n");
+ if (test_bit(NFC_HCI_QUIRK_SHORT_CLEAR, &hdev->quirks))
+ param_len = 0;
+
return nfc_hci_execute_cmd(hdev, NFC_HCI_ADMIN_PIPE,
- NFC_HCI_ADM_CLEAR_ALL_PIPE, param, 2, NULL);
+ NFC_HCI_ADM_CLEAR_ALL_PIPE, param, param_len,
+ NULL);
}
int nfc_hci_disconnect_gate(struct nfc_hci_dev *hdev, u8 gate)
diff --git a/net/nfc/hci/core.c b/net/nfc/hci/core.c
index 7bea574d593..91020b210d8 100644
--- a/net/nfc/hci/core.c
+++ b/net/nfc/hci/core.c
@@ -57,6 +57,8 @@ static void nfc_hci_msg_tx_work(struct work_struct *work)
int r = 0;
mutex_lock(&hdev->msg_tx_mutex);
+ if (hdev->shutting_down)
+ goto exit;
if (hdev->cmd_pending_msg) {
if (timer_pending(&hdev->cmd_timer) == 0) {
@@ -295,6 +297,12 @@ void nfc_hci_event_received(struct nfc_hci_dev *hdev, u8 pipe, u8 event,
goto exit;
}
+ if (hdev->ops->event_received) {
+ r = hdev->ops->event_received(hdev, gate, event, skb);
+ if (r <= 0)
+ goto exit_noskb;
+ }
+
switch (event) {
case NFC_HCI_EVT_TARGET_DISCOVERED:
if (skb->len < 1) { /* no status data? */
@@ -320,17 +328,15 @@ void nfc_hci_event_received(struct nfc_hci_dev *hdev, u8 pipe, u8 event,
r = nfc_hci_target_discovered(hdev, gate);
break;
default:
- if (hdev->ops->event_received) {
- hdev->ops->event_received(hdev, gate, event, skb);
- return;
- }
-
+ pr_info("Discarded unknown event %x to gate %x\n", event, gate);
+ r = -EINVAL;
break;
}
exit:
kfree_skb(skb);
+exit_noskb:
if (r) {
/* TODO: There was an error dispatching the event,
* how to propagate up to nfc core?
@@ -669,8 +675,10 @@ static int hci_tm_send(struct nfc_dev *nfc_dev, struct sk_buff *skb)
if (hdev->ops->tm_send)
return hdev->ops->tm_send(hdev, skb);
- else
- return -ENOTSUPP;
+
+ kfree_skb(skb);
+
+ return -ENOTSUPP;
}
static int hci_check_presence(struct nfc_dev *nfc_dev,
@@ -787,7 +795,9 @@ 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,
+ unsigned long quirks,
u32 protocols,
+ u32 supported_se,
const char *llc_name,
int tx_headroom,
int tx_tailroom,
@@ -813,7 +823,7 @@ struct nfc_hci_dev *nfc_hci_allocate_device(struct nfc_hci_ops *ops,
return NULL;
}
- hdev->ndev = nfc_allocate_device(&hci_nfc_ops, protocols,
+ hdev->ndev = nfc_allocate_device(&hci_nfc_ops, protocols, supported_se,
tx_headroom + HCI_CMDS_HEADROOM,
tx_tailroom);
if (!hdev->ndev) {
@@ -830,6 +840,8 @@ struct nfc_hci_dev *nfc_hci_allocate_device(struct nfc_hci_ops *ops,
memset(hdev->gate2pipe, NFC_HCI_INVALID_PIPE, sizeof(hdev->gate2pipe));
+ hdev->quirks = quirks;
+
return hdev;
}
EXPORT_SYMBOL(nfc_hci_allocate_device);
@@ -868,6 +880,28 @@ void nfc_hci_unregister_device(struct nfc_hci_dev *hdev)
{
struct hci_msg *msg, *n;
+ mutex_lock(&hdev->msg_tx_mutex);
+
+ if (hdev->cmd_pending_msg) {
+ if (hdev->cmd_pending_msg->cb)
+ hdev->cmd_pending_msg->cb(
+ hdev->cmd_pending_msg->cb_context,
+ NULL, -ESHUTDOWN);
+ kfree(hdev->cmd_pending_msg);
+ hdev->cmd_pending_msg = NULL;
+ }
+
+ hdev->shutting_down = true;
+
+ mutex_unlock(&hdev->msg_tx_mutex);
+
+ del_timer_sync(&hdev->cmd_timer);
+ cancel_work_sync(&hdev->msg_tx_work);
+
+ cancel_work_sync(&hdev->msg_rx_work);
+
+ nfc_unregister_device(hdev->ndev);
+
skb_queue_purge(&hdev->rx_hcp_frags);
skb_queue_purge(&hdev->msg_rx_queue);
@@ -876,13 +910,6 @@ void nfc_hci_unregister_device(struct nfc_hci_dev *hdev)
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);
diff --git a/net/nfc/hci/hcp.c b/net/nfc/hci/hcp.c
index bc308a7ca60..b6b4109f234 100644
--- a/net/nfc/hci/hcp.c
+++ b/net/nfc/hci/hcp.c
@@ -105,6 +105,13 @@ int nfc_hci_hcp_message_tx(struct nfc_hci_dev *hdev, u8 pipe,
}
mutex_lock(&hdev->msg_tx_mutex);
+
+ if (hdev->shutting_down) {
+ err = -ESHUTDOWN;
+ mutex_unlock(&hdev->msg_tx_mutex);
+ goto out_skb_err;
+ }
+
list_add_tail(&cmd->msg_l, &hdev->msg_tx_queue);
mutex_unlock(&hdev->msg_tx_mutex);
diff --git a/net/nfc/llcp/commands.c b/net/nfc/llcp/commands.c
index df24be48d4d..c6bc3bd9505 100644
--- a/net/nfc/llcp/commands.c
+++ b/net/nfc/llcp/commands.c
@@ -304,6 +304,8 @@ int nfc_llcp_send_symm(struct nfc_dev *dev)
skb = llcp_add_header(skb, 0, 0, LLCP_PDU_SYMM);
+ __net_timestamp(skb);
+
nfc_llcp_send_to_raw_sock(local, skb, NFC_LLCP_DIRECTION_TX);
return nfc_data_exchange(dev, local->target_idx, skb,
diff --git a/net/nfc/llcp/llcp.c b/net/nfc/llcp/llcp.c
index ec43914c92a..746f5a2f980 100644
--- a/net/nfc/llcp/llcp.c
+++ b/net/nfc/llcp/llcp.c
@@ -54,7 +54,6 @@ static void nfc_llcp_socket_purge(struct nfc_llcp_sock *sock)
skb_queue_purge(&sock->tx_queue);
skb_queue_purge(&sock->tx_pending_queue);
- skb_queue_purge(&sock->tx_backlog_queue);
if (local == NULL)
return;
@@ -550,14 +549,13 @@ int nfc_llcp_set_remote_gb(struct nfc_dev *dev, u8 *gb, u8 gb_len)
pr_err("No LLCP device\n");
return -ENODEV;
}
+ if (gb_len < 3)
+ return -EINVAL;
memset(local->remote_gb, 0, NFC_MAX_GT_LEN);
memcpy(local->remote_gb, gb, gb_len);
local->remote_gb_len = gb_len;
- if (local->remote_gb == NULL || local->remote_gb_len == 0)
- return -ENODEV;
-
if (memcmp(local->remote_gb, llcp_magic, 3)) {
pr_err("MAC does not support LLCP\n");
return -EINVAL;
@@ -668,6 +666,8 @@ static void nfc_llcp_tx_work(struct work_struct *work)
if (ptype == LLCP_PDU_I)
copy_skb = skb_copy(skb, GFP_ATOMIC);
+ __net_timestamp(skb);
+
nfc_llcp_send_to_raw_sock(local, skb,
NFC_LLCP_DIRECTION_TX);
@@ -781,9 +781,15 @@ static void nfc_llcp_recv_ui(struct nfc_llcp_local *local,
/* There is no sequence with UI frames */
skb_pull(skb, LLCP_HEADER_SIZE);
- if (sock_queue_rcv_skb(&llcp_sock->sk, skb)) {
- pr_err("receive queue is full\n");
- skb_queue_head(&llcp_sock->tx_backlog_queue, skb);
+ if (!sock_queue_rcv_skb(&llcp_sock->sk, skb)) {
+ /*
+ * UI frames will be freed from the socket layer, so we
+ * need to keep them alive until someone receives them.
+ */
+ skb_get(skb);
+ } else {
+ pr_err("Receive queue is full\n");
+ kfree_skb(skb);
}
nfc_llcp_sock_put(llcp_sock);
@@ -976,9 +982,15 @@ static void nfc_llcp_recv_hdlc(struct nfc_llcp_local *local,
pr_err("Received out of sequence I PDU\n");
skb_pull(skb, LLCP_HEADER_SIZE + LLCP_SEQUENCE_SIZE);
- if (sock_queue_rcv_skb(&llcp_sock->sk, skb)) {
- pr_err("receive queue is full\n");
- skb_queue_head(&llcp_sock->tx_backlog_queue, skb);
+ if (!sock_queue_rcv_skb(&llcp_sock->sk, skb)) {
+ /*
+ * I frames will be freed from the socket layer, so we
+ * need to keep them alive until someone receives them.
+ */
+ skb_get(skb);
+ } else {
+ pr_err("Receive queue is full\n");
+ kfree_skb(skb);
}
}
@@ -1245,6 +1257,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);
+ __net_timestamp(skb);
+
nfc_llcp_send_to_raw_sock(local, skb, NFC_LLCP_DIRECTION_RX);
switch (ptype) {
@@ -1296,6 +1310,13 @@ static void nfc_llcp_rx_work(struct work_struct *work)
local->rx_pending = NULL;
}
+static void __nfc_llcp_recv(struct nfc_llcp_local *local, struct sk_buff *skb)
+{
+ local->rx_pending = skb;
+ del_timer(&local->link_timer);
+ schedule_work(&local->rx_work);
+}
+
void nfc_llcp_recv(void *data, struct sk_buff *skb, int err)
{
struct nfc_llcp_local *local = (struct nfc_llcp_local *) data;
@@ -1306,9 +1327,7 @@ void nfc_llcp_recv(void *data, struct sk_buff *skb, int err)
return;
}
- local->rx_pending = skb_get(skb);
- del_timer(&local->link_timer);
- schedule_work(&local->rx_work);
+ __nfc_llcp_recv(local, skb);
}
int nfc_llcp_data_received(struct nfc_dev *dev, struct sk_buff *skb)
@@ -1319,9 +1338,7 @@ int nfc_llcp_data_received(struct nfc_dev *dev, struct sk_buff *skb)
if (local == NULL)
return -ENODEV;
- local->rx_pending = skb_get(skb);
- del_timer(&local->link_timer);
- schedule_work(&local->rx_work);
+ __nfc_llcp_recv(local, skb);
return 0;
}
diff --git a/net/nfc/llcp/llcp.h b/net/nfc/llcp/llcp.h
index 0d62366f8cc..0eae5c50950 100644
--- a/net/nfc/llcp/llcp.h
+++ b/net/nfc/llcp/llcp.h
@@ -121,7 +121,6 @@ struct nfc_llcp_sock {
struct sk_buff_head tx_queue;
struct sk_buff_head tx_pending_queue;
- struct sk_buff_head tx_backlog_queue;
struct list_head accept_queue;
struct sock *parent;
diff --git a/net/nfc/llcp/sock.c b/net/nfc/llcp/sock.c
index fea22eb41b8..5332751943a 100644
--- a/net/nfc/llcp/sock.c
+++ b/net/nfc/llcp/sock.c
@@ -672,25 +672,27 @@ static int llcp_sock_recvmsg(struct kiocb *iocb, struct socket *sock,
copied = min_t(unsigned int, rlen, len);
cskb = skb;
- if (memcpy_toiovec(msg->msg_iov, cskb->data, copied)) {
+ if (skb_copy_datagram_iovec(cskb, 0, msg->msg_iov, copied)) {
if (!(flags & MSG_PEEK))
skb_queue_head(&sk->sk_receive_queue, skb);
return -EFAULT;
}
+ sock_recv_timestamp(msg, sk, skb);
+
if (sk->sk_type == SOCK_DGRAM && msg->msg_name) {
struct nfc_llcp_ui_cb *ui_cb = nfc_llcp_ui_skb_cb(skb);
- struct sockaddr_nfc_llcp sockaddr;
+ struct sockaddr_nfc_llcp *sockaddr =
+ (struct sockaddr_nfc_llcp *) msg->msg_name;
- pr_debug("Datagram socket %d %d\n", ui_cb->dsap, ui_cb->ssap);
+ msg->msg_namelen = sizeof(struct sockaddr_nfc_llcp);
- sockaddr.sa_family = AF_NFC;
- sockaddr.nfc_protocol = NFC_PROTO_NFC_DEP;
- sockaddr.dsap = ui_cb->dsap;
- sockaddr.ssap = ui_cb->ssap;
+ pr_debug("Datagram socket %d %d\n", ui_cb->dsap, ui_cb->ssap);
- memcpy(msg->msg_name, &sockaddr, sizeof(sockaddr));
- msg->msg_namelen = sizeof(sockaddr);
+ sockaddr->sa_family = AF_NFC;
+ sockaddr->nfc_protocol = NFC_PROTO_NFC_DEP;
+ sockaddr->dsap = ui_cb->dsap;
+ sockaddr->ssap = ui_cb->ssap;
}
/* Mark read part of skb as used */
@@ -806,7 +808,6 @@ struct sock *nfc_llcp_sock_alloc(struct socket *sock, int type, gfp_t gfp)
llcp_sock->reserved_ssap = LLCP_SAP_MAX;
skb_queue_head_init(&llcp_sock->tx_queue);
skb_queue_head_init(&llcp_sock->tx_pending_queue);
- skb_queue_head_init(&llcp_sock->tx_backlog_queue);
INIT_LIST_HEAD(&llcp_sock->accept_queue);
if (sock != NULL)
@@ -821,7 +822,6 @@ void nfc_llcp_sock_free(struct nfc_llcp_sock *sock)
skb_queue_purge(&sock->tx_queue);
skb_queue_purge(&sock->tx_pending_queue);
- skb_queue_purge(&sock->tx_backlog_queue);
list_del_init(&sock->accept_queue);
diff --git a/net/nfc/nci/core.c b/net/nfc/nci/core.c
index 5f98dc1bf03..48ada0ec749 100644
--- a/net/nfc/nci/core.c
+++ b/net/nfc/nci/core.c
@@ -658,6 +658,7 @@ static struct nfc_ops nci_nfc_ops = {
*/
struct nci_dev *nci_allocate_device(struct nci_ops *ops,
__u32 supported_protocols,
+ __u32 supported_se,
int tx_headroom, int tx_tailroom)
{
struct nci_dev *ndev;
@@ -680,6 +681,7 @@ struct nci_dev *nci_allocate_device(struct nci_ops *ops,
ndev->nfc_dev = nfc_allocate_device(&nci_nfc_ops,
supported_protocols,
+ supported_se,
tx_headroom + NCI_DATA_HDR_SIZE,
tx_tailroom);
if (!ndev->nfc_dev)
diff --git a/net/nfc/netlink.c b/net/nfc/netlink.c
index 3568ae16786..504b883439f 100644
--- a/net/nfc/netlink.c
+++ b/net/nfc/netlink.c
@@ -366,6 +366,7 @@ static int nfc_genl_send_device(struct sk_buff *msg, struct nfc_dev *dev,
if (nla_put_string(msg, NFC_ATTR_DEVICE_NAME, nfc_device_name(dev)) ||
nla_put_u32(msg, NFC_ATTR_DEVICE_INDEX, dev->idx) ||
nla_put_u32(msg, NFC_ATTR_PROTOCOLS, dev->supported_protocols) ||
+ nla_put_u32(msg, NFC_ATTR_SE, dev->supported_se) ||
nla_put_u8(msg, NFC_ATTR_DEVICE_POWERED, dev->dev_up) ||
nla_put_u8(msg, NFC_ATTR_RF_MODE, dev->rf_mode))
goto nla_put_failure;