xref: /netbsd-src/sys/dev/pcmcia/btbc.c (revision 481d3881954fd794ca5f2d880b68c53a5db8620e)
1*481d3881Srin /*	$NetBSD: btbc.c,v 1.18 2024/07/05 04:31:51 rin Exp $	*/
210448cd5Skiyohara /*
310448cd5Skiyohara  * Copyright (c) 2007 KIYOHARA Takashi
410448cd5Skiyohara  * All rights reserved.
510448cd5Skiyohara  *
610448cd5Skiyohara  * Redistribution and use in source and binary forms, with or without
710448cd5Skiyohara  * modification, are permitted provided that the following conditions
810448cd5Skiyohara  * are met:
910448cd5Skiyohara  * 1. Redistributions of source code must retain the above copyright
1010448cd5Skiyohara  *    notice, this list of conditions and the following disclaimer.
1110448cd5Skiyohara  * 2. Redistributions in binary form must reproduce the above copyright
1210448cd5Skiyohara  *    notice, this list of conditions and the following disclaimer in the
1310448cd5Skiyohara  *    documentation and/or other materials provided with the distribution.
1410448cd5Skiyohara  *
1510448cd5Skiyohara  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
1610448cd5Skiyohara  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
1710448cd5Skiyohara  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
1810448cd5Skiyohara  * DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
1910448cd5Skiyohara  * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
2010448cd5Skiyohara  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
2110448cd5Skiyohara  * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2210448cd5Skiyohara  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
2310448cd5Skiyohara  * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
2410448cd5Skiyohara  * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
2510448cd5Skiyohara  * POSSIBILITY OF SUCH DAMAGE.
2610448cd5Skiyohara  */
2710448cd5Skiyohara /*
2810448cd5Skiyohara  * This driver is support to the AnyCom BlueCard.  written with reference to
2910448cd5Skiyohara  * Linux driver: (drivers/bluetooth/bluecard_cs.c)
3010448cd5Skiyohara  */
3110448cd5Skiyohara 
3210448cd5Skiyohara #include <sys/cdefs.h>
33*481d3881Srin __KERNEL_RCSID(0, "$NetBSD: btbc.c,v 1.18 2024/07/05 04:31:51 rin Exp $");
3410448cd5Skiyohara 
3510448cd5Skiyohara #include <sys/param.h>
3610448cd5Skiyohara #include <sys/callout.h>
3710448cd5Skiyohara #include <sys/device.h>
3810448cd5Skiyohara #include <sys/errno.h>
3910448cd5Skiyohara #include <sys/kernel.h>
4010448cd5Skiyohara #include <sys/mbuf.h>
4110448cd5Skiyohara #include <sys/proc.h>
4210448cd5Skiyohara 
43a2a38285Sad #include <sys/bus.h>
44a2a38285Sad #include <sys/intr.h>
4510448cd5Skiyohara 
4610448cd5Skiyohara #include <dev/pcmcia/pcmciareg.h>
4710448cd5Skiyohara #include <dev/pcmcia/pcmciavar.h>
4810448cd5Skiyohara #include <dev/pcmcia/pcmciadevs.h>
4910448cd5Skiyohara 
5010448cd5Skiyohara #include <netbt/bluetooth.h>
5110448cd5Skiyohara #include <netbt/hci.h>
5210448cd5Skiyohara 
5310448cd5Skiyohara #include <dev/pcmcia/bluecardreg.h>
5410448cd5Skiyohara 
5510448cd5Skiyohara 
5610448cd5Skiyohara /* sc_state */				/* receiving */
5710448cd5Skiyohara #define BTBC_RECV_PKT_TYPE	0		/* packet type */
5810448cd5Skiyohara #define BTBC_RECV_ACL_HDR	1		/* acl header */
5910448cd5Skiyohara #define BTBC_RECV_SCO_HDR	2		/* sco header */
6010448cd5Skiyohara #define BTBC_RECV_EVENT_HDR	3		/* event header */
6110448cd5Skiyohara #define BTBC_RECV_ACL_DATA	4		/* acl packet data */
6210448cd5Skiyohara #define BTBC_RECV_SCO_DATA	5		/* sco packet data */
6310448cd5Skiyohara #define BTBC_RECV_EVENT_DATA	6		/* event packet data */
6410448cd5Skiyohara 
6510448cd5Skiyohara /* sc_flags */
66736a9db0Splunky #define BTBC_XMIT		(1 << 1)	/* transmit active */
67736a9db0Splunky #define BTBC_ENABLED		(1 << 2)	/* is enabled */
6810448cd5Skiyohara 
6910448cd5Skiyohara /* Default baud rate: 57600, 115200, 230400 or 460800 */
70a6caad52Skiyohara #ifndef BTBC_DEFAULT_BAUDRATE
713c75d069Skiyohara #define BTBC_DEFAULT_BAUDRATE	57600
72a6caad52Skiyohara #endif
7310448cd5Skiyohara 
7410448cd5Skiyohara struct btbc_softc {
75edb74239Splunky 	device_t sc_dev;
7610448cd5Skiyohara 
7710448cd5Skiyohara 	struct pcmcia_function *sc_pf;		/* our PCMCIA function */
7810448cd5Skiyohara 	struct pcmcia_io_handle sc_pcioh;	/* PCMCIA i/o space info */
7910448cd5Skiyohara 	int sc_flags;				/* flags */
8010448cd5Skiyohara 
81736a9db0Splunky 	struct hci_unit *sc_unit;		/* Bluetooth HCI Unit */
82736a9db0Splunky 	struct bt_stats sc_stats;		/* HCI stats */
8310448cd5Skiyohara 
8410448cd5Skiyohara 	/* hardware interrupt */
8510448cd5Skiyohara 	void *sc_intr;				/* cookie */
8610448cd5Skiyohara 	int sc_state;				/* receive state */
8710448cd5Skiyohara 	int sc_want;				/* how much we want */
8810448cd5Skiyohara 	struct mbuf *sc_rxp;			/* incoming packet */
8910448cd5Skiyohara 	struct mbuf *sc_txp;			/* outgoing packet */
9010448cd5Skiyohara 	int sc_txstate;
9110448cd5Skiyohara #define TXBUF1_EMPTY	(1 << 0)
9210448cd5Skiyohara #define TXBUF2_EMPTY	(1 << 1)
9310448cd5Skiyohara #define TXBUF_MASK	(1 << 2)
9410448cd5Skiyohara 
95736a9db0Splunky 	/* output queues */
96736a9db0Splunky 	MBUFQ_HEAD()	sc_cmdq;
97736a9db0Splunky 	MBUFQ_HEAD()	sc_aclq;
98736a9db0Splunky 	MBUFQ_HEAD()	sc_scoq;
99736a9db0Splunky 
10010448cd5Skiyohara 	callout_t sc_ledch;			/* callout handler for LED */
10110448cd5Skiyohara 	uint8_t sc_ctrlreg;			/* value for control register */
10210448cd5Skiyohara };
10310448cd5Skiyohara 
104d16a259fScegger static int btbc_match(device_t, cfdata_t, void *);
105edb74239Splunky static void btbc_attach(device_t, device_t, void *);
106edb74239Splunky static int btbc_detach(device_t, int);
107c1b390d4Sdyoung static bool btbc_suspend(device_t, const pmf_qual_t *);
108c1b390d4Sdyoung static bool btbc_resume(device_t, const pmf_qual_t *);
10910448cd5Skiyohara 
11010448cd5Skiyohara static void btbc_activity_led_timeout(void *);
11110448cd5Skiyohara static void btbc_enable_activity_led(struct btbc_softc *);
11210448cd5Skiyohara static int btbc_read(struct btbc_softc *, uint32_t, uint8_t *, int);
11310448cd5Skiyohara static int btbc_write(struct btbc_softc *, uint32_t, uint8_t *, int);
11410448cd5Skiyohara static int btbc_set_baudrate(struct btbc_softc *, int);
11510448cd5Skiyohara static void btbc_receive(struct btbc_softc *, uint32_t);
11610448cd5Skiyohara static void btbc_transmit(struct btbc_softc *);
11710448cd5Skiyohara static int btbc_intr(void *);
118736a9db0Splunky static void btbc_start(struct btbc_softc *);
11910448cd5Skiyohara 
1200b799668Splunky static int btbc_enable(device_t);
1210b799668Splunky static void btbc_disable(device_t);
122736a9db0Splunky static void btbc_output_cmd(device_t, struct mbuf *);
123736a9db0Splunky static void btbc_output_acl(device_t, struct mbuf *);
124736a9db0Splunky static void btbc_output_sco(device_t, struct mbuf *);
125736a9db0Splunky static void btbc_stats(device_t, struct bt_stats *, int);
12610448cd5Skiyohara 
127edb74239Splunky CFATTACH_DECL_NEW(btbc, sizeof(struct btbc_softc),
12841358e9bSplunky     btbc_match, btbc_attach, btbc_detach, NULL);
12910448cd5Skiyohara 
130736a9db0Splunky static const struct hci_if btbc_hci = {
131736a9db0Splunky 	.enable = btbc_enable,
132736a9db0Splunky 	.disable = btbc_disable,
133736a9db0Splunky 	.output_cmd = btbc_output_cmd,
134736a9db0Splunky 	.output_acl = btbc_output_acl,
135736a9db0Splunky 	.output_sco = btbc_output_sco,
136736a9db0Splunky 	.get_stats = btbc_stats,
137736a9db0Splunky 	.ipl = IPL_TTY,
138736a9db0Splunky };
13910448cd5Skiyohara 
14010448cd5Skiyohara /* ARGSUSED */
14110448cd5Skiyohara static int
btbc_match(device_t parent,cfdata_t match,void * aux)142d16a259fScegger btbc_match(device_t parent, cfdata_t match, void *aux)
14310448cd5Skiyohara {
14410448cd5Skiyohara 	struct pcmcia_attach_args *pa = aux;
14510448cd5Skiyohara 
14610448cd5Skiyohara 	if (pa->manufacturer == PCMCIA_VENDOR_ANYCOM)
14710448cd5Skiyohara 		if ((pa->product == PCMCIA_PRODUCT_ANYCOM_LSE041) ||
14810448cd5Skiyohara 		    (pa->product == PCMCIA_PRODUCT_ANYCOM_LSE039) ||
14910448cd5Skiyohara 		    (pa->product == PCMCIA_PRODUCT_ANYCOM_LSE139))
15010448cd5Skiyohara 			return 1;
15110448cd5Skiyohara 	return 0;
15210448cd5Skiyohara }
15310448cd5Skiyohara 
15410448cd5Skiyohara static int
btbc_pcmcia_validate_config(struct pcmcia_config_entry * cfe)15510448cd5Skiyohara btbc_pcmcia_validate_config(struct pcmcia_config_entry *cfe)
15610448cd5Skiyohara {
15710448cd5Skiyohara 
15810448cd5Skiyohara 	if (cfe->iftype != PCMCIA_IFTYPE_IO ||
15910448cd5Skiyohara 	    cfe->num_iospace < 1 || cfe->num_iospace > 2)
16010448cd5Skiyohara 		return EINVAL;
16110448cd5Skiyohara 	return 0;
16210448cd5Skiyohara }
16310448cd5Skiyohara 
16410448cd5Skiyohara /* ARGSUSED */
16510448cd5Skiyohara static void
btbc_attach(device_t parent,device_t self,void * aux)166edb74239Splunky btbc_attach(device_t parent, device_t self, void *aux)
16710448cd5Skiyohara {
1680b799668Splunky 	struct btbc_softc *sc = device_private(self);
16910448cd5Skiyohara 	struct pcmcia_attach_args *pa = aux;
17010448cd5Skiyohara 	struct pcmcia_config_entry *cfe;
17110448cd5Skiyohara 	int error;
17210448cd5Skiyohara 
173edb74239Splunky 	sc->sc_dev = self;
17410448cd5Skiyohara 	sc->sc_pf = pa->pf;
17510448cd5Skiyohara 
176736a9db0Splunky 	MBUFQ_INIT(&sc->sc_cmdq);
177736a9db0Splunky 	MBUFQ_INIT(&sc->sc_aclq);
178736a9db0Splunky 	MBUFQ_INIT(&sc->sc_scoq);
179736a9db0Splunky 
18010448cd5Skiyohara 	if ((error = pcmcia_function_configure(pa->pf,
18110448cd5Skiyohara 	    btbc_pcmcia_validate_config)) != 0) {
1827cca9485Splunky 		aprint_error_dev(self, "configure failed, error=%d\n", error);
18310448cd5Skiyohara 		return;
18410448cd5Skiyohara 	}
18510448cd5Skiyohara 
18610448cd5Skiyohara 	cfe = pa->pf->cfe;
18710448cd5Skiyohara 	sc->sc_pcioh = cfe->iospace[0].handle;
18810448cd5Skiyohara 
18910448cd5Skiyohara 	/* Attach Bluetooth unit */
19056a73a7dSrmind 	sc->sc_unit = hci_attach_pcb(&btbc_hci, self, 0);
191587a6627Splunky 	if (sc->sc_unit == NULL)
192587a6627Splunky 		aprint_error_dev(self, "HCI attach failed\n");
19310448cd5Skiyohara 
194587a6627Splunky 	if (!pmf_device_register(self, btbc_suspend, btbc_resume))
195587a6627Splunky 		aprint_error_dev(self, "couldn't establish power handler\n");
19610448cd5Skiyohara 
19710448cd5Skiyohara 	callout_init(&sc->sc_ledch, 0);
198faccfc33Splunky 	callout_setfunc(&sc->sc_ledch, btbc_activity_led_timeout, sc);
19910448cd5Skiyohara 
20010448cd5Skiyohara 	return;
20110448cd5Skiyohara }
20210448cd5Skiyohara 
20310448cd5Skiyohara /* ARGSUSED */
20410448cd5Skiyohara static int
btbc_detach(device_t self,int flags)205edb74239Splunky btbc_detach(device_t self, int flags)
20610448cd5Skiyohara {
2070b799668Splunky 	struct btbc_softc *sc = device_private(self);
20810448cd5Skiyohara 	int err = 0;
20910448cd5Skiyohara 
210587a6627Splunky 	pmf_device_deregister(self);
2110b799668Splunky 	btbc_disable(sc->sc_dev);
21210448cd5Skiyohara 
213433c9fcdSozaki-r 	callout_halt(&sc->sc_ledch, NULL);
214faccfc33Splunky 	callout_destroy(&sc->sc_ledch);
21510448cd5Skiyohara 
216736a9db0Splunky 	if (sc->sc_unit) {
21756a73a7dSrmind 		hci_detach_pcb(sc->sc_unit);
218736a9db0Splunky 		sc->sc_unit = NULL;
219736a9db0Splunky 	}
22010448cd5Skiyohara 
22110448cd5Skiyohara 	pcmcia_function_unconfigure(sc->sc_pf);
22210448cd5Skiyohara 
22310448cd5Skiyohara 	return err;
22410448cd5Skiyohara }
22510448cd5Skiyohara 
226587a6627Splunky static bool
btbc_suspend(device_t self,const pmf_qual_t * qual)227c1b390d4Sdyoung btbc_suspend(device_t self, const pmf_qual_t *qual)
22810448cd5Skiyohara {
229587a6627Splunky 	struct btbc_softc *sc = device_private(self);
23010448cd5Skiyohara 
231736a9db0Splunky 	if (sc->sc_unit) {
23256a73a7dSrmind 		hci_detach_pcb(sc->sc_unit);
233736a9db0Splunky 		sc->sc_unit = NULL;
234736a9db0Splunky 	}
23510448cd5Skiyohara 
236587a6627Splunky 	return true;
23710448cd5Skiyohara }
23810448cd5Skiyohara 
239587a6627Splunky 
240587a6627Splunky static bool
btbc_resume(device_t self,const pmf_qual_t * qual)241c1b390d4Sdyoung btbc_resume(device_t self, const pmf_qual_t *qual)
242587a6627Splunky {
243587a6627Splunky 	struct btbc_softc *sc = device_private(self);
244587a6627Splunky 
245587a6627Splunky 	KASSERT(sc->sc_unit == NULL);
24610448cd5Skiyohara 
24756a73a7dSrmind 	sc->sc_unit = hci_attach_pcb(&btbc_hci, sc->sc_dev, 0);
248587a6627Splunky 	if (sc->sc_unit == NULL)
249587a6627Splunky 		return false;
25010448cd5Skiyohara 
251587a6627Splunky 	return true;
25210448cd5Skiyohara }
25310448cd5Skiyohara 
25410448cd5Skiyohara static void
btbc_activity_led_timeout(void * arg)25510448cd5Skiyohara btbc_activity_led_timeout(void *arg)
25610448cd5Skiyohara {
25710448cd5Skiyohara 	struct btbc_softc *sc = arg;
25810448cd5Skiyohara 	uint8_t id;
25910448cd5Skiyohara 
26010448cd5Skiyohara 	id = bus_space_read_1(sc->sc_pcioh.iot, sc->sc_pcioh.ioh,
26110448cd5Skiyohara 	    BLUECARD_LEDCONTROL);
26210448cd5Skiyohara 	if (id & 0x20)
26310448cd5Skiyohara 		/* Disable activity LED */
26410448cd5Skiyohara 		bus_space_write_1(sc->sc_pcioh.iot, sc->sc_pcioh.ioh,
26510448cd5Skiyohara 		    BLUECARD_LEDCONTROL, 0x08 | 0x20);
26610448cd5Skiyohara 	else
26710448cd5Skiyohara 		/* Disable power LED */
26810448cd5Skiyohara 		bus_space_write_1(sc->sc_pcioh.iot, sc->sc_pcioh.ioh,
26910448cd5Skiyohara 		    BLUECARD_LEDCONTROL, 0x00);
27010448cd5Skiyohara }
27110448cd5Skiyohara 
27210448cd5Skiyohara static void
btbc_enable_activity_led(struct btbc_softc * sc)27310448cd5Skiyohara btbc_enable_activity_led(struct btbc_softc *sc)
27410448cd5Skiyohara {
27510448cd5Skiyohara 	uint8_t id;
27610448cd5Skiyohara 
27710448cd5Skiyohara 	id = bus_space_read_1(sc->sc_pcioh.iot, sc->sc_pcioh.ioh,
27810448cd5Skiyohara 	    BLUECARD_LEDCONTROL);
27910448cd5Skiyohara 	if (id & 0x20) {
28010448cd5Skiyohara 		/* Enable activity LED */
28110448cd5Skiyohara 		bus_space_write_1(sc->sc_pcioh.iot, sc->sc_pcioh.ioh,
28210448cd5Skiyohara 		    BLUECARD_LEDCONTROL, 0x10 | 0x40);
28310448cd5Skiyohara 
28410448cd5Skiyohara 		/* Stop the LED after hz/4 */
285faccfc33Splunky 		callout_schedule(&sc->sc_ledch, hz / 4);
28610448cd5Skiyohara 	} else {
28710448cd5Skiyohara 		/* Enable power LED */
28810448cd5Skiyohara 		bus_space_write_1(sc->sc_pcioh.iot, sc->sc_pcioh.ioh,
28910448cd5Skiyohara 		    BLUECARD_LEDCONTROL, 0x08 | 0x20);
29010448cd5Skiyohara 
29110448cd5Skiyohara 		/* Stop the LED after HZ/2 */
292faccfc33Splunky 		callout_schedule(&sc->sc_ledch, hz / 2);
29310448cd5Skiyohara 	}
29410448cd5Skiyohara }
29510448cd5Skiyohara 
29610448cd5Skiyohara static int
btbc_read(struct btbc_softc * sc,uint32_t offset,uint8_t * buf,int buflen)29710448cd5Skiyohara btbc_read(struct btbc_softc *sc, uint32_t offset, uint8_t *buf, int buflen)
29810448cd5Skiyohara {
29910448cd5Skiyohara 	int i, n, len;
30010448cd5Skiyohara 
30110448cd5Skiyohara 	bus_space_write_1(sc->sc_pcioh.iot, sc->sc_pcioh.ioh,
30210448cd5Skiyohara 	    BLUECARD_COMMAND, BLUECARD_COMMAND_RXWIN1);
30310448cd5Skiyohara 	len = bus_space_read_1(sc->sc_pcioh.iot, sc->sc_pcioh.ioh, offset);
30410448cd5Skiyohara 
30510448cd5Skiyohara 	n = 0;
30610448cd5Skiyohara 	i = 1;
30710448cd5Skiyohara 	while (n < len) {
30810448cd5Skiyohara 		if (i == 16) {
30910448cd5Skiyohara 			bus_space_write_1(sc->sc_pcioh.iot, sc->sc_pcioh.ioh,
31010448cd5Skiyohara 			    BLUECARD_COMMAND, BLUECARD_COMMAND_RXWIN2);
31110448cd5Skiyohara 			i = 0;
31210448cd5Skiyohara 		}
31310448cd5Skiyohara 
31410448cd5Skiyohara 		buf[n] = bus_space_read_1(sc->sc_pcioh.iot, sc->sc_pcioh.ioh,
31510448cd5Skiyohara 		    offset + i);
31610448cd5Skiyohara 		i++;
31710448cd5Skiyohara 		if (++n > buflen)
31810448cd5Skiyohara 			break;
31910448cd5Skiyohara 	}
32010448cd5Skiyohara 	return len;
32110448cd5Skiyohara }
32210448cd5Skiyohara 
32310448cd5Skiyohara static int
btbc_write(struct btbc_softc * sc,uint32_t offset,uint8_t * buf,int buflen)32410448cd5Skiyohara btbc_write(struct btbc_softc *sc, uint32_t offset, uint8_t *buf, int buflen)
32510448cd5Skiyohara {
32610448cd5Skiyohara         int i, actual;
32710448cd5Skiyohara 
32810448cd5Skiyohara 	actual = (buflen > 15) ? 15 : buflen;
32910448cd5Skiyohara 	bus_space_write_1(sc->sc_pcioh.iot, sc->sc_pcioh.ioh, offset, actual);
33010448cd5Skiyohara 	for (i = 0; i < actual; i++)
33110448cd5Skiyohara 		bus_space_write_1(sc->sc_pcioh.iot, sc->sc_pcioh.ioh,
33210448cd5Skiyohara 		    offset + i + 1, buf[i]);
33310448cd5Skiyohara 	return actual;
33410448cd5Skiyohara }
33510448cd5Skiyohara 
33610448cd5Skiyohara /*
33710448cd5Skiyohara  * send Ericsson baud rate command
33810448cd5Skiyohara  */
33910448cd5Skiyohara static int
btbc_set_baudrate(struct btbc_softc * sc,int baud)34010448cd5Skiyohara btbc_set_baudrate(struct btbc_softc *sc, int baud)
34110448cd5Skiyohara {
34210448cd5Skiyohara 	hci_cmd_hdr_t *p;
34310448cd5Skiyohara 	struct mbuf *m;
34410448cd5Skiyohara 	const uint16_t opcode = htole16(HCI_CMD_ERICSSON_SET_UART_BAUD_RATE);
34510448cd5Skiyohara 	uint8_t param;
34610448cd5Skiyohara 
34710448cd5Skiyohara 	m = m_gethdr(M_WAIT, MT_DATA);
34810448cd5Skiyohara 
34910448cd5Skiyohara 	switch (baud) {
35010448cd5Skiyohara 	case 460800:
35110448cd5Skiyohara 		param = 0x00;
35210448cd5Skiyohara 		break;
35310448cd5Skiyohara 
35410448cd5Skiyohara 	case 230400:
35510448cd5Skiyohara 		param = 0x01;
35610448cd5Skiyohara 		break;
35710448cd5Skiyohara 
35810448cd5Skiyohara         case 115200:
35910448cd5Skiyohara 		param = 0x02;
36010448cd5Skiyohara 		break;
36110448cd5Skiyohara 
36210448cd5Skiyohara 	case 57600:
36310448cd5Skiyohara 	default:
36410448cd5Skiyohara 		param = 0x03;
36510448cd5Skiyohara 		break;
36610448cd5Skiyohara 	}
36710448cd5Skiyohara 
36810448cd5Skiyohara 	p = mtod(m, hci_cmd_hdr_t *);
36910448cd5Skiyohara 	p->type = HCI_CMD_PKT;
37010448cd5Skiyohara 	p->opcode = opcode;
37110448cd5Skiyohara 	p->length = sizeof(param);
37210448cd5Skiyohara 	m->m_pkthdr.len = m->m_len = sizeof(hci_cmd_hdr_t);
37310448cd5Skiyohara 	m_copyback(m, sizeof(hci_cmd_hdr_t), p->length, &param);
37410448cd5Skiyohara 
375736a9db0Splunky 	btbc_output_cmd(sc->sc_dev, m);
37610448cd5Skiyohara 	return 0;
37710448cd5Skiyohara }
37810448cd5Skiyohara 
37910448cd5Skiyohara static void
btbc_receive(struct btbc_softc * sc,uint32_t offset)38010448cd5Skiyohara btbc_receive(struct btbc_softc *sc, uint32_t offset)
38110448cd5Skiyohara {
38210448cd5Skiyohara 	struct mbuf *m = sc->sc_rxp;
38310448cd5Skiyohara 	int count, space = 0, i;
38410448cd5Skiyohara 	uint8_t buf[31];
38510448cd5Skiyohara 
38610448cd5Skiyohara 	btbc_enable_activity_led(sc);
38710448cd5Skiyohara 
38810448cd5Skiyohara 	/*
38910448cd5Skiyohara 	 * If we already started a packet, find the
39010448cd5Skiyohara 	 * trailing end of it.
39110448cd5Skiyohara 	 */
39210448cd5Skiyohara 	if (m) {
39310448cd5Skiyohara 		while (m->m_next)
39410448cd5Skiyohara 			m = m->m_next;
39510448cd5Skiyohara 
39610448cd5Skiyohara 		space = M_TRAILINGSPACE(m);
39710448cd5Skiyohara 	}
39810448cd5Skiyohara 
39910448cd5Skiyohara 	count = btbc_read(sc, offset, buf, sizeof(buf));
40010448cd5Skiyohara 	i = 0;
40110448cd5Skiyohara 
40210448cd5Skiyohara 	while (i < count) {
40310448cd5Skiyohara 		if (space == 0) {
40410448cd5Skiyohara 			if (m == NULL) {
40510448cd5Skiyohara 				/* new packet */
40610448cd5Skiyohara 				MGETHDR(m, M_DONTWAIT, MT_DATA);
40710448cd5Skiyohara 				if (m == NULL) {
4087cca9485Splunky 					aprint_error_dev(sc->sc_dev,
4097cca9485Splunky 					    "out of memory\n");
410736a9db0Splunky 					sc->sc_stats.err_rx++;
41110448cd5Skiyohara 					return;		/* (lost sync) */
41210448cd5Skiyohara 				}
41310448cd5Skiyohara 
41410448cd5Skiyohara 				sc->sc_rxp = m;
41510448cd5Skiyohara 				m->m_pkthdr.len = m->m_len = 0;
41610448cd5Skiyohara 				space = MHLEN;
41710448cd5Skiyohara 
41810448cd5Skiyohara 				sc->sc_state = BTBC_RECV_PKT_TYPE;
41910448cd5Skiyohara 				sc->sc_want = 1;
42010448cd5Skiyohara 			} else {
42110448cd5Skiyohara 				/* extend mbuf */
42210448cd5Skiyohara 				MGET(m->m_next, M_DONTWAIT, MT_DATA);
42310448cd5Skiyohara 				if (m->m_next == NULL) {
4247cca9485Splunky 					aprint_error_dev(sc->sc_dev,
4257cca9485Splunky 					    "out of memory\n");
426736a9db0Splunky 					sc->sc_stats.err_rx++;
42710448cd5Skiyohara 					return;		/* (lost sync) */
42810448cd5Skiyohara 				}
42910448cd5Skiyohara 
43010448cd5Skiyohara 				m = m->m_next;
43110448cd5Skiyohara 				m->m_len = 0;
43210448cd5Skiyohara 				space = MLEN;
43310448cd5Skiyohara 
43410448cd5Skiyohara 				if (sc->sc_want > MINCLSIZE) {
43510448cd5Skiyohara 					MCLGET(m, M_DONTWAIT);
43610448cd5Skiyohara 					if (m->m_flags & M_EXT)
43710448cd5Skiyohara 						space = MCLBYTES;
43810448cd5Skiyohara 				}
43910448cd5Skiyohara 			}
44010448cd5Skiyohara 		}
44110448cd5Skiyohara 
44210448cd5Skiyohara 		mtod(m, uint8_t *)[m->m_len++] = buf[i];
44310448cd5Skiyohara 		space--;
44410448cd5Skiyohara 		sc->sc_rxp->m_pkthdr.len++;
445736a9db0Splunky 		sc->sc_stats.byte_rx++;
44610448cd5Skiyohara 
44710448cd5Skiyohara 		sc->sc_want--;
44810448cd5Skiyohara 		if (sc->sc_want > 0) {
44910448cd5Skiyohara 			i++;
45010448cd5Skiyohara 			continue; /* want more */
45110448cd5Skiyohara 		}
45210448cd5Skiyohara 
45310448cd5Skiyohara 		switch (sc->sc_state) {
45410448cd5Skiyohara 		case BTBC_RECV_PKT_TYPE:		/* Got packet type */
45510448cd5Skiyohara 			switch (buf[i]) {
45610448cd5Skiyohara 			case 0x00:	/* init packet */
45710448cd5Skiyohara 				m_freem(sc->sc_rxp);
45810448cd5Skiyohara 				sc->sc_rxp = NULL;
45910448cd5Skiyohara 				break;
46010448cd5Skiyohara 
46110448cd5Skiyohara 			case HCI_ACL_DATA_PKT:
46210448cd5Skiyohara 				sc->sc_state = BTBC_RECV_ACL_HDR;
46310448cd5Skiyohara 				sc->sc_want = sizeof(hci_acldata_hdr_t) - 1;
46410448cd5Skiyohara 				break;
46510448cd5Skiyohara 
46610448cd5Skiyohara 			case HCI_SCO_DATA_PKT:
46710448cd5Skiyohara 				sc->sc_state = BTBC_RECV_SCO_HDR;
46810448cd5Skiyohara 				sc->sc_want = sizeof(hci_scodata_hdr_t) - 1;
46910448cd5Skiyohara 				break;
47010448cd5Skiyohara 
47110448cd5Skiyohara 			case HCI_EVENT_PKT:
47210448cd5Skiyohara 				sc->sc_state = BTBC_RECV_EVENT_HDR;
47310448cd5Skiyohara 				sc->sc_want = sizeof(hci_event_hdr_t) - 1;
47410448cd5Skiyohara 				break;
47510448cd5Skiyohara 
47610448cd5Skiyohara 			default:
4777cca9485Splunky 				aprint_error_dev(sc->sc_dev,
4787cca9485Splunky 				    "Unknown packet type=%#x!\n", buf[i]);
479736a9db0Splunky 				sc->sc_stats.err_rx++;
48010448cd5Skiyohara 				m_freem(sc->sc_rxp);
48110448cd5Skiyohara 				sc->sc_rxp = NULL;
48210448cd5Skiyohara 				return;		/* (lost sync) */
48310448cd5Skiyohara 			}
48410448cd5Skiyohara 
48510448cd5Skiyohara 			break;
48610448cd5Skiyohara 
48710448cd5Skiyohara 		/*
48810448cd5Skiyohara 		 * we assume (correctly of course :) that the packet headers
48910448cd5Skiyohara 		 * all fit into a single pkthdr mbuf
49010448cd5Skiyohara 		 */
49110448cd5Skiyohara 		case BTBC_RECV_ACL_HDR:		/* Got ACL Header */
49210448cd5Skiyohara 			sc->sc_state = BTBC_RECV_ACL_DATA;
49310448cd5Skiyohara 			sc->sc_want = mtod(m, hci_acldata_hdr_t *)->length;
49410448cd5Skiyohara 			sc->sc_want = le16toh(sc->sc_want);
49510448cd5Skiyohara 			break;
49610448cd5Skiyohara 
49710448cd5Skiyohara 		case BTBC_RECV_SCO_HDR:		/* Got SCO Header */
49810448cd5Skiyohara 			sc->sc_state = BTBC_RECV_SCO_DATA;
49910448cd5Skiyohara 			sc->sc_want =  mtod(m, hci_scodata_hdr_t *)->length;
50010448cd5Skiyohara 			break;
50110448cd5Skiyohara 
50210448cd5Skiyohara 		case BTBC_RECV_EVENT_HDR:	/* Got Event Header */
50310448cd5Skiyohara 			sc->sc_state = BTBC_RECV_EVENT_DATA;
50410448cd5Skiyohara 			sc->sc_want =  mtod(m, hci_event_hdr_t *)->length;
50510448cd5Skiyohara 			break;
50610448cd5Skiyohara 
50710448cd5Skiyohara 		case BTBC_RECV_ACL_DATA:	/* ACL Packet Complete */
508736a9db0Splunky 			if (!hci_input_acl(sc->sc_unit, sc->sc_rxp))
509736a9db0Splunky 				sc->sc_stats.err_rx++;
510736a9db0Splunky 
511736a9db0Splunky 			sc->sc_stats.acl_rx++;
51210448cd5Skiyohara 			sc->sc_rxp = m = NULL;
51310448cd5Skiyohara 			space = 0;
51410448cd5Skiyohara 			break;
51510448cd5Skiyohara 
51610448cd5Skiyohara 		case BTBC_RECV_SCO_DATA:	/* SCO Packet Complete */
517736a9db0Splunky 			if (!hci_input_sco(sc->sc_unit, sc->sc_rxp))
518736a9db0Splunky 				sc->sc_stats.err_rx++;
519736a9db0Splunky 
520736a9db0Splunky 			sc->sc_stats.sco_rx++;
52110448cd5Skiyohara 			sc->sc_rxp = m = NULL;
52210448cd5Skiyohara 			space = 0;
52310448cd5Skiyohara 			break;
52410448cd5Skiyohara 
52510448cd5Skiyohara 		case BTBC_RECV_EVENT_DATA:	/* Event Packet Complete */
526736a9db0Splunky 			if (!hci_input_event(sc->sc_unit, sc->sc_rxp))
527736a9db0Splunky 				sc->sc_stats.err_rx++;
528736a9db0Splunky 
529736a9db0Splunky 			sc->sc_stats.evt_rx++;
53010448cd5Skiyohara 			sc->sc_rxp = m = NULL;
53110448cd5Skiyohara 			space = 0;
53210448cd5Skiyohara 			break;
53310448cd5Skiyohara 
53410448cd5Skiyohara 		default:
53510448cd5Skiyohara 			panic("%s: invalid state %d!\n",
536edb74239Splunky 				device_xname(sc->sc_dev), sc->sc_state);
53710448cd5Skiyohara 		}
53810448cd5Skiyohara 		i++;
53910448cd5Skiyohara 	}
54010448cd5Skiyohara }
54110448cd5Skiyohara 
54210448cd5Skiyohara /*
54310448cd5Skiyohara  * write data from current packet to Transmit FIFO.
54410448cd5Skiyohara  * restart when done.
54510448cd5Skiyohara  */
54610448cd5Skiyohara static void
btbc_transmit(struct btbc_softc * sc)54710448cd5Skiyohara btbc_transmit(struct btbc_softc *sc)
54810448cd5Skiyohara {
54910448cd5Skiyohara 	hci_cmd_hdr_t *p;
55010448cd5Skiyohara 	struct mbuf *m;
5513c75d069Skiyohara 	int count, set_baudrate, n, s;
55210448cd5Skiyohara 	uint32_t offset, command;
55310448cd5Skiyohara 	uint8_t *rptr;
55410448cd5Skiyohara 
55510448cd5Skiyohara 	m = sc->sc_txp;
55610448cd5Skiyohara 	if (m == NULL) {
557736a9db0Splunky 		sc->sc_flags &= ~BTBC_XMIT;
558736a9db0Splunky 		btbc_start(sc);
55910448cd5Skiyohara 		return;
56010448cd5Skiyohara 	}
56110448cd5Skiyohara 
56210448cd5Skiyohara 	set_baudrate = 0;
56310448cd5Skiyohara 	p = mtod(m, hci_cmd_hdr_t *);
56410448cd5Skiyohara 	if ((void *)m->m_pktdat == (void *)p) {
56510448cd5Skiyohara 		const uint16_t opcode =
56610448cd5Skiyohara 		    htole16(HCI_CMD_ERICSSON_SET_UART_BAUD_RATE);
56710448cd5Skiyohara 
56810448cd5Skiyohara 		if (p->type == HCI_CMD_PKT &&
56910448cd5Skiyohara 		    p->opcode == opcode &&
57010448cd5Skiyohara 		    p->length == 1) {
57110448cd5Skiyohara 			set_baudrate = 1;
57210448cd5Skiyohara 			sc->sc_txp = NULL;	/* safe reentrant */
57310448cd5Skiyohara 		}
57410448cd5Skiyohara 	}
57510448cd5Skiyohara 
57610448cd5Skiyohara 	count = 0;
57710448cd5Skiyohara 	rptr = mtod(m, uint8_t *);
57810448cd5Skiyohara 	for(;;) {
5793c75d069Skiyohara 		if (m->m_len == 0) {
58010448cd5Skiyohara 			m = m->m_next;
58110448cd5Skiyohara 			if (m == NULL) {
58210448cd5Skiyohara 				m = sc->sc_txp;
58310448cd5Skiyohara 				sc->sc_txp = NULL;
58410448cd5Skiyohara 
58510448cd5Skiyohara 				if (M_GETCTX(m, void *) == NULL)
58610448cd5Skiyohara 					m_freem(m);
587736a9db0Splunky 				else if (!hci_complete_sco(sc->sc_unit, m))
588736a9db0Splunky 					sc->sc_stats.err_tx++;
58910448cd5Skiyohara 
59010448cd5Skiyohara 				break;
59110448cd5Skiyohara 			}
59210448cd5Skiyohara 
59310448cd5Skiyohara 			rptr = mtod(m, uint8_t *);
59410448cd5Skiyohara 			continue;
59510448cd5Skiyohara 		}
59610448cd5Skiyohara 
59710448cd5Skiyohara 		s = splhigh();
59810448cd5Skiyohara 		if (sc->sc_txstate & TXBUF_MASK) {
59910448cd5Skiyohara 			if (sc->sc_txstate & TXBUF2_EMPTY) {
60010448cd5Skiyohara 				offset = BLUECARD_BUF2;
60110448cd5Skiyohara 				command = BLUECARD_COMMAND_TXBUF2;
60210448cd5Skiyohara 				sc->sc_txstate &= ~(TXBUF2_EMPTY | TXBUF_MASK);
60310448cd5Skiyohara 			} else {
60410448cd5Skiyohara 				splx(s);
60510448cd5Skiyohara 				break;
60610448cd5Skiyohara 			}
60710448cd5Skiyohara 		} else {
60810448cd5Skiyohara 			if (sc->sc_txstate & TXBUF1_EMPTY) {
60910448cd5Skiyohara 				offset = BLUECARD_BUF1;
61010448cd5Skiyohara 				command = BLUECARD_COMMAND_TXBUF1;
61110448cd5Skiyohara 				sc->sc_txstate &= ~TXBUF1_EMPTY;
61210448cd5Skiyohara 				sc->sc_txstate |= TXBUF_MASK;
61310448cd5Skiyohara 			} else {
61410448cd5Skiyohara 				splx(s);
61510448cd5Skiyohara 				break;
61610448cd5Skiyohara 			}
61710448cd5Skiyohara 		}
61810448cd5Skiyohara 		splx(s);
61910448cd5Skiyohara 
62010448cd5Skiyohara 		if (set_baudrate) {
62110448cd5Skiyohara 			/* Disable RTS */
62210448cd5Skiyohara 			sc->sc_ctrlreg |= BLUECARD_CONTROL_RTS;
62310448cd5Skiyohara 			bus_space_write_1(sc->sc_pcioh.iot, sc->sc_pcioh.ioh,
62410448cd5Skiyohara 			    BLUECARD_CONTROL, sc->sc_ctrlreg);
62510448cd5Skiyohara 		}
62610448cd5Skiyohara 
62710448cd5Skiyohara 		/* Activate LED */
62810448cd5Skiyohara 		btbc_enable_activity_led(sc);
62910448cd5Skiyohara 
63010448cd5Skiyohara 		/* Send frame */
6313c75d069Skiyohara 		n = btbc_write(sc, offset, rptr, m->m_len);
63210448cd5Skiyohara 		count += n;
63310448cd5Skiyohara 		rptr += n;
6343c75d069Skiyohara 		m_adj(m, n);
63510448cd5Skiyohara 
63610448cd5Skiyohara 		/* Tell the FPGA to send the data */
63710448cd5Skiyohara 		bus_space_write_1(sc->sc_pcioh.iot, sc->sc_pcioh.ioh,
63810448cd5Skiyohara 		    BLUECARD_COMMAND, command);
63910448cd5Skiyohara 
64010448cd5Skiyohara 		if (set_baudrate) {
64110448cd5Skiyohara 			unsigned char baud_reg;
64210448cd5Skiyohara 
64310448cd5Skiyohara 			switch (*(uint8_t *)(p + 1)) {
64410448cd5Skiyohara 			case 0x00:	/* baud rate 460800 */
64510448cd5Skiyohara 				baud_reg = BLUECARD_CONTROL_BAUDRATE_460800;
64610448cd5Skiyohara 				break;
64710448cd5Skiyohara 			case 0x01:	/* baud rate 230400 */
64810448cd5Skiyohara 				baud_reg = BLUECARD_CONTROL_BAUDRATE_230400;
64910448cd5Skiyohara 				break;
65010448cd5Skiyohara 			case 0x02:	/* baud rate 115200 */
65110448cd5Skiyohara 				baud_reg = BLUECARD_CONTROL_BAUDRATE_115200;
65210448cd5Skiyohara 				break;
65310448cd5Skiyohara 			case 0x03:	/* baud rate 57600 */
65410448cd5Skiyohara 			default:
65510448cd5Skiyohara 				baud_reg = BLUECARD_CONTROL_BAUDRATE_57600;
65610448cd5Skiyohara 				break;
65710448cd5Skiyohara 			}
65810448cd5Skiyohara 
65910448cd5Skiyohara 			/* Wait until the command reaches the baseband */
66010448cd5Skiyohara 			tsleep(sc, PCATCH, "btbc_wait", hz / 5);
66110448cd5Skiyohara 
66210448cd5Skiyohara 			/* Set baud on baseband */
66310448cd5Skiyohara 			sc->sc_ctrlreg &= ~BLUECARD_CONTROL_BAUDRATE_MASK;
66410448cd5Skiyohara 			sc->sc_ctrlreg |= baud_reg;
66510448cd5Skiyohara 			bus_space_write_1(sc->sc_pcioh.iot, sc->sc_pcioh.ioh,
66610448cd5Skiyohara 			    BLUECARD_CONTROL, sc->sc_ctrlreg);
66710448cd5Skiyohara 
66810448cd5Skiyohara 			/* Enable RTS */
66910448cd5Skiyohara 			sc->sc_ctrlreg &= ~BLUECARD_CONTROL_RTS;
67010448cd5Skiyohara 			bus_space_write_1(sc->sc_pcioh.iot, sc->sc_pcioh.ioh,
67110448cd5Skiyohara 			    BLUECARD_CONTROL, sc->sc_ctrlreg);
67210448cd5Skiyohara 
67310448cd5Skiyohara 			/* Wait before the next HCI packet can be send */
67410448cd5Skiyohara 			tsleep(sc, PCATCH, "btbc_wait", hz);
67510448cd5Skiyohara 
67610448cd5Skiyohara 			m_freem(m);
67710448cd5Skiyohara 			break;
67810448cd5Skiyohara 		}
67910448cd5Skiyohara 	}
680736a9db0Splunky 	sc->sc_stats.byte_tx += count;
68110448cd5Skiyohara }
68210448cd5Skiyohara 
68310448cd5Skiyohara static int
btbc_intr(void * arg)68410448cd5Skiyohara btbc_intr(void *arg)
68510448cd5Skiyohara {
68610448cd5Skiyohara 	struct btbc_softc *sc = arg;
68710448cd5Skiyohara 	int handled = 0;
68810448cd5Skiyohara 	uint8_t isr;
68910448cd5Skiyohara 
69010448cd5Skiyohara 	isr = bus_space_read_1(sc->sc_pcioh.iot, sc->sc_pcioh.ioh,
69110448cd5Skiyohara 	    BLUECARD_INTERRUPT);
69210448cd5Skiyohara 	if (isr != 0x00 && isr != 0xff) {
69310448cd5Skiyohara 		if (isr & BLUECARD_INTERRUPT_RXBUF1) {
69410448cd5Skiyohara 			isr &= ~BLUECARD_INTERRUPT_RXBUF1;
69510448cd5Skiyohara 			handled = 1;
69610448cd5Skiyohara 			btbc_receive(sc, BLUECARD_BUF1);
69710448cd5Skiyohara 			bus_space_write_1(sc->sc_pcioh.iot, sc->sc_pcioh.ioh,
69810448cd5Skiyohara 			    BLUECARD_INTERRUPT, BLUECARD_INTERRUPT_RXBUF1);
69910448cd5Skiyohara 			bus_space_write_1(sc->sc_pcioh.iot, sc->sc_pcioh.ioh,
70010448cd5Skiyohara 			    BLUECARD_COMMAND, BLUECARD_COMMAND_RXBUF1);
70110448cd5Skiyohara 		}
70210448cd5Skiyohara 		if (isr & BLUECARD_INTERRUPT_RXBUF2) {
70310448cd5Skiyohara 			isr &= ~BLUECARD_INTERRUPT_RXBUF2;
70410448cd5Skiyohara 			handled = 1;
70510448cd5Skiyohara 			btbc_receive(sc, BLUECARD_BUF2);
70610448cd5Skiyohara 			bus_space_write_1(sc->sc_pcioh.iot, sc->sc_pcioh.ioh,
70710448cd5Skiyohara 			    BLUECARD_INTERRUPT, BLUECARD_INTERRUPT_RXBUF2);
70810448cd5Skiyohara 			bus_space_write_1(sc->sc_pcioh.iot, sc->sc_pcioh.ioh,
70910448cd5Skiyohara 			    BLUECARD_COMMAND, BLUECARD_COMMAND_RXBUF2);
71010448cd5Skiyohara 		}
71110448cd5Skiyohara 		if (isr & BLUECARD_INTERRUPT_TXBUF1) {
71210448cd5Skiyohara 			isr &= ~BLUECARD_INTERRUPT_TXBUF1;
71310448cd5Skiyohara 			handled = 1;
71410448cd5Skiyohara 			sc->sc_txstate |= TXBUF1_EMPTY;
71510448cd5Skiyohara 			bus_space_write_1(sc->sc_pcioh.iot, sc->sc_pcioh.ioh,
71610448cd5Skiyohara 			    BLUECARD_INTERRUPT, BLUECARD_INTERRUPT_TXBUF1);
71710448cd5Skiyohara 			btbc_transmit(sc);
71810448cd5Skiyohara 		}
71910448cd5Skiyohara 		if (isr & BLUECARD_INTERRUPT_TXBUF2) {
72010448cd5Skiyohara 			isr &= ~BLUECARD_INTERRUPT_TXBUF2;
72110448cd5Skiyohara 			handled = 1;
72210448cd5Skiyohara 			sc->sc_txstate |= TXBUF2_EMPTY;
72310448cd5Skiyohara 			bus_space_write_1(sc->sc_pcioh.iot, sc->sc_pcioh.ioh,
72410448cd5Skiyohara 			    BLUECARD_INTERRUPT, BLUECARD_INTERRUPT_TXBUF2);
72510448cd5Skiyohara 			btbc_transmit(sc);
72610448cd5Skiyohara 		}
72710448cd5Skiyohara 
72810448cd5Skiyohara 		if (isr & 0x40) {	/* card eject ? */
7297cca9485Splunky 			aprint_normal_dev(sc->sc_dev, "card eject?\n");
73010448cd5Skiyohara 			isr &= ~0x40;
73110448cd5Skiyohara 			bus_space_write_1(sc->sc_pcioh.iot, sc->sc_pcioh.ioh,
73210448cd5Skiyohara 			    BLUECARD_INTERRUPT, 0x40);
73310448cd5Skiyohara 		}
73410448cd5Skiyohara 		if (isr != 0x00) {
7357cca9485Splunky 			aprint_error_dev(sc->sc_dev,
7367cca9485Splunky 			    "unknown interrupt: isr=0x%x\n", isr);
73710448cd5Skiyohara 			bus_space_write_1(sc->sc_pcioh.iot, sc->sc_pcioh.ioh,
73810448cd5Skiyohara 			    BLUECARD_INTERRUPT, isr);
73910448cd5Skiyohara 		}
74010448cd5Skiyohara 	}
74110448cd5Skiyohara 
74210448cd5Skiyohara 	return handled;
74310448cd5Skiyohara }
74410448cd5Skiyohara 
74510448cd5Skiyohara /*
74610448cd5Skiyohara  * start sending on btbc
747736a9db0Splunky  *
748736a9db0Splunky  * should be called at spltty() and when BTBC_XMIT is not set
74910448cd5Skiyohara  */
75010448cd5Skiyohara static void
btbc_start(struct btbc_softc * sc)751736a9db0Splunky btbc_start(struct btbc_softc *sc)
75210448cd5Skiyohara {
75310448cd5Skiyohara 	struct mbuf *m;
75410448cd5Skiyohara 
755736a9db0Splunky 	KASSERT((sc->sc_flags & BTBC_XMIT) == 0);
75610448cd5Skiyohara 	KASSERT(sc->sc_txp == NULL);
75710448cd5Skiyohara 
758736a9db0Splunky 	if (MBUFQ_FIRST(&sc->sc_cmdq)) {
759736a9db0Splunky 		MBUFQ_DEQUEUE(&sc->sc_cmdq, m);
760736a9db0Splunky 		sc->sc_stats.cmd_tx++;
76110448cd5Skiyohara 		goto start;
76210448cd5Skiyohara 	}
76310448cd5Skiyohara 
764736a9db0Splunky 	if (MBUFQ_FIRST(&sc->sc_scoq)) {
765736a9db0Splunky 		MBUFQ_DEQUEUE(&sc->sc_scoq, m);
766736a9db0Splunky 		sc->sc_stats.sco_tx++;
76710448cd5Skiyohara 		goto start;
76810448cd5Skiyohara 	}
76910448cd5Skiyohara 
770736a9db0Splunky 	if (MBUFQ_FIRST(&sc->sc_aclq)) {
771736a9db0Splunky 		MBUFQ_DEQUEUE(&sc->sc_aclq, m);
772736a9db0Splunky 		sc->sc_stats.acl_tx++;
77310448cd5Skiyohara 		goto start;
77410448cd5Skiyohara 	}
77510448cd5Skiyohara 
77610448cd5Skiyohara 	/* Nothing to send */
77710448cd5Skiyohara 	return;
77810448cd5Skiyohara 
77910448cd5Skiyohara start:
78010448cd5Skiyohara 	sc->sc_txp = m;
781736a9db0Splunky 	sc->sc_flags |= BTBC_XMIT;
78210448cd5Skiyohara 	btbc_transmit(sc);
78310448cd5Skiyohara }
78410448cd5Skiyohara 
78510448cd5Skiyohara static int
btbc_enable(device_t self)7860b799668Splunky btbc_enable(device_t self)
78710448cd5Skiyohara {
7880b799668Splunky 	struct btbc_softc *sc = device_private(self);
789736a9db0Splunky 	int err, s;
79010448cd5Skiyohara 	uint8_t id, ctrl;
79110448cd5Skiyohara 
792736a9db0Splunky 	if (sc->sc_flags & BTBC_ENABLED)
79310448cd5Skiyohara 		return 0;
79410448cd5Skiyohara 
795736a9db0Splunky 	s = spltty();
796736a9db0Splunky 
79710448cd5Skiyohara 	sc->sc_txstate = TXBUF1_EMPTY | TXBUF2_EMPTY;
79810448cd5Skiyohara 	sc->sc_intr = pcmcia_intr_establish(sc->sc_pf, IPL_TTY, btbc_intr, sc);
79910448cd5Skiyohara 	if (sc->sc_intr == NULL) {
80010448cd5Skiyohara 		err = EIO;
80110448cd5Skiyohara 		goto fail1;
80210448cd5Skiyohara 	}
80310448cd5Skiyohara 
80410448cd5Skiyohara 	err = pcmcia_function_enable(sc->sc_pf);
80510448cd5Skiyohara 	if (err)
80610448cd5Skiyohara 		goto fail2;
80710448cd5Skiyohara 
808736a9db0Splunky 	sc->sc_flags |= BTBC_ENABLED;
809736a9db0Splunky 	sc->sc_flags &= ~BTBC_XMIT;
81010448cd5Skiyohara 
81110448cd5Skiyohara 	/* Reset card */
81210448cd5Skiyohara 	ctrl = BLUECARD_CONTROL_RESET | BLUECARD_CONTROL_CARDRESET;
81310448cd5Skiyohara 	bus_space_write_1(sc->sc_pcioh.iot, sc->sc_pcioh.ioh, BLUECARD_CONTROL,
81410448cd5Skiyohara 	    ctrl);
81510448cd5Skiyohara 
81610448cd5Skiyohara 	/* Turn FPGA off */
81710448cd5Skiyohara 	bus_space_write_1(sc->sc_pcioh.iot, sc->sc_pcioh.ioh,
81810448cd5Skiyohara 	    BLUECARD_CARDRESET, 0x80);
81910448cd5Skiyohara 
82010448cd5Skiyohara 	/* Wait some time */
82110448cd5Skiyohara 	tsleep(sc, PCATCH, "btbc_reset", 1);
82210448cd5Skiyohara 
82310448cd5Skiyohara 	/* Turn FPGA on */
82410448cd5Skiyohara 	bus_space_write_1(sc->sc_pcioh.iot, sc->sc_pcioh.ioh,
82510448cd5Skiyohara 	    BLUECARD_CARDRESET, 0x00);
82610448cd5Skiyohara 
82710448cd5Skiyohara 	/* Activate card */
82810448cd5Skiyohara 	ctrl = BLUECARD_CONTROL_ON | BLUECARD_CONTROL_RESPU;
82910448cd5Skiyohara 	bus_space_write_1(sc->sc_pcioh.iot, sc->sc_pcioh.ioh, BLUECARD_CONTROL,
83010448cd5Skiyohara 	    ctrl);
83110448cd5Skiyohara 
83210448cd5Skiyohara 	tsleep(sc, PCATCH, "btbc_enable", 1);
83310448cd5Skiyohara 	sc->sc_ctrlreg = ctrl;
83410448cd5Skiyohara 
83510448cd5Skiyohara 	/* Enable interrupt */
83610448cd5Skiyohara 	bus_space_write_1(sc->sc_pcioh.iot, sc->sc_pcioh.ioh,
83710448cd5Skiyohara 	    BLUECARD_INTERRUPT, 0xff);
83810448cd5Skiyohara 	sc->sc_ctrlreg |= BLUECARD_CONTROL_INTERRUPT;
83910448cd5Skiyohara 	bus_space_write_1(sc->sc_pcioh.iot, sc->sc_pcioh.ioh, BLUECARD_CONTROL,
84010448cd5Skiyohara 	    sc->sc_ctrlreg);
84110448cd5Skiyohara 
84210448cd5Skiyohara 	id = bus_space_read_1(sc->sc_pcioh.iot,
84310448cd5Skiyohara 	    sc->sc_pcioh.ioh, BLUECARD_LEDCONTROL);
84410448cd5Skiyohara 	switch (id & 0x0f) {
84510448cd5Skiyohara 	case 0x02:
84610448cd5Skiyohara 		/* Enable LED */
84710448cd5Skiyohara 		bus_space_write_1(sc->sc_pcioh.iot, sc->sc_pcioh.ioh,
84810448cd5Skiyohara 		    BLUECARD_LEDCONTROL, 0x08 | 0x20);
84910448cd5Skiyohara 		break;
85010448cd5Skiyohara 
85110448cd5Skiyohara 	case 0x03:
85210448cd5Skiyohara 		/* Disable RTS */
85310448cd5Skiyohara 		ctrl |= BLUECARD_CONTROL_RTS;
85410448cd5Skiyohara 		bus_space_write_1(sc->sc_pcioh.iot, sc->sc_pcioh.ioh,
85510448cd5Skiyohara 		    BLUECARD_CONTROL, ctrl);
85610448cd5Skiyohara 
85710448cd5Skiyohara 		/* Set baud rate */
85810448cd5Skiyohara 		ctrl |= BLUECARD_CONTROL_BAUDRATE_460800;
85910448cd5Skiyohara 		bus_space_write_1(sc->sc_pcioh.iot, sc->sc_pcioh.ioh,
86010448cd5Skiyohara 		    BLUECARD_CONTROL, ctrl);
86110448cd5Skiyohara 
86210448cd5Skiyohara 		/* Enable RTS */
86310448cd5Skiyohara 		ctrl &= ~BLUECARD_CONTROL_RTS;
86410448cd5Skiyohara 		bus_space_write_1(sc->sc_pcioh.iot, sc->sc_pcioh.ioh,
86510448cd5Skiyohara 		    BLUECARD_CONTROL, ctrl);
86610448cd5Skiyohara 		break;
86710448cd5Skiyohara 	}
86810448cd5Skiyohara 
86910448cd5Skiyohara 	/* Start the RX buffers */
87010448cd5Skiyohara 	bus_space_write_1(sc->sc_pcioh.iot, sc->sc_pcioh.ioh,
87110448cd5Skiyohara 	    BLUECARD_COMMAND, BLUECARD_COMMAND_RXBUF1);
87210448cd5Skiyohara 	bus_space_write_1(sc->sc_pcioh.iot, sc->sc_pcioh.ioh,
87310448cd5Skiyohara 	    BLUECARD_COMMAND, BLUECARD_COMMAND_RXBUF2);
87410448cd5Skiyohara 
87510448cd5Skiyohara 	/* XXX: Control the point at which RTS is enabled */
87610448cd5Skiyohara 	bus_space_write_1(sc->sc_pcioh.iot, sc->sc_pcioh.ioh,
87710448cd5Skiyohara 	    BLUECARD_RXCONTROL, BLUECARD_RXCONTROL_RTSLEVEL(0x0f) | 1);
87810448cd5Skiyohara 
87910448cd5Skiyohara 	/* Timeout before it is safe to send the first HCI packet */
88010448cd5Skiyohara 	tsleep(sc, PCATCH, "btbc_enable", hz * 2);
88110448cd5Skiyohara 
88210448cd5Skiyohara 	btbc_set_baudrate(sc, BTBC_DEFAULT_BAUDRATE);
88310448cd5Skiyohara 
884736a9db0Splunky 	splx(s);
88510448cd5Skiyohara 	return 0;
88610448cd5Skiyohara 
88710448cd5Skiyohara fail2:
88810448cd5Skiyohara 	pcmcia_intr_disestablish(sc->sc_pf, sc->sc_intr);
88910448cd5Skiyohara 	sc->sc_intr = NULL;
89010448cd5Skiyohara fail1:
891736a9db0Splunky 	splx(s);
89210448cd5Skiyohara 	return err;
89310448cd5Skiyohara }
89410448cd5Skiyohara 
89510448cd5Skiyohara static void
btbc_disable(device_t self)8960b799668Splunky btbc_disable(device_t self)
89710448cd5Skiyohara {
8980b799668Splunky 	struct btbc_softc *sc = device_private(self);
899736a9db0Splunky 	int s;
90010448cd5Skiyohara 
901736a9db0Splunky 	if ((sc->sc_flags & BTBC_ENABLED) == 0)
90210448cd5Skiyohara 		return;
90310448cd5Skiyohara 
904736a9db0Splunky 	s = spltty();
905736a9db0Splunky 
90610448cd5Skiyohara 	pcmcia_function_disable(sc->sc_pf);
90710448cd5Skiyohara 
90810448cd5Skiyohara 	if (sc->sc_intr) {
90910448cd5Skiyohara 		pcmcia_intr_disestablish(sc->sc_pf, sc->sc_intr);
91010448cd5Skiyohara 		sc->sc_intr = NULL;
91110448cd5Skiyohara 	}
91210448cd5Skiyohara 
91310448cd5Skiyohara 	m_freem(sc->sc_rxp);
91410448cd5Skiyohara 	sc->sc_rxp = NULL;
91510448cd5Skiyohara 
91610448cd5Skiyohara 	m_freem(sc->sc_txp);
91710448cd5Skiyohara 	sc->sc_txp = NULL;
91810448cd5Skiyohara 
919736a9db0Splunky 	MBUFQ_DRAIN(&sc->sc_cmdq);
920736a9db0Splunky 	MBUFQ_DRAIN(&sc->sc_aclq);
921736a9db0Splunky 	MBUFQ_DRAIN(&sc->sc_scoq);
922736a9db0Splunky 
923736a9db0Splunky 	sc->sc_flags &= ~BTBC_ENABLED;
92410448cd5Skiyohara 
92510448cd5Skiyohara 	/* Disable LED */
92610448cd5Skiyohara 	bus_space_write_1(sc->sc_pcioh.iot, sc->sc_pcioh.ioh,
92710448cd5Skiyohara 	    BLUECARD_LEDCONTROL, 0x00);
92810448cd5Skiyohara 
92910448cd5Skiyohara 	/* Reset card */
93010448cd5Skiyohara 	sc->sc_ctrlreg = BLUECARD_CONTROL_RESET | BLUECARD_CONTROL_CARDRESET;
93110448cd5Skiyohara 	bus_space_write_1(sc->sc_pcioh.iot, sc->sc_pcioh.ioh, BLUECARD_CONTROL,
93210448cd5Skiyohara 	    sc->sc_ctrlreg);
93310448cd5Skiyohara 
93410448cd5Skiyohara 	/* Turn FPGA off */
93510448cd5Skiyohara 	bus_space_write_1(sc->sc_pcioh.iot, sc->sc_pcioh.ioh,
93610448cd5Skiyohara 	    BLUECARD_CARDRESET, 0x80);
937736a9db0Splunky 
938736a9db0Splunky 	splx(s);
939736a9db0Splunky }
940736a9db0Splunky 
941736a9db0Splunky static void
btbc_output_cmd(device_t self,struct mbuf * m)942736a9db0Splunky btbc_output_cmd(device_t self, struct mbuf *m)
943736a9db0Splunky {
944736a9db0Splunky 	struct btbc_softc *sc = device_private(self);
945736a9db0Splunky 	int s;
946736a9db0Splunky 
947736a9db0Splunky 	KASSERT(sc->sc_flags & BTBC_ENABLED);
948736a9db0Splunky 
949736a9db0Splunky 	M_SETCTX(m, NULL);
950736a9db0Splunky 
951736a9db0Splunky 	s = spltty();
952736a9db0Splunky 	MBUFQ_ENQUEUE(&sc->sc_cmdq, m);
953736a9db0Splunky 	if ((sc->sc_flags & BTBC_XMIT) == 0)
954736a9db0Splunky 		btbc_start(sc);
955736a9db0Splunky 
956736a9db0Splunky 	splx(s);
957736a9db0Splunky }
958736a9db0Splunky 
959736a9db0Splunky static void
btbc_output_acl(device_t self,struct mbuf * m)960736a9db0Splunky btbc_output_acl(device_t self, struct mbuf *m)
961736a9db0Splunky {
962736a9db0Splunky 	struct btbc_softc *sc = device_private(self);
963736a9db0Splunky 	int s;
964736a9db0Splunky 
965736a9db0Splunky 	KASSERT(sc->sc_flags & BTBC_ENABLED);
966736a9db0Splunky 
967736a9db0Splunky 	M_SETCTX(m, NULL);
968736a9db0Splunky 
969736a9db0Splunky 	s = spltty();
970736a9db0Splunky 	MBUFQ_ENQUEUE(&sc->sc_aclq, m);
971736a9db0Splunky 	if ((sc->sc_flags & BTBC_XMIT) == 0)
972736a9db0Splunky 		btbc_start(sc);
973736a9db0Splunky 
974736a9db0Splunky 	splx(s);
975736a9db0Splunky }
976736a9db0Splunky 
977736a9db0Splunky static void
btbc_output_sco(device_t self,struct mbuf * m)978736a9db0Splunky btbc_output_sco(device_t self, struct mbuf *m)
979736a9db0Splunky {
980736a9db0Splunky 	struct btbc_softc *sc = device_private(self);
981736a9db0Splunky 	int s;
982736a9db0Splunky 
983736a9db0Splunky 	KASSERT(sc->sc_flags & BTBC_ENABLED);
984736a9db0Splunky 
985736a9db0Splunky 	s = spltty();
986736a9db0Splunky 	MBUFQ_ENQUEUE(&sc->sc_scoq, m);
987736a9db0Splunky 	if ((sc->sc_flags & BTBC_XMIT) == 0)
988736a9db0Splunky 		btbc_start(sc);
989736a9db0Splunky 
990736a9db0Splunky 	splx(s);
991736a9db0Splunky }
992736a9db0Splunky 
993736a9db0Splunky static void
btbc_stats(device_t self,struct bt_stats * dest,int flush)994736a9db0Splunky btbc_stats(device_t self, struct bt_stats *dest, int flush)
995736a9db0Splunky {
996736a9db0Splunky 	struct btbc_softc *sc = device_private(self);
997736a9db0Splunky 	int s;
998736a9db0Splunky 
999736a9db0Splunky 	s = spltty();
1000736a9db0Splunky 	memcpy(dest, &sc->sc_stats, sizeof(struct bt_stats));
1001736a9db0Splunky 
1002736a9db0Splunky 	if (flush)
1003736a9db0Splunky 		memset(&sc->sc_stats, 0, sizeof(struct bt_stats));
1004736a9db0Splunky 
1005736a9db0Splunky 	splx(s);
100610448cd5Skiyohara }
1007