xref: /netbsd/sys/dev/bluetooth/btuart.c (revision 53bdbc3f)
1*53bdbc3fSriastradh /*	$NetBSD: btuart.c,v 1.31 2022/10/26 23:44:03 riastradh Exp $	*/
2486e4624Splunky 
3486e4624Splunky /*-
410abd6f2Skiyohara  * Copyright (c) 2006, 2007 KIYOHARA Takashi
510abd6f2Skiyohara  * All rights reserved.
610abd6f2Skiyohara  *
710abd6f2Skiyohara  * Redistribution and use in source and binary forms, with or without
810abd6f2Skiyohara  * modification, are permitted provided that the following conditions
910abd6f2Skiyohara  * are met:
1010abd6f2Skiyohara  * 1. Redistributions of source code must retain the above copyright
1110abd6f2Skiyohara  *    notice, this list of conditions and the following disclaimer.
1210abd6f2Skiyohara  * 2. Redistributions in binary form must reproduce the above copyright
1310abd6f2Skiyohara  *    notice, this list of conditions and the following disclaimer in the
1410abd6f2Skiyohara  *    documentation and/or other materials provided with the distribution.
1510abd6f2Skiyohara  *
1610abd6f2Skiyohara  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
1710abd6f2Skiyohara  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
1810abd6f2Skiyohara  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
1910abd6f2Skiyohara  * DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
2010abd6f2Skiyohara  * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
2110abd6f2Skiyohara  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
2210abd6f2Skiyohara  * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2310abd6f2Skiyohara  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
2410abd6f2Skiyohara  * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
2510abd6f2Skiyohara  * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
2610abd6f2Skiyohara  * POSSIBILITY OF SUCH DAMAGE.
2710abd6f2Skiyohara  */
2810abd6f2Skiyohara 
2910abd6f2Skiyohara #include <sys/cdefs.h>
30*53bdbc3fSriastradh __KERNEL_RCSID(0, "$NetBSD: btuart.c,v 1.31 2022/10/26 23:44:03 riastradh Exp $");
3110abd6f2Skiyohara 
3210abd6f2Skiyohara #include <sys/param.h>
33486e4624Splunky #include <sys/conf.h>
3410abd6f2Skiyohara #include <sys/device.h>
3510abd6f2Skiyohara #include <sys/errno.h>
3610abd6f2Skiyohara #include <sys/fcntl.h>
3710abd6f2Skiyohara #include <sys/kauth.h>
3810abd6f2Skiyohara #include <sys/kernel.h>
3910abd6f2Skiyohara #include <sys/malloc.h>
4010abd6f2Skiyohara #include <sys/mbuf.h>
4110abd6f2Skiyohara #include <sys/proc.h>
4210abd6f2Skiyohara #include <sys/syslimits.h>
4310abd6f2Skiyohara #include <sys/systm.h>
4410abd6f2Skiyohara #include <sys/tty.h>
4510abd6f2Skiyohara 
46a2a38285Sad #include <sys/bus.h>
47a2a38285Sad #include <sys/intr.h>
4810abd6f2Skiyohara 
4910abd6f2Skiyohara #include <netbt/bluetooth.h>
5010abd6f2Skiyohara #include <netbt/hci.h>
5110abd6f2Skiyohara 
52fdc2d3a9Suebayasi #include "ioconf.h"
53fdc2d3a9Suebayasi 
5410abd6f2Skiyohara struct btuart_softc {
55edb74239Splunky 	device_t	sc_dev;
56486e4624Splunky 	struct tty *	sc_tp;		/* tty pointer */
5710abd6f2Skiyohara 
58486e4624Splunky 	bool		sc_enabled;	/* device is enabled */
59486e4624Splunky 	struct hci_unit *sc_unit;	/* Bluetooth HCI handle */
60736a9db0Splunky 	struct bt_stats	sc_stats;
6110abd6f2Skiyohara 
6210abd6f2Skiyohara 	int		sc_state;	/* receive state */
63486e4624Splunky 	int		sc_want;	/* how much we want */
64486e4624Splunky 	struct mbuf *	sc_rxp;		/* incoming packet */
65486e4624Splunky 
66486e4624Splunky 	bool		sc_xmit;	/* transmit is active */
67486e4624Splunky 	struct mbuf *	sc_txp;		/* outgoing packet */
68486e4624Splunky 
69486e4624Splunky 	/* transmit queues */
70486e4624Splunky 	MBUFQ_HEAD()	sc_cmdq;
71486e4624Splunky 	MBUFQ_HEAD()	sc_aclq;
72486e4624Splunky 	MBUFQ_HEAD()	sc_scoq;
73486e4624Splunky };
74486e4624Splunky 
75486e4624Splunky /* sc_state */
7610abd6f2Skiyohara #define BTUART_RECV_PKT_TYPE	0	/* packet type */
7710abd6f2Skiyohara #define BTUART_RECV_ACL_HDR	1	/* acl header */
7810abd6f2Skiyohara #define BTUART_RECV_SCO_HDR	2	/* sco header */
7910abd6f2Skiyohara #define BTUART_RECV_EVENT_HDR	3	/* event header */
8010abd6f2Skiyohara #define BTUART_RECV_ACL_DATA	4	/* acl packet data */
8110abd6f2Skiyohara #define BTUART_RECV_SCO_DATA	5	/* sco packet data */
8210abd6f2Skiyohara #define BTUART_RECV_EVENT_DATA	6	/* event packet data */
83736a9db0Splunky 
8432c49409Scegger static int btuart_match(device_t, cfdata_t, void *);
85edb74239Splunky static void btuart_attach(device_t, device_t, void *);
86edb74239Splunky static int btuart_detach(device_t, int);
8710abd6f2Skiyohara 
88486e4624Splunky static int btuartopen(dev_t, struct tty *);
89486e4624Splunky static int btuartclose(struct tty *, int);
90486e4624Splunky static int btuartioctl(struct tty *, u_long, void *, int, struct lwp *);
91486e4624Splunky static int btuartinput(int, struct tty *);
92486e4624Splunky static int btuartstart(struct tty *);
9310abd6f2Skiyohara 
94486e4624Splunky static int btuart_enable(device_t);
95486e4624Splunky static void btuart_disable(device_t);
96486e4624Splunky static void btuart_output_cmd(device_t, struct mbuf *);
97486e4624Splunky static void btuart_output_acl(device_t, struct mbuf *);
98486e4624Splunky static void btuart_output_sco(device_t, struct mbuf *);
99486e4624Splunky static void btuart_stats(device_t, struct bt_stats *, int);
10010abd6f2Skiyohara 
10110abd6f2Skiyohara /*
10210abd6f2Skiyohara  * It doesn't need to be exported, as only btuartattach() uses it,
10310abd6f2Skiyohara  * but there's no "official" way to make it static.
10410abd6f2Skiyohara  */
105edb74239Splunky CFATTACH_DECL_NEW(btuart, sizeof(struct btuart_softc),
10610abd6f2Skiyohara     btuart_match, btuart_attach, btuart_detach, NULL);
10710abd6f2Skiyohara 
108486e4624Splunky static struct linesw btuart_disc = {
10910abd6f2Skiyohara 	.l_name =	"btuart",
110486e4624Splunky 	.l_open =	btuartopen,
111486e4624Splunky 	.l_close =	btuartclose,
11210abd6f2Skiyohara 	.l_read =	ttyerrio,
11310abd6f2Skiyohara 	.l_write =	ttyerrio,
114486e4624Splunky 	.l_ioctl =	btuartioctl,
115486e4624Splunky 	.l_rint =	btuartinput,
116486e4624Splunky 	.l_start =	btuartstart,
11710abd6f2Skiyohara 	.l_modem =	ttymodem,
118486e4624Splunky 	.l_poll =	ttyerrpoll,
11910abd6f2Skiyohara };
12010abd6f2Skiyohara 
121736a9db0Splunky static const struct hci_if btuart_hci = {
122486e4624Splunky 	.enable =	btuart_enable,
123486e4624Splunky 	.disable =	btuart_disable,
124486e4624Splunky 	.output_cmd =	btuart_output_cmd,
125486e4624Splunky 	.output_acl =	btuart_output_acl,
126486e4624Splunky 	.output_sco =	btuart_output_sco,
127486e4624Splunky 	.get_stats =	btuart_stats,
128736a9db0Splunky 	.ipl =		IPL_TTY,
129736a9db0Splunky };
130736a9db0Splunky 
131486e4624Splunky /*****************************************************************************
132486e4624Splunky  *
133486e4624Splunky  *	autoconf(9) functions
134486e4624Splunky  */
13510abd6f2Skiyohara 
136486e4624Splunky /*
137486e4624Splunky  * pseudo-device attach routine.
138486e4624Splunky  */
13910abd6f2Skiyohara void
btuartattach(int num __unused)14010abd6f2Skiyohara btuartattach(int num __unused)
14110abd6f2Skiyohara {
14210abd6f2Skiyohara 	int error;
14310abd6f2Skiyohara 
144486e4624Splunky 	error = ttyldisc_attach(&btuart_disc);
14510abd6f2Skiyohara 	if (error) {
14610abd6f2Skiyohara 		aprint_error("%s: unable to register line discipline, "
14710abd6f2Skiyohara 		    "error = %d\n", btuart_cd.cd_name, error);
148486e4624Splunky 
14910abd6f2Skiyohara 		return;
15010abd6f2Skiyohara 	}
15110abd6f2Skiyohara 
15210abd6f2Skiyohara 	error = config_cfattach_attach(btuart_cd.cd_name, &btuart_ca);
15310abd6f2Skiyohara 	if (error) {
15410abd6f2Skiyohara 		aprint_error("%s: unable to register cfattach, error = %d\n",
15510abd6f2Skiyohara 		    btuart_cd.cd_name, error);
156486e4624Splunky 
15710abd6f2Skiyohara 		config_cfdriver_detach(&btuart_cd);
158486e4624Splunky 		(void) ttyldisc_detach(&btuart_disc);
15910abd6f2Skiyohara 	}
16010abd6f2Skiyohara }
16110abd6f2Skiyohara 
16210abd6f2Skiyohara /*
16310abd6f2Skiyohara  * Autoconf match routine.
16410abd6f2Skiyohara  */
16510abd6f2Skiyohara static int
btuart_match(device_t self __unused,cfdata_t cfdata __unused,void * arg __unused)16632c49409Scegger btuart_match(device_t self __unused, cfdata_t cfdata __unused,
1676119aca0Skiyohara 	     void *arg __unused)
16810abd6f2Skiyohara {
16910abd6f2Skiyohara 
17010abd6f2Skiyohara 	/* pseudo-device; always present */
17110abd6f2Skiyohara 	return 1;
17210abd6f2Skiyohara }
17310abd6f2Skiyohara 
17410abd6f2Skiyohara /*
175486e4624Splunky  * Autoconf attach routine.
176486e4624Splunky  * Called by config_attach_pseudo(9) when we open the line discipline.
17710abd6f2Skiyohara  */
17810abd6f2Skiyohara static void
btuart_attach(device_t parent __unused,device_t self,void * aux __unused)1796119aca0Skiyohara btuart_attach(device_t parent __unused, device_t self, void *aux __unused)
18010abd6f2Skiyohara {
18110abd6f2Skiyohara 	struct btuart_softc *sc = device_private(self);
18210abd6f2Skiyohara 
183edb74239Splunky 	sc->sc_dev = self;
184edb74239Splunky 
185736a9db0Splunky 	MBUFQ_INIT(&sc->sc_cmdq);
186736a9db0Splunky 	MBUFQ_INIT(&sc->sc_aclq);
187736a9db0Splunky 	MBUFQ_INIT(&sc->sc_scoq);
188736a9db0Splunky 
18910abd6f2Skiyohara 	/* Attach Bluetooth unit */
1908db4ab26Srmind 	sc->sc_unit = hci_attach_pcb(&btuart_hci, self, 0);
191486e4624Splunky 	if (sc->sc_unit == NULL)
192486e4624Splunky 		aprint_error_dev(self, "HCI attach failed\n");
19310abd6f2Skiyohara }
19410abd6f2Skiyohara 
19510abd6f2Skiyohara /*
196486e4624Splunky  * Autoconf detach routine.
197486e4624Splunky  * Called when we close the line discipline.
19810abd6f2Skiyohara  */
19910abd6f2Skiyohara static int
btuart_detach(device_t self,int flags __unused)200edb74239Splunky btuart_detach(device_t self, int flags __unused)
20110abd6f2Skiyohara {
20210abd6f2Skiyohara 	struct btuart_softc *sc = device_private(self);
20310abd6f2Skiyohara 
204486e4624Splunky 	btuart_disable(self);
205486e4624Splunky 
206736a9db0Splunky 	if (sc->sc_unit) {
2078db4ab26Srmind 		hci_detach_pcb(sc->sc_unit);
208736a9db0Splunky 		sc->sc_unit = NULL;
209736a9db0Splunky 	}
21010abd6f2Skiyohara 
21110abd6f2Skiyohara 	return 0;
21210abd6f2Skiyohara }
21310abd6f2Skiyohara 
214486e4624Splunky /*****************************************************************************
21510abd6f2Skiyohara  *
21610abd6f2Skiyohara  *	Line discipline functions.
21710abd6f2Skiyohara  */
218486e4624Splunky 
21910abd6f2Skiyohara static int
btuartopen(dev_t devno __unused,struct tty * tp)220486e4624Splunky btuartopen(dev_t devno __unused, struct tty *tp)
22110abd6f2Skiyohara {
22210abd6f2Skiyohara 	struct btuart_softc *sc;
2236119aca0Skiyohara 	device_t dev;
22432c49409Scegger 	cfdata_t cfdata;
22510abd6f2Skiyohara 	struct lwp *l = curlwp;		/* XXX */
22610abd6f2Skiyohara 	int error, unit, s;
22710abd6f2Skiyohara 
228b8530420Selad 	error = kauth_authorize_device(l->l_cred, KAUTH_DEVICE_BLUETOOTH_BTUART,
229b8530420Selad 	    KAUTH_ARG(KAUTH_REQ_DEVICE_BLUETOOTH_BTUART_ADD), NULL, NULL, NULL);
230b8530420Selad 	if (error)
231b8530420Selad 		return (error);
23210abd6f2Skiyohara 
23310abd6f2Skiyohara 	s = spltty();
23410abd6f2Skiyohara 
235486e4624Splunky 	if (tp->t_linesw == &btuart_disc) {
236486e4624Splunky 		sc = tp->t_sc;
23710abd6f2Skiyohara 		if (sc != NULL) {
23810abd6f2Skiyohara 			splx(s);
23910abd6f2Skiyohara 			return EBUSY;
24010abd6f2Skiyohara 		}
24110abd6f2Skiyohara 	}
24210abd6f2Skiyohara 
24310abd6f2Skiyohara 	cfdata = malloc(sizeof(struct cfdata), M_DEVBUF, M_WAITOK);
24410abd6f2Skiyohara 	for (unit = 0; unit < btuart_cd.cd_ndevs; unit++)
24517dbee90Scegger 		if (device_lookup(&btuart_cd, unit) == NULL)
24610abd6f2Skiyohara 			break;
247486e4624Splunky 
248486e4624Splunky 	cfdata->cf_name = btuart_cd.cd_name;
249486e4624Splunky 	cfdata->cf_atname = btuart_cd.cd_name;
25010abd6f2Skiyohara 	cfdata->cf_unit = unit;
251fdf9144eSplunky 	cfdata->cf_fstate = FSTATE_STAR;
25210abd6f2Skiyohara 
2536119aca0Skiyohara 	dev = config_attach_pseudo(cfdata);
2546119aca0Skiyohara 	if (dev == NULL) {
255486e4624Splunky 		free(cfdata, M_DEVBUF);
25610abd6f2Skiyohara 		splx(s);
25710abd6f2Skiyohara 		return EIO;
25810abd6f2Skiyohara 	}
2596119aca0Skiyohara 	sc = device_private(dev);
260486e4624Splunky 
261461a86f9Schristos 	aprint_normal_dev(dev, "major %llu minor %llu\n",
262461a86f9Schristos 	    (unsigned long long)major(tp->t_dev),
263461a86f9Schristos 	    (unsigned long long)minor(tp->t_dev));
264486e4624Splunky 
26510abd6f2Skiyohara 	sc->sc_tp = tp;
266486e4624Splunky 	tp->t_sc = sc;
267486e4624Splunky 
268*53bdbc3fSriastradh 	ttylock(tp);
26910abd6f2Skiyohara 	ttyflush(tp, FREAD | FWRITE);
270*53bdbc3fSriastradh 	ttyunlock(tp);
27110abd6f2Skiyohara 
27210abd6f2Skiyohara 	splx(s);
27310abd6f2Skiyohara 
27410abd6f2Skiyohara 	return 0;
27510abd6f2Skiyohara }
27610abd6f2Skiyohara 
27710abd6f2Skiyohara static int
btuartclose(struct tty * tp,int flag __unused)278486e4624Splunky btuartclose(struct tty *tp, int flag __unused)
27910abd6f2Skiyohara {
280486e4624Splunky 	struct btuart_softc *sc = tp->t_sc;
28132c49409Scegger 	cfdata_t cfdata;
282486e4624Splunky 	int s;
28310abd6f2Skiyohara 
28410abd6f2Skiyohara 	s = spltty();
285486e4624Splunky 
286*53bdbc3fSriastradh 	ttylock(tp);
28710abd6f2Skiyohara 	ttyflush(tp, FREAD | FWRITE);
288*53bdbc3fSriastradh 	ttyunlock(tp);	/* XXX */
289486e4624Splunky 
29010abd6f2Skiyohara 	ttyldisc_release(tp->t_linesw);
29110abd6f2Skiyohara 	tp->t_linesw = ttyldisc_default();
292486e4624Splunky 
29310abd6f2Skiyohara 	if (sc != NULL) {
29410abd6f2Skiyohara 		tp->t_sc = NULL;
29510abd6f2Skiyohara 		if (sc->sc_tp == tp) {
296edb74239Splunky 			cfdata = device_cfdata(sc->sc_dev);
297edb74239Splunky 			config_detach(sc->sc_dev, 0);
29810abd6f2Skiyohara 			free(cfdata, M_DEVBUF);
29910abd6f2Skiyohara 		}
30010abd6f2Skiyohara 	}
301486e4624Splunky 
30210abd6f2Skiyohara 	splx(s);
303486e4624Splunky 
30410abd6f2Skiyohara 	return 0;
30510abd6f2Skiyohara }
30610abd6f2Skiyohara 
30710abd6f2Skiyohara static int
btuartioctl(struct tty * tp,u_long cmd,void * data __unused,int flag __unused,struct lwp * l __unused)308486e4624Splunky btuartioctl(struct tty *tp, u_long cmd, void *data __unused,
30910abd6f2Skiyohara     int flag __unused, struct lwp *l __unused)
31010abd6f2Skiyohara {
311486e4624Splunky 	struct btuart_softc *sc = tp->t_sc;
312486e4624Splunky 	int error;
31310abd6f2Skiyohara 
314bad41ba1Sknakahara 	/*
315bad41ba1Sknakahara 	 * XXX
316bad41ba1Sknakahara 	 * This function can be called without KERNEL_LOCK when caller's
317bad41ba1Sknakahara 	 * struct cdevsw is set D_MPSAFE. Is KERNEL_LOCK required?
318bad41ba1Sknakahara 	 */
319bad41ba1Sknakahara 
32010abd6f2Skiyohara 	if (sc == NULL || tp != sc->sc_tp)
32110abd6f2Skiyohara 		return EPASSTHROUGH;
32210abd6f2Skiyohara 
32310abd6f2Skiyohara 	switch(cmd) {
32410abd6f2Skiyohara 	default:
32510abd6f2Skiyohara 		error = EPASSTHROUGH;
32610abd6f2Skiyohara 		break;
32710abd6f2Skiyohara 	}
32810abd6f2Skiyohara 
32910abd6f2Skiyohara 	return error;
33010abd6f2Skiyohara }
33110abd6f2Skiyohara 
33210abd6f2Skiyohara static int
btuartinput(int c,struct tty * tp)333486e4624Splunky btuartinput(int c, struct tty *tp)
33410abd6f2Skiyohara {
335486e4624Splunky 	struct btuart_softc *sc = tp->t_sc;
33610abd6f2Skiyohara 	struct mbuf *m = sc->sc_rxp;
33710abd6f2Skiyohara 	int space = 0;
33810abd6f2Skiyohara 
339486e4624Splunky 	if (!sc->sc_enabled)
340486e4624Splunky 		return 0;
341486e4624Splunky 
34210abd6f2Skiyohara 	c &= TTY_CHARMASK;
34310abd6f2Skiyohara 
34410abd6f2Skiyohara 	/* If we already started a packet, find the trailing end of it. */
34510abd6f2Skiyohara 	if (m) {
34610abd6f2Skiyohara 		while (m->m_next)
34710abd6f2Skiyohara 			m = m->m_next;
34810abd6f2Skiyohara 
34910abd6f2Skiyohara 		space = M_TRAILINGSPACE(m);
35010abd6f2Skiyohara 	}
35110abd6f2Skiyohara 
35210abd6f2Skiyohara 	if (space == 0) {
35310abd6f2Skiyohara 		if (m == NULL) {
35410abd6f2Skiyohara 			/* new packet */
35510abd6f2Skiyohara 			MGETHDR(m, M_DONTWAIT, MT_DATA);
35610abd6f2Skiyohara 			if (m == NULL) {
35723849e1fSkiyohara 				aprint_error_dev(sc->sc_dev, "out of memory\n");
358736a9db0Splunky 				sc->sc_stats.err_rx++;
35910abd6f2Skiyohara 				return 0;	/* (lost sync) */
36010abd6f2Skiyohara 			}
36110abd6f2Skiyohara 
36210abd6f2Skiyohara 			sc->sc_rxp = m;
36310abd6f2Skiyohara 			m->m_pkthdr.len = m->m_len = 0;
36410abd6f2Skiyohara 			space = MHLEN;
36510abd6f2Skiyohara 
36610abd6f2Skiyohara 			sc->sc_state = BTUART_RECV_PKT_TYPE;
36710abd6f2Skiyohara 			sc->sc_want = 1;
36810abd6f2Skiyohara 		} else {
36910abd6f2Skiyohara 			/* extend mbuf */
37010abd6f2Skiyohara 			MGET(m->m_next, M_DONTWAIT, MT_DATA);
37110abd6f2Skiyohara 			if (m->m_next == NULL) {
37223849e1fSkiyohara 				aprint_error_dev(sc->sc_dev, "out of memory\n");
373736a9db0Splunky 				sc->sc_stats.err_rx++;
37410abd6f2Skiyohara 				return 0;	/* (lost sync) */
37510abd6f2Skiyohara 			}
37610abd6f2Skiyohara 
37710abd6f2Skiyohara 			m = m->m_next;
37810abd6f2Skiyohara 			m->m_len = 0;
37910abd6f2Skiyohara 			space = MLEN;
38010abd6f2Skiyohara 
38110abd6f2Skiyohara 			if (sc->sc_want > MINCLSIZE) {
38210abd6f2Skiyohara 				MCLGET(m, M_DONTWAIT);
38310abd6f2Skiyohara 				if (m->m_flags & M_EXT)
38410abd6f2Skiyohara 					space = MCLBYTES;
38510abd6f2Skiyohara 			}
38610abd6f2Skiyohara 		}
38710abd6f2Skiyohara 	}
38810abd6f2Skiyohara 
38910abd6f2Skiyohara 	mtod(m, uint8_t *)[m->m_len++] = c;
39010abd6f2Skiyohara 	sc->sc_rxp->m_pkthdr.len++;
391736a9db0Splunky 	sc->sc_stats.byte_rx++;
39210abd6f2Skiyohara 
39310abd6f2Skiyohara 	sc->sc_want--;
39410abd6f2Skiyohara 	if (sc->sc_want > 0)
39510abd6f2Skiyohara 		return 0;	/* want more */
39610abd6f2Skiyohara 
39710abd6f2Skiyohara 	switch (sc->sc_state) {
39810abd6f2Skiyohara 	case BTUART_RECV_PKT_TYPE:	/* Got packet type */
39910abd6f2Skiyohara 
40010abd6f2Skiyohara 		switch (c) {
40110abd6f2Skiyohara 		case HCI_ACL_DATA_PKT:
40210abd6f2Skiyohara 			sc->sc_state = BTUART_RECV_ACL_HDR;
40310abd6f2Skiyohara 			sc->sc_want = sizeof(hci_acldata_hdr_t) - 1;
40410abd6f2Skiyohara 			break;
40510abd6f2Skiyohara 
40610abd6f2Skiyohara 		case HCI_SCO_DATA_PKT:
40710abd6f2Skiyohara 			sc->sc_state = BTUART_RECV_SCO_HDR;
40810abd6f2Skiyohara 			sc->sc_want = sizeof(hci_scodata_hdr_t) - 1;
40910abd6f2Skiyohara 			break;
41010abd6f2Skiyohara 
41110abd6f2Skiyohara 		case HCI_EVENT_PKT:
41210abd6f2Skiyohara 			sc->sc_state = BTUART_RECV_EVENT_HDR;
41310abd6f2Skiyohara 			sc->sc_want = sizeof(hci_event_hdr_t) - 1;
41410abd6f2Skiyohara 			break;
41510abd6f2Skiyohara 
41610abd6f2Skiyohara 		default:
4177cca9485Splunky 			aprint_error_dev(sc->sc_dev,
4187cca9485Splunky 			    "Unknown packet type=%#x!\n", c);
419736a9db0Splunky 			sc->sc_stats.err_rx++;
42010abd6f2Skiyohara 			m_freem(sc->sc_rxp);
42110abd6f2Skiyohara 			sc->sc_rxp = NULL;
42210abd6f2Skiyohara 			return 0;	/* (lost sync) */
42310abd6f2Skiyohara 		}
42410abd6f2Skiyohara 
42510abd6f2Skiyohara 		break;
42610abd6f2Skiyohara 
42710abd6f2Skiyohara 	/*
42810abd6f2Skiyohara 	 * we assume (correctly of course :) that the packet headers all fit
42910abd6f2Skiyohara 	 * into a single pkthdr mbuf
43010abd6f2Skiyohara 	 */
43110abd6f2Skiyohara 	case BTUART_RECV_ACL_HDR:	/* Got ACL Header */
43210abd6f2Skiyohara 		sc->sc_state = BTUART_RECV_ACL_DATA;
43310abd6f2Skiyohara 		sc->sc_want = mtod(m, hci_acldata_hdr_t *)->length;
43410abd6f2Skiyohara 		sc->sc_want = le16toh(sc->sc_want);
43510abd6f2Skiyohara 		break;
43610abd6f2Skiyohara 
43710abd6f2Skiyohara 	case BTUART_RECV_SCO_HDR:	/* Got SCO Header */
43810abd6f2Skiyohara 		sc->sc_state = BTUART_RECV_SCO_DATA;
43910abd6f2Skiyohara 		sc->sc_want =  mtod(m, hci_scodata_hdr_t *)->length;
44010abd6f2Skiyohara 		break;
44110abd6f2Skiyohara 
44210abd6f2Skiyohara 	case BTUART_RECV_EVENT_HDR:	/* Got Event Header */
44310abd6f2Skiyohara 		sc->sc_state = BTUART_RECV_EVENT_DATA;
44410abd6f2Skiyohara 		sc->sc_want =  mtod(m, hci_event_hdr_t *)->length;
44510abd6f2Skiyohara 		break;
44610abd6f2Skiyohara 
44710abd6f2Skiyohara 	case BTUART_RECV_ACL_DATA:	/* ACL Packet Complete */
448486e4624Splunky 		if (!hci_input_acl(sc->sc_unit, sc->sc_rxp))
449736a9db0Splunky 			sc->sc_stats.err_rx++;
450736a9db0Splunky 
451736a9db0Splunky 		sc->sc_stats.acl_rx++;
45210abd6f2Skiyohara 		sc->sc_rxp = m = NULL;
45310abd6f2Skiyohara 		break;
45410abd6f2Skiyohara 
45510abd6f2Skiyohara 	case BTUART_RECV_SCO_DATA:	/* SCO Packet Complete */
456486e4624Splunky 		if (!hci_input_sco(sc->sc_unit, sc->sc_rxp))
457736a9db0Splunky 			sc->sc_stats.err_rx++;
458736a9db0Splunky 
459736a9db0Splunky 		sc->sc_stats.sco_rx++;
46010abd6f2Skiyohara 		sc->sc_rxp = m = NULL;
46110abd6f2Skiyohara 		break;
46210abd6f2Skiyohara 
46310abd6f2Skiyohara 	case BTUART_RECV_EVENT_DATA:	/* Event Packet Complete */
464486e4624Splunky 		if (!hci_input_event(sc->sc_unit, sc->sc_rxp))
465736a9db0Splunky 			sc->sc_stats.err_rx++;
466736a9db0Splunky 
467736a9db0Splunky 		sc->sc_stats.evt_rx++;
46810abd6f2Skiyohara 		sc->sc_rxp = m = NULL;
46910abd6f2Skiyohara 		break;
47010abd6f2Skiyohara 
47110abd6f2Skiyohara 	default:
47210abd6f2Skiyohara 		panic("%s: invalid state %d!\n",
473edb74239Splunky 		    device_xname(sc->sc_dev), sc->sc_state);
47410abd6f2Skiyohara 	}
47510abd6f2Skiyohara 
47610abd6f2Skiyohara 	return 0;
47710abd6f2Skiyohara }
47810abd6f2Skiyohara 
47910abd6f2Skiyohara static int
btuartstart(struct tty * tp)480486e4624Splunky btuartstart(struct tty *tp)
48110abd6f2Skiyohara {
482486e4624Splunky 	struct btuart_softc *sc = tp->t_sc;
48310abd6f2Skiyohara 	struct mbuf *m;
48410abd6f2Skiyohara 	int count, rlen;
48510abd6f2Skiyohara 	uint8_t *rptr;
48610abd6f2Skiyohara 
487486e4624Splunky 	if (!sc->sc_enabled)
488486e4624Splunky 		return 0;
489486e4624Splunky 
49010abd6f2Skiyohara 	m = sc->sc_txp;
49110abd6f2Skiyohara 	if (m == NULL) {
492486e4624Splunky 		if (MBUFQ_FIRST(&sc->sc_cmdq)) {
493486e4624Splunky 			MBUFQ_DEQUEUE(&sc->sc_cmdq, m);
494486e4624Splunky 			sc->sc_stats.cmd_tx++;
495486e4624Splunky 		} else if (MBUFQ_FIRST(&sc->sc_scoq)) {
496486e4624Splunky 			MBUFQ_DEQUEUE(&sc->sc_scoq, m);
497486e4624Splunky 			sc->sc_stats.sco_tx++;
498486e4624Splunky 		} else if (MBUFQ_FIRST(&sc->sc_aclq)) {
499486e4624Splunky 			MBUFQ_DEQUEUE(&sc->sc_aclq, m);
500486e4624Splunky 			sc->sc_stats.acl_tx++;
501486e4624Splunky 		} else {
502486e4624Splunky 			sc->sc_xmit = false;
503486e4624Splunky 			return 0; /* no more to send */
504486e4624Splunky 		}
505486e4624Splunky 
506486e4624Splunky 		sc->sc_txp = m;
507486e4624Splunky 		sc->sc_xmit = true;
50810abd6f2Skiyohara 	}
50910abd6f2Skiyohara 
51010abd6f2Skiyohara 	count = 0;
51110abd6f2Skiyohara 	rlen = 0;
51210abd6f2Skiyohara 	rptr = mtod(m, uint8_t *);
51310abd6f2Skiyohara 
51410abd6f2Skiyohara 	for(;;) {
51510abd6f2Skiyohara 		if (rlen >= m->m_len) {
51610abd6f2Skiyohara 			m = m->m_next;
51710abd6f2Skiyohara 			if (m == NULL) {
51810abd6f2Skiyohara 				m = sc->sc_txp;
51910abd6f2Skiyohara 				sc->sc_txp = NULL;
52010abd6f2Skiyohara 
52110abd6f2Skiyohara 				if (M_GETCTX(m, void *) == NULL)
52210abd6f2Skiyohara 					m_freem(m);
523736a9db0Splunky 				else if (!hci_complete_sco(sc->sc_unit, m))
524736a9db0Splunky 					sc->sc_stats.err_tx++;
52510abd6f2Skiyohara 
52610abd6f2Skiyohara 				break;
52710abd6f2Skiyohara 			}
52810abd6f2Skiyohara 
52910abd6f2Skiyohara 			rlen = 0;
53010abd6f2Skiyohara 			rptr = mtod(m, uint8_t *);
53110abd6f2Skiyohara 			continue;
53210abd6f2Skiyohara 		}
53310abd6f2Skiyohara 
53410abd6f2Skiyohara 		if (putc(*rptr++, &tp->t_outq) < 0) {
53510abd6f2Skiyohara 			m_adj(m, rlen);
53610abd6f2Skiyohara 			break;
53710abd6f2Skiyohara 		}
53810abd6f2Skiyohara 		rlen++;
53910abd6f2Skiyohara 		count++;
54010abd6f2Skiyohara 	}
54110abd6f2Skiyohara 
542736a9db0Splunky 	sc->sc_stats.byte_tx += count;
54310abd6f2Skiyohara 
5448817d8b1Splunky 	if (tp->t_outq.c_cc != 0 && tp->t_oproc != NULL)
54510abd6f2Skiyohara 		(*tp->t_oproc)(tp);
54610abd6f2Skiyohara 
54710abd6f2Skiyohara 	return 0;
54810abd6f2Skiyohara }
54910abd6f2Skiyohara 
550486e4624Splunky /*****************************************************************************
551486e4624Splunky  *
552486e4624Splunky  *	bluetooth(9) functions
55310abd6f2Skiyohara  */
554486e4624Splunky 
55510abd6f2Skiyohara static int
btuart_enable(device_t self)556486e4624Splunky btuart_enable(device_t self)
55710abd6f2Skiyohara {
5580b799668Splunky 	struct btuart_softc *sc = device_private(self);
559736a9db0Splunky 	int s;
56010abd6f2Skiyohara 
561486e4624Splunky 	if (sc->sc_enabled)
56210abd6f2Skiyohara 		return 0;
56310abd6f2Skiyohara 
564736a9db0Splunky 	s = spltty();
565736a9db0Splunky 
566486e4624Splunky 	sc->sc_enabled = true;
567486e4624Splunky 	sc->sc_xmit = false;
568736a9db0Splunky 
569736a9db0Splunky 	splx(s);
57010abd6f2Skiyohara 
57110abd6f2Skiyohara 	return 0;
57210abd6f2Skiyohara }
57310abd6f2Skiyohara 
57410abd6f2Skiyohara static void
btuart_disable(device_t self)575486e4624Splunky btuart_disable(device_t self)
57610abd6f2Skiyohara {
5770b799668Splunky 	struct btuart_softc *sc = device_private(self);
578736a9db0Splunky 	int s;
57910abd6f2Skiyohara 
580486e4624Splunky 	if (!sc->sc_enabled)
58110abd6f2Skiyohara 		return;
58210abd6f2Skiyohara 
583736a9db0Splunky 	s = spltty();
584736a9db0Splunky 
58510abd6f2Skiyohara 	if (sc->sc_rxp) {
58610abd6f2Skiyohara 		m_freem(sc->sc_rxp);
58710abd6f2Skiyohara 		sc->sc_rxp = NULL;
58810abd6f2Skiyohara 	}
58910abd6f2Skiyohara 
59010abd6f2Skiyohara 	if (sc->sc_txp) {
59110abd6f2Skiyohara 		m_freem(sc->sc_txp);
59210abd6f2Skiyohara 		sc->sc_txp = NULL;
59310abd6f2Skiyohara 	}
59410abd6f2Skiyohara 
595736a9db0Splunky 	MBUFQ_DRAIN(&sc->sc_cmdq);
596736a9db0Splunky 	MBUFQ_DRAIN(&sc->sc_aclq);
597736a9db0Splunky 	MBUFQ_DRAIN(&sc->sc_scoq);
598736a9db0Splunky 
599486e4624Splunky 	sc->sc_enabled = false;
600736a9db0Splunky 
601736a9db0Splunky 	splx(s);
60210abd6f2Skiyohara }
60310abd6f2Skiyohara 
60410abd6f2Skiyohara static void
btuart_output_cmd(device_t self,struct mbuf * m)605486e4624Splunky btuart_output_cmd(device_t self, struct mbuf *m)
606736a9db0Splunky {
607736a9db0Splunky 	struct btuart_softc *sc = device_private(self);
608736a9db0Splunky 	int s;
609736a9db0Splunky 
610486e4624Splunky 	KASSERT(sc->sc_enabled);
611736a9db0Splunky 
612736a9db0Splunky 	M_SETCTX(m, NULL);
613736a9db0Splunky 
614736a9db0Splunky 	s = spltty();
615736a9db0Splunky 	MBUFQ_ENQUEUE(&sc->sc_cmdq, m);
616486e4624Splunky 	if (!sc->sc_xmit)
617486e4624Splunky 		btuartstart(sc->sc_tp);
618736a9db0Splunky 
619736a9db0Splunky 	splx(s);
620736a9db0Splunky }
621736a9db0Splunky 
622736a9db0Splunky static void
btuart_output_acl(device_t self,struct mbuf * m)623486e4624Splunky btuart_output_acl(device_t self, struct mbuf *m)
624736a9db0Splunky {
625736a9db0Splunky 	struct btuart_softc *sc = device_private(self);
626736a9db0Splunky 	int s;
627736a9db0Splunky 
628486e4624Splunky 	KASSERT(sc->sc_enabled);
629736a9db0Splunky 
630736a9db0Splunky 	M_SETCTX(m, NULL);
631736a9db0Splunky 
632736a9db0Splunky 	s = spltty();
633736a9db0Splunky 	MBUFQ_ENQUEUE(&sc->sc_aclq, m);
634486e4624Splunky 	if (!sc->sc_xmit)
635486e4624Splunky 		btuartstart(sc->sc_tp);
636736a9db0Splunky 
637736a9db0Splunky 	splx(s);
638736a9db0Splunky }
639736a9db0Splunky 
640736a9db0Splunky static void
btuart_output_sco(device_t self,struct mbuf * m)641486e4624Splunky btuart_output_sco(device_t self, struct mbuf *m)
642736a9db0Splunky {
643736a9db0Splunky 	struct btuart_softc *sc = device_private(self);
644736a9db0Splunky 	int s;
645736a9db0Splunky 
646486e4624Splunky 	KASSERT(sc->sc_enabled);
647736a9db0Splunky 
648736a9db0Splunky 	s = spltty();
649736a9db0Splunky 	MBUFQ_ENQUEUE(&sc->sc_scoq, m);
650486e4624Splunky 	if (!sc->sc_xmit)
651486e4624Splunky 		btuartstart(sc->sc_tp);
652736a9db0Splunky 
653736a9db0Splunky 	splx(s);
654736a9db0Splunky }
655736a9db0Splunky 
656736a9db0Splunky static void
btuart_stats(device_t self,struct bt_stats * dest,int flush)657486e4624Splunky btuart_stats(device_t self, struct bt_stats *dest, int flush)
658736a9db0Splunky {
659736a9db0Splunky 	struct btuart_softc *sc = device_private(self);
660736a9db0Splunky 	int s;
661736a9db0Splunky 
662736a9db0Splunky 	s = spltty();
663736a9db0Splunky 
664736a9db0Splunky 	memcpy(dest, &sc->sc_stats, sizeof(struct bt_stats));
665736a9db0Splunky 
666736a9db0Splunky 	if (flush)
667736a9db0Splunky 		memset(&sc->sc_stats, 0, sizeof(struct bt_stats));
668736a9db0Splunky 
669736a9db0Splunky 	splx(s);
670736a9db0Splunky }
671