xref: /netbsd-src/sys/netbt/l2cap_socket.c (revision d48f14661dda8638fee055ba15d35bdfb29b9fa8)
1 /*	$NetBSD: l2cap_socket.c,v 1.1 2006/06/19 15:44:45 gdamore 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.1 2006/06/19 15:44:45 gdamore Exp $");
35 
36 #include <sys/param.h>
37 #include <sys/domain.h>
38 #include <sys/kernel.h>
39 #include <sys/mbuf.h>
40 #include <sys/proc.h>
41 #include <sys/protosw.h>
42 #include <sys/socket.h>
43 #include <sys/socketvar.h>
44 #include <sys/systm.h>
45 
46 #include <netbt/bluetooth.h>
47 #include <netbt/l2cap.h>
48 
49 /*
50  * L2CAP Sockets
51  *
52  *	SOCK_SEQPACKET - normal L2CAP connection
53  *
54  *	SOCK_DGRAM - connectionless L2CAP - XXX not yet
55  */
56 
57 static void l2cap_connecting(void *);
58 static void l2cap_connected(void *);
59 static void l2cap_disconnected(void *, int);
60 static void *l2cap_newconn(void *, struct sockaddr_bt *, struct sockaddr_bt *);
61 static void l2cap_complete(void *, int);
62 static void l2cap_input(void *, struct mbuf *);
63 
64 static const struct btproto l2cap_proto = {
65 	l2cap_connecting,
66 	l2cap_connected,
67 	l2cap_disconnected,
68 	l2cap_newconn,
69 	l2cap_complete,
70 	l2cap_input
71 };
72 
73 /* sysctl variables */
74 int l2cap_sendspace = 4096;
75 int l2cap_recvspace = 4096;
76 
77 /*
78  * User Request.
79  * up is socket
80  * m is either
81  *	optional mbuf chain containing message
82  *	ioctl command (PRU_CONTROL)
83  * nam is either
84  *	optional mbuf chain containing an address
85  *	ioctl data (PRU_CONTROL)
86  *	optionally protocol number (PRU_ATTACH)
87  *	message flags (PRU_RCVD)
88  * ctl is either
89  *	optional mbuf chain containing socket options
90  *	optional interface pointer (PRU_CONTROL, PRU_PURGEIF)
91  * l is pointer to process requesting action (if any)
92  *
93  * we are responsible for disposing of m and ctl if
94  * they are mbuf chains
95  */
96 int
97 l2cap_usrreq(struct socket *up, int req, struct mbuf *m,
98 		struct mbuf *nam, struct mbuf *ctl, struct lwp *l)
99 {
100 	struct l2cap_channel *pcb = up->so_pcb;
101 	struct sockaddr_bt *sa;
102 	struct mbuf *m0;
103 	int err = 0;
104 
105 	DPRINTFN(2, "%s\n", prurequests[req]);
106 
107 	switch (req) {
108 	case PRU_CONTROL:
109 		return EPASSTHROUGH;
110 
111 	case PRU_PURGEIF:
112 		return EOPNOTSUPP;
113 
114 	case PRU_ATTACH:
115 		if (pcb != NULL)
116 			return EINVAL;
117 
118 		/*
119 		 * For L2CAP socket PCB we just use an l2cap_channel structure
120 		 * since we have nothing to add..
121 		 */
122 		err = soreserve(up, l2cap_sendspace, l2cap_recvspace);
123 		if (err)
124 			return err;
125 
126 		return l2cap_attach((struct l2cap_channel **)&up->so_pcb,
127 					&l2cap_proto, up);
128 	}
129 
130 	if (pcb == NULL) {
131 		err = EINVAL;
132 		goto release;
133 	}
134 
135 	switch(req) {
136 	case PRU_DISCONNECT:
137 		soisdisconnecting(up);
138 		return l2cap_disconnect(pcb, up->so_linger);
139 
140 	case PRU_ABORT:
141 		l2cap_disconnect(pcb, 0);
142 		soisdisconnected(up);
143 		/* fall through to */
144 	case PRU_DETACH:
145 		return l2cap_detach((struct l2cap_channel **)&up->so_pcb);
146 
147 	case PRU_BIND:
148 		KASSERT(nam);
149 		sa = mtod(nam, struct sockaddr_bt *);
150 
151 		if (sa->bt_len != sizeof(struct sockaddr_bt))
152 			return EINVAL;
153 
154 		if (sa->bt_family != AF_BLUETOOTH)
155 			return EAFNOSUPPORT;
156 
157 		return l2cap_bind(pcb, sa);
158 
159 	case PRU_CONNECT:
160 		KASSERT(nam);
161 		sa = mtod(nam, struct sockaddr_bt *);
162 
163 		if (sa->bt_len != sizeof(struct sockaddr_bt))
164 			return EINVAL;
165 
166 		if (sa->bt_family != AF_BLUETOOTH)
167 			return EAFNOSUPPORT;
168 
169 		soisconnecting(up);
170 		return l2cap_connect(pcb, sa);
171 
172 	case PRU_PEERADDR:
173 		KASSERT(nam);
174 		sa = mtod(nam, struct sockaddr_bt *);
175 		nam->m_len = sizeof(struct sockaddr_bt);
176 		return l2cap_peeraddr(pcb, sa);
177 
178 	case PRU_SOCKADDR:
179 		KASSERT(nam);
180 		sa = mtod(nam, struct sockaddr_bt *);
181 		nam->m_len = sizeof(struct sockaddr_bt);
182 		return l2cap_sockaddr(pcb, sa);
183 
184 	case PRU_SHUTDOWN:
185 		socantsendmore(up);
186 		break;
187 
188 	case PRU_SEND:
189 		KASSERT(m);
190 		if (m->m_pkthdr.len == 0)
191 			break;
192 
193 		if (m->m_pkthdr.len > pcb->lc_omtu) {
194 			err = EMSGSIZE;
195 			break;
196 		}
197 
198 		m0 = m_copypacket(m, M_DONTWAIT);
199 		if (m0 == NULL) {
200 			err = ENOMEM;
201 			break;
202 		}
203 
204 		if (ctl)	/* no use for that */
205 			m_freem(ctl);
206 
207 		sbappendrecord(&up->so_snd, m);
208 		return l2cap_send(pcb, m0);
209 
210 	case PRU_SENSE:
211 		return 0;		/* (no release) */
212 
213 	case PRU_RCVD:
214 	case PRU_RCVOOB:
215 		return EOPNOTSUPP;	/* (no release) */
216 
217 	case PRU_LISTEN:
218 		return l2cap_listen(pcb);
219 
220 	case PRU_ACCEPT:
221 		KASSERT(nam);
222 		sa = mtod(nam, struct sockaddr_bt *);
223 		nam->m_len = sizeof(struct sockaddr_bt);
224 		return l2cap_peeraddr(pcb, sa);
225 
226 	case PRU_CONNECT2:
227 	case PRU_SENDOOB:
228 	case PRU_FASTTIMO:
229 	case PRU_SLOWTIMO:
230 	case PRU_PROTORCV:
231 	case PRU_PROTOSEND:
232 		err = EOPNOTSUPP;
233 		break;
234 
235 	default:
236 		UNKNOWN(req);
237 		err = EOPNOTSUPP;
238 		break;
239 	}
240 
241 release:
242 	if (m) m_freem(m);
243 	if (ctl) m_freem(ctl);
244 	return err;
245 }
246 
247 /*
248  * l2cap_ctloutput(request, socket, level, optname, opt)
249  *
250  *	Apply configuration commands to channel. This corresponds to
251  *	"Reconfigure Channel Request" in the L2CAP specification.
252  */
253 int
254 l2cap_ctloutput(int req, struct socket *so, int level,
255 		int optname, struct mbuf **opt)
256 {
257 	struct l2cap_channel *pcb = so->so_pcb;
258 	struct mbuf *m;
259 	int err = 0;
260 
261 	DPRINTFN(2, "%s\n", prcorequests[req]);
262 
263 	if (level != BTPROTO_L2CAP)
264 		return 0;		// err?
265 
266 	switch(req) {
267 	case PRCO_GETOPT:
268 		m = m_get(M_WAIT, MT_SOOPTS);
269 		m->m_len = l2cap_getopt(pcb, optname, mtod(m, void *));
270 		if (m->m_len == 0) {
271 			m_freem(m);
272 			m = NULL;
273 			err = EINVAL;
274 		}
275 		*opt = m;
276 		break;
277 
278 	case PRCO_SETOPT:
279 		m = *opt;
280 		KASSERT(m != NULL);
281 		err = l2cap_setopt(pcb, optname, mtod(m, void *));
282 		m_freem(m);
283 		break;
284 
285 	default:
286 		err = EINVAL;
287 		break;
288 	}
289 
290 	return err;
291 }
292 
293 /**********************************************************************
294  *
295  *	L2CAP Protocol socket callbacks
296  *
297  */
298 
299 static void
300 l2cap_connecting(void *arg)
301 {
302 	struct socket *so = arg;
303 
304 	DPRINTF("Connecting\n");
305 	soisconnecting(so);
306 }
307 
308 static void
309 l2cap_connected(void *arg)
310 {
311 	struct socket *so = arg;
312 
313 	DPRINTF("Connected\n");
314 	soisconnected(so);
315 }
316 
317 static void
318 l2cap_disconnected(void *arg, int err)
319 {
320 	struct socket *so = arg;
321 
322 	DPRINTF("Disconnected (%d)\n", err);
323 
324 	so->so_error = err;
325 	soisdisconnected(so);
326 }
327 
328 static void *
329 l2cap_newconn(void *arg, struct sockaddr_bt *laddr, struct sockaddr_bt *raddr)
330 {
331 	struct socket *so = arg;
332 
333 	DPRINTF("New Connection\n");
334 	so = sonewconn(so, 0);
335 	if (so == NULL)
336 		return NULL;
337 
338 	soisconnecting(so);
339 
340 	return so->so_pcb;
341 }
342 
343 static void
344 l2cap_complete(void *arg, int count)
345 {
346 	struct socket *so = arg;
347 
348 	while (count-- > 0)
349 		sbdroprecord(&so->so_snd);
350 
351 	sowwakeup(so);
352 }
353 
354 static void
355 l2cap_input(void *arg, struct mbuf *m)
356 {
357 	struct socket *so = arg;
358 
359 	if (m->m_pkthdr.len > sbspace(&so->so_rcv)) {
360 		printf("%s: packet (%d bytes) dropped (socket buffer full)\n",
361 			__func__, m->m_pkthdr.len);
362 		m_freem(m);
363 		return;
364 	}
365 
366 	DPRINTFN(10, "received %d bytes\n", m->m_pkthdr.len);
367 
368 	sbappendrecord(&so->so_rcv, m);
369 	sorwakeup(so);
370 }
371