1/*	$NetBSD: sco_socket.c,v 1.10 2008/04/24 11:38:37 ad 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.10 2008/04/24 11:38:37 ad 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
60static void sco_connecting(void *);
61static void sco_connected(void *);
62static void sco_disconnected(void *, int);
63static void *sco_newconn(void *, struct sockaddr_bt *, struct sockaddr_bt *);
64static void sco_complete(void *, int);
65static void sco_linkmode(void *, int);
66static void sco_input(void *, struct mbuf *);
67
68static 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
78int sco_sendspace = 4096;
79int 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 */
97int
98sco_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
244release:
245	if (m) m_freem(m);
246	if (ctl) m_freem(ctl);
247	return err;
248}
249
250/*
251 * get/set socket options
252 */
253int
254sco_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 */
289static void
290sco_connecting(void *arg)
291{
292	struct socket *so = arg;
293
294	DPRINTF("Connecting\n");
295	soisconnecting(so);
296}
297
298static void
299sco_connected(void *arg)
300{
301	struct socket *so = arg;
302
303	DPRINTF("Connected\n");
304	soisconnected(so);
305}
306
307static void
308sco_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
318static void *
319sco_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
333static void
334sco_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
344static void
345sco_linkmode(void *arg, int mode)
346{
347}
348
349static void
350sco_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