xref: /netbsd-src/sys/netbt/sco_socket.c (revision c0179c282a5968435315a82f4128c61372c68fc3)
1 /*	$NetBSD: sco_socket.c,v 1.5 2006/11/16 01:33:45 christos 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.5 2006/11/16 01:33:45 christos 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_LISTEN:
208 		return sco_listen(pcb);
209 
210 	case PRU_ACCEPT:
211 		KASSERT(nam);
212 		sa = mtod(nam, struct sockaddr_bt *);
213 		nam->m_len = sizeof(struct sockaddr_bt);
214 		return sco_peeraddr(pcb, sa);
215 
216 	case PRU_CONNECT2:
217 	case PRU_SENDOOB:
218 	case PRU_FASTTIMO:
219 	case PRU_SLOWTIMO:
220 	case PRU_PROTORCV:
221 	case PRU_PROTOSEND:
222 		err = EOPNOTSUPP;
223 		break;
224 
225 	default:
226 		UNKNOWN(req);
227 		err = EOPNOTSUPP;
228 		break;
229 	}
230 
231 release:
232 	if (m) m_freem(m);
233 	if (ctl) m_freem(ctl);
234 	return err;
235 }
236 
237 /*
238  * get/set socket options
239  */
240 int
241 sco_ctloutput(int req, struct socket *so, int level,
242 		int optname, struct mbuf **opt)
243 {
244 	struct sco_pcb *pcb = (struct sco_pcb *)so->so_pcb;
245 	struct mbuf *m;
246 	int err = 0;
247 
248 	DPRINTFN(2, "req %s\n", prcorequests[req]);
249 
250 	if (pcb == NULL)
251 		return EINVAL;
252 
253 	if (level != BTPROTO_SCO)
254 		return 0;
255 
256 	switch(req) {
257 	case PRCO_GETOPT:
258 		m = m_get(M_WAIT, MT_SOOPTS);
259 		m->m_len = sco_getopt(pcb, optname, mtod(m, uint8_t *));
260 		if (m->m_len == 0) {
261 			m_freem(m);
262 			m = NULL;
263 			err = EINVAL;
264 		}
265 		*opt = m;
266 		break;
267 
268 	case PRCO_SETOPT:
269 		m = *opt;
270 		KASSERT(m != NULL);
271 		err = sco_setopt(pcb, optname, mtod(m, uint8_t *));
272 		m_freem(m);
273 		break;
274 
275 	default:
276 		err = EINVAL;
277 		break;
278 	}
279 
280 	return err;
281 }
282 
283 /*****************************************************************************
284  *
285  *	SCO Protocol socket callbacks
286  *
287  */
288 static void
289 sco_connecting(void *arg)
290 {
291 	struct socket *so = arg;
292 
293 	DPRINTF("Connecting\n");
294 	soisconnecting(so);
295 }
296 
297 static void
298 sco_connected(void *arg)
299 {
300 	struct socket *so = arg;
301 
302 	DPRINTF("Connected\n");
303 	soisconnected(so);
304 }
305 
306 static void
307 sco_disconnected(void *arg, int err)
308 {
309 	struct socket *so = arg;
310 
311 	DPRINTF("Disconnected (%d)\n", err);
312 
313 	so->so_error = err;
314 	soisdisconnected(so);
315 }
316 
317 static void *
318 sco_newconn(void *arg, struct sockaddr_bt *laddr,
319     struct sockaddr_bt *raddr)
320 {
321 	struct socket *so = arg;
322 
323 	DPRINTF("New Connection\n");
324 	so = sonewconn(so, 0);
325 	if (so == NULL)
326 		return NULL;
327 
328 	soisconnecting(so);
329 	return so->so_pcb;
330 }
331 
332 static void
333 sco_complete(void *arg, int num)
334 {
335 	struct socket *so = arg;
336 
337 	while (num-- > 0)
338 		sbdroprecord(&so->so_snd);
339 
340 	sowwakeup(so);
341 }
342 
343 static void
344 sco_input(void *arg, struct mbuf *m)
345 {
346 	struct socket *so = arg;
347 
348 	/*
349 	 * since this data is time sensitive, if the buffer
350 	 * is full we just dump data until the latest one
351 	 * will fit.
352 	 */
353 
354 	while (m->m_pkthdr.len > sbspace(&so->so_rcv))
355 		sbdroprecord(&so->so_rcv);
356 
357 	DPRINTFN(10, "received %d bytes\n", m->m_pkthdr.len);
358 
359 	sbappendrecord(&so->so_rcv, m);
360 	sorwakeup(so);
361 }
362