1/*	$NetBSD: sco_socket.c,v 1.38 2019/01/28 12:53:01 martin 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.38 2019/01/28 12:53:01 martin 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
81static int
82sco_attach(struct socket *so, int proto)
83{
84	int error;
85
86	KASSERT(so->so_pcb == NULL);
87
88	if (so->so_lock == NULL) {
89		mutex_obj_hold(bt_lock);
90		so->so_lock = bt_lock;
91		solock(so);
92	}
93	KASSERT(solocked(so));
94
95	error = soreserve(so, sco_sendspace, sco_recvspace);
96	if (error) {
97		return error;
98	}
99	return sco_attach_pcb((struct sco_pcb **)&so->so_pcb, &sco_proto, so);
100}
101
102static void
103sco_detach(struct socket *so)
104{
105	KASSERT(so->so_pcb != NULL);
106	sco_detach_pcb((struct sco_pcb **)&so->so_pcb);
107	KASSERT(so->so_pcb == NULL);
108}
109
110static int
111sco_accept(struct socket *so, struct sockaddr *nam)
112{
113	struct sco_pcb *pcb = so->so_pcb;
114
115	KASSERT(solocked(so));
116	KASSERT(nam != NULL);
117
118	if (pcb == NULL)
119		return EINVAL;
120
121	return sco_peeraddr_pcb(pcb, (struct sockaddr_bt *)nam);
122}
123
124static int
125sco_bind(struct socket *so, struct sockaddr *nam, struct lwp *l)
126{
127	struct sco_pcb *pcb = so->so_pcb;
128	struct sockaddr_bt *sa = (struct sockaddr_bt *)nam;
129
130	KASSERT(solocked(so));
131	KASSERT(nam != NULL);
132
133	if (pcb == NULL)
134		return EINVAL;
135
136	if (sa->bt_len != sizeof(struct sockaddr_bt))
137		return EINVAL;
138
139	if (sa->bt_family != AF_BLUETOOTH)
140		return EAFNOSUPPORT;
141
142	return sco_bind_pcb(pcb, sa);
143}
144
145static int
146sco_listen(struct socket *so, struct lwp *l)
147{
148	struct sco_pcb *pcb = so->so_pcb;
149
150	KASSERT(solocked(so));
151
152	if (pcb == NULL)
153		return EINVAL;
154
155	return sco_listen_pcb(pcb);
156}
157
158static int
159sco_connect(struct socket *so, struct sockaddr *nam, struct lwp *l)
160{
161	struct sco_pcb *pcb = so->so_pcb;
162	struct sockaddr_bt *sa = (struct sockaddr_bt *)nam;
163
164	KASSERT(solocked(so));
165	KASSERT(nam != NULL);
166
167	if (pcb == NULL)
168		return EINVAL;
169
170	if (sa->bt_len != sizeof(struct sockaddr_bt))
171		return EINVAL;
172
173	if (sa->bt_family != AF_BLUETOOTH)
174		return EAFNOSUPPORT;
175
176	soisconnecting(so);
177	return sco_connect_pcb(pcb, sa);
178}
179
180static int
181sco_connect2(struct socket *so, struct socket *so2)
182{
183	struct sco_pcb *pcb = so->so_pcb;
184
185	KASSERT(solocked(so));
186
187	if (pcb == NULL)
188		return EINVAL;
189
190	return EOPNOTSUPP;
191}
192
193static int
194sco_disconnect(struct socket *so)
195{
196	struct sco_pcb *pcb = so->so_pcb;
197
198	KASSERT(solocked(so));
199
200	if (pcb == NULL)
201		return EINVAL;
202
203	soisdisconnecting(so);
204	return sco_disconnect_pcb(pcb, so->so_linger);
205}
206
207static int
208sco_shutdown(struct socket *so)
209{
210	KASSERT(solocked(so));
211
212	socantsendmore(so);
213	return 0;
214}
215
216static int
217sco_abort(struct socket *so)
218{
219	struct sco_pcb *pcb = so->so_pcb;
220
221	KASSERT(solocked(so));
222
223	if (pcb == NULL)
224		return EINVAL;
225
226	sco_disconnect_pcb(pcb, 0);
227	soisdisconnected(so);
228	sco_detach(so);
229	return 0;
230}
231
232static int
233sco_ioctl(struct socket *so, u_long cmd, void *nam, struct ifnet *ifp)
234{
235	return EOPNOTSUPP;
236}
237
238static int
239sco_stat(struct socket *so, struct stat *ub)
240{
241	KASSERT(solocked(so));
242
243	return 0;
244}
245
246static int
247sco_peeraddr(struct socket *so, struct sockaddr *nam)
248{
249	struct sco_pcb *pcb = (struct sco_pcb *)so->so_pcb;
250
251	KASSERT(solocked(so));
252	KASSERT(pcb != NULL);
253	KASSERT(nam != NULL);
254
255	return sco_peeraddr_pcb(pcb, (struct sockaddr_bt *)nam);
256}
257
258static int
259sco_sockaddr(struct socket *so, struct sockaddr *nam)
260{
261	struct sco_pcb *pcb = (struct sco_pcb *)so->so_pcb;
262
263	KASSERT(solocked(so));
264	KASSERT(pcb != NULL);
265	KASSERT(nam != NULL);
266
267	return sco_sockaddr_pcb(pcb, (struct sockaddr_bt *)nam);
268}
269
270static int
271sco_rcvd(struct socket *so, int flags, struct lwp *l)
272{
273	KASSERT(solocked(so));
274
275	return EOPNOTSUPP;
276}
277
278static int
279sco_recvoob(struct socket *so, struct mbuf *m, int flags)
280{
281	KASSERT(solocked(so));
282
283	return EOPNOTSUPP;
284}
285
286static int
287sco_send(struct socket *so, struct mbuf *m, struct sockaddr *nam,
288    struct mbuf *control, struct lwp *l)
289{
290	struct sco_pcb *pcb = so->so_pcb;
291	int err = 0;
292	struct mbuf *m0;
293
294	KASSERT(solocked(so));
295	KASSERT(m != NULL);
296
297	if (control) /* no use for that */
298		m_freem(control);
299
300	if (pcb == NULL) {
301		err = EINVAL;
302		goto release;
303	}
304
305	if (m->m_pkthdr.len == 0)
306		goto release;
307
308	if (m->m_pkthdr.len > pcb->sp_mtu) {
309		err = EMSGSIZE;
310		goto release;
311	}
312
313	m0 = m_copypacket(m, M_DONTWAIT);
314	if (m0 == NULL) {
315		err = ENOMEM;
316		goto release;
317	}
318
319	sbappendrecord(&so->so_snd, m);
320	return sco_send_pcb(pcb, m0);
321
322release:
323	m_freem(m);
324	return err;
325}
326
327static int
328sco_sendoob(struct socket *so, struct mbuf *m, struct mbuf *control)
329{
330	KASSERT(solocked(so));
331
332	m_freem(m);
333	m_freem(control);
334
335	return EOPNOTSUPP;
336}
337
338static int
339sco_purgeif(struct socket *so, struct ifnet *ifp)
340{
341
342	return EOPNOTSUPP;
343}
344
345/*
346 * get/set socket options
347 */
348int
349sco_ctloutput(int req, struct socket *so, struct sockopt *sopt)
350{
351	struct sco_pcb *pcb = (struct sco_pcb *)so->so_pcb;
352	int err = 0;
353
354	DPRINTFN(2, "req %s\n", prcorequests[req]);
355
356	if (pcb == NULL)
357		return EINVAL;
358
359	if (sopt->sopt_level != BTPROTO_SCO)
360		return ENOPROTOOPT;
361
362	switch(req) {
363	case PRCO_GETOPT:
364		err = sco_getopt(pcb, sopt);
365		break;
366
367	case PRCO_SETOPT:
368		err = sco_setopt(pcb, sopt);
369		break;
370
371	default:
372		err = ENOPROTOOPT;
373		break;
374	}
375
376	return err;
377}
378
379/*****************************************************************************
380 *
381 *	SCO Protocol socket callbacks
382 *
383 */
384static void
385sco_connecting(void *arg)
386{
387	struct socket *so = arg;
388
389	DPRINTF("Connecting\n");
390	soisconnecting(so);
391}
392
393static void
394sco_connected(void *arg)
395{
396	struct socket *so = arg;
397
398	DPRINTF("Connected\n");
399	soisconnected(so);
400}
401
402static void
403sco_disconnected(void *arg, int err)
404{
405	struct socket *so = arg;
406
407	DPRINTF("Disconnected (%d)\n", err);
408
409	so->so_error = err;
410	soisdisconnected(so);
411}
412
413static void *
414sco_newconn(void *arg, struct sockaddr_bt *laddr,
415    struct sockaddr_bt *raddr)
416{
417	struct socket *so = arg;
418
419	DPRINTF("New Connection\n");
420	so = sonewconn(so, false);
421	if (so == NULL)
422		return NULL;
423
424	soisconnecting(so);
425	return so->so_pcb;
426}
427
428static void
429sco_complete(void *arg, int num)
430{
431	struct socket *so = arg;
432
433	while (num-- > 0)
434		sbdroprecord(&so->so_snd);
435
436	sowwakeup(so);
437}
438
439static void
440sco_linkmode(void *arg, int mode)
441{
442}
443
444static void
445sco_input(void *arg, struct mbuf *m)
446{
447	struct socket *so = arg;
448
449	/*
450	 * since this data is time sensitive, if the buffer
451	 * is full we just dump data until the latest one
452	 * will fit.
453	 */
454
455	while (m->m_pkthdr.len > sbspace(&so->so_rcv))
456		sbdroprecord(&so->so_rcv);
457
458	DPRINTFN(10, "received %d bytes\n", m->m_pkthdr.len);
459
460	sbappendrecord(&so->so_rcv, m);
461	sorwakeup(so);
462}
463
464PR_WRAP_USRREQS(sco)
465
466#define	sco_attach		sco_attach_wrapper
467#define	sco_detach		sco_detach_wrapper
468#define	sco_accept		sco_accept_wrapper
469#define	sco_bind		sco_bind_wrapper
470#define	sco_listen		sco_listen_wrapper
471#define	sco_connect		sco_connect_wrapper
472#define	sco_connect2		sco_connect2_wrapper
473#define	sco_disconnect		sco_disconnect_wrapper
474#define	sco_shutdown		sco_shutdown_wrapper
475#define	sco_abort		sco_abort_wrapper
476#define	sco_ioctl		sco_ioctl_wrapper
477#define	sco_stat		sco_stat_wrapper
478#define	sco_peeraddr		sco_peeraddr_wrapper
479#define	sco_sockaddr		sco_sockaddr_wrapper
480#define	sco_rcvd		sco_rcvd_wrapper
481#define	sco_recvoob		sco_recvoob_wrapper
482#define	sco_send		sco_send_wrapper
483#define	sco_sendoob		sco_sendoob_wrapper
484#define	sco_purgeif		sco_purgeif_wrapper
485
486const struct pr_usrreqs sco_usrreqs = {
487	.pr_attach	= sco_attach,
488	.pr_detach	= sco_detach,
489	.pr_accept	= sco_accept,
490	.pr_bind	= sco_bind,
491	.pr_listen	= sco_listen,
492	.pr_connect	= sco_connect,
493	.pr_connect2	= sco_connect2,
494	.pr_disconnect	= sco_disconnect,
495	.pr_shutdown	= sco_shutdown,
496	.pr_abort	= sco_abort,
497	.pr_ioctl	= sco_ioctl,
498	.pr_stat	= sco_stat,
499	.pr_peeraddr	= sco_peeraddr,
500	.pr_sockaddr	= sco_sockaddr,
501	.pr_rcvd	= sco_rcvd,
502	.pr_recvoob	= sco_recvoob,
503	.pr_send	= sco_send,
504	.pr_sendoob	= sco_sendoob,
505	.pr_purgeif	= sco_purgeif,
506};
507