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