diff options
Diffstat (limited to 'net/nfc/rawsock.c')
| -rw-r--r-- | net/nfc/rawsock.c | 139 |
1 files changed, 109 insertions, 30 deletions
diff --git a/net/nfc/rawsock.c b/net/nfc/rawsock.c index 2e2f8c6a61f..11c3544ea54 100644 --- a/net/nfc/rawsock.c +++ b/net/nfc/rawsock.c @@ -16,9 +16,7 @@ * 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. + * along with this program; if not, see <http://www.gnu.org/licenses/>. */ #define pr_fmt(fmt) KBUILD_MODNAME ": %s: " fmt, __func__ @@ -29,6 +27,24 @@ #include "nfc.h" +static struct nfc_sock_list raw_sk_list = { + .lock = __RW_LOCK_UNLOCKED(raw_sk_list.lock) +}; + +static void nfc_sock_link(struct nfc_sock_list *l, struct sock *sk) +{ + write_lock(&l->lock); + sk_add_node(sk, &l->head); + write_unlock(&l->lock); +} + +static void nfc_sock_unlink(struct nfc_sock_list *l, struct sock *sk) +{ + write_lock(&l->lock); + sk_del_node_init(sk); + write_unlock(&l->lock); +} + static void rawsock_write_queue_purge(struct sock *sk) { pr_debug("sk=%p\n", sk); @@ -54,7 +70,13 @@ static int rawsock_release(struct socket *sock) { struct sock *sk = sock->sk; - pr_debug("sock=%p\n", sock); + pr_debug("sock=%p sk=%p\n", sock, sk); + + if (!sk) + return 0; + + if (sock->type == SOCK_RAW) + nfc_sock_unlink(&raw_sk_list, sk); sock_orphan(sk); sock_put(sk); @@ -63,7 +85,7 @@ static int rawsock_release(struct socket *sock) } static int rawsock_connect(struct socket *sock, struct sockaddr *_addr, - int len, int flags) + int len, int flags) { struct sock *sk = sock->sk; struct sockaddr_nfc *addr = (struct sockaddr_nfc *)_addr; @@ -73,7 +95,7 @@ static int rawsock_connect(struct socket *sock, struct sockaddr *_addr, pr_debug("sock=%p sk=%p flags=%d\n", sock, sk, flags); if (!addr || len < sizeof(struct sockaddr_nfc) || - addr->sa_family != AF_NFC) + addr->sa_family != AF_NFC) return -EINVAL; pr_debug("addr dev_idx=%u target_idx=%u protocol=%u\n", @@ -92,14 +114,8 @@ static int rawsock_connect(struct socket *sock, struct sockaddr *_addr, goto error; } - if (addr->target_idx > dev->target_idx - 1 || - addr->target_idx < dev->target_idx - dev->n_targets) { - rc = -EINVAL; - goto error; - } - - if (addr->target_idx > dev->target_idx - 1 || - addr->target_idx < dev->target_idx - dev->n_targets) { + if (addr->target_idx > dev->target_next_idx - 1 || + addr->target_idx < dev->target_next_idx - dev->n_targets) { rc = -EINVAL; goto error; } @@ -132,7 +148,7 @@ static int rawsock_add_header(struct sk_buff *skb) } static void rawsock_data_exchange_complete(void *context, struct sk_buff *skb, - int err) + int err) { struct sock *sk = (struct sock *) context; @@ -145,11 +161,11 @@ static void rawsock_data_exchange_complete(void *context, struct sk_buff *skb, err = rawsock_add_header(skb); if (err) - goto error; + goto error_skb; err = sock_queue_rcv_skb(sk, skb); if (err) - goto error; + goto error_skb; spin_lock_bh(&sk->sk_write_queue.lock); if (!skb_queue_empty(&sk->sk_write_queue)) @@ -161,6 +177,9 @@ static void rawsock_data_exchange_complete(void *context, struct sk_buff *skb, sock_put(sk); return; +error_skb: + kfree_skb(skb); + error: rawsock_report_error(sk, err); sock_put(sk); @@ -185,7 +204,7 @@ static void rawsock_tx_work(struct work_struct *work) sock_hold(sk); rc = nfc_data_exchange(dev, target_idx, skb, - rawsock_data_exchange_complete, sk); + rawsock_data_exchange_complete, sk); if (rc) { rawsock_report_error(sk, rc); sock_put(sk); @@ -193,7 +212,7 @@ static void rawsock_tx_work(struct work_struct *work) } static int rawsock_sendmsg(struct kiocb *iocb, struct socket *sock, - struct msghdr *msg, size_t len) + struct msghdr *msg, size_t len) { struct sock *sk = sock->sk; struct nfc_dev *dev = nfc_rawsock(sk)->dev; @@ -230,7 +249,7 @@ static int rawsock_sendmsg(struct kiocb *iocb, struct socket *sock, } static int rawsock_recvmsg(struct kiocb *iocb, struct socket *sock, - struct msghdr *msg, size_t len, int flags) + struct msghdr *msg, size_t len, int flags) { int noblock = flags & MSG_DONTWAIT; struct sock *sk = sock->sk; @@ -244,8 +263,6 @@ static int rawsock_recvmsg(struct kiocb *iocb, struct socket *sock, if (!skb) return rc; - msg->msg_namelen = 0; - copied = skb->len; if (len < copied) { msg->msg_flags |= MSG_TRUNC; @@ -259,7 +276,6 @@ static int rawsock_recvmsg(struct kiocb *iocb, struct socket *sock, return rc ? : copied; } - static const struct proto_ops rawsock_ops = { .family = PF_NFC, .owner = THIS_MODULE, @@ -280,13 +296,33 @@ static const struct proto_ops rawsock_ops = { .mmap = sock_no_mmap, }; +static const struct proto_ops rawsock_raw_ops = { + .family = PF_NFC, + .owner = THIS_MODULE, + .release = rawsock_release, + .bind = sock_no_bind, + .connect = sock_no_connect, + .socketpair = sock_no_socketpair, + .accept = sock_no_accept, + .getname = sock_no_getname, + .poll = datagram_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 = rawsock_recvmsg, + .mmap = sock_no_mmap, +}; + static void rawsock_destruct(struct sock *sk) { pr_debug("sk=%p\n", sk); if (sk->sk_state == TCP_ESTABLISHED) { nfc_deactivate_target(nfc_rawsock(sk)->dev, - nfc_rawsock(sk)->target_idx); + nfc_rawsock(sk)->target_idx); nfc_put_device(nfc_rawsock(sk)->dev); } @@ -299,16 +335,19 @@ static void rawsock_destruct(struct sock *sk) } static int rawsock_create(struct net *net, struct socket *sock, - const struct nfc_protocol *nfc_proto) + const struct nfc_protocol *nfc_proto) { struct sock *sk; pr_debug("sock=%p\n", sock); - if (sock->type != SOCK_SEQPACKET) + if ((sock->type != SOCK_SEQPACKET) && (sock->type != SOCK_RAW)) return -ESOCKTNOSUPPORT; - sock->ops = &rawsock_ops; + if (sock->type == SOCK_RAW) + sock->ops = &rawsock_raw_ops; + else + sock->ops = &rawsock_ops; sk = sk_alloc(net, PF_NFC, GFP_ATOMIC, nfc_proto->proto); if (!sk) @@ -318,13 +357,53 @@ static int rawsock_create(struct net *net, struct socket *sock, sk->sk_protocol = nfc_proto->id; sk->sk_destruct = rawsock_destruct; sock->state = SS_UNCONNECTED; - - INIT_WORK(&nfc_rawsock(sk)->tx_work, rawsock_tx_work); - nfc_rawsock(sk)->tx_work_scheduled = false; + if (sock->type == SOCK_RAW) + nfc_sock_link(&raw_sk_list, sk); + else { + INIT_WORK(&nfc_rawsock(sk)->tx_work, rawsock_tx_work); + nfc_rawsock(sk)->tx_work_scheduled = false; + } return 0; } +void nfc_send_to_raw_sock(struct nfc_dev *dev, struct sk_buff *skb, + u8 payload_type, u8 direction) +{ + struct sk_buff *skb_copy = NULL, *nskb; + struct sock *sk; + u8 *data; + + read_lock(&raw_sk_list.lock); + + sk_for_each(sk, &raw_sk_list.head) { + if (!skb_copy) { + skb_copy = __pskb_copy_fclone(skb, NFC_RAW_HEADER_SIZE, + GFP_ATOMIC, true); + if (!skb_copy) + continue; + + data = skb_push(skb_copy, NFC_RAW_HEADER_SIZE); + + data[0] = dev ? dev->idx : 0xFF; + data[1] = direction & 0x01; + data[1] |= (payload_type << 1); + } + + nskb = skb_clone(skb_copy, GFP_ATOMIC); + if (!nskb) + continue; + + if (sock_queue_rcv_skb(sk, nskb)) + kfree_skb(nskb); + } + + read_unlock(&raw_sk_list.lock); + + kfree_skb(skb_copy); +} +EXPORT_SYMBOL(nfc_send_to_raw_sock); + static struct proto rawsock_proto = { .name = "NFC_RAW", .owner = THIS_MODULE, |
