18c2ecf20Sopenharmony_ci/*
28c2ecf20Sopenharmony_ci   BNEP implementation for Linux Bluetooth stack (BlueZ).
38c2ecf20Sopenharmony_ci   Copyright (C) 2001-2002 Inventel Systemes
48c2ecf20Sopenharmony_ci   Written 2001-2002 by
58c2ecf20Sopenharmony_ci	David Libault  <david.libault@inventel.fr>
68c2ecf20Sopenharmony_ci
78c2ecf20Sopenharmony_ci   Copyright (C) 2002 Maxim Krasnyansky <maxk@qualcomm.com>
88c2ecf20Sopenharmony_ci
98c2ecf20Sopenharmony_ci   This program is free software; you can redistribute it and/or modify
108c2ecf20Sopenharmony_ci   it under the terms of the GNU General Public License version 2 as
118c2ecf20Sopenharmony_ci   published by the Free Software Foundation;
128c2ecf20Sopenharmony_ci
138c2ecf20Sopenharmony_ci   THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
148c2ecf20Sopenharmony_ci   OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
158c2ecf20Sopenharmony_ci   FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF THIRD PARTY RIGHTS.
168c2ecf20Sopenharmony_ci   IN NO EVENT SHALL THE COPYRIGHT HOLDER(S) AND AUTHOR(S) BE LIABLE FOR ANY
178c2ecf20Sopenharmony_ci   CLAIM, OR ANY SPECIAL INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES
188c2ecf20Sopenharmony_ci   WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
198c2ecf20Sopenharmony_ci   ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
208c2ecf20Sopenharmony_ci   OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
218c2ecf20Sopenharmony_ci
228c2ecf20Sopenharmony_ci   ALL LIABILITY, INCLUDING LIABILITY FOR INFRINGEMENT OF ANY PATENTS,
238c2ecf20Sopenharmony_ci   COPYRIGHTS, TRADEMARKS OR OTHER RIGHTS, RELATING TO USE OF THIS
248c2ecf20Sopenharmony_ci   SOFTWARE IS DISCLAIMED.
258c2ecf20Sopenharmony_ci*/
268c2ecf20Sopenharmony_ci
278c2ecf20Sopenharmony_ci#include <linux/export.h>
288c2ecf20Sopenharmony_ci#include <linux/file.h>
298c2ecf20Sopenharmony_ci
308c2ecf20Sopenharmony_ci#include "bnep.h"
318c2ecf20Sopenharmony_ci
328c2ecf20Sopenharmony_cistatic struct bt_sock_list bnep_sk_list = {
338c2ecf20Sopenharmony_ci	.lock = __RW_LOCK_UNLOCKED(bnep_sk_list.lock)
348c2ecf20Sopenharmony_ci};
358c2ecf20Sopenharmony_ci
368c2ecf20Sopenharmony_cistatic int bnep_sock_release(struct socket *sock)
378c2ecf20Sopenharmony_ci{
388c2ecf20Sopenharmony_ci	struct sock *sk = sock->sk;
398c2ecf20Sopenharmony_ci
408c2ecf20Sopenharmony_ci	BT_DBG("sock %p sk %p", sock, sk);
418c2ecf20Sopenharmony_ci
428c2ecf20Sopenharmony_ci	if (!sk)
438c2ecf20Sopenharmony_ci		return 0;
448c2ecf20Sopenharmony_ci
458c2ecf20Sopenharmony_ci	bt_sock_unlink(&bnep_sk_list, sk);
468c2ecf20Sopenharmony_ci
478c2ecf20Sopenharmony_ci	sock_orphan(sk);
488c2ecf20Sopenharmony_ci	sock_put(sk);
498c2ecf20Sopenharmony_ci	return 0;
508c2ecf20Sopenharmony_ci}
518c2ecf20Sopenharmony_ci
528c2ecf20Sopenharmony_cistatic int do_bnep_sock_ioctl(struct socket *sock, unsigned int cmd, void __user *argp)
538c2ecf20Sopenharmony_ci{
548c2ecf20Sopenharmony_ci	struct bnep_connlist_req cl;
558c2ecf20Sopenharmony_ci	struct bnep_connadd_req  ca;
568c2ecf20Sopenharmony_ci	struct bnep_conndel_req  cd;
578c2ecf20Sopenharmony_ci	struct bnep_conninfo ci;
588c2ecf20Sopenharmony_ci	struct socket *nsock;
598c2ecf20Sopenharmony_ci	__u32 supp_feat = BIT(BNEP_SETUP_RESPONSE);
608c2ecf20Sopenharmony_ci	int err;
618c2ecf20Sopenharmony_ci
628c2ecf20Sopenharmony_ci	BT_DBG("cmd %x arg %p", cmd, argp);
638c2ecf20Sopenharmony_ci
648c2ecf20Sopenharmony_ci	switch (cmd) {
658c2ecf20Sopenharmony_ci	case BNEPCONNADD:
668c2ecf20Sopenharmony_ci		if (!capable(CAP_NET_ADMIN))
678c2ecf20Sopenharmony_ci			return -EPERM;
688c2ecf20Sopenharmony_ci
698c2ecf20Sopenharmony_ci		if (copy_from_user(&ca, argp, sizeof(ca)))
708c2ecf20Sopenharmony_ci			return -EFAULT;
718c2ecf20Sopenharmony_ci
728c2ecf20Sopenharmony_ci		nsock = sockfd_lookup(ca.sock, &err);
738c2ecf20Sopenharmony_ci		if (!nsock)
748c2ecf20Sopenharmony_ci			return err;
758c2ecf20Sopenharmony_ci
768c2ecf20Sopenharmony_ci		if (nsock->sk->sk_state != BT_CONNECTED) {
778c2ecf20Sopenharmony_ci			sockfd_put(nsock);
788c2ecf20Sopenharmony_ci			return -EBADFD;
798c2ecf20Sopenharmony_ci		}
808c2ecf20Sopenharmony_ci		ca.device[sizeof(ca.device)-1] = 0;
818c2ecf20Sopenharmony_ci
828c2ecf20Sopenharmony_ci		err = bnep_add_connection(&ca, nsock);
838c2ecf20Sopenharmony_ci		if (!err) {
848c2ecf20Sopenharmony_ci			if (copy_to_user(argp, &ca, sizeof(ca)))
858c2ecf20Sopenharmony_ci				err = -EFAULT;
868c2ecf20Sopenharmony_ci		} else
878c2ecf20Sopenharmony_ci			sockfd_put(nsock);
888c2ecf20Sopenharmony_ci
898c2ecf20Sopenharmony_ci		return err;
908c2ecf20Sopenharmony_ci
918c2ecf20Sopenharmony_ci	case BNEPCONNDEL:
928c2ecf20Sopenharmony_ci		if (!capable(CAP_NET_ADMIN))
938c2ecf20Sopenharmony_ci			return -EPERM;
948c2ecf20Sopenharmony_ci
958c2ecf20Sopenharmony_ci		if (copy_from_user(&cd, argp, sizeof(cd)))
968c2ecf20Sopenharmony_ci			return -EFAULT;
978c2ecf20Sopenharmony_ci
988c2ecf20Sopenharmony_ci		return bnep_del_connection(&cd);
998c2ecf20Sopenharmony_ci
1008c2ecf20Sopenharmony_ci	case BNEPGETCONNLIST:
1018c2ecf20Sopenharmony_ci		if (copy_from_user(&cl, argp, sizeof(cl)))
1028c2ecf20Sopenharmony_ci			return -EFAULT;
1038c2ecf20Sopenharmony_ci
1048c2ecf20Sopenharmony_ci		if (cl.cnum <= 0)
1058c2ecf20Sopenharmony_ci			return -EINVAL;
1068c2ecf20Sopenharmony_ci
1078c2ecf20Sopenharmony_ci		err = bnep_get_connlist(&cl);
1088c2ecf20Sopenharmony_ci		if (!err && copy_to_user(argp, &cl, sizeof(cl)))
1098c2ecf20Sopenharmony_ci			return -EFAULT;
1108c2ecf20Sopenharmony_ci
1118c2ecf20Sopenharmony_ci		return err;
1128c2ecf20Sopenharmony_ci
1138c2ecf20Sopenharmony_ci	case BNEPGETCONNINFO:
1148c2ecf20Sopenharmony_ci		if (copy_from_user(&ci, argp, sizeof(ci)))
1158c2ecf20Sopenharmony_ci			return -EFAULT;
1168c2ecf20Sopenharmony_ci
1178c2ecf20Sopenharmony_ci		err = bnep_get_conninfo(&ci);
1188c2ecf20Sopenharmony_ci		if (!err && copy_to_user(argp, &ci, sizeof(ci)))
1198c2ecf20Sopenharmony_ci			return -EFAULT;
1208c2ecf20Sopenharmony_ci
1218c2ecf20Sopenharmony_ci		return err;
1228c2ecf20Sopenharmony_ci
1238c2ecf20Sopenharmony_ci	case BNEPGETSUPPFEAT:
1248c2ecf20Sopenharmony_ci		if (copy_to_user(argp, &supp_feat, sizeof(supp_feat)))
1258c2ecf20Sopenharmony_ci			return -EFAULT;
1268c2ecf20Sopenharmony_ci
1278c2ecf20Sopenharmony_ci		return 0;
1288c2ecf20Sopenharmony_ci
1298c2ecf20Sopenharmony_ci	default:
1308c2ecf20Sopenharmony_ci		return -EINVAL;
1318c2ecf20Sopenharmony_ci	}
1328c2ecf20Sopenharmony_ci
1338c2ecf20Sopenharmony_ci	return 0;
1348c2ecf20Sopenharmony_ci}
1358c2ecf20Sopenharmony_ci
1368c2ecf20Sopenharmony_cistatic int bnep_sock_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg)
1378c2ecf20Sopenharmony_ci{
1388c2ecf20Sopenharmony_ci	return do_bnep_sock_ioctl(sock, cmd, (void __user *)arg);
1398c2ecf20Sopenharmony_ci}
1408c2ecf20Sopenharmony_ci
1418c2ecf20Sopenharmony_ci#ifdef CONFIG_COMPAT
1428c2ecf20Sopenharmony_cistatic int bnep_sock_compat_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg)
1438c2ecf20Sopenharmony_ci{
1448c2ecf20Sopenharmony_ci	void __user *argp = compat_ptr(arg);
1458c2ecf20Sopenharmony_ci	if (cmd == BNEPGETCONNLIST) {
1468c2ecf20Sopenharmony_ci		struct bnep_connlist_req cl;
1478c2ecf20Sopenharmony_ci		unsigned __user *p = argp;
1488c2ecf20Sopenharmony_ci		u32 uci;
1498c2ecf20Sopenharmony_ci		int err;
1508c2ecf20Sopenharmony_ci
1518c2ecf20Sopenharmony_ci		if (get_user(cl.cnum, p) || get_user(uci, p + 1))
1528c2ecf20Sopenharmony_ci			return -EFAULT;
1538c2ecf20Sopenharmony_ci
1548c2ecf20Sopenharmony_ci		cl.ci = compat_ptr(uci);
1558c2ecf20Sopenharmony_ci
1568c2ecf20Sopenharmony_ci		if (cl.cnum <= 0)
1578c2ecf20Sopenharmony_ci			return -EINVAL;
1588c2ecf20Sopenharmony_ci
1598c2ecf20Sopenharmony_ci		err = bnep_get_connlist(&cl);
1608c2ecf20Sopenharmony_ci
1618c2ecf20Sopenharmony_ci		if (!err && put_user(cl.cnum, p))
1628c2ecf20Sopenharmony_ci			err = -EFAULT;
1638c2ecf20Sopenharmony_ci
1648c2ecf20Sopenharmony_ci		return err;
1658c2ecf20Sopenharmony_ci	}
1668c2ecf20Sopenharmony_ci
1678c2ecf20Sopenharmony_ci	return do_bnep_sock_ioctl(sock, cmd, argp);
1688c2ecf20Sopenharmony_ci}
1698c2ecf20Sopenharmony_ci#endif
1708c2ecf20Sopenharmony_ci
1718c2ecf20Sopenharmony_cistatic const struct proto_ops bnep_sock_ops = {
1728c2ecf20Sopenharmony_ci	.family		= PF_BLUETOOTH,
1738c2ecf20Sopenharmony_ci	.owner		= THIS_MODULE,
1748c2ecf20Sopenharmony_ci	.release	= bnep_sock_release,
1758c2ecf20Sopenharmony_ci	.ioctl		= bnep_sock_ioctl,
1768c2ecf20Sopenharmony_ci#ifdef CONFIG_COMPAT
1778c2ecf20Sopenharmony_ci	.compat_ioctl	= bnep_sock_compat_ioctl,
1788c2ecf20Sopenharmony_ci#endif
1798c2ecf20Sopenharmony_ci	.bind		= sock_no_bind,
1808c2ecf20Sopenharmony_ci	.getname	= sock_no_getname,
1818c2ecf20Sopenharmony_ci	.sendmsg	= sock_no_sendmsg,
1828c2ecf20Sopenharmony_ci	.recvmsg	= sock_no_recvmsg,
1838c2ecf20Sopenharmony_ci	.listen		= sock_no_listen,
1848c2ecf20Sopenharmony_ci	.shutdown	= sock_no_shutdown,
1858c2ecf20Sopenharmony_ci	.connect	= sock_no_connect,
1868c2ecf20Sopenharmony_ci	.socketpair	= sock_no_socketpair,
1878c2ecf20Sopenharmony_ci	.accept		= sock_no_accept,
1888c2ecf20Sopenharmony_ci	.mmap		= sock_no_mmap
1898c2ecf20Sopenharmony_ci};
1908c2ecf20Sopenharmony_ci
1918c2ecf20Sopenharmony_cistatic struct proto bnep_proto = {
1928c2ecf20Sopenharmony_ci	.name		= "BNEP",
1938c2ecf20Sopenharmony_ci	.owner		= THIS_MODULE,
1948c2ecf20Sopenharmony_ci	.obj_size	= sizeof(struct bt_sock)
1958c2ecf20Sopenharmony_ci};
1968c2ecf20Sopenharmony_ci
1978c2ecf20Sopenharmony_cistatic int bnep_sock_create(struct net *net, struct socket *sock, int protocol,
1988c2ecf20Sopenharmony_ci			    int kern)
1998c2ecf20Sopenharmony_ci{
2008c2ecf20Sopenharmony_ci	struct sock *sk;
2018c2ecf20Sopenharmony_ci
2028c2ecf20Sopenharmony_ci	BT_DBG("sock %p", sock);
2038c2ecf20Sopenharmony_ci
2048c2ecf20Sopenharmony_ci	if (sock->type != SOCK_RAW)
2058c2ecf20Sopenharmony_ci		return -ESOCKTNOSUPPORT;
2068c2ecf20Sopenharmony_ci
2078c2ecf20Sopenharmony_ci	sk = sk_alloc(net, PF_BLUETOOTH, GFP_ATOMIC, &bnep_proto, kern);
2088c2ecf20Sopenharmony_ci	if (!sk)
2098c2ecf20Sopenharmony_ci		return -ENOMEM;
2108c2ecf20Sopenharmony_ci
2118c2ecf20Sopenharmony_ci	sock_init_data(sock, sk);
2128c2ecf20Sopenharmony_ci
2138c2ecf20Sopenharmony_ci	sock->ops = &bnep_sock_ops;
2148c2ecf20Sopenharmony_ci
2158c2ecf20Sopenharmony_ci	sock->state = SS_UNCONNECTED;
2168c2ecf20Sopenharmony_ci
2178c2ecf20Sopenharmony_ci	sock_reset_flag(sk, SOCK_ZAPPED);
2188c2ecf20Sopenharmony_ci
2198c2ecf20Sopenharmony_ci	sk->sk_protocol = protocol;
2208c2ecf20Sopenharmony_ci	sk->sk_state	= BT_OPEN;
2218c2ecf20Sopenharmony_ci
2228c2ecf20Sopenharmony_ci	bt_sock_link(&bnep_sk_list, sk);
2238c2ecf20Sopenharmony_ci	return 0;
2248c2ecf20Sopenharmony_ci}
2258c2ecf20Sopenharmony_ci
2268c2ecf20Sopenharmony_cistatic const struct net_proto_family bnep_sock_family_ops = {
2278c2ecf20Sopenharmony_ci	.family = PF_BLUETOOTH,
2288c2ecf20Sopenharmony_ci	.owner	= THIS_MODULE,
2298c2ecf20Sopenharmony_ci	.create = bnep_sock_create
2308c2ecf20Sopenharmony_ci};
2318c2ecf20Sopenharmony_ci
2328c2ecf20Sopenharmony_ciint __init bnep_sock_init(void)
2338c2ecf20Sopenharmony_ci{
2348c2ecf20Sopenharmony_ci	int err;
2358c2ecf20Sopenharmony_ci
2368c2ecf20Sopenharmony_ci	err = proto_register(&bnep_proto, 0);
2378c2ecf20Sopenharmony_ci	if (err < 0)
2388c2ecf20Sopenharmony_ci		return err;
2398c2ecf20Sopenharmony_ci
2408c2ecf20Sopenharmony_ci	err = bt_sock_register(BTPROTO_BNEP, &bnep_sock_family_ops);
2418c2ecf20Sopenharmony_ci	if (err < 0) {
2428c2ecf20Sopenharmony_ci		BT_ERR("Can't register BNEP socket");
2438c2ecf20Sopenharmony_ci		goto error;
2448c2ecf20Sopenharmony_ci	}
2458c2ecf20Sopenharmony_ci
2468c2ecf20Sopenharmony_ci	err = bt_procfs_init(&init_net, "bnep", &bnep_sk_list, NULL);
2478c2ecf20Sopenharmony_ci	if (err < 0) {
2488c2ecf20Sopenharmony_ci		BT_ERR("Failed to create BNEP proc file");
2498c2ecf20Sopenharmony_ci		bt_sock_unregister(BTPROTO_BNEP);
2508c2ecf20Sopenharmony_ci		goto error;
2518c2ecf20Sopenharmony_ci	}
2528c2ecf20Sopenharmony_ci
2538c2ecf20Sopenharmony_ci	BT_INFO("BNEP socket layer initialized");
2548c2ecf20Sopenharmony_ci
2558c2ecf20Sopenharmony_ci	return 0;
2568c2ecf20Sopenharmony_ci
2578c2ecf20Sopenharmony_cierror:
2588c2ecf20Sopenharmony_ci	proto_unregister(&bnep_proto);
2598c2ecf20Sopenharmony_ci	return err;
2608c2ecf20Sopenharmony_ci}
2618c2ecf20Sopenharmony_ci
2628c2ecf20Sopenharmony_civoid __exit bnep_sock_cleanup(void)
2638c2ecf20Sopenharmony_ci{
2648c2ecf20Sopenharmony_ci	bt_procfs_cleanup(&init_net, "bnep");
2658c2ecf20Sopenharmony_ci	bt_sock_unregister(BTPROTO_BNEP);
2668c2ecf20Sopenharmony_ci	proto_unregister(&bnep_proto);
2678c2ecf20Sopenharmony_ci}
268