xref: /openbmc/linux/net/bluetooth/hidp/sock.c (revision 56c6eed1)
1 /*
2    HIDP implementation for Linux Bluetooth stack (BlueZ).
3    Copyright (C) 2003-2004 Marcel Holtmann <marcel@holtmann.org>
4 
5    This program is free software; you can redistribute it and/or modify
6    it under the terms of the GNU General Public License version 2 as
7    published by the Free Software Foundation;
8 
9    THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
10    OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
11    FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF THIRD PARTY RIGHTS.
12    IN NO EVENT SHALL THE COPYRIGHT HOLDER(S) AND AUTHOR(S) BE LIABLE FOR ANY
13    CLAIM, OR ANY SPECIAL INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES
14    WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15    ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16    OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17 
18    ALL LIABILITY, INCLUDING LIABILITY FOR INFRINGEMENT OF ANY PATENTS,
19    COPYRIGHTS, TRADEMARKS OR OTHER RIGHTS, RELATING TO USE OF THIS
20    SOFTWARE IS DISCLAIMED.
21 */
22 
23 #include <linux/export.h>
24 #include <linux/file.h>
25 
26 #include "hidp.h"
27 
28 static struct bt_sock_list hidp_sk_list = {
29 	.lock = __RW_LOCK_UNLOCKED(hidp_sk_list.lock)
30 };
31 
32 static int hidp_sock_release(struct socket *sock)
33 {
34 	struct sock *sk = sock->sk;
35 
36 	BT_DBG("sock %p sk %p", sock, sk);
37 
38 	if (!sk)
39 		return 0;
40 
41 	bt_sock_unlink(&hidp_sk_list, sk);
42 
43 	sock_orphan(sk);
44 	sock_put(sk);
45 
46 	return 0;
47 }
48 
49 static int do_hidp_sock_ioctl(struct socket *sock, unsigned int cmd, void __user *argp)
50 {
51 	struct hidp_connadd_req ca;
52 	struct hidp_conndel_req cd;
53 	struct hidp_connlist_req cl;
54 	struct hidp_conninfo ci;
55 	struct socket *csock;
56 	struct socket *isock;
57 	int err;
58 
59 	BT_DBG("cmd %x arg %p", cmd, argp);
60 
61 	switch (cmd) {
62 	case HIDPCONNADD:
63 		if (!capable(CAP_NET_ADMIN))
64 			return -EPERM;
65 
66 		if (copy_from_user(&ca, argp, sizeof(ca)))
67 			return -EFAULT;
68 
69 		csock = sockfd_lookup(ca.ctrl_sock, &err);
70 		if (!csock)
71 			return err;
72 
73 		isock = sockfd_lookup(ca.intr_sock, &err);
74 		if (!isock) {
75 			sockfd_put(csock);
76 			return err;
77 		}
78 
79 		err = hidp_connection_add(&ca, csock, isock);
80 		if (!err && copy_to_user(argp, &ca, sizeof(ca)))
81 			err = -EFAULT;
82 
83 		sockfd_put(csock);
84 		sockfd_put(isock);
85 
86 		return err;
87 
88 	case HIDPCONNDEL:
89 		if (!capable(CAP_NET_ADMIN))
90 			return -EPERM;
91 
92 		if (copy_from_user(&cd, argp, sizeof(cd)))
93 			return -EFAULT;
94 
95 		return hidp_connection_del(&cd);
96 
97 	case HIDPGETCONNLIST:
98 		if (copy_from_user(&cl, argp, sizeof(cl)))
99 			return -EFAULT;
100 
101 		if (cl.cnum <= 0)
102 			return -EINVAL;
103 
104 		err = hidp_get_connlist(&cl);
105 		if (!err && copy_to_user(argp, &cl, sizeof(cl)))
106 			return -EFAULT;
107 
108 		return err;
109 
110 	case HIDPGETCONNINFO:
111 		if (copy_from_user(&ci, argp, sizeof(ci)))
112 			return -EFAULT;
113 
114 		err = hidp_get_conninfo(&ci);
115 		if (!err && copy_to_user(argp, &ci, sizeof(ci)))
116 			return -EFAULT;
117 
118 		return err;
119 	}
120 
121 	return -EINVAL;
122 }
123 
124 static int hidp_sock_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg)
125 {
126 	return do_hidp_sock_ioctl(sock, cmd, (void __user *)arg);
127 }
128 
129 #ifdef CONFIG_COMPAT
130 struct compat_hidp_connadd_req {
131 	int   ctrl_sock;	/* Connected control socket */
132 	int   intr_sock;	/* Connected interrupt socket */
133 	__u16 parser;
134 	__u16 rd_size;
135 	compat_uptr_t rd_data;
136 	__u8  country;
137 	__u8  subclass;
138 	__u16 vendor;
139 	__u16 product;
140 	__u16 version;
141 	__u32 flags;
142 	__u32 idle_to;
143 	char  name[128];
144 };
145 
146 static int hidp_sock_compat_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg)
147 {
148 	void __user *argp = compat_ptr(arg);
149 	int err;
150 
151 	if (cmd == HIDPGETCONNLIST) {
152 		struct hidp_connlist_req cl;
153 		u32 __user *p = argp;
154 		u32 uci;
155 
156 		if (get_user(cl.cnum, p) || get_user(uci, p + 1))
157 			return -EFAULT;
158 
159 		cl.ci = compat_ptr(uci);
160 
161 		if (cl.cnum <= 0)
162 			return -EINVAL;
163 
164 		err = hidp_get_connlist(&cl);
165 
166 		if (!err && put_user(cl.cnum, p))
167 			err = -EFAULT;
168 
169 		return err;
170 	} else if (cmd == HIDPCONNADD) {
171 		struct compat_hidp_connadd_req ca32;
172 		struct hidp_connadd_req ca;
173 		struct socket *csock;
174 		struct socket *isock;
175 
176 		if (!capable(CAP_NET_ADMIN))
177 			return -EPERM;
178 
179 		if (copy_from_user(&ca32, (void __user *) arg, sizeof(ca32)))
180 			return -EFAULT;
181 
182 		ca.ctrl_sock = ca32.ctrl_sock;
183 		ca.intr_sock = ca32.intr_sock;
184 		ca.parser = ca32.parser;
185 		ca.rd_size = ca32.rd_size;
186 		ca.rd_data = compat_ptr(ca32.rd_data);
187 		ca.country = ca32.country;
188 		ca.subclass = ca32.subclass;
189 		ca.vendor = ca32.vendor;
190 		ca.product = ca32.product;
191 		ca.version = ca32.version;
192 		ca.flags = ca32.flags;
193 		ca.idle_to = ca32.idle_to;
194 		memcpy(ca.name, ca32.name, 128);
195 
196 		csock = sockfd_lookup(ca.ctrl_sock, &err);
197 		if (!csock)
198 			return err;
199 
200 		isock = sockfd_lookup(ca.intr_sock, &err);
201 		if (!isock) {
202 			sockfd_put(csock);
203 			return err;
204 		}
205 
206 		err = hidp_connection_add(&ca, csock, isock);
207 		if (!err && copy_to_user(argp, &ca32, sizeof(ca32)))
208 			err = -EFAULT;
209 
210 		sockfd_put(csock);
211 		sockfd_put(isock);
212 
213 		return err;
214 	}
215 
216 	return hidp_sock_ioctl(sock, cmd, arg);
217 }
218 #endif
219 
220 static const struct proto_ops hidp_sock_ops = {
221 	.family		= PF_BLUETOOTH,
222 	.owner		= THIS_MODULE,
223 	.release	= hidp_sock_release,
224 	.ioctl		= hidp_sock_ioctl,
225 #ifdef CONFIG_COMPAT
226 	.compat_ioctl	= hidp_sock_compat_ioctl,
227 #endif
228 	.bind		= sock_no_bind,
229 	.getname	= sock_no_getname,
230 	.sendmsg	= sock_no_sendmsg,
231 	.recvmsg	= sock_no_recvmsg,
232 	.listen		= sock_no_listen,
233 	.shutdown	= sock_no_shutdown,
234 	.setsockopt	= sock_no_setsockopt,
235 	.getsockopt	= sock_no_getsockopt,
236 	.connect	= sock_no_connect,
237 	.socketpair	= sock_no_socketpair,
238 	.accept		= sock_no_accept,
239 	.mmap		= sock_no_mmap
240 };
241 
242 static struct proto hidp_proto = {
243 	.name		= "HIDP",
244 	.owner		= THIS_MODULE,
245 	.obj_size	= sizeof(struct bt_sock)
246 };
247 
248 static int hidp_sock_create(struct net *net, struct socket *sock, int protocol,
249 			    int kern)
250 {
251 	struct sock *sk;
252 
253 	BT_DBG("sock %p", sock);
254 
255 	if (sock->type != SOCK_RAW)
256 		return -ESOCKTNOSUPPORT;
257 
258 	sk = sk_alloc(net, PF_BLUETOOTH, GFP_ATOMIC, &hidp_proto, kern);
259 	if (!sk)
260 		return -ENOMEM;
261 
262 	sock_init_data(sock, sk);
263 
264 	sock->ops = &hidp_sock_ops;
265 
266 	sock->state = SS_UNCONNECTED;
267 
268 	sock_reset_flag(sk, SOCK_ZAPPED);
269 
270 	sk->sk_protocol = protocol;
271 	sk->sk_state	= BT_OPEN;
272 
273 	bt_sock_link(&hidp_sk_list, sk);
274 
275 	return 0;
276 }
277 
278 static const struct net_proto_family hidp_sock_family_ops = {
279 	.family	= PF_BLUETOOTH,
280 	.owner	= THIS_MODULE,
281 	.create	= hidp_sock_create
282 };
283 
284 int __init hidp_init_sockets(void)
285 {
286 	int err;
287 
288 	err = proto_register(&hidp_proto, 0);
289 	if (err < 0)
290 		return err;
291 
292 	err = bt_sock_register(BTPROTO_HIDP, &hidp_sock_family_ops);
293 	if (err < 0) {
294 		BT_ERR("Can't register HIDP socket");
295 		goto error;
296 	}
297 
298 	err = bt_procfs_init(&init_net, "hidp", &hidp_sk_list, NULL);
299 	if (err < 0) {
300 		BT_ERR("Failed to create HIDP proc file");
301 		bt_sock_unregister(BTPROTO_HIDP);
302 		goto error;
303 	}
304 
305 	BT_INFO("HIDP socket layer initialized");
306 
307 	return 0;
308 
309 error:
310 	proto_unregister(&hidp_proto);
311 	return err;
312 }
313 
314 void __exit hidp_cleanup_sockets(void)
315 {
316 	bt_procfs_cleanup(&init_net, "hidp");
317 	bt_sock_unregister(BTPROTO_HIDP);
318 	proto_unregister(&hidp_proto);
319 }
320