diff options
Diffstat (limited to 'sys/dev/sdmmc')
-rw-r--r-- | sys/dev/sdmmc/sbt.c | 199 |
1 files changed, 150 insertions, 49 deletions
diff --git a/sys/dev/sdmmc/sbt.c b/sys/dev/sdmmc/sbt.c index 070d53c5ee8..6f94f164f64 100644 --- a/sys/dev/sdmmc/sbt.c +++ b/sys/dev/sdmmc/sbt.c @@ -1,4 +1,4 @@ -/* $OpenBSD: sbt.c,v 1.9 2007/06/19 07:59:57 uwe Exp $ */ +/* $OpenBSD: sbt.c,v 1.10 2008/02/24 21:34:48 uwe Exp $ */ /* * Copyright (c) 2007 Uwe Stuehler <uwe@openbsd.org> @@ -53,13 +53,20 @@ struct sbt_softc { struct device sc_dev; /* base device */ - struct hci_unit sc_unit; /* MI host controller */ struct sdmmc_function *sc_sf; /* SDIO function */ - struct proc *sc_thread; /* inquiry thread */ + struct workq *sc_workq; /* transfer deferred packets */ + int sc_enabled; /* HCI enabled */ int sc_dying; /* shutdown in progress */ + int sc_busy; /* transmitting or receiving */ void *sc_ih; u_char *sc_buf; int sc_rxtry; + + struct hci_unit *sc_unit; /* MI host controller */ + struct bt_stats sc_stats; /* MI bluetooth stats */ + struct ifqueue sc_cmdq; + struct ifqueue sc_acltxq; + struct ifqueue sc_scotxq; }; int sbt_match(struct device *, void *, void *); @@ -71,17 +78,20 @@ int sbt_read_packet(struct sbt_softc *, u_char *, size_t *); int sbt_intr(void *); -int sbt_enable(struct hci_unit *); -void sbt_disable(struct hci_unit *); -void sbt_start(struct hci_unit *, struct ifqueue *, int); -void sbt_start_cmd(struct hci_unit *); -void sbt_start_acl(struct hci_unit *); -void sbt_start_sco(struct hci_unit *); +int sbt_enable(struct device *); +void sbt_disable(struct device *); +void sbt_start(struct sbt_softc *, struct mbuf *, struct ifqueue *, int); +void sbt_xmit_cmd(struct device *, struct mbuf *); +void sbt_xmit_acl(struct device *, struct mbuf *); +void sbt_xmit_sco(struct device *, struct mbuf *); +void sbt_start_task(void *, void *); + +void sbt_stats(struct device *, struct bt_stats *, int); #undef DPRINTF #define SBT_DEBUG #ifdef SBT_DEBUG -int sbt_debug = 1; +int sbt_debug = 0; #define DPRINTF(s) printf s #define DNPRINTF(n, s) do { if ((n) <= sbt_debug) printf s; } while (0) #else @@ -114,6 +124,16 @@ static const struct sbt_product { SDMMC_CIS_SOCKETCOM_BTCARD } }; +const struct hci_if sbt_hci = { + .enable = sbt_enable, + .disable = sbt_disable, + .output_cmd = sbt_xmit_cmd, + .output_acl = sbt_xmit_acl, + .output_sco = sbt_xmit_sco, + .get_stats = sbt_stats, + .ipl = IPL_SDMMC +}; + int sbt_match(struct device *parent, void *match, void *aux) { @@ -155,13 +175,20 @@ sbt_attach(struct device *parent, struct device *self, void *aux) /* It may be Type-B, but we use it only in Type-A mode. */ printf("%s: SDIO Bluetooth Type-A\n", DEVNAME(sc)); - sc->sc_buf = malloc(SBT_PKT_BUFSIZ, M_DEVBUF, - M_NOWAIT | M_CANFAIL); + /* Create a shared buffer for receive and transmit. */ + sc->sc_buf = malloc(SBT_PKT_BUFSIZ, M_DEVBUF, M_NOWAIT | M_CANFAIL); if (sc->sc_buf == NULL) { printf("%s: can't allocate cmd buffer\n", DEVNAME(sc)); return; } + /* Create a work thread to transmit deferred packets. */ + sc->sc_workq = workq_create(DEVNAME(sc), 1); + if (sc->sc_workq == NULL) { + printf("%s: can't allocate workq\n", DEVNAME(sc)); + return; + } + /* Enable the HCI packet transport read interrupt. */ CSR_WRITE_1(sc, SBT_REG_IENA, ISTAT_INTRD); @@ -176,15 +203,7 @@ sbt_attach(struct device *parent, struct device *self, void *aux) /* * Attach Bluetooth unit (machine-independent HCI). */ - sc->sc_unit.hci_softc = self; - sc->sc_unit.hci_devname = DEVNAME(sc); - sc->sc_unit.hci_enable = sbt_enable; - sc->sc_unit.hci_disable = sbt_disable; - sc->sc_unit.hci_start_cmd = sbt_start_cmd; - sc->sc_unit.hci_start_acl = sbt_start_acl; - sc->sc_unit.hci_start_sco = sbt_start_sco; - sc->sc_unit.hci_ipl = IPL_TTY; /* XXX */ - hci_attach(&sc->sc_unit); + sc->sc_unit = hci_attach(&sbt_hci, &sc->sc_dev, 0); } int @@ -193,14 +212,25 @@ sbt_detach(struct device *self, int flags) struct sbt_softc *sc = (struct sbt_softc *)self; sc->sc_dying = 1; - while (sc->sc_thread != NULL) - tsleep(sc, PWAIT, "dying", 0); - hci_detach(&sc->sc_unit); + while (sc->sc_busy) + tsleep(&sc->sc_busy, PWAIT, "sbtdie", 0); + + /* Detach HCI interface */ + if (sc->sc_unit) { + hci_detach(sc->sc_unit); + sc->sc_unit = NULL; + } if (sc->sc_ih != NULL) sdmmc_intr_disestablish(sc->sc_ih); + if (sc->sc_workq != NULL) + workq_destroy(sc->sc_workq); + + if (sc->sc_buf != NULL) + free(sc->sc_buf, M_DEVBUF); + return 0; } @@ -302,6 +332,7 @@ out: * Interrupt handling */ +/* This function is called from the SDIO interrupt thread. */ int sbt_intr(void *arg) { @@ -311,6 +342,7 @@ sbt_intr(void *arg) size_t len; int s; + /* Block further SDIO interrupts; XXX not really needed? */ s = splsdmmc(); status = CSR_READ_1(sc, SBT_REG_ISTAT); @@ -348,27 +380,27 @@ eoi: case HCI_ACL_DATA_PKT: DNPRINTF(1,("%s: recv ACL packet (%d bytes)\n", DEVNAME(sc), m->m_pkthdr.len)); - hci_input_acl(&sc->sc_unit, m); + hci_input_acl(sc->sc_unit, m); break; case HCI_SCO_DATA_PKT: DNPRINTF(1,("%s: recv SCO packet (%d bytes)\n", DEVNAME(sc), m->m_pkthdr.len)); - hci_input_sco(&sc->sc_unit, m); + hci_input_sco(sc->sc_unit, m); break; case HCI_EVENT_PKT: DNPRINTF(1,("%s: recv EVENT packet (%d bytes)\n", DEVNAME(sc), m->m_pkthdr.len)); - hci_input_event(&sc->sc_unit, m); + hci_input_event(sc->sc_unit, m); break; default: DPRINTF(("%s: recv 0x%x packet (%d bytes)\n", DEVNAME(sc), sc->sc_buf[0], m->m_pkthdr.len)); - sc->sc_unit.hci_stats.err_rx++; + sc->sc_stats.err_rx++; m_free(m); break; } } else - sc->sc_unit.hci_stats.err_rx++; + sc->sc_stats.err_rx++; splx(s); @@ -382,22 +414,27 @@ eoi: */ int -sbt_enable(struct hci_unit *unit) +sbt_enable(struct device *self) { - if (unit->hci_flags & BTF_RUNNING) + struct sbt_softc *sc = (struct sbt_softc *)self; + + if (sc->sc_enabled) return 0; - unit->hci_flags |= BTF_RUNNING; - unit->hci_flags &= ~BTF_XMIT; + sc->sc_enabled = 1; return 0; } void -sbt_disable(struct hci_unit *unit) +sbt_disable(struct device *self) { - if (!(unit->hci_flags & BTF_RUNNING)) + struct sbt_softc *sc = (struct sbt_softc *)self; + int s; + + if (!sc->sc_enabled) return; + s = splsdmmc(); #ifdef notyet /* XXX */ if (sc->sc_rxp) { m_freem(sc->sc_rxp); @@ -409,22 +446,38 @@ sbt_disable(struct hci_unit *unit) sc->sc_txp = NULL; } #endif - - unit->hci_flags &= ~BTF_RUNNING; + sc->sc_enabled = 0; + splx(s); } void -sbt_start(struct hci_unit *unit, struct ifqueue *q, int xmit) +sbt_start(struct sbt_softc *sc, struct mbuf *m, struct ifqueue *q, int xmit) { - struct sbt_softc *sc = (struct sbt_softc *)unit->hci_softc; - struct mbuf *m; + int s; int len; #ifdef SBT_DEBUG const char *what; #endif - if (sc->sc_dying || IF_IS_EMPTY(q)) + s = splsdmmc(); + if (m != NULL) + IF_ENQUEUE(q, m); + + if (sc->sc_dying || IF_IS_EMPTY(q)) { + splx(s); return; + } + + if (curproc == NULL || sc->sc_busy) { + (void)workq_add_task(sc->sc_workq, 0, sbt_start_task, + sc, (void *)xmit); + splx(s); + return; + } + + /* Defer additional transfers and reception of packets. */ + sdmmc_intr_disable(sc->sc_sf); + sc->sc_busy++; IF_DEQUEUE(q, m); @@ -444,7 +497,7 @@ sbt_start(struct hci_unit *unit, struct ifqueue *q, int xmit) what, m->m_pkthdr.len)); #endif - unit->hci_flags |= xmit; + sc->sc_unit->hci_flags |= xmit; len = m->m_pkthdr.len; m_copydata(m, 0, len, sc->sc_buf); @@ -453,23 +506,71 @@ sbt_start(struct hci_unit *unit, struct ifqueue *q, int xmit) if (sbt_write_packet(sc, sc->sc_buf, len)) DPRINTF(("%s: sbt_write_packet failed\n", DEVNAME(sc))); - unit->hci_flags &= ~xmit; + sc->sc_unit->hci_flags &= ~xmit; + + sc->sc_busy--; + sdmmc_intr_enable(sc->sc_sf); + + if (sc->sc_dying) + wakeup(&sc->sc_busy); + + splx(s); +} + +void +sbt_xmit_cmd(struct device *self, struct mbuf *m) +{ + struct sbt_softc *sc = (struct sbt_softc *)self; + + sbt_start(sc, m, &sc->sc_cmdq, BTF_XMIT_CMD); } void -sbt_start_cmd(struct hci_unit *unit) +sbt_xmit_acl(struct device *self, struct mbuf *m) { - sbt_start(unit, &unit->hci_cmdq, BTF_XMIT_CMD); + struct sbt_softc *sc = (struct sbt_softc *)self; + + sbt_start(sc, m, &sc->sc_acltxq, BTF_XMIT_ACL); } void -sbt_start_acl(struct hci_unit *unit) +sbt_xmit_sco(struct device *self, struct mbuf *m) { - sbt_start(unit, &unit->hci_acltxq, BTF_XMIT_ACL); + struct sbt_softc *sc = (struct sbt_softc *)self; + + sbt_start(sc, m, &sc->sc_scotxq, BTF_XMIT_SCO); } void -sbt_start_sco(struct hci_unit *unit) +sbt_start_task(void *arg1, void *arg2) { - sbt_start(unit, &unit->hci_scotxq, BTF_XMIT_SCO); + struct sbt_softc *sc = arg1; + int xmit = (int)arg2; + + switch (xmit) { + case BTF_XMIT_CMD: + sbt_xmit_cmd(&sc->sc_dev, NULL); + break; + case BTF_XMIT_ACL: + sbt_xmit_acl(&sc->sc_dev, NULL); + break; + case BTF_XMIT_SCO: + sbt_xmit_sco(&sc->sc_dev, NULL); + break; + } +} + +void +sbt_stats(struct device *self, struct bt_stats *dest, int flush) +{ + struct sbt_softc *sc = (struct sbt_softc *)self; + int s; + + s = splsdmmc(); + memcpy(dest, &sc->sc_stats, sizeof(struct bt_stats)); + + if (flush) + memset(&sc->sc_stats, 0, sizeof(struct bt_stats)); + + splx(s); } |