xref: /netbsd-src/sys/netbt/l2cap_socket.c (revision b1c86f5f087524e68db12794ee9c3e3da1ab17a0)
1 /*	$NetBSD: l2cap_socket.c,v 1.9 2008/08/06 15:01:24 plunky Exp $	*/
2 
3 /*-
4  * Copyright (c) 2005 Iain Hibbert.
5  * Copyright (c) 2006 Itronix Inc.
6  * All rights reserved.
7  *
8  * Redistribution and use in source and binary forms, with or without
9  * modification, are permitted provided that the following conditions
10  * are met:
11  * 1. Redistributions of source code must retain the above copyright
12  *    notice, this list of conditions and the following disclaimer.
13  * 2. Redistributions in binary form must reproduce the above copyright
14  *    notice, this list of conditions and the following disclaimer in the
15  *    documentation and/or other materials provided with the distribution.
16  * 3. The name of Itronix Inc. may not be used to endorse
17  *    or promote products derived from this software without specific
18  *    prior written permission.
19  *
20  * THIS SOFTWARE IS PROVIDED BY ITRONIX INC. ``AS IS'' AND
21  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
22  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
23  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL ITRONIX INC. BE LIABLE FOR ANY
24  * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
25  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
26  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
27  * ON ANY THEORY OF LIABILITY, WHETHER IN
28  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
29  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
30  * POSSIBILITY OF SUCH DAMAGE.
31  */
32 
33 #include <sys/cdefs.h>
34 __KERNEL_RCSID(0, "$NetBSD: l2cap_socket.c,v 1.9 2008/08/06 15:01:24 plunky Exp $");
35 
36 /* load symbolic names */
37 #ifdef BLUETOOTH_DEBUG
38 #define PRUREQUESTS
39 #define PRCOREQUESTS
40 #endif
41 
42 #include <sys/param.h>
43 #include <sys/domain.h>
44 #include <sys/kernel.h>
45 #include <sys/mbuf.h>
46 #include <sys/proc.h>
47 #include <sys/protosw.h>
48 #include <sys/socket.h>
49 #include <sys/socketvar.h>
50 #include <sys/systm.h>
51 
52 #include <netbt/bluetooth.h>
53 #include <netbt/l2cap.h>
54 
55 /*
56  * L2CAP Sockets
57  *
58  *	SOCK_SEQPACKET - normal L2CAP connection
59  *
60  *	SOCK_DGRAM - connectionless L2CAP - XXX not yet
61  */
62 
63 static void l2cap_connecting(void *);
64 static void l2cap_connected(void *);
65 static void l2cap_disconnected(void *, int);
66 static void *l2cap_newconn(void *, struct sockaddr_bt *, struct sockaddr_bt *);
67 static void l2cap_complete(void *, int);
68 static void l2cap_linkmode(void *, int);
69 static void l2cap_input(void *, struct mbuf *);
70 
71 static const struct btproto l2cap_proto = {
72 	l2cap_connecting,
73 	l2cap_connected,
74 	l2cap_disconnected,
75 	l2cap_newconn,
76 	l2cap_complete,
77 	l2cap_linkmode,
78 	l2cap_input,
79 };
80 
81 /* sysctl variables */
82 int l2cap_sendspace = 4096;
83 int l2cap_recvspace = 4096;
84 
85 /*
86  * User Request.
87  * up is socket
88  * m is either
89  *	optional mbuf chain containing message
90  *	ioctl command (PRU_CONTROL)
91  * nam is either
92  *	optional mbuf chain containing an address
93  *	ioctl data (PRU_CONTROL)
94  *	optionally protocol number (PRU_ATTACH)
95  *	message flags (PRU_RCVD)
96  * ctl is either
97  *	optional mbuf chain containing socket options
98  *	optional interface pointer (PRU_CONTROL, PRU_PURGEIF)
99  * l is pointer to process requesting action (if any)
100  *
101  * we are responsible for disposing of m and ctl if
102  * they are mbuf chains
103  */
104 int
105 l2cap_usrreq(struct socket *up, int req, struct mbuf *m,
106     struct mbuf *nam, struct mbuf *ctl, struct lwp *l)
107 {
108 	struct l2cap_channel *pcb = up->so_pcb;
109 	struct sockaddr_bt *sa;
110 	struct mbuf *m0;
111 	int err = 0;
112 
113 	DPRINTFN(2, "%s\n", prurequests[req]);
114 
115 	switch (req) {
116 	case PRU_CONTROL:
117 		return EPASSTHROUGH;
118 
119 	case PRU_PURGEIF:
120 		return EOPNOTSUPP;
121 
122 	case PRU_ATTACH:
123 		if (up->so_lock == NULL) {
124 			mutex_obj_hold(bt_lock);
125 			up->so_lock = bt_lock;
126 			solock(up);
127 		}
128 		KASSERT(solocked(up));
129 		if (pcb != NULL)
130 			return EINVAL;
131 		/*
132 		 * For L2CAP socket PCB we just use an l2cap_channel structure
133 		 * since we have nothing to add..
134 		 */
135 		err = soreserve(up, l2cap_sendspace, l2cap_recvspace);
136 		if (err)
137 			return err;
138 
139 		return l2cap_attach((struct l2cap_channel **)&up->so_pcb,
140 					&l2cap_proto, up);
141 	}
142 
143 	if (pcb == NULL) {
144 		err = EINVAL;
145 		goto release;
146 	}
147 
148 	switch(req) {
149 	case PRU_DISCONNECT:
150 		soisdisconnecting(up);
151 		return l2cap_disconnect(pcb, up->so_linger);
152 
153 	case PRU_ABORT:
154 		l2cap_disconnect(pcb, 0);
155 		soisdisconnected(up);
156 		/* fall through to */
157 	case PRU_DETACH:
158 		return l2cap_detach((struct l2cap_channel **)&up->so_pcb);
159 
160 	case PRU_BIND:
161 		KASSERT(nam != NULL);
162 		sa = mtod(nam, struct sockaddr_bt *);
163 
164 		if (sa->bt_len != sizeof(struct sockaddr_bt))
165 			return EINVAL;
166 
167 		if (sa->bt_family != AF_BLUETOOTH)
168 			return EAFNOSUPPORT;
169 
170 		return l2cap_bind(pcb, sa);
171 
172 	case PRU_CONNECT:
173 		KASSERT(nam != NULL);
174 		sa = mtod(nam, struct sockaddr_bt *);
175 
176 		if (sa->bt_len != sizeof(struct sockaddr_bt))
177 			return EINVAL;
178 
179 		if (sa->bt_family != AF_BLUETOOTH)
180 			return EAFNOSUPPORT;
181 
182 		soisconnecting(up);
183 		return l2cap_connect(pcb, sa);
184 
185 	case PRU_PEERADDR:
186 		KASSERT(nam != NULL);
187 		sa = mtod(nam, struct sockaddr_bt *);
188 		nam->m_len = sizeof(struct sockaddr_bt);
189 		return l2cap_peeraddr(pcb, sa);
190 
191 	case PRU_SOCKADDR:
192 		KASSERT(nam != NULL);
193 		sa = mtod(nam, struct sockaddr_bt *);
194 		nam->m_len = sizeof(struct sockaddr_bt);
195 		return l2cap_sockaddr(pcb, sa);
196 
197 	case PRU_SHUTDOWN:
198 		socantsendmore(up);
199 		break;
200 
201 	case PRU_SEND:
202 		KASSERT(m != NULL);
203 		if (m->m_pkthdr.len == 0)
204 			break;
205 
206 		if (m->m_pkthdr.len > pcb->lc_omtu) {
207 			err = EMSGSIZE;
208 			break;
209 		}
210 
211 		m0 = m_copypacket(m, M_DONTWAIT);
212 		if (m0 == NULL) {
213 			err = ENOMEM;
214 			break;
215 		}
216 
217 		if (ctl)	/* no use for that */
218 			m_freem(ctl);
219 
220 		sbappendrecord(&up->so_snd, m);
221 		return l2cap_send(pcb, m0);
222 
223 	case PRU_SENSE:
224 		return 0;		/* (no release) */
225 
226 	case PRU_RCVD:
227 	case PRU_RCVOOB:
228 		return EOPNOTSUPP;	/* (no release) */
229 
230 	case PRU_LISTEN:
231 		return l2cap_listen(pcb);
232 
233 	case PRU_ACCEPT:
234 		KASSERT(nam != NULL);
235 		sa = mtod(nam, struct sockaddr_bt *);
236 		nam->m_len = sizeof(struct sockaddr_bt);
237 		return l2cap_peeraddr(pcb, sa);
238 
239 	case PRU_CONNECT2:
240 	case PRU_SENDOOB:
241 	case PRU_FASTTIMO:
242 	case PRU_SLOWTIMO:
243 	case PRU_PROTORCV:
244 	case PRU_PROTOSEND:
245 		err = EOPNOTSUPP;
246 		break;
247 
248 	default:
249 		UNKNOWN(req);
250 		err = EOPNOTSUPP;
251 		break;
252 	}
253 
254 release:
255 	if (m) m_freem(m);
256 	if (ctl) m_freem(ctl);
257 	return err;
258 }
259 
260 /*
261  * l2cap_ctloutput(req, socket, sockopt)
262  *
263  *	Apply configuration commands to channel. This corresponds to
264  *	"Reconfigure Channel Request" in the L2CAP specification.
265  */
266 int
267 l2cap_ctloutput(int req, struct socket *so, struct sockopt *sopt)
268 {
269 	struct l2cap_channel *pcb = so->so_pcb;
270 	int err = 0;
271 
272 	DPRINTFN(2, "%s\n", prcorequests[req]);
273 
274 	if (pcb == NULL)
275 		return EINVAL;
276 
277 	if (sopt->sopt_level != BTPROTO_L2CAP)
278 		return ENOPROTOOPT;
279 
280 	switch(req) {
281 	case PRCO_GETOPT:
282 		err = l2cap_getopt(pcb, sopt);
283 		break;
284 
285 	case PRCO_SETOPT:
286 		err = l2cap_setopt(pcb, sopt);
287 		break;
288 
289 	default:
290 		err = ENOPROTOOPT;
291 		break;
292 	}
293 
294 	return err;
295 }
296 
297 /**********************************************************************
298  *
299  *	L2CAP Protocol socket callbacks
300  *
301  */
302 
303 static void
304 l2cap_connecting(void *arg)
305 {
306 	struct socket *so = arg;
307 
308 	DPRINTF("Connecting\n");
309 	soisconnecting(so);
310 }
311 
312 static void
313 l2cap_connected(void *arg)
314 {
315 	struct socket *so = arg;
316 
317 	DPRINTF("Connected\n");
318 	soisconnected(so);
319 }
320 
321 static void
322 l2cap_disconnected(void *arg, int err)
323 {
324 	struct socket *so = arg;
325 
326 	DPRINTF("Disconnected (%d)\n", err);
327 
328 	so->so_error = err;
329 	soisdisconnected(so);
330 }
331 
332 static void *
333 l2cap_newconn(void *arg, struct sockaddr_bt *laddr,
334     struct sockaddr_bt *raddr)
335 {
336 	struct socket *so = arg;
337 
338 	DPRINTF("New Connection\n");
339 	so = sonewconn(so, 0);
340 	if (so == NULL)
341 		return NULL;
342 
343 	soisconnecting(so);
344 
345 	return so->so_pcb;
346 }
347 
348 static void
349 l2cap_complete(void *arg, int count)
350 {
351 	struct socket *so = arg;
352 
353 	while (count-- > 0)
354 		sbdroprecord(&so->so_snd);
355 
356 	sowwakeup(so);
357 }
358 
359 static void
360 l2cap_linkmode(void *arg, int new)
361 {
362 	struct socket *so = arg;
363 	struct sockopt sopt;
364 	int mode;
365 
366 	DPRINTF("auth %s, encrypt %s, secure %s\n",
367 		(new & L2CAP_LM_AUTH ? "on" : "off"),
368 		(new & L2CAP_LM_ENCRYPT ? "on" : "off"),
369 		(new & L2CAP_LM_SECURE ? "on" : "off"));
370 
371 	sockopt_init(&sopt, BTPROTO_L2CAP, SO_L2CAP_LM, 0);
372 	(void)l2cap_getopt(so->so_pcb, &sopt);
373 	(void)sockopt_getint(&sopt, &mode);
374 	sockopt_destroy(&sopt);
375 
376 	if (((mode & L2CAP_LM_AUTH) && !(new & L2CAP_LM_AUTH))
377 	    || ((mode & L2CAP_LM_ENCRYPT) && !(new & L2CAP_LM_ENCRYPT))
378 	    || ((mode & L2CAP_LM_SECURE) && !(new & L2CAP_LM_SECURE)))
379 		l2cap_disconnect(so->so_pcb, 0);
380 }
381 
382 static void
383 l2cap_input(void *arg, struct mbuf *m)
384 {
385 	struct socket *so = arg;
386 
387 	if (m->m_pkthdr.len > sbspace(&so->so_rcv)) {
388 		printf("%s: packet (%d bytes) dropped (socket buffer full)\n",
389 			__func__, m->m_pkthdr.len);
390 		m_freem(m);
391 		return;
392 	}
393 
394 	DPRINTFN(10, "received %d bytes\n", m->m_pkthdr.len);
395 
396 	sbappendrecord(&so->so_rcv, m);
397 	sorwakeup(so);
398 }
399