1 /* $NetBSD: sco_socket.c,v 1.1 2006/06/19 15:44:45 gdamore 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.1 2006/06/19 15:44:45 gdamore Exp $"); 34 35 #include <sys/param.h> 36 #include <sys/domain.h> 37 #include <sys/kernel.h> 38 #include <sys/mbuf.h> 39 #include <sys/proc.h> 40 #include <sys/protosw.h> 41 #include <sys/socket.h> 42 #include <sys/socketvar.h> 43 #include <sys/systm.h> 44 45 #include <netbt/bluetooth.h> 46 #include <netbt/hci.h> 47 #include <netbt/sco.h> 48 49 /******************************************************************************* 50 * 51 * SCO SOCK_SEQPACKET sockets - low latency audio data 52 */ 53 54 static void sco_connecting(void *); 55 static void sco_connected(void *); 56 static void sco_disconnected(void *, int); 57 static void *sco_newconn(void *, struct sockaddr_bt *, struct sockaddr_bt *); 58 static void sco_complete(void *, int); 59 static void sco_input(void *, struct mbuf *); 60 61 static const struct btproto sco_proto = { 62 sco_connecting, 63 sco_connected, 64 sco_disconnected, 65 sco_newconn, 66 sco_complete, 67 sco_input, 68 }; 69 70 int sco_sendspace = 4096; 71 int sco_recvspace = 4096; 72 73 /* 74 * User Request. 75 * up is socket 76 * m is either 77 * optional mbuf chain containing message 78 * ioctl command (PRU_CONTROL) 79 * nam is either 80 * optional mbuf chain containing an address 81 * ioctl data (PRU_CONTROL) 82 * optionally, protocol number (PRU_ATTACH) 83 * ctl is optional mbuf chain containing socket options 84 * l is pointer to process requesting action (if any) 85 * 86 * we are responsible for disposing of m and ctl if 87 * they are mbuf chains 88 */ 89 int 90 sco_usrreq(struct socket *up, int req, struct mbuf *m, 91 struct mbuf *nam, struct mbuf *ctl, struct lwp *l) 92 { 93 struct sco_pcb *pcb = (struct sco_pcb *)up->so_pcb; 94 struct sockaddr_bt *sa; 95 struct mbuf *m0; 96 int err = 0; 97 98 DPRINTFN(2, "%s\n", prurequests[req]); 99 100 switch(req) { 101 case PRU_CONTROL: 102 return EOPNOTSUPP; 103 104 case PRU_PURGEIF: 105 return EOPNOTSUPP; 106 107 case PRU_ATTACH: 108 if (pcb) 109 return EINVAL; 110 111 err = soreserve(up, sco_sendspace, sco_recvspace); 112 if (err) 113 return err; 114 115 return sco_attach((struct sco_pcb **)&up->so_pcb, 116 &sco_proto, up); 117 } 118 119 /* anything after here *requires* a pcb */ 120 if (pcb == NULL) { 121 err = EINVAL; 122 goto release; 123 } 124 125 switch(req) { 126 case PRU_DISCONNECT: 127 soisdisconnecting(up); 128 return sco_disconnect(pcb, up->so_linger); 129 130 case PRU_ABORT: 131 sco_disconnect(pcb, 0); 132 soisdisconnected(up); 133 /* fall through to */ 134 case PRU_DETACH: 135 return sco_detach((struct sco_pcb **)&up->so_pcb); 136 137 case PRU_BIND: 138 KASSERT(nam); 139 sa = mtod(nam, struct sockaddr_bt *); 140 141 if (sa->bt_len != sizeof(struct sockaddr_bt)) 142 return EINVAL; 143 144 if (sa->bt_family != AF_BLUETOOTH) 145 return EAFNOSUPPORT; 146 147 return sco_bind(pcb, sa); 148 149 case PRU_CONNECT: 150 KASSERT(nam); 151 sa = mtod(nam, struct sockaddr_bt *); 152 153 if (sa->bt_len != sizeof(struct sockaddr_bt)) 154 return EINVAL; 155 156 if (sa->bt_family != AF_BLUETOOTH) 157 return EAFNOSUPPORT; 158 159 soisconnecting(up); 160 return sco_connect(pcb, sa); 161 162 case PRU_PEERADDR: 163 KASSERT(nam); 164 sa = mtod(nam, struct sockaddr_bt *); 165 nam->m_len = sizeof(struct sockaddr_bt); 166 return sco_peeraddr(pcb, sa); 167 168 case PRU_SOCKADDR: 169 KASSERT(nam); 170 sa = mtod(nam, struct sockaddr_bt *); 171 nam->m_len = sizeof(struct sockaddr_bt); 172 return sco_sockaddr(pcb, sa); 173 174 case PRU_SHUTDOWN: 175 socantsendmore(up); 176 break; 177 178 case PRU_SEND: 179 KASSERT(m); 180 if (m->m_pkthdr.len == 0) 181 break; 182 183 if (m->m_pkthdr.len > pcb->sp_mtu) { 184 err = EMSGSIZE; 185 break; 186 } 187 188 m0 = m_copypacket(m, M_DONTWAIT); 189 if (m0 == NULL) { 190 err = ENOMEM; 191 break; 192 } 193 194 if (ctl) /* no use for that */ 195 m_freem(ctl); 196 197 sbappendrecord(&up->so_snd, m); 198 return sco_send(pcb, m0); 199 200 case PRU_SENSE: 201 return 0; /* (no sense - Doh!) */ 202 203 case PRU_RCVD: 204 case PRU_RCVOOB: 205 return EOPNOTSUPP; /* (no release) */ 206 207 case PRU_ACCEPT: 208 KASSERT(nam); 209 sa = mtod(nam, struct sockaddr_bt *); 210 nam->m_len = sizeof(struct sockaddr_bt); 211 return sco_peeraddr(pcb, sa); 212 213 case PRU_CONNECT2: 214 case PRU_LISTEN: 215 case PRU_SENDOOB: 216 case PRU_FASTTIMO: 217 case PRU_SLOWTIMO: 218 case PRU_PROTORCV: 219 case PRU_PROTOSEND: 220 err = EOPNOTSUPP; 221 break; 222 223 default: 224 UNKNOWN(req); 225 err = EOPNOTSUPP; 226 break; 227 } 228 229 release: 230 if (m) m_freem(m); 231 if (ctl) m_freem(ctl); 232 return err; 233 } 234 235 /* 236 * get/set socket options 237 */ 238 int 239 sco_ctloutput(int req, struct socket *so, int level, 240 int optname, struct mbuf **opt) 241 { 242 struct sco_pcb *pcb = (struct sco_pcb *)so->so_pcb; 243 struct mbuf *m; 244 int err = 0; 245 246 DPRINTFN(2, "req %s\n", prcorequests[req]); 247 248 if (pcb == NULL) 249 return EINVAL; 250 251 if (level != BTPROTO_SCO) 252 return 0; 253 254 switch(req) { 255 case PRCO_GETOPT: 256 m = m_get(M_WAIT, MT_SOOPTS); 257 m->m_len = sco_getopt(pcb, optname, mtod(m, uint8_t *)); 258 if (m->m_len == 0) { 259 m_freem(m); 260 m = NULL; 261 err = EINVAL; 262 } 263 *opt = m; 264 break; 265 266 case PRCO_SETOPT: 267 m = *opt; 268 KASSERT(m != NULL); 269 err = sco_setopt(pcb, optname, mtod(m, uint8_t *)); 270 m_freem(m); 271 break; 272 273 default: 274 err = EINVAL; 275 break; 276 } 277 278 return err; 279 } 280 281 /***************************************************************************** 282 * 283 * SCO Protocol socket callbacks 284 * 285 */ 286 static void 287 sco_connecting(void *arg) 288 { 289 struct socket *so = arg; 290 291 DPRINTF("Connecting\n"); 292 soisconnecting(so); 293 } 294 295 static void 296 sco_connected(void *arg) 297 { 298 struct socket *so = arg; 299 300 DPRINTF("Connected\n"); 301 soisconnected(so); 302 } 303 304 static void 305 sco_disconnected(void *arg, int err) 306 { 307 struct socket *so = arg; 308 309 DPRINTF("Disconnected (%d)\n", err); 310 311 so->so_error = err; 312 soisdisconnected(so); 313 } 314 315 static void * 316 sco_newconn(void *arg, struct sockaddr_bt *laddr, struct sockaddr_bt *raddr) 317 { 318 // struct socket *so = arg; 319 320 DPRINTF("New Connection"); 321 return NULL; 322 } 323 324 static void 325 sco_complete(void *arg, int num) 326 { 327 struct socket *so = arg; 328 329 while (num-- > 0) 330 sbdroprecord(&so->so_snd); 331 332 sowwakeup(so); 333 } 334 335 static void 336 sco_input(void *arg, struct mbuf *m) 337 { 338 struct socket *so = arg; 339 340 /* 341 * since this data is time sensitive, if the buffer 342 * is full we just dump data until the latest one 343 * will fit. 344 */ 345 346 while (m->m_pkthdr.len > sbspace(&so->so_rcv)) 347 sbdroprecord(&so->so_rcv); 348 349 DPRINTFN(10, "received %d bytes\n", m->m_pkthdr.len); 350 351 sbappendrecord(&so->so_rcv, m); 352 sorwakeup(so); 353 } 354