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