NFC: llcp: Implement socket options
authorSamuel Ortiz <sameo@linux.intel.com>
Fri, 22 Feb 2013 09:53:25 +0000 (10:53 +0100)
committerSamuel Ortiz <sameo@linux.intel.com>
Sun, 10 Mar 2013 21:20:05 +0000 (22:20 +0100)
Some LLCP services (e.g. the validation ones) require some control over
the LLCP link parameters like the receive window (RW) or the MIU extension
(MIUX). This can only be done through socket options.

Signed-off-by: Samuel Ortiz <sameo@linux.intel.com>
include/linux/socket.h
include/uapi/linux/nfc.h
net/nfc/llcp/llcp.h
net/nfc/llcp/sock.c

index 2b9f74b0ffea5bf34ea8e38140938055d0a79ec4..428c37a1f95ce2eea8d9785ef69fc6050b5b7c96 100644 (file)
@@ -298,6 +298,7 @@ struct ucred {
 #define SOL_IUCV       277
 #define SOL_CAIF       278
 #define SOL_ALG                279
+#define SOL_NFC                280
 
 /* IPX options */
 #define IPX_TYPE       1
index 7969f46f1bb344f55d704ae06b5bcdc609b07b85..855630fe731d64164f1d140c703048ef1c23a0fd 100644 (file)
@@ -220,4 +220,8 @@ struct sockaddr_nfc_llcp {
 #define NFC_LLCP_DIRECTION_RX          0x00
 #define NFC_LLCP_DIRECTION_TX          0x01
 
+/* socket option names */
+#define NFC_LLCP_RW   0
+#define NFC_LLCP_MIUX 1
+
 #endif /*__LINUX_NFC_H */
index 32cec81939e65c7297e1c97022b7a44291901e11..5f117adac2e542740f79fd36eb619d189b5ecd34 100644 (file)
@@ -104,6 +104,9 @@ struct nfc_llcp_sock {
        u8 dsap;
        char *service_name;
        size_t service_name_len;
+       u8 rw;
+       u16 miux;
+
 
        /* Remote link parameters */
        u8 remote_rw;
index cc564992ba95bd374187befe60ed34ace126616b..9357a756f7a9c146c47b31cd9eb4db028fca5323 100644 (file)
@@ -223,6 +223,121 @@ error:
        return ret;
 }
 
+static int nfc_llcp_setsockopt(struct socket *sock, int level, int optname,
+                              char __user *optval, unsigned int optlen)
+{
+       struct sock *sk = sock->sk;
+       struct nfc_llcp_sock *llcp_sock = nfc_llcp_sock(sk);
+       u32 opt;
+       int err = 0;
+
+       pr_debug("%p optname %d\n", sk, optname);
+
+       if (level != SOL_NFC)
+               return -ENOPROTOOPT;
+
+       lock_sock(sk);
+
+       switch (optname) {
+       case NFC_LLCP_RW:
+               if (sk->sk_state == LLCP_CONNECTED ||
+                   sk->sk_state == LLCP_BOUND ||
+                   sk->sk_state == LLCP_LISTEN) {
+                       err = -EINVAL;
+                       break;
+               }
+
+               if (get_user(opt, (u32 __user *) optval)) {
+                       err = -EFAULT;
+                       break;
+               }
+
+               if (opt > LLCP_MAX_RW) {
+                       err = -EINVAL;
+                       break;
+               }
+
+               llcp_sock->rw = (u8) opt;
+
+               break;
+
+       case NFC_LLCP_MIUX:
+               if (sk->sk_state == LLCP_CONNECTED ||
+                   sk->sk_state == LLCP_BOUND ||
+                   sk->sk_state == LLCP_LISTEN) {
+                       err = -EINVAL;
+                       break;
+               }
+
+               if (get_user(opt, (u32 __user *) optval)) {
+                       err = -EFAULT;
+                       break;
+               }
+
+               if (opt > LLCP_MAX_MIUX) {
+                       err = -EINVAL;
+                       break;
+               }
+
+               llcp_sock->miux = (u16) opt;
+
+               break;
+
+       default:
+               err = -ENOPROTOOPT;
+               break;
+       }
+
+       release_sock(sk);
+
+       return err;
+}
+
+static int nfc_llcp_getsockopt(struct socket *sock, int level, int optname,
+                              char __user *optval, int __user *optlen)
+{
+       struct sock *sk = sock->sk;
+       struct nfc_llcp_sock *llcp_sock = nfc_llcp_sock(sk);
+       int len, err = 0;
+
+       pr_debug("%p optname %d\n", sk, optname);
+
+       if (level != SOL_NFC)
+               return -ENOPROTOOPT;
+
+       if (get_user(len, optlen))
+               return -EFAULT;
+
+       len = min_t(u32, len, sizeof(u32));
+
+       lock_sock(sk);
+
+       switch (optname) {
+       case NFC_LLCP_RW:
+               if (put_user(llcp_sock->rw, (u32 __user *) optval))
+                       err = -EFAULT;
+
+               break;
+
+       case NFC_LLCP_MIUX:
+               if (put_user(llcp_sock->miux, (u32 __user *) optval))
+                       err = -EFAULT;
+
+               break;
+
+       default:
+               err = -ENOPROTOOPT;
+               break;
+       }
+
+       release_sock(sk);
+
+       if (put_user(len, optlen))
+               return -EFAULT;
+
+       return err;
+}
+
 void nfc_llcp_accept_unlink(struct sock *sk)
 {
        struct nfc_llcp_sock *llcp_sock = nfc_llcp_sock(sk);
@@ -735,8 +850,8 @@ static const struct proto_ops llcp_sock_ops = {
        .ioctl          = sock_no_ioctl,
        .listen         = llcp_sock_listen,
        .shutdown       = sock_no_shutdown,
-       .setsockopt     = sock_no_setsockopt,
-       .getsockopt     = sock_no_getsockopt,
+       .setsockopt     = nfc_llcp_setsockopt,
+       .getsockopt     = nfc_llcp_getsockopt,
        .sendmsg        = llcp_sock_sendmsg,
        .recvmsg        = llcp_sock_recvmsg,
        .mmap           = sock_no_mmap,