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, ¶m);
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