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