diff options
author | Uwe Stuehler <uwe@cvs.openbsd.org> | 2007-05-31 18:45:10 +0000 |
---|---|---|
committer | Uwe Stuehler <uwe@cvs.openbsd.org> | 2007-05-31 18:45:10 +0000 |
commit | da5a482a72f7f38dc54bda07efd0edda06f811ce (patch) | |
tree | 80be357eeeb5ec7ff7cb85aa9fb44d09b8e6cd6e /sys | |
parent | 8e0f64113fcf1880f0133a226da5aa0c3ccaa68c (diff) |
An SDIO Bluetooth driver, only initial bits and therefore not enabled yet
Diffstat (limited to 'sys')
-rw-r--r-- | sys/arch/i386/conf/GENERIC | 5 | ||||
-rw-r--r-- | sys/dev/sdmmc/files.sdmmc | 7 | ||||
-rw-r--r-- | sys/dev/sdmmc/sbt.c | 481 |
3 files changed, 491 insertions, 2 deletions
diff --git a/sys/arch/i386/conf/GENERIC b/sys/arch/i386/conf/GENERIC index 6e51d4f1e97..c46ac60f2eb 100644 --- a/sys/arch/i386/conf/GENERIC +++ b/sys/arch/i386/conf/GENERIC @@ -1,4 +1,4 @@ -# $OpenBSD: GENERIC,v 1.563 2007/05/29 18:18:19 tom Exp $ +# $OpenBSD: GENERIC,v 1.564 2007/05/31 18:45:08 uwe Exp $ # # For further information on compiling OpenBSD kernels, see the config(8) # man page. @@ -265,6 +265,9 @@ sdhc* at pci? # SD Host Controller sdmmc* at sdhc? # SD/MMC bus scsibus* at sdmmc? # SCSI emulation +# SDIO devices +#sbt* at sdmmc? # Bluetooth + npx0 at isa? port 0xf0 irq 13 # math coprocessor isadma0 at isa? isapnp0 at isa? diff --git a/sys/dev/sdmmc/files.sdmmc b/sys/dev/sdmmc/files.sdmmc index db96764fb5f..0212c702145 100644 --- a/sys/dev/sdmmc/files.sdmmc +++ b/sys/dev/sdmmc/files.sdmmc @@ -1,4 +1,4 @@ -# $OpenBSD: files.sdmmc,v 1.2 2006/06/01 21:53:41 uwe Exp $ +# $OpenBSD: files.sdmmc,v 1.3 2007/05/31 18:45:09 uwe Exp $ # # Config file and device description for machine-independent SD/MMC code. # Included by ports that need it. @@ -11,3 +11,8 @@ file dev/sdmmc/sdmmc_cis.c sdmmc file dev/sdmmc/sdmmc_io.c sdmmc file dev/sdmmc/sdmmc_mem.c sdmmc file dev/sdmmc/sdmmc_scsi.c sdmmc + +# Bluetooth SDIO cards (Type-A/B) +device sbt: btbus, bluetooth +attach sbt at sdmmc +file dev/sdmmc/sbt.c sbt diff --git a/sys/dev/sdmmc/sbt.c b/sys/dev/sdmmc/sbt.c new file mode 100644 index 00000000000..9eaa3067d12 --- /dev/null +++ b/sys/dev/sdmmc/sbt.c @@ -0,0 +1,481 @@ +/* $OpenBSD: sbt.c,v 1.1 2007/05/31 18:45:09 uwe Exp $ */ + +/* + * Copyright (c) 2007 Uwe Stuehler <uwe@openbsd.org> + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +/* Driver for Type-A/B SDIO Bluetooth cards */ + +#include <sys/param.h> +#include <sys/device.h> +#include <sys/kernel.h> +#include <sys/kthread.h> +#include <sys/malloc.h> +#include <sys/mbuf.h> +#include <sys/proc.h> +#include <sys/queue.h> +#include <sys/socket.h> +#include <sys/systm.h> + +#include <netbt/hci.h> + +#include <dev/sdmmc/sdmmcdevs.h> +#include <dev/sdmmc/sdmmcvar.h> + +#define CSR_READ_1(sc, reg) sdmmc_io_read_1((sc)->sc_sf, (reg)) +#define CSR_WRITE_1(sc, reg, val) sdmmc_io_write_1((sc)->sc_sf, (reg), (val)) + +#define SBT_REG_DAT 0x00 /* receiver/transmitter data */ +#define SBT_REG_RPC 0x10 /* read packet control */ +#define SBT_REG_WPC 0x11 /* write packet control */ +#define WPC_PCWRT (1<<0) /* packet write retry */ +#define SBT_REG_RC 0x12 /* retry control status/set */ +#define SBT_REG_ISTAT 0x13 /* interrupt status */ +#define ISTAT_INTRD (1<<0) /* packet available for read */ +#define SBT_REG_ICLR 0x13 /* interrupt clear */ +#define SBT_REG_IENA 0x14 /* interrupt enable */ +#define SBT_REG_BTMODE 0x20 /* SDIO Bluetooth card mode */ +#define BTMODE_TYPEB (1<<0) /* 1=Type-B, 0=Type-A */ + +#define SBT_BUFSIZ_HCI 65535 + +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 */ + int sc_dying; /* shutdown in progress */ + void *sc_ih; + u_char *sc_buf; +}; + +int sbt_match(struct device *, void *, void *); +void sbt_attach(struct device *, struct device *, void *); +int sbt_detach(struct device *, int); +void sbt_create_thread(void *); + +void sbt_thread0(void *); +void sbt_thread(void *); + +int sbt_write_packet(struct sbt_softc *, u_char *, size_t); +int sbt_read_packet(struct sbt_softc *, u_char *, size_t *); + +int sbt_intr_pending(struct sbt_softc *); +int sbt_intr(void *); + +int sbt_enable(struct hci_unit *); +void sbt_disable(struct hci_unit *); +void sbt_start_cmd(struct hci_unit *); +void sbt_start_acl(struct hci_unit *); +void sbt_start_sco(struct hci_unit *); + +#undef DPRINTF +#ifdef SBT_DEBUG +#define DPRINTF(s) printf s +#else +#define DPRINTF(s) do {} while (0) +#endif + +#define DEVNAME(sc) ((sc)->sc_dev.dv_xname) + +struct cfattach sbt_ca = { + sizeof(struct sbt_softc), sbt_match, sbt_attach, sbt_detach +}; + +struct cfdriver sbt_cd = { + NULL, "sbt", DV_DULL +}; + +extern struct cfdriver bthub_cd; + + +/* + * Autoconf glue + */ + +static const struct sbt_product { + u_int16_t sp_vendor; + u_int16_t sp_product; + const char *sp_cisinfo[4]; +} sbt_products[] = { + { SDMMC_VENDOR_SOCKETCOM, + SDMMC_PRODUCT_SOCKETCOM_BTCARD, + SDMMC_CIS_SOCKETCOM_BTCARD } +}; + +int +sbt_match(struct device *parent, void *match, void *aux) +{ + struct sdmmc_attach_args *sa = aux; + const struct sbt_product *sp; + struct sdmmc_function *sf; + int i; + + if (sa->sf == NULL) + return 0; /* not SDIO */ + + sf = sa->sf->sc->sc_fn0; + sp = &sbt_products[0]; + + for (i = 0; i < sizeof(sbt_products) / sizeof(sbt_products[0]); + i++, sp = &sbt_products[i]) + if (sp->sp_vendor == sf->cis.manufacturer && + sp->sp_product == sf->cis.product) + return 1; + return 0; +} + +void +sbt_attach(struct device *parent, struct device *self, void *aux) +{ + struct sbt_softc *sc = (struct sbt_softc *)self; + struct sdmmc_attach_args *sa = aux; + + printf("\n"); + + sc->sc_sf = sa->sf; + + (void)sdmmc_io_function_disable(sc->sc_sf); + if (sdmmc_io_function_enable(sc->sc_sf)) { + printf("%s: function not ready\n", DEVNAME(sc)); + return; + } + + /* 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_BUFSIZ_HCI, M_DEVBUF, + M_NOWAIT | M_CANFAIL); + if (sc->sc_buf == NULL) { + printf("%s: can't allocate cmd buffer\n", DEVNAME(sc)); + return; + } + + /* Enable the HCI packet transport read interrupt. */ + CSR_WRITE_1(sc, SBT_REG_IENA, ISTAT_INTRD); + + /* Enable the card interrupt for this function. */ + sc->sc_ih = sdmmc_intr_establish(parent, sbt_intr, sc, DEVNAME(sc)); + if (sc->sc_ih == NULL) { + printf("%s: can't establish interrupt\n", DEVNAME(sc)); + return; + } + sdmmc_intr_enable(sc->sc_sf); + + /* Create a thread for the packet transport. */ +#ifdef DO_CONFIG_PENDING + config_pending_incr(); +#endif + kthread_create_deferred(sbt_create_thread, sc); + + /* + * 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); +} + +void +sbt_create_thread(void *arg) +{ + struct sbt_softc *sc = arg; + struct proc *thread0; + + if (kthread_create(sbt_thread, sc, &sc->sc_thread, "%s", + DEVNAME(sc)) != 0) + printf("%s: unable to create thread\n", DEVNAME(sc)); + if (kthread_create(sbt_thread0, sc, &thread0, "%s (hci)", + DEVNAME(sc)) != 0) + printf("%s: unable to create hci thread\n", DEVNAME(sc)); +#ifdef DO_CONFIG_PENDING + config_pending_decr(); +#endif +} + +int +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); + + if (sc->sc_ih != NULL) + sdmmc_intr_disestablish(sc->sc_ih); + + return 0; +} + + +/* + * Bluetooth HCI packet transport thread (*caugh*) + */ + +void +sbt_thread0(void *arg) +{ + struct sbt_softc *sc = arg; + + /* XXX wrong place */ + if (!(sc->sc_unit.hci_flags & BTF_UP) && + hci_enable(&sc->sc_unit) == 0) + sc->sc_unit.hci_flags |= BTF_UP; + + kthread_exit(0); +} + +void +sbt_thread(void *arg) +{ + struct sbt_softc *sc = arg; + + while (!sc->sc_dying) { +#ifdef SBT_POLLING + if (sbt_intr_pending(sc) && sbt_intr(sc)) + continue; +#endif + tsleep(sc, PPAUSE, "slack", hz / 4); + } + + sc->sc_thread = NULL; + wakeup(sc); + kthread_exit(0); +} + +/* + * Bluetooth HCI packet transport + */ + +int +sbt_write_packet(struct sbt_softc *sc, u_char *buf, size_t len) +{ + u_char hdr[3]; + int error = EIO; + int retry = 3; + +again: + if (retry-- == 0) { + printf("sbt_write_cmd: giving up :-(\n"); + return error; + } + + /* Restart the current packet. */ + sdmmc_io_write_1(sc->sc_sf, SBT_REG_WPC, WPC_PCWRT); + + /* Write the packet length. */ + hdr[0] = len & 0xff; + hdr[1] = (len >> 8) & 0xff; + hdr[2] = (len >> 16) & 0xff; + error = sdmmc_io_write_multi_1(sc->sc_sf, SBT_REG_DAT, hdr, 3); + if (error) { + DPRINTF(("sbt_write_packet: failed to send length\n")); + goto again; + } + + error = sdmmc_io_write_multi_1(sc->sc_sf, SBT_REG_DAT, buf, len); + if (error) { + DPRINTF(("sbt_write_packet: failed to send packet data\n")); + goto again; + } + return 0; +} + +int +sbt_read_packet(struct sbt_softc *sc, u_char *buf, size_t *lenp) +{ + int error, retry = 3; + u_char hdr[3]; + size_t len; + +again: + if (retry-- == 0) + return error; + + error = sdmmc_io_read_multi_1(sc->sc_sf, SBT_REG_DAT, hdr, 3); + if (error) { + DPRINTF(("sbt_read_packet: failed to read length\n")); + goto again; + } + len = (hdr[0] | (hdr[1] << 8) | (hdr[2] << 16)) - 3; + if (len > *lenp) { + error = ENOMEM; + goto again; + } + + error = sdmmc_io_read_multi_1(sc->sc_sf, SBT_REG_DAT, buf, len); + if (error) { + DPRINTF(("sbt_read_packet: failed to read packet data\n")); + goto again; + } + + /* acknowledge read packet */ + CSR_WRITE_1(sc, SBT_REG_RPC, 0); + + *lenp = len; + return 0; +} + +/* + * Interrupt handling + */ + +int +sbt_intr_pending(struct sbt_softc *sc) +{ + u_char val = CSR_READ_1(sc, SBT_REG_ISTAT); + if (val != 0) + printf("sbt_intr_pending %x\n", val); + return val != 0; +} + +int +sbt_intr(void *arg) +{ + struct sbt_softc *sc = arg; + struct mbuf *m = NULL; + u_int8_t status; + size_t len; + + status = CSR_READ_1(sc, SBT_REG_ISTAT); + CSR_WRITE_1(sc, SBT_REG_ICLR, status); + printf("sbt_intr status=0x%x\n"); + + if ((status & ISTAT_INTRD) == 0) + return 0; /* shared SDIO card interrupt? */ + + len = SBT_BUFSIZ_HCI; + if (sbt_read_packet(sc, sc->sc_buf, &len) != 0) { + printf("sbt_intr: read failed\n"); + goto eoi; + } + + MGETHDR(m, M_DONTWAIT, MT_DATA); + if (m == NULL) { + printf("sbt_intr: MGETHDR failed\n"); + goto eoi; + } + + m->m_pkthdr.len = m->m_len = MHLEN; + m_copyback(m, 0, len, sc->sc_buf); + if (m->m_pkthdr.len == MAX(MHLEN, len)) { + m->m_pkthdr.len = len; + m->m_len = MIN(MHLEN, m->m_pkthdr.len); + } else { + printf("sbt_intr: m_copyback failed\n"); + m_free(m); + m = NULL; + } + +eoi: + if (m != NULL) + hci_input_event(&sc->sc_unit, m); + else + sc->sc_unit.hci_stats.err_rx++; + + /* Claim this interrupt. */ + return 1; +} + + +/* + * Bluetooth HCI unit functions + */ + +int +sbt_enable(struct hci_unit *unit) +{ + printf("sbt_enable\n"); + + if (unit->hci_flags & BTF_RUNNING) + return 0; + + unit->hci_flags |= BTF_RUNNING; + unit->hci_flags &= ~BTF_XMIT; + return 0; +} + +void +sbt_disable(struct hci_unit *unit) +{ + printf("sbt_disable\n"); + + if (!(unit->hci_flags & BTF_RUNNING)) + return; + +#ifdef notyet /* XXX */ + if (sc->sc_rxp) { + m_freem(sc->sc_rxp); + sc->sc_rxp = NULL; + } + + if (sc->sc_txp) { + m_freem(sc->sc_txp); + sc->sc_txp = NULL; + } +#endif + + unit->hci_flags &= ~BTF_RUNNING; +} + +void +sbt_start_cmd(struct hci_unit *unit) +{ + struct sbt_softc *sc = (struct sbt_softc *)unit->hci_softc; + struct mbuf *m; + int len; + + if (sc->sc_dying || IF_IS_EMPTY(&unit->hci_cmdq)) + return; + + IF_DEQUEUE(&unit->hci_cmdq, m); + + DPRINTF(("%s: xmit CMD packet (%d bytes)\n", + unit->hci_devname, m->m_pkthdr.len)); + + unit->hci_flags |= BTF_XMIT_CMD; + + len = m->m_pkthdr.len; + m_copydata(m, 0, len, sc->sc_buf); + m_freem(m); + + if (sbt_write_packet(sc, sc->sc_buf, len)) + printf("%s: sbt_write_packet failed\n", + unit->hci_devname); + + unit->hci_flags &= ~BTF_XMIT_CMD; +} + +void +sbt_start_acl(struct hci_unit *unit) +{ + printf("sbt_start_acl\n"); +} + +void +sbt_start_sco(struct hci_unit *unit) +{ + printf("sbt_start_sco\n"); +} + |