1 /* $NetBSD: rfcomm_socket.c,v 1.10 2008/08/06 15:01:24 plunky Exp $ */ 2 3 /*- 4 * Copyright (c) 2006 Itronix Inc. 5 * All rights reserved. 6 * 7 * Written by Iain Hibbert for Itronix Inc. 8 * 9 * Redistribution and use in source and binary forms, with or without 10 * modification, are permitted provided that the following conditions 11 * are met: 12 * 1. Redistributions of source code must retain the above copyright 13 * notice, this list of conditions and the following disclaimer. 14 * 2. Redistributions in binary form must reproduce the above copyright 15 * notice, this list of conditions and the following disclaimer in the 16 * documentation and/or other materials provided with the distribution. 17 * 3. The name of Itronix Inc. may not be used to endorse 18 * or promote products derived from this software without specific 19 * prior written permission. 20 * 21 * THIS SOFTWARE IS PROVIDED BY ITRONIX INC. ``AS IS'' AND 22 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 23 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 24 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL ITRONIX INC. BE LIABLE FOR ANY 25 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 26 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 27 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 28 * ON ANY THEORY OF LIABILITY, WHETHER IN 29 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 30 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 31 * POSSIBILITY OF SUCH DAMAGE. 32 */ 33 34 #include <sys/cdefs.h> 35 __KERNEL_RCSID(0, "$NetBSD: rfcomm_socket.c,v 1.10 2008/08/06 15:01:24 plunky Exp $"); 36 37 /* load symbolic names */ 38 #ifdef BLUETOOTH_DEBUG 39 #define PRUREQUESTS 40 #define PRCOREQUESTS 41 #endif 42 43 #include <sys/param.h> 44 #include <sys/domain.h> 45 #include <sys/kernel.h> 46 #include <sys/mbuf.h> 47 #include <sys/proc.h> 48 #include <sys/protosw.h> 49 #include <sys/socket.h> 50 #include <sys/socketvar.h> 51 #include <sys/systm.h> 52 53 #include <netbt/bluetooth.h> 54 #include <netbt/rfcomm.h> 55 56 /**************************************************************************** 57 * 58 * RFCOMM SOCK_STREAM Sockets - serial line emulation 59 * 60 */ 61 62 static void rfcomm_connecting(void *); 63 static void rfcomm_connected(void *); 64 static void rfcomm_disconnected(void *, int); 65 static void *rfcomm_newconn(void *, struct sockaddr_bt *, struct sockaddr_bt *); 66 static void rfcomm_complete(void *, int); 67 static void rfcomm_linkmode(void *, int); 68 static void rfcomm_input(void *, struct mbuf *); 69 70 static const struct btproto rfcomm_proto = { 71 rfcomm_connecting, 72 rfcomm_connected, 73 rfcomm_disconnected, 74 rfcomm_newconn, 75 rfcomm_complete, 76 rfcomm_linkmode, 77 rfcomm_input, 78 }; 79 80 /* sysctl variables */ 81 int rfcomm_sendspace = 4096; 82 int rfcomm_recvspace = 4096; 83 84 /* 85 * User Request. 86 * up is socket 87 * m is either 88 * optional mbuf chain containing message 89 * ioctl command (PRU_CONTROL) 90 * nam is either 91 * optional mbuf chain containing an address 92 * ioctl data (PRU_CONTROL) 93 * optionally protocol number (PRU_ATTACH) 94 * message flags (PRU_RCVD) 95 * ctl is either 96 * optional mbuf chain containing socket options 97 * optional interface pointer (PRU_CONTROL, PRU_PURGEIF) 98 * l is pointer to process requesting action (if any) 99 * 100 * we are responsible for disposing of m and ctl if 101 * they are mbuf chains 102 */ 103 int 104 rfcomm_usrreq(struct socket *up, int req, struct mbuf *m, 105 struct mbuf *nam, struct mbuf *ctl, struct lwp *l) 106 { 107 struct rfcomm_dlc *pcb = up->so_pcb; 108 struct sockaddr_bt *sa; 109 struct mbuf *m0; 110 int err = 0; 111 112 DPRINTFN(2, "%s\n", prurequests[req]); 113 114 switch (req) { 115 case PRU_CONTROL: 116 return EPASSTHROUGH; 117 118 case PRU_PURGEIF: 119 return EOPNOTSUPP; 120 121 case PRU_ATTACH: 122 if (up->so_lock == NULL) { 123 mutex_obj_hold(bt_lock); 124 up->so_lock = bt_lock; 125 solock(up); 126 } 127 KASSERT(solocked(up)); 128 if (pcb != NULL) 129 return EINVAL; 130 /* 131 * Since we have nothing to add, we attach the DLC 132 * structure directly to our PCB pointer. 133 */ 134 err = soreserve(up, rfcomm_sendspace, rfcomm_recvspace); 135 if (err) 136 return err; 137 138 err = rfcomm_attach((struct rfcomm_dlc **)&up->so_pcb, 139 &rfcomm_proto, up); 140 if (err) 141 return err; 142 143 err = rfcomm_rcvd(up->so_pcb, sbspace(&up->so_rcv)); 144 if (err) { 145 rfcomm_detach((struct rfcomm_dlc **)&up->so_pcb); 146 return err; 147 } 148 149 return 0; 150 } 151 152 if (pcb == NULL) { 153 err = EINVAL; 154 goto release; 155 } 156 157 switch(req) { 158 case PRU_DISCONNECT: 159 soisdisconnecting(up); 160 return rfcomm_disconnect(pcb, up->so_linger); 161 162 case PRU_ABORT: 163 rfcomm_disconnect(pcb, 0); 164 soisdisconnected(up); 165 /* fall through to */ 166 case PRU_DETACH: 167 return rfcomm_detach((struct rfcomm_dlc **)&up->so_pcb); 168 169 case PRU_BIND: 170 KASSERT(nam != NULL); 171 sa = mtod(nam, struct sockaddr_bt *); 172 173 if (sa->bt_len != sizeof(struct sockaddr_bt)) 174 return EINVAL; 175 176 if (sa->bt_family != AF_BLUETOOTH) 177 return EAFNOSUPPORT; 178 179 return rfcomm_bind(pcb, sa); 180 181 case PRU_CONNECT: 182 KASSERT(nam != NULL); 183 sa = mtod(nam, struct sockaddr_bt *); 184 185 if (sa->bt_len != sizeof(struct sockaddr_bt)) 186 return EINVAL; 187 188 if (sa->bt_family != AF_BLUETOOTH) 189 return EAFNOSUPPORT; 190 191 soisconnecting(up); 192 return rfcomm_connect(pcb, sa); 193 194 case PRU_PEERADDR: 195 KASSERT(nam != NULL); 196 sa = mtod(nam, struct sockaddr_bt *); 197 nam->m_len = sizeof(struct sockaddr_bt); 198 return rfcomm_peeraddr(pcb, sa); 199 200 case PRU_SOCKADDR: 201 KASSERT(nam != NULL); 202 sa = mtod(nam, struct sockaddr_bt *); 203 nam->m_len = sizeof(struct sockaddr_bt); 204 return rfcomm_sockaddr(pcb, sa); 205 206 case PRU_SHUTDOWN: 207 socantsendmore(up); 208 break; 209 210 case PRU_SEND: 211 KASSERT(m != NULL); 212 213 if (ctl) /* no use for that */ 214 m_freem(ctl); 215 216 m0 = m_copypacket(m, M_DONTWAIT); 217 if (m0 == NULL) 218 return ENOMEM; 219 220 sbappendstream(&up->so_snd, m); 221 222 return rfcomm_send(pcb, m0); 223 224 case PRU_SENSE: 225 return 0; /* (no release) */ 226 227 case PRU_RCVD: 228 return rfcomm_rcvd(pcb, sbspace(&up->so_rcv)); 229 230 case PRU_RCVOOB: 231 return EOPNOTSUPP; /* (no release) */ 232 233 case PRU_LISTEN: 234 return rfcomm_listen(pcb); 235 236 case PRU_ACCEPT: 237 KASSERT(nam != NULL); 238 sa = mtod(nam, struct sockaddr_bt *); 239 nam->m_len = sizeof(struct sockaddr_bt); 240 return rfcomm_peeraddr(pcb, sa); 241 242 case PRU_CONNECT2: 243 case PRU_SENDOOB: 244 case PRU_FASTTIMO: 245 case PRU_SLOWTIMO: 246 case PRU_PROTORCV: 247 case PRU_PROTOSEND: 248 err = EOPNOTSUPP; 249 break; 250 251 default: 252 UNKNOWN(req); 253 err = EOPNOTSUPP; 254 break; 255 } 256 257 release: 258 if (m) m_freem(m); 259 if (ctl) m_freem(ctl); 260 return err; 261 } 262 263 /* 264 * rfcomm_ctloutput(req, socket, sockopt) 265 * 266 */ 267 int 268 rfcomm_ctloutput(int req, struct socket *so, struct sockopt *sopt) 269 { 270 struct rfcomm_dlc *pcb = so->so_pcb; 271 int err = 0; 272 273 DPRINTFN(2, "%s\n", prcorequests[req]); 274 275 if (pcb == NULL) 276 return EINVAL; 277 278 if (sopt->sopt_level != BTPROTO_RFCOMM) 279 return ENOPROTOOPT; 280 281 switch(req) { 282 case PRCO_GETOPT: 283 err = rfcomm_getopt(pcb, sopt); 284 break; 285 286 case PRCO_SETOPT: 287 err = rfcomm_setopt(pcb, sopt); 288 break; 289 290 default: 291 err = ENOPROTOOPT; 292 break; 293 } 294 295 return err; 296 } 297 298 /********************************************************************** 299 * 300 * RFCOMM callbacks 301 */ 302 303 static void 304 rfcomm_connecting(void *arg) 305 { 306 /* struct socket *so = arg; */ 307 308 KASSERT(arg != NULL); 309 DPRINTF("Connecting\n"); 310 } 311 312 static void 313 rfcomm_connected(void *arg) 314 { 315 struct socket *so = arg; 316 317 KASSERT(so != NULL); 318 DPRINTF("Connected\n"); 319 soisconnected(so); 320 } 321 322 static void 323 rfcomm_disconnected(void *arg, int err) 324 { 325 struct socket *so = arg; 326 327 KASSERT(so != NULL); 328 DPRINTF("Disconnected\n"); 329 330 so->so_error = err; 331 soisdisconnected(so); 332 } 333 334 static void * 335 rfcomm_newconn(void *arg, struct sockaddr_bt *laddr, 336 struct sockaddr_bt *raddr) 337 { 338 struct socket *so = arg; 339 340 DPRINTF("New Connection\n"); 341 so = sonewconn(so, 0); 342 if (so == NULL) 343 return NULL; 344 345 soisconnecting(so); 346 347 return so->so_pcb; 348 } 349 350 /* 351 * rfcomm_complete(rfcomm_dlc, length) 352 * 353 * length bytes are sent and may be removed from socket buffer 354 */ 355 static void 356 rfcomm_complete(void *arg, int length) 357 { 358 struct socket *so = arg; 359 360 sbdrop(&so->so_snd, length); 361 sowwakeup(so); 362 } 363 364 /* 365 * rfcomm_linkmode(rfcomm_dlc, new) 366 * 367 * link mode change notification. 368 */ 369 static void 370 rfcomm_linkmode(void *arg, int new) 371 { 372 struct socket *so = arg; 373 struct sockopt sopt; 374 int mode; 375 376 DPRINTF("auth %s, encrypt %s, secure %s\n", 377 (new & RFCOMM_LM_AUTH ? "on" : "off"), 378 (new & RFCOMM_LM_ENCRYPT ? "on" : "off"), 379 (new & RFCOMM_LM_SECURE ? "on" : "off")); 380 381 sockopt_init(&sopt, BTPROTO_RFCOMM, SO_RFCOMM_LM, 0); 382 (void)rfcomm_getopt(so->so_pcb, &sopt); 383 (void)sockopt_getint(&sopt, &mode); 384 sockopt_destroy(&sopt); 385 386 if (((mode & RFCOMM_LM_AUTH) && !(new & RFCOMM_LM_AUTH)) 387 || ((mode & RFCOMM_LM_ENCRYPT) && !(new & RFCOMM_LM_ENCRYPT)) 388 || ((mode & RFCOMM_LM_SECURE) && !(new & RFCOMM_LM_SECURE))) 389 rfcomm_disconnect(so->so_pcb, 0); 390 } 391 392 /* 393 * rfcomm_input(rfcomm_dlc, mbuf) 394 */ 395 static void 396 rfcomm_input(void *arg, struct mbuf *m) 397 { 398 struct socket *so = arg; 399 400 KASSERT(so != NULL); 401 402 if (m->m_pkthdr.len > sbspace(&so->so_rcv)) { 403 printf("%s: %d bytes dropped (socket buffer full)\n", 404 __func__, m->m_pkthdr.len); 405 m_freem(m); 406 return; 407 } 408 409 DPRINTFN(10, "received %d bytes\n", m->m_pkthdr.len); 410 411 sbappendstream(&so->so_rcv, m); 412 sorwakeup(so); 413 } 414