xref: /netbsd-src/sys/netbt/sco_socket.c (revision d48f14661dda8638fee055ba15d35bdfb29b9fa8)
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