l2cap_socket.c revision 1.7 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