xref: /netbsd/sys/netbt/sco_socket.c (revision 6550d01e)
1 /*	$NetBSD: sco_socket.c,v 1.11 2008/08/06 15:01:24 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.11 2008/08/06 15:01:24 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 (up->so_lock == NULL) {
117 			mutex_obj_hold(bt_lock);
118 			up->so_lock = bt_lock;
119 			solock(up);
120 		}
121 		KASSERT(solocked(up));
122 		if (pcb)
123 			return EINVAL;
124 		err = soreserve(up, sco_sendspace, sco_recvspace);
125 		if (err)
126 			return err;
127 
128 		return sco_attach((struct sco_pcb **)&up->so_pcb,
129 					&sco_proto, up);
130 	}
131 
132 	/* anything after here *requires* a pcb */
133 	if (pcb == NULL) {
134 		err = EINVAL;
135 		goto release;
136 	}
137 
138 	switch(req) {
139 	case PRU_DISCONNECT:
140 		soisdisconnecting(up);
141 		return sco_disconnect(pcb, up->so_linger);
142 
143 	case PRU_ABORT:
144 		sco_disconnect(pcb, 0);
145 		soisdisconnected(up);
146 		/* fall through to */
147 	case PRU_DETACH:
148 		return sco_detach((struct sco_pcb **)&up->so_pcb);
149 
150 	case PRU_BIND:
151 		KASSERT(nam != NULL);
152 		sa = mtod(nam, struct sockaddr_bt *);
153 
154 		if (sa->bt_len != sizeof(struct sockaddr_bt))
155 			return EINVAL;
156 
157 		if (sa->bt_family != AF_BLUETOOTH)
158 			return EAFNOSUPPORT;
159 
160 		return sco_bind(pcb, sa);
161 
162 	case PRU_CONNECT:
163 		KASSERT(nam != NULL);
164 		sa = mtod(nam, struct sockaddr_bt *);
165 
166 		if (sa->bt_len != sizeof(struct sockaddr_bt))
167 			return EINVAL;
168 
169 		if (sa->bt_family != AF_BLUETOOTH)
170 			return EAFNOSUPPORT;
171 
172 		soisconnecting(up);
173 		return sco_connect(pcb, sa);
174 
175 	case PRU_PEERADDR:
176 		KASSERT(nam != NULL);
177 		sa = mtod(nam, struct sockaddr_bt *);
178 		nam->m_len = sizeof(struct sockaddr_bt);
179 		return sco_peeraddr(pcb, sa);
180 
181 	case PRU_SOCKADDR:
182 		KASSERT(nam != NULL);
183 		sa = mtod(nam, struct sockaddr_bt *);
184 		nam->m_len = sizeof(struct sockaddr_bt);
185 		return sco_sockaddr(pcb, sa);
186 
187 	case PRU_SHUTDOWN:
188 		socantsendmore(up);
189 		break;
190 
191 	case PRU_SEND:
192 		KASSERT(m != NULL);
193 		if (m->m_pkthdr.len == 0)
194 			break;
195 
196 		if (m->m_pkthdr.len > pcb->sp_mtu) {
197 			err = EMSGSIZE;
198 			break;
199 		}
200 
201 		m0 = m_copypacket(m, M_DONTWAIT);
202 		if (m0 == NULL) {
203 			err = ENOMEM;
204 			break;
205 		}
206 
207 		if (ctl) /* no use for that */
208 			m_freem(ctl);
209 
210 		sbappendrecord(&up->so_snd, m);
211 		return sco_send(pcb, m0);
212 
213 	case PRU_SENSE:
214 		return 0;		/* (no sense - Doh!) */
215 
216 	case PRU_RCVD:
217 	case PRU_RCVOOB:
218 		return EOPNOTSUPP;	/* (no release) */
219 
220 	case PRU_LISTEN:
221 		return sco_listen(pcb);
222 
223 	case PRU_ACCEPT:
224 		KASSERT(nam != NULL);
225 		sa = mtod(nam, struct sockaddr_bt *);
226 		nam->m_len = sizeof(struct sockaddr_bt);
227 		return sco_peeraddr(pcb, sa);
228 
229 	case PRU_CONNECT2:
230 	case PRU_SENDOOB:
231 	case PRU_FASTTIMO:
232 	case PRU_SLOWTIMO:
233 	case PRU_PROTORCV:
234 	case PRU_PROTOSEND:
235 		err = EOPNOTSUPP;
236 		break;
237 
238 	default:
239 		UNKNOWN(req);
240 		err = EOPNOTSUPP;
241 		break;
242 	}
243 
244 release:
245 	if (m) m_freem(m);
246 	if (ctl) m_freem(ctl);
247 	return err;
248 }
249 
250 /*
251  * get/set socket options
252  */
253 int
254 sco_ctloutput(int req, struct socket *so, struct sockopt *sopt)
255 {
256 	struct sco_pcb *pcb = (struct sco_pcb *)so->so_pcb;
257 	int err = 0;
258 
259 	DPRINTFN(2, "req %s\n", prcorequests[req]);
260 
261 	if (pcb == NULL)
262 		return EINVAL;
263 
264 	if (sopt->sopt_level != BTPROTO_SCO)
265 		return ENOPROTOOPT;
266 
267 	switch(req) {
268 	case PRCO_GETOPT:
269 		err = sco_getopt(pcb, sopt);
270 		break;
271 
272 	case PRCO_SETOPT:
273 		err = sco_setopt(pcb, sopt);
274 		break;
275 
276 	default:
277 		err = ENOPROTOOPT;
278 		break;
279 	}
280 
281 	return err;
282 }
283 
284 /*****************************************************************************
285  *
286  *	SCO Protocol socket callbacks
287  *
288  */
289 static void
290 sco_connecting(void *arg)
291 {
292 	struct socket *so = arg;
293 
294 	DPRINTF("Connecting\n");
295 	soisconnecting(so);
296 }
297 
298 static void
299 sco_connected(void *arg)
300 {
301 	struct socket *so = arg;
302 
303 	DPRINTF("Connected\n");
304 	soisconnected(so);
305 }
306 
307 static void
308 sco_disconnected(void *arg, int err)
309 {
310 	struct socket *so = arg;
311 
312 	DPRINTF("Disconnected (%d)\n", err);
313 
314 	so->so_error = err;
315 	soisdisconnected(so);
316 }
317 
318 static void *
319 sco_newconn(void *arg, struct sockaddr_bt *laddr,
320     struct sockaddr_bt *raddr)
321 {
322 	struct socket *so = arg;
323 
324 	DPRINTF("New Connection\n");
325 	so = sonewconn(so, 0);
326 	if (so == NULL)
327 		return NULL;
328 
329 	soisconnecting(so);
330 	return so->so_pcb;
331 }
332 
333 static void
334 sco_complete(void *arg, int num)
335 {
336 	struct socket *so = arg;
337 
338 	while (num-- > 0)
339 		sbdroprecord(&so->so_snd);
340 
341 	sowwakeup(so);
342 }
343 
344 static void
345 sco_linkmode(void *arg, int mode)
346 {
347 }
348 
349 static void
350 sco_input(void *arg, struct mbuf *m)
351 {
352 	struct socket *so = arg;
353 
354 	/*
355 	 * since this data is time sensitive, if the buffer
356 	 * is full we just dump data until the latest one
357 	 * will fit.
358 	 */
359 
360 	while (m->m_pkthdr.len > sbspace(&so->so_rcv))
361 		sbdroprecord(&so->so_rcv);
362 
363 	DPRINTFN(10, "received %d bytes\n", m->m_pkthdr.len);
364 
365 	sbappendrecord(&so->so_rcv, m);
366 	sorwakeup(so);
367 }
368