11da177e4SLinus Torvalds /* 21da177e4SLinus Torvalds HIDP implementation for Linux Bluetooth stack (BlueZ). 31da177e4SLinus Torvalds Copyright (C) 2003-2004 Marcel Holtmann <marcel@holtmann.org> 41da177e4SLinus Torvalds 51da177e4SLinus Torvalds This program is free software; you can redistribute it and/or modify 61da177e4SLinus Torvalds it under the terms of the GNU General Public License version 2 as 71da177e4SLinus Torvalds published by the Free Software Foundation; 81da177e4SLinus Torvalds 91da177e4SLinus Torvalds THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS 101da177e4SLinus Torvalds OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 111da177e4SLinus Torvalds FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF THIRD PARTY RIGHTS. 121da177e4SLinus Torvalds IN NO EVENT SHALL THE COPYRIGHT HOLDER(S) AND AUTHOR(S) BE LIABLE FOR ANY 131da177e4SLinus Torvalds CLAIM, OR ANY SPECIAL INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES 141da177e4SLinus Torvalds WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 151da177e4SLinus Torvalds ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 161da177e4SLinus Torvalds OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 171da177e4SLinus Torvalds 181da177e4SLinus Torvalds ALL LIABILITY, INCLUDING LIABILITY FOR INFRINGEMENT OF ANY PATENTS, 191da177e4SLinus Torvalds COPYRIGHTS, TRADEMARKS OR OTHER RIGHTS, RELATING TO USE OF THIS 201da177e4SLinus Torvalds SOFTWARE IS DISCLAIMED. 211da177e4SLinus Torvalds */ 221da177e4SLinus Torvalds 231da177e4SLinus Torvalds #include <linux/module.h> 241da177e4SLinus Torvalds 251da177e4SLinus Torvalds #include <linux/types.h> 264fc268d2SRandy Dunlap #include <linux/capability.h> 271da177e4SLinus Torvalds #include <linux/errno.h> 281da177e4SLinus Torvalds #include <linux/kernel.h> 291da177e4SLinus Torvalds #include <linux/slab.h> 301da177e4SLinus Torvalds #include <linux/poll.h> 311da177e4SLinus Torvalds #include <linux/fcntl.h> 321da177e4SLinus Torvalds #include <linux/skbuff.h> 331da177e4SLinus Torvalds #include <linux/socket.h> 341da177e4SLinus Torvalds #include <linux/ioctl.h> 351da177e4SLinus Torvalds #include <linux/file.h> 361da177e4SLinus Torvalds #include <linux/init.h> 37e9c5702eSMarcel Holtmann #include <linux/compat.h> 381da177e4SLinus Torvalds #include <net/sock.h> 391da177e4SLinus Torvalds 401da177e4SLinus Torvalds #include "hidp.h" 411da177e4SLinus Torvalds 421da177e4SLinus Torvalds static int hidp_sock_release(struct socket *sock) 431da177e4SLinus Torvalds { 441da177e4SLinus Torvalds struct sock *sk = sock->sk; 451da177e4SLinus Torvalds 461da177e4SLinus Torvalds BT_DBG("sock %p sk %p", sock, sk); 471da177e4SLinus Torvalds 481da177e4SLinus Torvalds if (!sk) 491da177e4SLinus Torvalds return 0; 501da177e4SLinus Torvalds 511da177e4SLinus Torvalds sock_orphan(sk); 521da177e4SLinus Torvalds sock_put(sk); 531da177e4SLinus Torvalds 541da177e4SLinus Torvalds return 0; 551da177e4SLinus Torvalds } 561da177e4SLinus Torvalds 571da177e4SLinus Torvalds static int hidp_sock_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg) 581da177e4SLinus Torvalds { 591da177e4SLinus Torvalds void __user *argp = (void __user *) arg; 601da177e4SLinus Torvalds struct hidp_connadd_req ca; 611da177e4SLinus Torvalds struct hidp_conndel_req cd; 621da177e4SLinus Torvalds struct hidp_connlist_req cl; 631da177e4SLinus Torvalds struct hidp_conninfo ci; 641da177e4SLinus Torvalds struct socket *csock; 651da177e4SLinus Torvalds struct socket *isock; 661da177e4SLinus Torvalds int err; 671da177e4SLinus Torvalds 681da177e4SLinus Torvalds BT_DBG("cmd %x arg %lx", cmd, arg); 691da177e4SLinus Torvalds 701da177e4SLinus Torvalds switch (cmd) { 711da177e4SLinus Torvalds case HIDPCONNADD: 721da177e4SLinus Torvalds if (!capable(CAP_NET_ADMIN)) 731da177e4SLinus Torvalds return -EACCES; 741da177e4SLinus Torvalds 751da177e4SLinus Torvalds if (copy_from_user(&ca, argp, sizeof(ca))) 761da177e4SLinus Torvalds return -EFAULT; 771da177e4SLinus Torvalds 781da177e4SLinus Torvalds csock = sockfd_lookup(ca.ctrl_sock, &err); 791da177e4SLinus Torvalds if (!csock) 801da177e4SLinus Torvalds return err; 811da177e4SLinus Torvalds 821da177e4SLinus Torvalds isock = sockfd_lookup(ca.intr_sock, &err); 831da177e4SLinus Torvalds if (!isock) { 8467b23219SJulia Lawall sockfd_put(csock); 851da177e4SLinus Torvalds return err; 861da177e4SLinus Torvalds } 871da177e4SLinus Torvalds 881da177e4SLinus Torvalds if (csock->sk->sk_state != BT_CONNECTED || isock->sk->sk_state != BT_CONNECTED) { 8967b23219SJulia Lawall sockfd_put(csock); 9067b23219SJulia Lawall sockfd_put(isock); 911da177e4SLinus Torvalds return -EBADFD; 921da177e4SLinus Torvalds } 931da177e4SLinus Torvalds 941da177e4SLinus Torvalds err = hidp_add_connection(&ca, csock, isock); 951da177e4SLinus Torvalds if (!err) { 961da177e4SLinus Torvalds if (copy_to_user(argp, &ca, sizeof(ca))) 971da177e4SLinus Torvalds err = -EFAULT; 981da177e4SLinus Torvalds } else { 9967b23219SJulia Lawall sockfd_put(csock); 10067b23219SJulia Lawall sockfd_put(isock); 1011da177e4SLinus Torvalds } 1021da177e4SLinus Torvalds 1031da177e4SLinus Torvalds return err; 1041da177e4SLinus Torvalds 1051da177e4SLinus Torvalds case HIDPCONNDEL: 1061da177e4SLinus Torvalds if (!capable(CAP_NET_ADMIN)) 1071da177e4SLinus Torvalds return -EACCES; 1081da177e4SLinus Torvalds 1091da177e4SLinus Torvalds if (copy_from_user(&cd, argp, sizeof(cd))) 1101da177e4SLinus Torvalds return -EFAULT; 1111da177e4SLinus Torvalds 1121da177e4SLinus Torvalds return hidp_del_connection(&cd); 1131da177e4SLinus Torvalds 1141da177e4SLinus Torvalds case HIDPGETCONNLIST: 1151da177e4SLinus Torvalds if (copy_from_user(&cl, argp, sizeof(cl))) 1161da177e4SLinus Torvalds return -EFAULT; 1171da177e4SLinus Torvalds 1181da177e4SLinus Torvalds if (cl.cnum <= 0) 1191da177e4SLinus Torvalds return -EINVAL; 1201da177e4SLinus Torvalds 1211da177e4SLinus Torvalds err = hidp_get_connlist(&cl); 1221da177e4SLinus Torvalds if (!err && copy_to_user(argp, &cl, sizeof(cl))) 1231da177e4SLinus Torvalds return -EFAULT; 1241da177e4SLinus Torvalds 1251da177e4SLinus Torvalds return err; 1261da177e4SLinus Torvalds 1271da177e4SLinus Torvalds case HIDPGETCONNINFO: 1281da177e4SLinus Torvalds if (copy_from_user(&ci, argp, sizeof(ci))) 1291da177e4SLinus Torvalds return -EFAULT; 1301da177e4SLinus Torvalds 1311da177e4SLinus Torvalds err = hidp_get_conninfo(&ci); 1321da177e4SLinus Torvalds if (!err && copy_to_user(argp, &ci, sizeof(ci))) 1331da177e4SLinus Torvalds return -EFAULT; 1341da177e4SLinus Torvalds 1351da177e4SLinus Torvalds return err; 1361da177e4SLinus Torvalds } 1371da177e4SLinus Torvalds 1381da177e4SLinus Torvalds return -EINVAL; 1391da177e4SLinus Torvalds } 1401da177e4SLinus Torvalds 141e9c5702eSMarcel Holtmann #ifdef CONFIG_COMPAT 142e9c5702eSMarcel Holtmann struct compat_hidp_connadd_req { 143e9c5702eSMarcel Holtmann int ctrl_sock; // Connected control socket 144e9c5702eSMarcel Holtmann int intr_sock; // Connteted interrupt socket 145e9c5702eSMarcel Holtmann __u16 parser; 146e9c5702eSMarcel Holtmann __u16 rd_size; 147e9c5702eSMarcel Holtmann compat_uptr_t rd_data; 148e9c5702eSMarcel Holtmann __u8 country; 149e9c5702eSMarcel Holtmann __u8 subclass; 150e9c5702eSMarcel Holtmann __u16 vendor; 151e9c5702eSMarcel Holtmann __u16 product; 152e9c5702eSMarcel Holtmann __u16 version; 153e9c5702eSMarcel Holtmann __u32 flags; 154e9c5702eSMarcel Holtmann __u32 idle_to; 155e9c5702eSMarcel Holtmann char name[128]; 156e9c5702eSMarcel Holtmann }; 157e9c5702eSMarcel Holtmann 158e9c5702eSMarcel Holtmann static int hidp_sock_compat_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg) 159e9c5702eSMarcel Holtmann { 160e9c5702eSMarcel Holtmann if (cmd == HIDPGETCONNLIST) { 161e9c5702eSMarcel Holtmann struct hidp_connlist_req cl; 162e9c5702eSMarcel Holtmann uint32_t uci; 163e9c5702eSMarcel Holtmann int err; 164e9c5702eSMarcel Holtmann 165e9c5702eSMarcel Holtmann if (get_user(cl.cnum, (uint32_t __user *) arg) || 166e9c5702eSMarcel Holtmann get_user(uci, (u32 __user *) (arg + 4))) 167e9c5702eSMarcel Holtmann return -EFAULT; 168e9c5702eSMarcel Holtmann 169e9c5702eSMarcel Holtmann cl.ci = compat_ptr(uci); 170e9c5702eSMarcel Holtmann 171e9c5702eSMarcel Holtmann if (cl.cnum <= 0) 172e9c5702eSMarcel Holtmann return -EINVAL; 173e9c5702eSMarcel Holtmann 174e9c5702eSMarcel Holtmann err = hidp_get_connlist(&cl); 175e9c5702eSMarcel Holtmann 176e9c5702eSMarcel Holtmann if (!err && put_user(cl.cnum, (uint32_t __user *) arg)) 177e9c5702eSMarcel Holtmann err = -EFAULT; 178e9c5702eSMarcel Holtmann 179e9c5702eSMarcel Holtmann return err; 180e9c5702eSMarcel Holtmann } else if (cmd == HIDPCONNADD) { 181e9c5702eSMarcel Holtmann struct compat_hidp_connadd_req ca; 182e9c5702eSMarcel Holtmann struct hidp_connadd_req __user *uca; 183e9c5702eSMarcel Holtmann 184e9c5702eSMarcel Holtmann uca = compat_alloc_user_space(sizeof(*uca)); 185e9c5702eSMarcel Holtmann 18655e74744SAl Viro if (copy_from_user(&ca, (void __user *) arg, sizeof(ca))) 187e9c5702eSMarcel Holtmann return -EFAULT; 188e9c5702eSMarcel Holtmann 189e9c5702eSMarcel Holtmann if (put_user(ca.ctrl_sock, &uca->ctrl_sock) || 190e9c5702eSMarcel Holtmann put_user(ca.intr_sock, &uca->intr_sock) || 191e9c5702eSMarcel Holtmann put_user(ca.parser, &uca->parser) || 192a83d6c0dSMarcel Holtmann put_user(ca.rd_size, &uca->rd_size) || 193e9c5702eSMarcel Holtmann put_user(compat_ptr(ca.rd_data), &uca->rd_data) || 194e9c5702eSMarcel Holtmann put_user(ca.country, &uca->country) || 195e9c5702eSMarcel Holtmann put_user(ca.subclass, &uca->subclass) || 196e9c5702eSMarcel Holtmann put_user(ca.vendor, &uca->vendor) || 197e9c5702eSMarcel Holtmann put_user(ca.product, &uca->product) || 198e9c5702eSMarcel Holtmann put_user(ca.version, &uca->version) || 199e9c5702eSMarcel Holtmann put_user(ca.flags, &uca->flags) || 200e9c5702eSMarcel Holtmann put_user(ca.idle_to, &uca->idle_to) || 201e9c5702eSMarcel Holtmann copy_to_user(&uca->name[0], &ca.name[0], 128)) 202e9c5702eSMarcel Holtmann return -EFAULT; 203e9c5702eSMarcel Holtmann 204e9c5702eSMarcel Holtmann arg = (unsigned long) uca; 205e9c5702eSMarcel Holtmann 206e9c5702eSMarcel Holtmann /* Fall through. We don't actually write back any _changes_ 207e9c5702eSMarcel Holtmann to the structure anyway, so there's no need to copy back 208e9c5702eSMarcel Holtmann into the original compat version */ 209e9c5702eSMarcel Holtmann } 210e9c5702eSMarcel Holtmann 211e9c5702eSMarcel Holtmann return hidp_sock_ioctl(sock, cmd, arg); 212e9c5702eSMarcel Holtmann } 213e9c5702eSMarcel Holtmann #endif 214e9c5702eSMarcel Holtmann 21590ddc4f0SEric Dumazet static const struct proto_ops hidp_sock_ops = { 2161da177e4SLinus Torvalds .family = PF_BLUETOOTH, 2171da177e4SLinus Torvalds .owner = THIS_MODULE, 2181da177e4SLinus Torvalds .release = hidp_sock_release, 2191da177e4SLinus Torvalds .ioctl = hidp_sock_ioctl, 220e9c5702eSMarcel Holtmann #ifdef CONFIG_COMPAT 221e9c5702eSMarcel Holtmann .compat_ioctl = hidp_sock_compat_ioctl, 222e9c5702eSMarcel Holtmann #endif 2231da177e4SLinus Torvalds .bind = sock_no_bind, 2241da177e4SLinus Torvalds .getname = sock_no_getname, 2251da177e4SLinus Torvalds .sendmsg = sock_no_sendmsg, 2261da177e4SLinus Torvalds .recvmsg = sock_no_recvmsg, 2271da177e4SLinus Torvalds .poll = sock_no_poll, 2281da177e4SLinus Torvalds .listen = sock_no_listen, 2291da177e4SLinus Torvalds .shutdown = sock_no_shutdown, 2301da177e4SLinus Torvalds .setsockopt = sock_no_setsockopt, 2311da177e4SLinus Torvalds .getsockopt = sock_no_getsockopt, 2321da177e4SLinus Torvalds .connect = sock_no_connect, 2331da177e4SLinus Torvalds .socketpair = sock_no_socketpair, 2341da177e4SLinus Torvalds .accept = sock_no_accept, 2351da177e4SLinus Torvalds .mmap = sock_no_mmap 2361da177e4SLinus Torvalds }; 2371da177e4SLinus Torvalds 2381da177e4SLinus Torvalds static struct proto hidp_proto = { 2391da177e4SLinus Torvalds .name = "HIDP", 2401da177e4SLinus Torvalds .owner = THIS_MODULE, 2411da177e4SLinus Torvalds .obj_size = sizeof(struct bt_sock) 2421da177e4SLinus Torvalds }; 2431da177e4SLinus Torvalds 2441b8d7ae4SEric W. Biederman static int hidp_sock_create(struct net *net, struct socket *sock, int protocol) 2451da177e4SLinus Torvalds { 2461da177e4SLinus Torvalds struct sock *sk; 2471da177e4SLinus Torvalds 2481da177e4SLinus Torvalds BT_DBG("sock %p", sock); 2491da177e4SLinus Torvalds 2501da177e4SLinus Torvalds if (sock->type != SOCK_RAW) 2511da177e4SLinus Torvalds return -ESOCKTNOSUPPORT; 2521da177e4SLinus Torvalds 2536257ff21SPavel Emelyanov sk = sk_alloc(net, PF_BLUETOOTH, GFP_ATOMIC, &hidp_proto); 2541da177e4SLinus Torvalds if (!sk) 2551da177e4SLinus Torvalds return -ENOMEM; 2561da177e4SLinus Torvalds 2571da177e4SLinus Torvalds sock_init_data(sock, sk); 2581da177e4SLinus Torvalds 2591da177e4SLinus Torvalds sock->ops = &hidp_sock_ops; 2601da177e4SLinus Torvalds 2611da177e4SLinus Torvalds sock->state = SS_UNCONNECTED; 2621da177e4SLinus Torvalds 2631da177e4SLinus Torvalds sock_reset_flag(sk, SOCK_ZAPPED); 2641da177e4SLinus Torvalds 2651da177e4SLinus Torvalds sk->sk_protocol = protocol; 2661da177e4SLinus Torvalds sk->sk_state = BT_OPEN; 2671da177e4SLinus Torvalds 2681da177e4SLinus Torvalds return 0; 2691da177e4SLinus Torvalds } 2701da177e4SLinus Torvalds 271*ec1b4cf7SStephen Hemminger static const struct net_proto_family hidp_sock_family_ops = { 2721da177e4SLinus Torvalds .family = PF_BLUETOOTH, 2731da177e4SLinus Torvalds .owner = THIS_MODULE, 2741da177e4SLinus Torvalds .create = hidp_sock_create 2751da177e4SLinus Torvalds }; 2761da177e4SLinus Torvalds 2771da177e4SLinus Torvalds int __init hidp_init_sockets(void) 2781da177e4SLinus Torvalds { 2791da177e4SLinus Torvalds int err; 2801da177e4SLinus Torvalds 2811da177e4SLinus Torvalds err = proto_register(&hidp_proto, 0); 2821da177e4SLinus Torvalds if (err < 0) 2831da177e4SLinus Torvalds return err; 2841da177e4SLinus Torvalds 2851da177e4SLinus Torvalds err = bt_sock_register(BTPROTO_HIDP, &hidp_sock_family_ops); 2861da177e4SLinus Torvalds if (err < 0) 2871da177e4SLinus Torvalds goto error; 2881da177e4SLinus Torvalds 2891da177e4SLinus Torvalds return 0; 2901da177e4SLinus Torvalds 2911da177e4SLinus Torvalds error: 2921da177e4SLinus Torvalds BT_ERR("Can't register HIDP socket"); 2931da177e4SLinus Torvalds proto_unregister(&hidp_proto); 2941da177e4SLinus Torvalds return err; 2951da177e4SLinus Torvalds } 2961da177e4SLinus Torvalds 2971da177e4SLinus Torvalds void __exit hidp_cleanup_sockets(void) 2981da177e4SLinus Torvalds { 2991da177e4SLinus Torvalds if (bt_sock_unregister(BTPROTO_HIDP) < 0) 3001da177e4SLinus Torvalds BT_ERR("Can't unregister HIDP socket"); 3011da177e4SLinus Torvalds 3021da177e4SLinus Torvalds proto_unregister(&hidp_proto); 3031da177e4SLinus Torvalds } 304