11ccea77eSThomas Gleixner // SPDX-License-Identifier: GPL-2.0-or-later
223b7869cSLauro Ramos Venancio /*
323b7869cSLauro Ramos Venancio * Copyright (C) 2011 Instituto Nokia de Tecnologia
423b7869cSLauro Ramos Venancio *
523b7869cSLauro Ramos Venancio * Authors:
623b7869cSLauro Ramos Venancio * Aloisio Almeida Jr <aloisio.almeida@openbossa.org>
723b7869cSLauro Ramos Venancio * Lauro Ramos Venancio <lauro.venancio@openbossa.org>
823b7869cSLauro Ramos Venancio */
923b7869cSLauro Ramos Venancio
1052858b51SSamuel Ortiz #define pr_fmt(fmt) KBUILD_MODNAME ": %s: " fmt, __func__
11ed1e0ad8SJoe Perches
1223b7869cSLauro Ramos Venancio #include <net/tcp_states.h>
1323b7869cSLauro Ramos Venancio #include <linux/nfc.h>
14bc3b2d7fSPaul Gortmaker #include <linux/export.h>
15*7e8cdc97SDmitry Vyukov #include <linux/kcov.h>
1623b7869cSLauro Ramos Venancio
1723b7869cSLauro Ramos Venancio #include "nfc.h"
1823b7869cSLauro Ramos Venancio
1957be1f3fSHiren Tandel static struct nfc_sock_list raw_sk_list = {
2057be1f3fSHiren Tandel .lock = __RW_LOCK_UNLOCKED(raw_sk_list.lock)
2157be1f3fSHiren Tandel };
2257be1f3fSHiren Tandel
nfc_sock_link(struct nfc_sock_list * l,struct sock * sk)23db3287daSFengguang Wu static void nfc_sock_link(struct nfc_sock_list *l, struct sock *sk)
2457be1f3fSHiren Tandel {
2557be1f3fSHiren Tandel write_lock(&l->lock);
2657be1f3fSHiren Tandel sk_add_node(sk, &l->head);
2757be1f3fSHiren Tandel write_unlock(&l->lock);
2857be1f3fSHiren Tandel }
2957be1f3fSHiren Tandel
nfc_sock_unlink(struct nfc_sock_list * l,struct sock * sk)30db3287daSFengguang Wu static void nfc_sock_unlink(struct nfc_sock_list *l, struct sock *sk)
3157be1f3fSHiren Tandel {
3257be1f3fSHiren Tandel write_lock(&l->lock);
3357be1f3fSHiren Tandel sk_del_node_init(sk);
3457be1f3fSHiren Tandel write_unlock(&l->lock);
3557be1f3fSHiren Tandel }
3657be1f3fSHiren Tandel
rawsock_write_queue_purge(struct sock * sk)3723b7869cSLauro Ramos Venancio static void rawsock_write_queue_purge(struct sock *sk)
3823b7869cSLauro Ramos Venancio {
3920c239c1SJoe Perches pr_debug("sk=%p\n", sk);
4023b7869cSLauro Ramos Venancio
4123b7869cSLauro Ramos Venancio spin_lock_bh(&sk->sk_write_queue.lock);
4223b7869cSLauro Ramos Venancio __skb_queue_purge(&sk->sk_write_queue);
4323b7869cSLauro Ramos Venancio nfc_rawsock(sk)->tx_work_scheduled = false;
4423b7869cSLauro Ramos Venancio spin_unlock_bh(&sk->sk_write_queue.lock);
4523b7869cSLauro Ramos Venancio }
4623b7869cSLauro Ramos Venancio
rawsock_report_error(struct sock * sk,int err)4723b7869cSLauro Ramos Venancio static void rawsock_report_error(struct sock *sk, int err)
4823b7869cSLauro Ramos Venancio {
4920c239c1SJoe Perches pr_debug("sk=%p err=%d\n", sk, err);
5023b7869cSLauro Ramos Venancio
5123b7869cSLauro Ramos Venancio sk->sk_shutdown = SHUTDOWN_MASK;
5223b7869cSLauro Ramos Venancio sk->sk_err = -err;
53e3ae2365SAlexander Aring sk_error_report(sk);
5423b7869cSLauro Ramos Venancio
5523b7869cSLauro Ramos Venancio rawsock_write_queue_purge(sk);
5623b7869cSLauro Ramos Venancio }
5723b7869cSLauro Ramos Venancio
rawsock_release(struct socket * sock)5823b7869cSLauro Ramos Venancio static int rawsock_release(struct socket *sock)
5923b7869cSLauro Ramos Venancio {
6023b7869cSLauro Ramos Venancio struct sock *sk = sock->sk;
6123b7869cSLauro Ramos Venancio
6203e934f6SEric Dumazet pr_debug("sock=%p sk=%p\n", sock, sk);
6303e934f6SEric Dumazet
6403e934f6SEric Dumazet if (!sk)
6503e934f6SEric Dumazet return 0;
6623b7869cSLauro Ramos Venancio
6757be1f3fSHiren Tandel if (sock->type == SOCK_RAW)
6857be1f3fSHiren Tandel nfc_sock_unlink(&raw_sk_list, sk);
6957be1f3fSHiren Tandel
7023b7869cSLauro Ramos Venancio sock_orphan(sk);
7123b7869cSLauro Ramos Venancio sock_put(sk);
7223b7869cSLauro Ramos Venancio
7323b7869cSLauro Ramos Venancio return 0;
7423b7869cSLauro Ramos Venancio }
7523b7869cSLauro Ramos Venancio
rawsock_connect(struct socket * sock,struct sockaddr * _addr,int len,int flags)7623b7869cSLauro Ramos Venancio static int rawsock_connect(struct socket *sock, struct sockaddr *_addr,
7723b7869cSLauro Ramos Venancio int len, int flags)
7823b7869cSLauro Ramos Venancio {
7923b7869cSLauro Ramos Venancio struct sock *sk = sock->sk;
8023b7869cSLauro Ramos Venancio struct sockaddr_nfc *addr = (struct sockaddr_nfc *)_addr;
8123b7869cSLauro Ramos Venancio struct nfc_dev *dev;
8223b7869cSLauro Ramos Venancio int rc = 0;
8323b7869cSLauro Ramos Venancio
8420c239c1SJoe Perches pr_debug("sock=%p sk=%p flags=%d\n", sock, sk, flags);
8523b7869cSLauro Ramos Venancio
8623b7869cSLauro Ramos Venancio if (!addr || len < sizeof(struct sockaddr_nfc) ||
8723b7869cSLauro Ramos Venancio addr->sa_family != AF_NFC)
8823b7869cSLauro Ramos Venancio return -EINVAL;
8923b7869cSLauro Ramos Venancio
9020c239c1SJoe Perches pr_debug("addr dev_idx=%u target_idx=%u protocol=%u\n",
9120c239c1SJoe Perches addr->dev_idx, addr->target_idx, addr->nfc_protocol);
9223b7869cSLauro Ramos Venancio
9323b7869cSLauro Ramos Venancio lock_sock(sk);
9423b7869cSLauro Ramos Venancio
9523b7869cSLauro Ramos Venancio if (sock->state == SS_CONNECTED) {
9623b7869cSLauro Ramos Venancio rc = -EISCONN;
9723b7869cSLauro Ramos Venancio goto error;
9823b7869cSLauro Ramos Venancio }
9923b7869cSLauro Ramos Venancio
10023b7869cSLauro Ramos Venancio dev = nfc_get_device(addr->dev_idx);
10123b7869cSLauro Ramos Venancio if (!dev) {
10223b7869cSLauro Ramos Venancio rc = -ENODEV;
10323b7869cSLauro Ramos Venancio goto error;
10423b7869cSLauro Ramos Venancio }
10523b7869cSLauro Ramos Venancio
10601ae0eeaSEric Lapuyade if (addr->target_idx > dev->target_next_idx - 1 ||
10701ae0eeaSEric Lapuyade addr->target_idx < dev->target_next_idx - dev->n_targets) {
108c4fbb651SSamuel Ortiz rc = -EINVAL;
1093a30537cSPan Bian goto put_dev;
110c4fbb651SSamuel Ortiz }
111c4fbb651SSamuel Ortiz
11223b7869cSLauro Ramos Venancio rc = nfc_activate_target(dev, addr->target_idx, addr->nfc_protocol);
11323b7869cSLauro Ramos Venancio if (rc)
11423b7869cSLauro Ramos Venancio goto put_dev;
11523b7869cSLauro Ramos Venancio
11623b7869cSLauro Ramos Venancio nfc_rawsock(sk)->dev = dev;
11723b7869cSLauro Ramos Venancio nfc_rawsock(sk)->target_idx = addr->target_idx;
11823b7869cSLauro Ramos Venancio sock->state = SS_CONNECTED;
11923b7869cSLauro Ramos Venancio sk->sk_state = TCP_ESTABLISHED;
12023b7869cSLauro Ramos Venancio sk->sk_state_change(sk);
12123b7869cSLauro Ramos Venancio
12223b7869cSLauro Ramos Venancio release_sock(sk);
12323b7869cSLauro Ramos Venancio return 0;
12423b7869cSLauro Ramos Venancio
12523b7869cSLauro Ramos Venancio put_dev:
12623b7869cSLauro Ramos Venancio nfc_put_device(dev);
12723b7869cSLauro Ramos Venancio error:
12823b7869cSLauro Ramos Venancio release_sock(sk);
12923b7869cSLauro Ramos Venancio return rc;
13023b7869cSLauro Ramos Venancio }
13123b7869cSLauro Ramos Venancio
rawsock_add_header(struct sk_buff * skb)13223b7869cSLauro Ramos Venancio static int rawsock_add_header(struct sk_buff *skb)
13323b7869cSLauro Ramos Venancio {
134d58ff351SJohannes Berg *(u8 *)skb_push(skb, NFC_HEADER_SIZE) = 0;
13523b7869cSLauro Ramos Venancio
13623b7869cSLauro Ramos Venancio return 0;
13723b7869cSLauro Ramos Venancio }
13823b7869cSLauro Ramos Venancio
rawsock_data_exchange_complete(void * context,struct sk_buff * skb,int err)13923b7869cSLauro Ramos Venancio static void rawsock_data_exchange_complete(void *context, struct sk_buff *skb,
14023b7869cSLauro Ramos Venancio int err)
14123b7869cSLauro Ramos Venancio {
14223b7869cSLauro Ramos Venancio struct sock *sk = (struct sock *) context;
14323b7869cSLauro Ramos Venancio
144afa79d08SChangbin Du BUG_ON(in_hardirq());
14523b7869cSLauro Ramos Venancio
14620c239c1SJoe Perches pr_debug("sk=%p err=%d\n", sk, err);
14723b7869cSLauro Ramos Venancio
14823b7869cSLauro Ramos Venancio if (err)
14923b7869cSLauro Ramos Venancio goto error;
15023b7869cSLauro Ramos Venancio
15123b7869cSLauro Ramos Venancio err = rawsock_add_header(skb);
15223b7869cSLauro Ramos Venancio if (err)
1534cf7e032SThierry Escande goto error_skb;
15423b7869cSLauro Ramos Venancio
15523b7869cSLauro Ramos Venancio err = sock_queue_rcv_skb(sk, skb);
15623b7869cSLauro Ramos Venancio if (err)
1574cf7e032SThierry Escande goto error_skb;
15823b7869cSLauro Ramos Venancio
15923b7869cSLauro Ramos Venancio spin_lock_bh(&sk->sk_write_queue.lock);
16023b7869cSLauro Ramos Venancio if (!skb_queue_empty(&sk->sk_write_queue))
16123b7869cSLauro Ramos Venancio schedule_work(&nfc_rawsock(sk)->tx_work);
16223b7869cSLauro Ramos Venancio else
16323b7869cSLauro Ramos Venancio nfc_rawsock(sk)->tx_work_scheduled = false;
16423b7869cSLauro Ramos Venancio spin_unlock_bh(&sk->sk_write_queue.lock);
16523b7869cSLauro Ramos Venancio
16623b7869cSLauro Ramos Venancio sock_put(sk);
16723b7869cSLauro Ramos Venancio return;
16823b7869cSLauro Ramos Venancio
1694cf7e032SThierry Escande error_skb:
1704cf7e032SThierry Escande kfree_skb(skb);
1714cf7e032SThierry Escande
17223b7869cSLauro Ramos Venancio error:
17323b7869cSLauro Ramos Venancio rawsock_report_error(sk, err);
17423b7869cSLauro Ramos Venancio sock_put(sk);
17523b7869cSLauro Ramos Venancio }
17623b7869cSLauro Ramos Venancio
rawsock_tx_work(struct work_struct * work)17723b7869cSLauro Ramos Venancio static void rawsock_tx_work(struct work_struct *work)
17823b7869cSLauro Ramos Venancio {
17923b7869cSLauro Ramos Venancio struct sock *sk = to_rawsock_sk(work);
18023b7869cSLauro Ramos Venancio struct nfc_dev *dev = nfc_rawsock(sk)->dev;
18123b7869cSLauro Ramos Venancio u32 target_idx = nfc_rawsock(sk)->target_idx;
18223b7869cSLauro Ramos Venancio struct sk_buff *skb;
18323b7869cSLauro Ramos Venancio int rc;
18423b7869cSLauro Ramos Venancio
18520c239c1SJoe Perches pr_debug("sk=%p target_idx=%u\n", sk, target_idx);
18623b7869cSLauro Ramos Venancio
18723b7869cSLauro Ramos Venancio if (sk->sk_shutdown & SEND_SHUTDOWN) {
18823b7869cSLauro Ramos Venancio rawsock_write_queue_purge(sk);
18923b7869cSLauro Ramos Venancio return;
19023b7869cSLauro Ramos Venancio }
19123b7869cSLauro Ramos Venancio
19223b7869cSLauro Ramos Venancio skb = skb_dequeue(&sk->sk_write_queue);
193*7e8cdc97SDmitry Vyukov kcov_remote_start_common(skb_get_kcov_handle(skb));
19423b7869cSLauro Ramos Venancio
19523b7869cSLauro Ramos Venancio sock_hold(sk);
19623b7869cSLauro Ramos Venancio rc = nfc_data_exchange(dev, target_idx, skb,
19723b7869cSLauro Ramos Venancio rawsock_data_exchange_complete, sk);
19823b7869cSLauro Ramos Venancio if (rc) {
19923b7869cSLauro Ramos Venancio rawsock_report_error(sk, rc);
20023b7869cSLauro Ramos Venancio sock_put(sk);
20123b7869cSLauro Ramos Venancio }
202*7e8cdc97SDmitry Vyukov kcov_remote_stop();
20323b7869cSLauro Ramos Venancio }
20423b7869cSLauro Ramos Venancio
rawsock_sendmsg(struct socket * sock,struct msghdr * msg,size_t len)2051b784140SYing Xue static int rawsock_sendmsg(struct socket *sock, struct msghdr *msg, size_t len)
20623b7869cSLauro Ramos Venancio {
20723b7869cSLauro Ramos Venancio struct sock *sk = sock->sk;
208e8753043SSamuel Ortiz struct nfc_dev *dev = nfc_rawsock(sk)->dev;
20923b7869cSLauro Ramos Venancio struct sk_buff *skb;
21023b7869cSLauro Ramos Venancio int rc;
21123b7869cSLauro Ramos Venancio
21220c239c1SJoe Perches pr_debug("sock=%p sk=%p len=%zu\n", sock, sk, len);
21323b7869cSLauro Ramos Venancio
21423b7869cSLauro Ramos Venancio if (msg->msg_namelen)
21523b7869cSLauro Ramos Venancio return -EOPNOTSUPP;
21623b7869cSLauro Ramos Venancio
21723b7869cSLauro Ramos Venancio if (sock->state != SS_CONNECTED)
21823b7869cSLauro Ramos Venancio return -ENOTCONN;
21923b7869cSLauro Ramos Venancio
2207c7cd3bfSSamuel Ortiz skb = nfc_alloc_send_skb(dev, sk, msg->msg_flags, len, &rc);
2217c7cd3bfSSamuel Ortiz if (skb == NULL)
22223b7869cSLauro Ramos Venancio return rc;
22323b7869cSLauro Ramos Venancio
2246ce8e9ceSAl Viro rc = memcpy_from_msg(skb_put(skb, len), msg, len);
22523b7869cSLauro Ramos Venancio if (rc < 0) {
22623b7869cSLauro Ramos Venancio kfree_skb(skb);
22723b7869cSLauro Ramos Venancio return rc;
22823b7869cSLauro Ramos Venancio }
22923b7869cSLauro Ramos Venancio
23023b7869cSLauro Ramos Venancio spin_lock_bh(&sk->sk_write_queue.lock);
23123b7869cSLauro Ramos Venancio __skb_queue_tail(&sk->sk_write_queue, skb);
23223b7869cSLauro Ramos Venancio if (!nfc_rawsock(sk)->tx_work_scheduled) {
23323b7869cSLauro Ramos Venancio schedule_work(&nfc_rawsock(sk)->tx_work);
23423b7869cSLauro Ramos Venancio nfc_rawsock(sk)->tx_work_scheduled = true;
23523b7869cSLauro Ramos Venancio }
23623b7869cSLauro Ramos Venancio spin_unlock_bh(&sk->sk_write_queue.lock);
23723b7869cSLauro Ramos Venancio
23823b7869cSLauro Ramos Venancio return len;
23923b7869cSLauro Ramos Venancio }
24023b7869cSLauro Ramos Venancio
rawsock_recvmsg(struct socket * sock,struct msghdr * msg,size_t len,int flags)2411b784140SYing Xue static int rawsock_recvmsg(struct socket *sock, struct msghdr *msg, size_t len,
2421b784140SYing Xue int flags)
24323b7869cSLauro Ramos Venancio {
24423b7869cSLauro Ramos Venancio struct sock *sk = sock->sk;
24523b7869cSLauro Ramos Venancio struct sk_buff *skb;
24623b7869cSLauro Ramos Venancio int copied;
24723b7869cSLauro Ramos Venancio int rc;
24823b7869cSLauro Ramos Venancio
24920c239c1SJoe Perches pr_debug("sock=%p sk=%p len=%zu flags=%d\n", sock, sk, len, flags);
25023b7869cSLauro Ramos Venancio
251f4b41f06SOliver Hartkopp skb = skb_recv_datagram(sk, flags, &rc);
25223b7869cSLauro Ramos Venancio if (!skb)
25323b7869cSLauro Ramos Venancio return rc;
25423b7869cSLauro Ramos Venancio
25523b7869cSLauro Ramos Venancio copied = skb->len;
25623b7869cSLauro Ramos Venancio if (len < copied) {
25723b7869cSLauro Ramos Venancio msg->msg_flags |= MSG_TRUNC;
25823b7869cSLauro Ramos Venancio copied = len;
25923b7869cSLauro Ramos Venancio }
26023b7869cSLauro Ramos Venancio
26151f3d02bSDavid S. Miller rc = skb_copy_datagram_msg(skb, 0, msg, copied);
26223b7869cSLauro Ramos Venancio
26323b7869cSLauro Ramos Venancio skb_free_datagram(sk, skb);
26423b7869cSLauro Ramos Venancio
26523b7869cSLauro Ramos Venancio return rc ? : copied;
26623b7869cSLauro Ramos Venancio }
26723b7869cSLauro Ramos Venancio
26823b7869cSLauro Ramos Venancio static const struct proto_ops rawsock_ops = {
26923b7869cSLauro Ramos Venancio .family = PF_NFC,
27023b7869cSLauro Ramos Venancio .owner = THIS_MODULE,
27123b7869cSLauro Ramos Venancio .release = rawsock_release,
27223b7869cSLauro Ramos Venancio .bind = sock_no_bind,
27323b7869cSLauro Ramos Venancio .connect = rawsock_connect,
27423b7869cSLauro Ramos Venancio .socketpair = sock_no_socketpair,
27523b7869cSLauro Ramos Venancio .accept = sock_no_accept,
27623b7869cSLauro Ramos Venancio .getname = sock_no_getname,
277a11e1d43SLinus Torvalds .poll = datagram_poll,
27823b7869cSLauro Ramos Venancio .ioctl = sock_no_ioctl,
27923b7869cSLauro Ramos Venancio .listen = sock_no_listen,
28023b7869cSLauro Ramos Venancio .shutdown = sock_no_shutdown,
28123b7869cSLauro Ramos Venancio .sendmsg = rawsock_sendmsg,
28223b7869cSLauro Ramos Venancio .recvmsg = rawsock_recvmsg,
28323b7869cSLauro Ramos Venancio .mmap = sock_no_mmap,
28423b7869cSLauro Ramos Venancio };
28523b7869cSLauro Ramos Venancio
28657be1f3fSHiren Tandel static const struct proto_ops rawsock_raw_ops = {
28757be1f3fSHiren Tandel .family = PF_NFC,
28857be1f3fSHiren Tandel .owner = THIS_MODULE,
28957be1f3fSHiren Tandel .release = rawsock_release,
29057be1f3fSHiren Tandel .bind = sock_no_bind,
29157be1f3fSHiren Tandel .connect = sock_no_connect,
29257be1f3fSHiren Tandel .socketpair = sock_no_socketpair,
29357be1f3fSHiren Tandel .accept = sock_no_accept,
29457be1f3fSHiren Tandel .getname = sock_no_getname,
295a11e1d43SLinus Torvalds .poll = datagram_poll,
29657be1f3fSHiren Tandel .ioctl = sock_no_ioctl,
29757be1f3fSHiren Tandel .listen = sock_no_listen,
29857be1f3fSHiren Tandel .shutdown = sock_no_shutdown,
29957be1f3fSHiren Tandel .sendmsg = sock_no_sendmsg,
30057be1f3fSHiren Tandel .recvmsg = rawsock_recvmsg,
30157be1f3fSHiren Tandel .mmap = sock_no_mmap,
30257be1f3fSHiren Tandel };
30357be1f3fSHiren Tandel
rawsock_destruct(struct sock * sk)30423b7869cSLauro Ramos Venancio static void rawsock_destruct(struct sock *sk)
30523b7869cSLauro Ramos Venancio {
30620c239c1SJoe Perches pr_debug("sk=%p\n", sk);
30723b7869cSLauro Ramos Venancio
30823b7869cSLauro Ramos Venancio if (sk->sk_state == TCP_ESTABLISHED) {
30923b7869cSLauro Ramos Venancio nfc_deactivate_target(nfc_rawsock(sk)->dev,
31096d4581fSChristophe Ricard nfc_rawsock(sk)->target_idx,
31196d4581fSChristophe Ricard NFC_TARGET_MODE_IDLE);
31223b7869cSLauro Ramos Venancio nfc_put_device(nfc_rawsock(sk)->dev);
31323b7869cSLauro Ramos Venancio }
31423b7869cSLauro Ramos Venancio
31523b7869cSLauro Ramos Venancio skb_queue_purge(&sk->sk_receive_queue);
31623b7869cSLauro Ramos Venancio
31723b7869cSLauro Ramos Venancio if (!sock_flag(sk, SOCK_DEAD)) {
318ed1e0ad8SJoe Perches pr_err("Freeing alive NFC raw socket %p\n", sk);
31923b7869cSLauro Ramos Venancio return;
32023b7869cSLauro Ramos Venancio }
32123b7869cSLauro Ramos Venancio }
32223b7869cSLauro Ramos Venancio
rawsock_create(struct net * net,struct socket * sock,const struct nfc_protocol * nfc_proto,int kern)32323b7869cSLauro Ramos Venancio static int rawsock_create(struct net *net, struct socket *sock,
32411aa9c28SEric W. Biederman const struct nfc_protocol *nfc_proto, int kern)
32523b7869cSLauro Ramos Venancio {
32623b7869cSLauro Ramos Venancio struct sock *sk;
32723b7869cSLauro Ramos Venancio
32820c239c1SJoe Perches pr_debug("sock=%p\n", sock);
32923b7869cSLauro Ramos Venancio
33057be1f3fSHiren Tandel if ((sock->type != SOCK_SEQPACKET) && (sock->type != SOCK_RAW))
33123b7869cSLauro Ramos Venancio return -ESOCKTNOSUPPORT;
33223b7869cSLauro Ramos Venancio
33326896f01SQingyu Li if (sock->type == SOCK_RAW) {
3348ab78863SJeimon if (!ns_capable(net->user_ns, CAP_NET_RAW))
33526896f01SQingyu Li return -EPERM;
33657be1f3fSHiren Tandel sock->ops = &rawsock_raw_ops;
33726896f01SQingyu Li } else {
33823b7869cSLauro Ramos Venancio sock->ops = &rawsock_ops;
33926896f01SQingyu Li }
34023b7869cSLauro Ramos Venancio
34111aa9c28SEric W. Biederman sk = sk_alloc(net, PF_NFC, GFP_ATOMIC, nfc_proto->proto, kern);
34223b7869cSLauro Ramos Venancio if (!sk)
34323b7869cSLauro Ramos Venancio return -ENOMEM;
34423b7869cSLauro Ramos Venancio
34523b7869cSLauro Ramos Venancio sock_init_data(sock, sk);
34623b7869cSLauro Ramos Venancio sk->sk_protocol = nfc_proto->id;
34723b7869cSLauro Ramos Venancio sk->sk_destruct = rawsock_destruct;
34823b7869cSLauro Ramos Venancio sock->state = SS_UNCONNECTED;
34957be1f3fSHiren Tandel if (sock->type == SOCK_RAW)
35057be1f3fSHiren Tandel nfc_sock_link(&raw_sk_list, sk);
35157be1f3fSHiren Tandel else {
35223b7869cSLauro Ramos Venancio INIT_WORK(&nfc_rawsock(sk)->tx_work, rawsock_tx_work);
35323b7869cSLauro Ramos Venancio nfc_rawsock(sk)->tx_work_scheduled = false;
35457be1f3fSHiren Tandel }
35523b7869cSLauro Ramos Venancio
35623b7869cSLauro Ramos Venancio return 0;
35723b7869cSLauro Ramos Venancio }
35823b7869cSLauro Ramos Venancio
nfc_send_to_raw_sock(struct nfc_dev * dev,struct sk_buff * skb,u8 payload_type,u8 direction)35957be1f3fSHiren Tandel void nfc_send_to_raw_sock(struct nfc_dev *dev, struct sk_buff *skb,
36057be1f3fSHiren Tandel u8 payload_type, u8 direction)
36157be1f3fSHiren Tandel {
36257be1f3fSHiren Tandel struct sk_buff *skb_copy = NULL, *nskb;
36357be1f3fSHiren Tandel struct sock *sk;
36457be1f3fSHiren Tandel u8 *data;
36557be1f3fSHiren Tandel
36657be1f3fSHiren Tandel read_lock(&raw_sk_list.lock);
36757be1f3fSHiren Tandel
36857be1f3fSHiren Tandel sk_for_each(sk, &raw_sk_list.head) {
36957be1f3fSHiren Tandel if (!skb_copy) {
370bad93e9dSOctavian Purdila skb_copy = __pskb_copy_fclone(skb, NFC_RAW_HEADER_SIZE,
371bad93e9dSOctavian Purdila GFP_ATOMIC, true);
37257be1f3fSHiren Tandel if (!skb_copy)
37357be1f3fSHiren Tandel continue;
37457be1f3fSHiren Tandel
37557be1f3fSHiren Tandel data = skb_push(skb_copy, NFC_RAW_HEADER_SIZE);
37657be1f3fSHiren Tandel
37757be1f3fSHiren Tandel data[0] = dev ? dev->idx : 0xFF;
37857be1f3fSHiren Tandel data[1] = direction & 0x01;
37957be1f3fSHiren Tandel data[1] |= (payload_type << 1);
38057be1f3fSHiren Tandel }
38157be1f3fSHiren Tandel
38257be1f3fSHiren Tandel nskb = skb_clone(skb_copy, GFP_ATOMIC);
38357be1f3fSHiren Tandel if (!nskb)
38457be1f3fSHiren Tandel continue;
38557be1f3fSHiren Tandel
38657be1f3fSHiren Tandel if (sock_queue_rcv_skb(sk, nskb))
38757be1f3fSHiren Tandel kfree_skb(nskb);
38857be1f3fSHiren Tandel }
38957be1f3fSHiren Tandel
39057be1f3fSHiren Tandel read_unlock(&raw_sk_list.lock);
39157be1f3fSHiren Tandel
39257be1f3fSHiren Tandel kfree_skb(skb_copy);
39357be1f3fSHiren Tandel }
39457be1f3fSHiren Tandel EXPORT_SYMBOL(nfc_send_to_raw_sock);
39557be1f3fSHiren Tandel
39623b7869cSLauro Ramos Venancio static struct proto rawsock_proto = {
39723b7869cSLauro Ramos Venancio .name = "NFC_RAW",
39823b7869cSLauro Ramos Venancio .owner = THIS_MODULE,
39923b7869cSLauro Ramos Venancio .obj_size = sizeof(struct nfc_rawsock),
40023b7869cSLauro Ramos Venancio };
40123b7869cSLauro Ramos Venancio
40223b7869cSLauro Ramos Venancio static const struct nfc_protocol rawsock_nfc_proto = {
40323b7869cSLauro Ramos Venancio .id = NFC_SOCKPROTO_RAW,
40423b7869cSLauro Ramos Venancio .proto = &rawsock_proto,
40523b7869cSLauro Ramos Venancio .owner = THIS_MODULE,
40623b7869cSLauro Ramos Venancio .create = rawsock_create
40723b7869cSLauro Ramos Venancio };
40823b7869cSLauro Ramos Venancio
rawsock_init(void)40923b7869cSLauro Ramos Venancio int __init rawsock_init(void)
41023b7869cSLauro Ramos Venancio {
41123b7869cSLauro Ramos Venancio int rc;
41223b7869cSLauro Ramos Venancio
41323b7869cSLauro Ramos Venancio rc = nfc_proto_register(&rawsock_nfc_proto);
41423b7869cSLauro Ramos Venancio
41523b7869cSLauro Ramos Venancio return rc;
41623b7869cSLauro Ramos Venancio }
41723b7869cSLauro Ramos Venancio
rawsock_exit(void)41823b7869cSLauro Ramos Venancio void rawsock_exit(void)
41923b7869cSLauro Ramos Venancio {
42023b7869cSLauro Ramos Venancio nfc_proto_unregister(&rawsock_nfc_proto);
42123b7869cSLauro Ramos Venancio }
422