1 /* $NetBSD: btuart.c,v 1.22 2009/05/07 18:01:57 elad Exp $ */
4 * Copyright (c) 2006, 2007 KIYOHARA Takashi
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
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.
16 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
17 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
18 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
19 * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
20 * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
21 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
22 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
23 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
24 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
25 * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
26 * POSSIBILITY OF SUCH DAMAGE.
29 #include <sys/cdefs.h>
30 __KERNEL_RCSID(0, "$NetBSD: btuart.c,v 1.22 2009/05/07 18:01:57 elad Exp $");
32 #include <sys/param.h>
34 #include <sys/device.h>
35 #include <sys/errno.h>
36 #include <sys/fcntl.h>
37 #include <sys/kauth.h>
38 #include <sys/kernel.h>
39 #include <sys/malloc.h>
42 #include <sys/syslimits.h>
43 #include <sys/systm.h>
49 #include <netbt/bluetooth.h>
50 #include <netbt/hci.h>
56 struct tty
* sc_tp
; /* tty pointer */
58 bool sc_enabled
; /* device is enabled */
59 struct hci_unit
*sc_unit
; /* Bluetooth HCI handle */
60 struct bt_stats sc_stats
;
62 int sc_state
; /* receive state */
63 int sc_want
; /* how much we want */
64 struct mbuf
* sc_rxp
; /* incoming packet */
66 bool sc_xmit
; /* transmit is active */
67 struct mbuf
* sc_txp
; /* outgoing packet */
76 #define BTUART_RECV_PKT_TYPE 0 /* packet type */
77 #define BTUART_RECV_ACL_HDR 1 /* acl header */
78 #define BTUART_RECV_SCO_HDR 2 /* sco header */
79 #define BTUART_RECV_EVENT_HDR 3 /* event header */
80 #define BTUART_RECV_ACL_DATA 4 /* acl packet data */
81 #define BTUART_RECV_SCO_DATA 5 /* sco packet data */
82 #define BTUART_RECV_EVENT_DATA 6 /* event packet data */
84 void btuartattach(int);
85 static int btuart_match(device_t
, cfdata_t
, void *);
86 static void btuart_attach(device_t
, device_t
, void *);
87 static int btuart_detach(device_t
, int);
89 static int btuartopen(dev_t
, struct tty
*);
90 static int btuartclose(struct tty
*, int);
91 static int btuartioctl(struct tty
*, u_long
, void *, int, struct lwp
*);
92 static int btuartinput(int, struct tty
*);
93 static int btuartstart(struct tty
*);
95 static int btuart_enable(device_t
);
96 static void btuart_disable(device_t
);
97 static void btuart_output_cmd(device_t
, struct mbuf
*);
98 static void btuart_output_acl(device_t
, struct mbuf
*);
99 static void btuart_output_sco(device_t
, struct mbuf
*);
100 static void btuart_stats(device_t
, struct bt_stats
*, int);
103 * It doesn't need to be exported, as only btuartattach() uses it,
104 * but there's no "official" way to make it static.
106 CFATTACH_DECL_NEW(btuart
, sizeof(struct btuart_softc
),
107 btuart_match
, btuart_attach
, btuart_detach
, NULL
);
109 static struct linesw btuart_disc
= {
111 .l_open
= btuartopen
,
112 .l_close
= btuartclose
,
115 .l_ioctl
= btuartioctl
,
116 .l_rint
= btuartinput
,
117 .l_start
= btuartstart
,
119 .l_poll
= ttyerrpoll
,
122 static const struct hci_if btuart_hci
= {
123 .enable
= btuart_enable
,
124 .disable
= btuart_disable
,
125 .output_cmd
= btuart_output_cmd
,
126 .output_acl
= btuart_output_acl
,
127 .output_sco
= btuart_output_sco
,
128 .get_stats
= btuart_stats
,
132 /*****************************************************************************
134 * autoconf(9) functions
138 * pseudo-device attach routine.
141 btuartattach(int num __unused
)
145 error
= ttyldisc_attach(&btuart_disc
);
147 aprint_error("%s: unable to register line discipline, "
148 "error = %d\n", btuart_cd
.cd_name
, error
);
153 error
= config_cfattach_attach(btuart_cd
.cd_name
, &btuart_ca
);
155 aprint_error("%s: unable to register cfattach, error = %d\n",
156 btuart_cd
.cd_name
, error
);
158 config_cfdriver_detach(&btuart_cd
);
159 (void) ttyldisc_detach(&btuart_disc
);
164 * Autoconf match routine.
167 btuart_match(device_t self __unused
, cfdata_t cfdata __unused
,
171 /* pseudo-device; always present */
176 * Autoconf attach routine.
177 * Called by config_attach_pseudo(9) when we open the line discipline.
180 btuart_attach(device_t parent __unused
, device_t self
, void *aux __unused
)
182 struct btuart_softc
*sc
= device_private(self
);
186 MBUFQ_INIT(&sc
->sc_cmdq
);
187 MBUFQ_INIT(&sc
->sc_aclq
);
188 MBUFQ_INIT(&sc
->sc_scoq
);
190 /* Attach Bluetooth unit */
191 sc
->sc_unit
= hci_attach(&btuart_hci
, self
, 0);
192 if (sc
->sc_unit
== NULL
)
193 aprint_error_dev(self
, "HCI attach failed\n");
197 * Autoconf detach routine.
198 * Called when we close the line discipline.
201 btuart_detach(device_t self
, int flags __unused
)
203 struct btuart_softc
*sc
= device_private(self
);
205 btuart_disable(self
);
208 hci_detach(sc
->sc_unit
);
215 /*****************************************************************************
217 * Line discipline functions.
221 btuartopen(dev_t devno __unused
, struct tty
*tp
)
223 struct btuart_softc
*sc
;
226 struct lwp
*l
= curlwp
; /* XXX */
229 error
= kauth_authorize_device(l
->l_cred
, KAUTH_DEVICE_BLUETOOTH_BTUART
,
230 KAUTH_ARG(KAUTH_REQ_DEVICE_BLUETOOTH_BTUART_ADD
), NULL
, NULL
, NULL
);
236 if (tp
->t_linesw
== &btuart_disc
) {
244 KASSERT(tp
->t_oproc
!= NULL
);
246 cfdata
= malloc(sizeof(struct cfdata
), M_DEVBUF
, M_WAITOK
);
247 for (unit
= 0; unit
< btuart_cd
.cd_ndevs
; unit
++)
248 if (device_lookup(&btuart_cd
, unit
) == NULL
)
251 cfdata
->cf_name
= btuart_cd
.cd_name
;
252 cfdata
->cf_atname
= btuart_cd
.cd_name
;
253 cfdata
->cf_unit
= unit
;
254 cfdata
->cf_fstate
= FSTATE_STAR
;
256 dev
= config_attach_pseudo(cfdata
);
258 free(cfdata
, M_DEVBUF
);
262 sc
= device_private(dev
);
264 aprint_normal_dev(dev
, "major %llu minor %llu\n",
265 (unsigned long long)major(tp
->t_dev
),
266 (unsigned long long)minor(tp
->t_dev
));
271 mutex_spin_enter(&tty_lock
);
272 ttyflush(tp
, FREAD
| FWRITE
);
273 mutex_spin_exit(&tty_lock
);
281 btuartclose(struct tty
*tp
, int flag __unused
)
283 struct btuart_softc
*sc
= tp
->t_sc
;
289 mutex_spin_enter(&tty_lock
);
290 ttyflush(tp
, FREAD
| FWRITE
);
291 mutex_spin_exit(&tty_lock
); /* XXX */
293 ttyldisc_release(tp
->t_linesw
);
294 tp
->t_linesw
= ttyldisc_default();
298 if (sc
->sc_tp
== tp
) {
299 cfdata
= device_cfdata(sc
->sc_dev
);
300 config_detach(sc
->sc_dev
, 0);
301 free(cfdata
, M_DEVBUF
);
311 btuartioctl(struct tty
*tp
, u_long cmd
, void *data __unused
,
312 int flag __unused
, struct lwp
*l __unused
)
314 struct btuart_softc
*sc
= tp
->t_sc
;
317 if (sc
== NULL
|| tp
!= sc
->sc_tp
)
322 error
= EPASSTHROUGH
;
330 btuartinput(int c
, struct tty
*tp
)
332 struct btuart_softc
*sc
= tp
->t_sc
;
333 struct mbuf
*m
= sc
->sc_rxp
;
341 /* If we already started a packet, find the trailing end of it. */
346 space
= M_TRAILINGSPACE(m
);
352 MGETHDR(m
, M_DONTWAIT
, MT_DATA
);
354 aprint_error_dev(sc
->sc_dev
,
356 sc
->sc_stats
.err_rx
++;
357 return 0; /* (lost sync) */
361 m
->m_pkthdr
.len
= m
->m_len
= 0;
364 sc
->sc_state
= BTUART_RECV_PKT_TYPE
;
368 MGET(m
->m_next
, M_DONTWAIT
, MT_DATA
);
369 if (m
->m_next
== NULL
) {
370 aprint_error_dev(sc
->sc_dev
,
372 sc
->sc_stats
.err_rx
++;
373 return 0; /* (lost sync) */
380 if (sc
->sc_want
> MINCLSIZE
) {
381 MCLGET(m
, M_DONTWAIT
);
382 if (m
->m_flags
& M_EXT
)
388 mtod(m
, uint8_t *)[m
->m_len
++] = c
;
389 sc
->sc_rxp
->m_pkthdr
.len
++;
390 sc
->sc_stats
.byte_rx
++;
394 return 0; /* want more */
396 switch (sc
->sc_state
) {
397 case BTUART_RECV_PKT_TYPE
: /* Got packet type */
400 case HCI_ACL_DATA_PKT
:
401 sc
->sc_state
= BTUART_RECV_ACL_HDR
;
402 sc
->sc_want
= sizeof(hci_acldata_hdr_t
) - 1;
405 case HCI_SCO_DATA_PKT
:
406 sc
->sc_state
= BTUART_RECV_SCO_HDR
;
407 sc
->sc_want
= sizeof(hci_scodata_hdr_t
) - 1;
411 sc
->sc_state
= BTUART_RECV_EVENT_HDR
;
412 sc
->sc_want
= sizeof(hci_event_hdr_t
) - 1;
416 aprint_error_dev(sc
->sc_dev
,
417 "Unknown packet type=%#x!\n", c
);
418 sc
->sc_stats
.err_rx
++;
421 return 0; /* (lost sync) */
427 * we assume (correctly of course :) that the packet headers all fit
428 * into a single pkthdr mbuf
430 case BTUART_RECV_ACL_HDR
: /* Got ACL Header */
431 sc
->sc_state
= BTUART_RECV_ACL_DATA
;
432 sc
->sc_want
= mtod(m
, hci_acldata_hdr_t
*)->length
;
433 sc
->sc_want
= le16toh(sc
->sc_want
);
436 case BTUART_RECV_SCO_HDR
: /* Got SCO Header */
437 sc
->sc_state
= BTUART_RECV_SCO_DATA
;
438 sc
->sc_want
= mtod(m
, hci_scodata_hdr_t
*)->length
;
441 case BTUART_RECV_EVENT_HDR
: /* Got Event Header */
442 sc
->sc_state
= BTUART_RECV_EVENT_DATA
;
443 sc
->sc_want
= mtod(m
, hci_event_hdr_t
*)->length
;
446 case BTUART_RECV_ACL_DATA
: /* ACL Packet Complete */
447 if (!hci_input_acl(sc
->sc_unit
, sc
->sc_rxp
))
448 sc
->sc_stats
.err_rx
++;
450 sc
->sc_stats
.acl_rx
++;
451 sc
->sc_rxp
= m
= NULL
;
454 case BTUART_RECV_SCO_DATA
: /* SCO Packet Complete */
455 if (!hci_input_sco(sc
->sc_unit
, sc
->sc_rxp
))
456 sc
->sc_stats
.err_rx
++;
458 sc
->sc_stats
.sco_rx
++;
459 sc
->sc_rxp
= m
= NULL
;
462 case BTUART_RECV_EVENT_DATA
: /* Event Packet Complete */
463 if (!hci_input_event(sc
->sc_unit
, sc
->sc_rxp
))
464 sc
->sc_stats
.err_rx
++;
466 sc
->sc_stats
.evt_rx
++;
467 sc
->sc_rxp
= m
= NULL
;
471 panic("%s: invalid state %d!\n",
472 device_xname(sc
->sc_dev
), sc
->sc_state
);
479 btuartstart(struct tty
*tp
)
481 struct btuart_softc
*sc
= tp
->t_sc
;
491 if (MBUFQ_FIRST(&sc
->sc_cmdq
)) {
492 MBUFQ_DEQUEUE(&sc
->sc_cmdq
, m
);
493 sc
->sc_stats
.cmd_tx
++;
494 } else if (MBUFQ_FIRST(&sc
->sc_scoq
)) {
495 MBUFQ_DEQUEUE(&sc
->sc_scoq
, m
);
496 sc
->sc_stats
.sco_tx
++;
497 } else if (MBUFQ_FIRST(&sc
->sc_aclq
)) {
498 MBUFQ_DEQUEUE(&sc
->sc_aclq
, m
);
499 sc
->sc_stats
.acl_tx
++;
502 return 0; /* no more to send */
511 rptr
= mtod(m
, uint8_t *);
514 if (rlen
>= m
->m_len
) {
520 if (M_GETCTX(m
, void *) == NULL
)
522 else if (!hci_complete_sco(sc
->sc_unit
, m
))
523 sc
->sc_stats
.err_tx
++;
529 rptr
= mtod(m
, uint8_t *);
533 if (putc(*rptr
++, &tp
->t_outq
) < 0) {
541 sc
->sc_stats
.byte_tx
+= count
;
543 if (tp
->t_outq
.c_cc
!= 0)
549 /*****************************************************************************
551 * bluetooth(9) functions
555 btuart_enable(device_t self
)
557 struct btuart_softc
*sc
= device_private(self
);
565 sc
->sc_enabled
= true;
574 btuart_disable(device_t self
)
576 struct btuart_softc
*sc
= device_private(self
);
594 MBUFQ_DRAIN(&sc
->sc_cmdq
);
595 MBUFQ_DRAIN(&sc
->sc_aclq
);
596 MBUFQ_DRAIN(&sc
->sc_scoq
);
598 sc
->sc_enabled
= false;
604 btuart_output_cmd(device_t self
, struct mbuf
*m
)
606 struct btuart_softc
*sc
= device_private(self
);
609 KASSERT(sc
->sc_enabled
);
614 MBUFQ_ENQUEUE(&sc
->sc_cmdq
, m
);
616 btuartstart(sc
->sc_tp
);
622 btuart_output_acl(device_t self
, struct mbuf
*m
)
624 struct btuart_softc
*sc
= device_private(self
);
627 KASSERT(sc
->sc_enabled
);
632 MBUFQ_ENQUEUE(&sc
->sc_aclq
, m
);
634 btuartstart(sc
->sc_tp
);
640 btuart_output_sco(device_t self
, struct mbuf
*m
)
642 struct btuart_softc
*sc
= device_private(self
);
645 KASSERT(sc
->sc_enabled
);
648 MBUFQ_ENQUEUE(&sc
->sc_scoq
, m
);
650 btuartstart(sc
->sc_tp
);
656 btuart_stats(device_t self
, struct bt_stats
*dest
, int flush
)
658 struct btuart_softc
*sc
= device_private(self
);
663 memcpy(dest
, &sc
->sc_stats
, sizeof(struct bt_stats
));
666 memset(&sc
->sc_stats
, 0, sizeof(struct bt_stats
));