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