xref: /netbsd-src/sys/netbt/l2cap_socket.c (revision 8b0f9554ff8762542c4defc4f70e1eb76fb508fa)
1 /*	$NetBSD: l2cap_socket.c,v 1.7 2007/04/21 06:15:23 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.7 2007/04/21 06:15:23 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 (pcb != NULL)
124 			return EINVAL;
125 
126 		/*
127 		 * For L2CAP socket PCB we just use an l2cap_channel structure
128 		 * since we have nothing to add..
129 		 */
130 		err = soreserve(up, l2cap_sendspace, l2cap_recvspace);
131 		if (err)
132 			return err;
133 
134 		return l2cap_attach((struct l2cap_channel **)&up->so_pcb,
135 					&l2cap_proto, up);
136 	}
137 
138 	if (pcb == NULL) {
139 		err = EINVAL;
140 		goto release;
141 	}
142 
143 	switch(req) {
144 	case PRU_DISCONNECT:
145 		soisdisconnecting(up);
146 		return l2cap_disconnect(pcb, up->so_linger);
147 
148 	case PRU_ABORT:
149 		l2cap_disconnect(pcb, 0);
150 		soisdisconnected(up);
151 		/* fall through to */
152 	case PRU_DETACH:
153 		return l2cap_detach((struct l2cap_channel **)&up->so_pcb);
154 
155 	case PRU_BIND:
156 		KASSERT(nam != NULL);
157 		sa = mtod(nam, struct sockaddr_bt *);
158 
159 		if (sa->bt_len != sizeof(struct sockaddr_bt))
160 			return EINVAL;
161 
162 		if (sa->bt_family != AF_BLUETOOTH)
163 			return EAFNOSUPPORT;
164 
165 		return l2cap_bind(pcb, sa);
166 
167 	case PRU_CONNECT:
168 		KASSERT(nam != NULL);
169 		sa = mtod(nam, struct sockaddr_bt *);
170 
171 		if (sa->bt_len != sizeof(struct sockaddr_bt))
172 			return EINVAL;
173 
174 		if (sa->bt_family != AF_BLUETOOTH)
175 			return EAFNOSUPPORT;
176 
177 		soisconnecting(up);
178 		return l2cap_connect(pcb, sa);
179 
180 	case PRU_PEERADDR:
181 		KASSERT(nam != NULL);
182 		sa = mtod(nam, struct sockaddr_bt *);
183 		nam->m_len = sizeof(struct sockaddr_bt);
184 		return l2cap_peeraddr(pcb, sa);
185 
186 	case PRU_SOCKADDR:
187 		KASSERT(nam != NULL);
188 		sa = mtod(nam, struct sockaddr_bt *);
189 		nam->m_len = sizeof(struct sockaddr_bt);
190 		return l2cap_sockaddr(pcb, sa);
191 
192 	case PRU_SHUTDOWN:
193 		socantsendmore(up);
194 		break;
195 
196 	case PRU_SEND:
197 		KASSERT(m != NULL);
198 		if (m->m_pkthdr.len == 0)
199 			break;
200 
201 		if (m->m_pkthdr.len > pcb->lc_omtu) {
202 			err = EMSGSIZE;
203 			break;
204 		}
205 
206 		m0 = m_copypacket(m, M_DONTWAIT);
207 		if (m0 == NULL) {
208 			err = ENOMEM;
209 			break;
210 		}
211 
212 		if (ctl)	/* no use for that */
213 			m_freem(ctl);
214 
215 		sbappendrecord(&up->so_snd, m);
216 		return l2cap_send(pcb, m0);
217 
218 	case PRU_SENSE:
219 		return 0;		/* (no release) */
220 
221 	case PRU_RCVD:
222 	case PRU_RCVOOB:
223 		return EOPNOTSUPP;	/* (no release) */
224 
225 	case PRU_LISTEN:
226 		return l2cap_listen(pcb);
227 
228 	case PRU_ACCEPT:
229 		KASSERT(nam != NULL);
230 		sa = mtod(nam, struct sockaddr_bt *);
231 		nam->m_len = sizeof(struct sockaddr_bt);
232 		return l2cap_peeraddr(pcb, sa);
233 
234 	case PRU_CONNECT2:
235 	case PRU_SENDOOB:
236 	case PRU_FASTTIMO:
237 	case PRU_SLOWTIMO:
238 	case PRU_PROTORCV:
239 	case PRU_PROTOSEND:
240 		err = EOPNOTSUPP;
241 		break;
242 
243 	default:
244 		UNKNOWN(req);
245 		err = EOPNOTSUPP;
246 		break;
247 	}
248 
249 release:
250 	if (m) m_freem(m);
251 	if (ctl) m_freem(ctl);
252 	return err;
253 }
254 
255 /*
256  * l2cap_ctloutput(request, socket, level, optname, opt)
257  *
258  *	Apply configuration commands to channel. This corresponds to
259  *	"Reconfigure Channel Request" in the L2CAP specification.
260  */
261 int
262 l2cap_ctloutput(int req, struct socket *so, int level,
263 		int optname, struct mbuf **opt)
264 {
265 	struct l2cap_channel *pcb = so->so_pcb;
266 	struct mbuf *m;
267 	int err = 0;
268 
269 	DPRINTFN(2, "%s\n", prcorequests[req]);
270 
271 	if (pcb == NULL)
272 		return EINVAL;
273 
274 	if (level != BTPROTO_L2CAP)
275 		return ENOPROTOOPT;
276 
277 	switch(req) {
278 	case PRCO_GETOPT:
279 		m = m_get(M_WAIT, MT_SOOPTS);
280 		m->m_len = l2cap_getopt(pcb, optname, mtod(m, void *));
281 		if (m->m_len == 0) {
282 			m_freem(m);
283 			m = NULL;
284 			err = ENOPROTOOPT;
285 		}
286 		*opt = m;
287 		break;
288 
289 	case PRCO_SETOPT:
290 		m = *opt;
291 		KASSERT(m != NULL);
292 		err = l2cap_setopt(pcb, optname, mtod(m, void *));
293 		m_freem(m);
294 		break;
295 
296 	default:
297 		err = ENOPROTOOPT;
298 		break;
299 	}
300 
301 	return err;
302 }
303 
304 /**********************************************************************
305  *
306  *	L2CAP Protocol socket callbacks
307  *
308  */
309 
310 static void
311 l2cap_connecting(void *arg)
312 {
313 	struct socket *so = arg;
314 
315 	DPRINTF("Connecting\n");
316 	soisconnecting(so);
317 }
318 
319 static void
320 l2cap_connected(void *arg)
321 {
322 	struct socket *so = arg;
323 
324 	DPRINTF("Connected\n");
325 	soisconnected(so);
326 }
327 
328 static void
329 l2cap_disconnected(void *arg, int err)
330 {
331 	struct socket *so = arg;
332 
333 	DPRINTF("Disconnected (%d)\n", err);
334 
335 	so->so_error = err;
336 	soisdisconnected(so);
337 }
338 
339 static void *
340 l2cap_newconn(void *arg, struct sockaddr_bt *laddr,
341     struct sockaddr_bt *raddr)
342 {
343 	struct socket *so = arg;
344 
345 	DPRINTF("New Connection\n");
346 	so = sonewconn(so, 0);
347 	if (so == NULL)
348 		return NULL;
349 
350 	soisconnecting(so);
351 
352 	return so->so_pcb;
353 }
354 
355 static void
356 l2cap_complete(void *arg, int count)
357 {
358 	struct socket *so = arg;
359 
360 	while (count-- > 0)
361 		sbdroprecord(&so->so_snd);
362 
363 	sowwakeup(so);
364 }
365 
366 static void
367 l2cap_linkmode(void *arg, int new)
368 {
369 	struct socket *so = arg;
370 	int mode;
371 
372 	DPRINTF("auth %s, encrypt %s, secure %s\n",
373 		(new & L2CAP_LM_AUTH ? "on" : "off"),
374 		(new & L2CAP_LM_ENCRYPT ? "on" : "off"),
375 		(new & L2CAP_LM_SECURE ? "on" : "off"));
376 
377 	(void)l2cap_getopt(so->so_pcb, SO_L2CAP_LM, &mode);
378 	if (((mode & L2CAP_LM_AUTH) && !(new & L2CAP_LM_AUTH))
379 	    || ((mode & L2CAP_LM_ENCRYPT) && !(new & L2CAP_LM_ENCRYPT))
380 	    || ((mode & L2CAP_LM_SECURE) && !(new & L2CAP_LM_SECURE)))
381 		l2cap_disconnect(so->so_pcb, 0);
382 }
383 
384 static void
385 l2cap_input(void *arg, struct mbuf *m)
386 {
387 	struct socket *so = arg;
388 
389 	if (m->m_pkthdr.len > sbspace(&so->so_rcv)) {
390 		printf("%s: packet (%d bytes) dropped (socket buffer full)\n",
391 			__func__, m->m_pkthdr.len);
392 		m_freem(m);
393 		return;
394 	}
395 
396 	DPRINTFN(10, "received %d bytes\n", m->m_pkthdr.len);
397 
398 	sbappendrecord(&so->so_rcv, m);
399 	sorwakeup(so);
400 }
401