1 /* $NetBSD: sco_socket.c,v 1.9 2007/04/21 06:15:23 plunky Exp $ */ 2 3 /*- 4 * Copyright (c) 2006 Itronix Inc. 5 * All rights reserved. 6 * 7 * Redistribution and use in source and binary forms, with or without 8 * modification, are permitted provided that the following conditions 9 * are met: 10 * 1. Redistributions of source code must retain the above copyright 11 * notice, this list of conditions and the following disclaimer. 12 * 2. Redistributions in binary form must reproduce the above copyright 13 * notice, this list of conditions and the following disclaimer in the 14 * documentation and/or other materials provided with the distribution. 15 * 3. The name of Itronix Inc. may not be used to endorse 16 * or promote products derived from this software without specific 17 * prior written permission. 18 * 19 * THIS SOFTWARE IS PROVIDED BY ITRONIX INC. ``AS IS'' AND 20 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 21 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 22 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL ITRONIX INC. BE LIABLE FOR ANY 23 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 24 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 25 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 26 * ON ANY THEORY OF LIABILITY, WHETHER IN 27 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 28 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 29 * POSSIBILITY OF SUCH DAMAGE. 30 */ 31 32 #include <sys/cdefs.h> 33 __KERNEL_RCSID(0, "$NetBSD: sco_socket.c,v 1.9 2007/04/21 06:15:23 plunky Exp $"); 34 35 /* load symbolic names */ 36 #ifdef BLUETOOTH_DEBUG 37 #define PRUREQUESTS 38 #define PRCOREQUESTS 39 #endif 40 41 #include <sys/param.h> 42 #include <sys/domain.h> 43 #include <sys/kernel.h> 44 #include <sys/mbuf.h> 45 #include <sys/proc.h> 46 #include <sys/protosw.h> 47 #include <sys/socket.h> 48 #include <sys/socketvar.h> 49 #include <sys/systm.h> 50 51 #include <netbt/bluetooth.h> 52 #include <netbt/hci.h> 53 #include <netbt/sco.h> 54 55 /******************************************************************************* 56 * 57 * SCO SOCK_SEQPACKET sockets - low latency audio data 58 */ 59 60 static void sco_connecting(void *); 61 static void sco_connected(void *); 62 static void sco_disconnected(void *, int); 63 static void *sco_newconn(void *, struct sockaddr_bt *, struct sockaddr_bt *); 64 static void sco_complete(void *, int); 65 static void sco_linkmode(void *, int); 66 static void sco_input(void *, struct mbuf *); 67 68 static const struct btproto sco_proto = { 69 sco_connecting, 70 sco_connected, 71 sco_disconnected, 72 sco_newconn, 73 sco_complete, 74 sco_linkmode, 75 sco_input, 76 }; 77 78 int sco_sendspace = 4096; 79 int sco_recvspace = 4096; 80 81 /* 82 * User Request. 83 * up is socket 84 * m is either 85 * optional mbuf chain containing message 86 * ioctl command (PRU_CONTROL) 87 * nam is either 88 * optional mbuf chain containing an address 89 * ioctl data (PRU_CONTROL) 90 * optionally, protocol number (PRU_ATTACH) 91 * ctl is optional mbuf chain containing socket options 92 * l is pointer to process requesting action (if any) 93 * 94 * we are responsible for disposing of m and ctl if 95 * they are mbuf chains 96 */ 97 int 98 sco_usrreq(struct socket *up, int req, struct mbuf *m, 99 struct mbuf *nam, struct mbuf *ctl, struct lwp *l) 100 { 101 struct sco_pcb *pcb = (struct sco_pcb *)up->so_pcb; 102 struct sockaddr_bt *sa; 103 struct mbuf *m0; 104 int err = 0; 105 106 DPRINTFN(2, "%s\n", prurequests[req]); 107 108 switch(req) { 109 case PRU_CONTROL: 110 return EOPNOTSUPP; 111 112 case PRU_PURGEIF: 113 return EOPNOTSUPP; 114 115 case PRU_ATTACH: 116 if (pcb) 117 return EINVAL; 118 119 err = soreserve(up, sco_sendspace, sco_recvspace); 120 if (err) 121 return err; 122 123 return sco_attach((struct sco_pcb **)&up->so_pcb, 124 &sco_proto, up); 125 } 126 127 /* anything after here *requires* a pcb */ 128 if (pcb == NULL) { 129 err = EINVAL; 130 goto release; 131 } 132 133 switch(req) { 134 case PRU_DISCONNECT: 135 soisdisconnecting(up); 136 return sco_disconnect(pcb, up->so_linger); 137 138 case PRU_ABORT: 139 sco_disconnect(pcb, 0); 140 soisdisconnected(up); 141 /* fall through to */ 142 case PRU_DETACH: 143 return sco_detach((struct sco_pcb **)&up->so_pcb); 144 145 case PRU_BIND: 146 KASSERT(nam != NULL); 147 sa = mtod(nam, struct sockaddr_bt *); 148 149 if (sa->bt_len != sizeof(struct sockaddr_bt)) 150 return EINVAL; 151 152 if (sa->bt_family != AF_BLUETOOTH) 153 return EAFNOSUPPORT; 154 155 return sco_bind(pcb, sa); 156 157 case PRU_CONNECT: 158 KASSERT(nam != NULL); 159 sa = mtod(nam, struct sockaddr_bt *); 160 161 if (sa->bt_len != sizeof(struct sockaddr_bt)) 162 return EINVAL; 163 164 if (sa->bt_family != AF_BLUETOOTH) 165 return EAFNOSUPPORT; 166 167 soisconnecting(up); 168 return sco_connect(pcb, sa); 169 170 case PRU_PEERADDR: 171 KASSERT(nam != NULL); 172 sa = mtod(nam, struct sockaddr_bt *); 173 nam->m_len = sizeof(struct sockaddr_bt); 174 return sco_peeraddr(pcb, sa); 175 176 case PRU_SOCKADDR: 177 KASSERT(nam != NULL); 178 sa = mtod(nam, struct sockaddr_bt *); 179 nam->m_len = sizeof(struct sockaddr_bt); 180 return sco_sockaddr(pcb, sa); 181 182 case PRU_SHUTDOWN: 183 socantsendmore(up); 184 break; 185 186 case PRU_SEND: 187 KASSERT(m != NULL); 188 if (m->m_pkthdr.len == 0) 189 break; 190 191 if (m->m_pkthdr.len > pcb->sp_mtu) { 192 err = EMSGSIZE; 193 break; 194 } 195 196 m0 = m_copypacket(m, M_DONTWAIT); 197 if (m0 == NULL) { 198 err = ENOMEM; 199 break; 200 } 201 202 if (ctl) /* no use for that */ 203 m_freem(ctl); 204 205 sbappendrecord(&up->so_snd, m); 206 return sco_send(pcb, m0); 207 208 case PRU_SENSE: 209 return 0; /* (no sense - Doh!) */ 210 211 case PRU_RCVD: 212 case PRU_RCVOOB: 213 return EOPNOTSUPP; /* (no release) */ 214 215 case PRU_LISTEN: 216 return sco_listen(pcb); 217 218 case PRU_ACCEPT: 219 KASSERT(nam != NULL); 220 sa = mtod(nam, struct sockaddr_bt *); 221 nam->m_len = sizeof(struct sockaddr_bt); 222 return sco_peeraddr(pcb, sa); 223 224 case PRU_CONNECT2: 225 case PRU_SENDOOB: 226 case PRU_FASTTIMO: 227 case PRU_SLOWTIMO: 228 case PRU_PROTORCV: 229 case PRU_PROTOSEND: 230 err = EOPNOTSUPP; 231 break; 232 233 default: 234 UNKNOWN(req); 235 err = EOPNOTSUPP; 236 break; 237 } 238 239 release: 240 if (m) m_freem(m); 241 if (ctl) m_freem(ctl); 242 return err; 243 } 244 245 /* 246 * get/set socket options 247 */ 248 int 249 sco_ctloutput(int req, struct socket *so, int level, 250 int optname, struct mbuf **opt) 251 { 252 struct sco_pcb *pcb = (struct sco_pcb *)so->so_pcb; 253 struct mbuf *m; 254 int err = 0; 255 256 DPRINTFN(2, "req %s\n", prcorequests[req]); 257 258 if (pcb == NULL) 259 return EINVAL; 260 261 if (level != BTPROTO_SCO) 262 return ENOPROTOOPT; 263 264 switch(req) { 265 case PRCO_GETOPT: 266 m = m_get(M_WAIT, MT_SOOPTS); 267 m->m_len = sco_getopt(pcb, optname, mtod(m, uint8_t *)); 268 if (m->m_len == 0) { 269 m_freem(m); 270 m = NULL; 271 err = ENOPROTOOPT; 272 } 273 *opt = m; 274 break; 275 276 case PRCO_SETOPT: 277 m = *opt; 278 KASSERT(m != NULL); 279 err = sco_setopt(pcb, optname, mtod(m, uint8_t *)); 280 m_freem(m); 281 break; 282 283 default: 284 err = ENOPROTOOPT; 285 break; 286 } 287 288 return err; 289 } 290 291 /***************************************************************************** 292 * 293 * SCO Protocol socket callbacks 294 * 295 */ 296 static void 297 sco_connecting(void *arg) 298 { 299 struct socket *so = arg; 300 301 DPRINTF("Connecting\n"); 302 soisconnecting(so); 303 } 304 305 static void 306 sco_connected(void *arg) 307 { 308 struct socket *so = arg; 309 310 DPRINTF("Connected\n"); 311 soisconnected(so); 312 } 313 314 static void 315 sco_disconnected(void *arg, int err) 316 { 317 struct socket *so = arg; 318 319 DPRINTF("Disconnected (%d)\n", err); 320 321 so->so_error = err; 322 soisdisconnected(so); 323 } 324 325 static void * 326 sco_newconn(void *arg, struct sockaddr_bt *laddr, 327 struct sockaddr_bt *raddr) 328 { 329 struct socket *so = arg; 330 331 DPRINTF("New Connection\n"); 332 so = sonewconn(so, 0); 333 if (so == NULL) 334 return NULL; 335 336 soisconnecting(so); 337 return so->so_pcb; 338 } 339 340 static void 341 sco_complete(void *arg, int num) 342 { 343 struct socket *so = arg; 344 345 while (num-- > 0) 346 sbdroprecord(&so->so_snd); 347 348 sowwakeup(so); 349 } 350 351 static void 352 sco_linkmode(void *arg, int mode) 353 { 354 } 355 356 static void 357 sco_input(void *arg, struct mbuf *m) 358 { 359 struct socket *so = arg; 360 361 /* 362 * since this data is time sensitive, if the buffer 363 * is full we just dump data until the latest one 364 * will fit. 365 */ 366 367 while (m->m_pkthdr.len > sbspace(&so->so_rcv)) 368 sbdroprecord(&so->so_rcv); 369 370 DPRINTFN(10, "received %d bytes\n", m->m_pkthdr.len); 371 372 sbappendrecord(&so->so_rcv, m); 373 sorwakeup(so); 374 } 375