Annotation of sys/dev/sdmmc/sbt.c, Revision 1.1
1.1 ! nbrk 1: /* $OpenBSD: sbt.c,v 1.9 2007/06/19 07:59:57 uwe Exp $ */
! 2:
! 3: /*
! 4: * Copyright (c) 2007 Uwe Stuehler <uwe@openbsd.org>
! 5: *
! 6: * Permission to use, copy, modify, and distribute this software for any
! 7: * purpose with or without fee is hereby granted, provided that the above
! 8: * copyright notice and this permission notice appear in all copies.
! 9: *
! 10: * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
! 11: * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
! 12: * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
! 13: * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
! 14: * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
! 15: * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
! 16: * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
! 17: */
! 18:
! 19: /* Driver for Type-A/B SDIO Bluetooth cards */
! 20:
! 21: #include <sys/param.h>
! 22: #include <sys/device.h>
! 23: #include <sys/malloc.h>
! 24: #include <sys/mbuf.h>
! 25: #include <sys/proc.h>
! 26: #include <sys/queue.h>
! 27: #include <sys/socket.h>
! 28: #include <sys/systm.h>
! 29:
! 30: #include <netbt/hci.h>
! 31:
! 32: #include <dev/sdmmc/sdmmcdevs.h>
! 33: #include <dev/sdmmc/sdmmcvar.h>
! 34:
! 35: #define CSR_READ_1(sc, reg) sdmmc_io_read_1((sc)->sc_sf, (reg))
! 36: #define CSR_WRITE_1(sc, reg, val) sdmmc_io_write_1((sc)->sc_sf, (reg), (val))
! 37:
! 38: #define SBT_REG_DAT 0x00 /* receiver/transmitter data */
! 39: #define SBT_REG_RPC 0x10 /* read packet control */
! 40: #define RPC_PCRRT (1<<0) /* packet read retry */
! 41: #define SBT_REG_WPC 0x11 /* write packet control */
! 42: #define WPC_PCWRT (1<<0) /* packet write retry */
! 43: #define SBT_REG_RC 0x12 /* retry control status/set */
! 44: #define SBT_REG_ISTAT 0x13 /* interrupt status */
! 45: #define ISTAT_INTRD (1<<0) /* packet available for read */
! 46: #define SBT_REG_ICLR 0x13 /* interrupt clear */
! 47: #define SBT_REG_IENA 0x14 /* interrupt enable */
! 48: #define SBT_REG_BTMODE 0x20 /* SDIO Bluetooth card mode */
! 49: #define BTMODE_TYPEB (1<<0) /* 1=Type-B, 0=Type-A */
! 50:
! 51: #define SBT_PKT_BUFSIZ 65540
! 52: #define SBT_RXTRY_MAX 5
! 53:
! 54: struct sbt_softc {
! 55: struct device sc_dev; /* base device */
! 56: struct hci_unit sc_unit; /* MI host controller */
! 57: struct sdmmc_function *sc_sf; /* SDIO function */
! 58: struct proc *sc_thread; /* inquiry thread */
! 59: int sc_dying; /* shutdown in progress */
! 60: void *sc_ih;
! 61: u_char *sc_buf;
! 62: int sc_rxtry;
! 63: };
! 64:
! 65: int sbt_match(struct device *, void *, void *);
! 66: void sbt_attach(struct device *, struct device *, void *);
! 67: int sbt_detach(struct device *, int);
! 68:
! 69: int sbt_write_packet(struct sbt_softc *, u_char *, size_t);
! 70: int sbt_read_packet(struct sbt_softc *, u_char *, size_t *);
! 71:
! 72: int sbt_intr(void *);
! 73:
! 74: int sbt_enable(struct hci_unit *);
! 75: void sbt_disable(struct hci_unit *);
! 76: void sbt_start(struct hci_unit *, struct ifqueue *, int);
! 77: void sbt_start_cmd(struct hci_unit *);
! 78: void sbt_start_acl(struct hci_unit *);
! 79: void sbt_start_sco(struct hci_unit *);
! 80:
! 81: #undef DPRINTF
! 82: #define SBT_DEBUG
! 83: #ifdef SBT_DEBUG
! 84: int sbt_debug = 1;
! 85: #define DPRINTF(s) printf s
! 86: #define DNPRINTF(n, s) do { if ((n) <= sbt_debug) printf s; } while (0)
! 87: #else
! 88: #define DPRINTF(s) do {} while (0)
! 89: #define DNPRINTF(n, s) do {} while (0)
! 90: #endif
! 91:
! 92: #define DEVNAME(sc) ((sc)->sc_dev.dv_xname)
! 93:
! 94: struct cfattach sbt_ca = {
! 95: sizeof(struct sbt_softc), sbt_match, sbt_attach, sbt_detach
! 96: };
! 97:
! 98: struct cfdriver sbt_cd = {
! 99: NULL, "sbt", DV_DULL
! 100: };
! 101:
! 102:
! 103: /*
! 104: * Autoconf glue
! 105: */
! 106:
! 107: static const struct sbt_product {
! 108: u_int16_t sp_vendor;
! 109: u_int16_t sp_product;
! 110: const char *sp_cisinfo[4];
! 111: } sbt_products[] = {
! 112: { SDMMC_VENDOR_SOCKETCOM,
! 113: SDMMC_PRODUCT_SOCKETCOM_BTCARD,
! 114: SDMMC_CIS_SOCKETCOM_BTCARD }
! 115: };
! 116:
! 117: int
! 118: sbt_match(struct device *parent, void *match, void *aux)
! 119: {
! 120: struct sdmmc_attach_args *sa = aux;
! 121: const struct sbt_product *sp;
! 122: struct sdmmc_function *sf;
! 123: int i;
! 124:
! 125: if (sa->sf == NULL)
! 126: return 0; /* not SDIO */
! 127:
! 128: sf = sa->sf->sc->sc_fn0;
! 129: sp = &sbt_products[0];
! 130:
! 131: for (i = 0; i < sizeof(sbt_products) / sizeof(sbt_products[0]);
! 132: i++, sp = &sbt_products[i])
! 133: if (sp->sp_vendor == sf->cis.manufacturer &&
! 134: sp->sp_product == sf->cis.product)
! 135: return 1;
! 136: return 0;
! 137: }
! 138:
! 139: void
! 140: sbt_attach(struct device *parent, struct device *self, void *aux)
! 141: {
! 142: struct sbt_softc *sc = (struct sbt_softc *)self;
! 143: struct sdmmc_attach_args *sa = aux;
! 144:
! 145: printf("\n");
! 146:
! 147: sc->sc_sf = sa->sf;
! 148:
! 149: (void)sdmmc_io_function_disable(sc->sc_sf);
! 150: if (sdmmc_io_function_enable(sc->sc_sf)) {
! 151: printf("%s: function not ready\n", DEVNAME(sc));
! 152: return;
! 153: }
! 154:
! 155: /* It may be Type-B, but we use it only in Type-A mode. */
! 156: printf("%s: SDIO Bluetooth Type-A\n", DEVNAME(sc));
! 157:
! 158: sc->sc_buf = malloc(SBT_PKT_BUFSIZ, M_DEVBUF,
! 159: M_NOWAIT | M_CANFAIL);
! 160: if (sc->sc_buf == NULL) {
! 161: printf("%s: can't allocate cmd buffer\n", DEVNAME(sc));
! 162: return;
! 163: }
! 164:
! 165: /* Enable the HCI packet transport read interrupt. */
! 166: CSR_WRITE_1(sc, SBT_REG_IENA, ISTAT_INTRD);
! 167:
! 168: /* Enable the card interrupt for this function. */
! 169: sc->sc_ih = sdmmc_intr_establish(parent, sbt_intr, sc, DEVNAME(sc));
! 170: if (sc->sc_ih == NULL) {
! 171: printf("%s: can't establish interrupt\n", DEVNAME(sc));
! 172: return;
! 173: }
! 174: sdmmc_intr_enable(sc->sc_sf);
! 175:
! 176: /*
! 177: * Attach Bluetooth unit (machine-independent HCI).
! 178: */
! 179: sc->sc_unit.hci_softc = self;
! 180: sc->sc_unit.hci_devname = DEVNAME(sc);
! 181: sc->sc_unit.hci_enable = sbt_enable;
! 182: sc->sc_unit.hci_disable = sbt_disable;
! 183: sc->sc_unit.hci_start_cmd = sbt_start_cmd;
! 184: sc->sc_unit.hci_start_acl = sbt_start_acl;
! 185: sc->sc_unit.hci_start_sco = sbt_start_sco;
! 186: sc->sc_unit.hci_ipl = IPL_TTY; /* XXX */
! 187: hci_attach(&sc->sc_unit);
! 188: }
! 189:
! 190: int
! 191: sbt_detach(struct device *self, int flags)
! 192: {
! 193: struct sbt_softc *sc = (struct sbt_softc *)self;
! 194:
! 195: sc->sc_dying = 1;
! 196: while (sc->sc_thread != NULL)
! 197: tsleep(sc, PWAIT, "dying", 0);
! 198:
! 199: hci_detach(&sc->sc_unit);
! 200:
! 201: if (sc->sc_ih != NULL)
! 202: sdmmc_intr_disestablish(sc->sc_ih);
! 203:
! 204: return 0;
! 205: }
! 206:
! 207:
! 208: /*
! 209: * Bluetooth HCI packet transport
! 210: */
! 211:
! 212: int
! 213: sbt_write_packet(struct sbt_softc *sc, u_char *buf, size_t len)
! 214: {
! 215: u_char hdr[3];
! 216: size_t pktlen;
! 217: int error = EIO;
! 218: int retry = 3;
! 219:
! 220: again:
! 221: if (retry-- == 0) {
! 222: DPRINTF(("%s: sbt_write_cmd: giving up\n", DEVNAME(sc)));
! 223: return error;
! 224: }
! 225:
! 226: /* Restart the current packet. */
! 227: sdmmc_io_write_1(sc->sc_sf, SBT_REG_WPC, WPC_PCWRT);
! 228:
! 229: /* Write the packet length. */
! 230: pktlen = len + 3;
! 231: hdr[0] = pktlen & 0xff;
! 232: hdr[1] = (pktlen >> 8) & 0xff;
! 233: hdr[2] = (pktlen >> 16) & 0xff;
! 234: error = sdmmc_io_write_multi_1(sc->sc_sf, SBT_REG_DAT, hdr, 3);
! 235: if (error) {
! 236: DPRINTF(("%s: sbt_write_packet: failed to send length\n",
! 237: DEVNAME(sc)));
! 238: goto again;
! 239: }
! 240:
! 241: error = sdmmc_io_write_multi_1(sc->sc_sf, SBT_REG_DAT, buf, len);
! 242: if (error) {
! 243: DPRINTF(("%s: sbt_write_packet: failed to send packet data\n",
! 244: DEVNAME(sc)));
! 245: goto again;
! 246: }
! 247: return 0;
! 248: }
! 249:
! 250: int
! 251: sbt_read_packet(struct sbt_softc *sc, u_char *buf, size_t *lenp)
! 252: {
! 253: u_char hdr[3];
! 254: size_t len;
! 255: int error;
! 256:
! 257: error = sdmmc_io_read_multi_1(sc->sc_sf, SBT_REG_DAT, hdr, 3);
! 258: if (error) {
! 259: DPRINTF(("%s: sbt_read_packet: failed to read length\n",
! 260: DEVNAME(sc)));
! 261: goto out;
! 262: }
! 263: len = (hdr[0] | (hdr[1] << 8) | (hdr[2] << 16)) - 3;
! 264: if (len > *lenp) {
! 265: DPRINTF(("%s: sbt_read_packet: len %u > %u\n",
! 266: DEVNAME(sc), len, *lenp));
! 267: error = ENOBUFS;
! 268: goto out;
! 269: }
! 270:
! 271: DNPRINTF(2,("%s: sbt_read_packet: reading len %u bytes\n",
! 272: DEVNAME(sc), len));
! 273: error = sdmmc_io_read_multi_1(sc->sc_sf, SBT_REG_DAT, buf, len);
! 274: if (error) {
! 275: DPRINTF(("%s: sbt_read_packet: failed to read packet data\n",
! 276: DEVNAME(sc)));
! 277: goto out;
! 278: }
! 279:
! 280: out:
! 281: if (error) {
! 282: if (sc->sc_rxtry >= SBT_RXTRY_MAX) {
! 283: /* Drop and request the next packet. */
! 284: sc->sc_rxtry = 0;
! 285: CSR_WRITE_1(sc, SBT_REG_RPC, 0);
! 286: } else {
! 287: /* Request the current packet again. */
! 288: sc->sc_rxtry++;
! 289: CSR_WRITE_1(sc, SBT_REG_RPC, RPC_PCRRT);
! 290: }
! 291: return error;
! 292: }
! 293:
! 294: /* acknowledge read packet */
! 295: CSR_WRITE_1(sc, SBT_REG_RPC, 0);
! 296:
! 297: *lenp = len;
! 298: return 0;
! 299: }
! 300:
! 301: /*
! 302: * Interrupt handling
! 303: */
! 304:
! 305: int
! 306: sbt_intr(void *arg)
! 307: {
! 308: struct sbt_softc *sc = arg;
! 309: struct mbuf *m = NULL;
! 310: u_int8_t status;
! 311: size_t len;
! 312: int s;
! 313:
! 314: s = splsdmmc();
! 315:
! 316: status = CSR_READ_1(sc, SBT_REG_ISTAT);
! 317: CSR_WRITE_1(sc, SBT_REG_ICLR, status);
! 318:
! 319: if ((status & ISTAT_INTRD) == 0)
! 320: return 0; /* shared SDIO card interrupt? */
! 321:
! 322: len = SBT_PKT_BUFSIZ;
! 323: if (sbt_read_packet(sc, sc->sc_buf, &len) != 0 || len == 0) {
! 324: DPRINTF(("%s: sbt_intr: read failed\n", DEVNAME(sc)));
! 325: goto eoi;
! 326: }
! 327:
! 328: MGETHDR(m, M_DONTWAIT, MT_DATA);
! 329: if (m == NULL) {
! 330: DPRINTF(("%s: sbt_intr: MGETHDR failed\n", DEVNAME(sc)));
! 331: goto eoi;
! 332: }
! 333:
! 334: m->m_pkthdr.len = m->m_len = MHLEN;
! 335: m_copyback(m, 0, len, sc->sc_buf);
! 336: if (m->m_pkthdr.len == MAX(MHLEN, len)) {
! 337: m->m_pkthdr.len = len;
! 338: m->m_len = MIN(MHLEN, m->m_pkthdr.len);
! 339: } else {
! 340: DPRINTF(("%s: sbt_intr: m_copyback failed\n", DEVNAME(sc)));
! 341: m_free(m);
! 342: m = NULL;
! 343: }
! 344:
! 345: eoi:
! 346: if (m != NULL) {
! 347: switch (sc->sc_buf[0]) {
! 348: case HCI_ACL_DATA_PKT:
! 349: DNPRINTF(1,("%s: recv ACL packet (%d bytes)\n",
! 350: DEVNAME(sc), m->m_pkthdr.len));
! 351: hci_input_acl(&sc->sc_unit, m);
! 352: break;
! 353: case HCI_SCO_DATA_PKT:
! 354: DNPRINTF(1,("%s: recv SCO packet (%d bytes)\n",
! 355: DEVNAME(sc), m->m_pkthdr.len));
! 356: hci_input_sco(&sc->sc_unit, m);
! 357: break;
! 358: case HCI_EVENT_PKT:
! 359: DNPRINTF(1,("%s: recv EVENT packet (%d bytes)\n",
! 360: DEVNAME(sc), m->m_pkthdr.len));
! 361: hci_input_event(&sc->sc_unit, m);
! 362: break;
! 363: default:
! 364: DPRINTF(("%s: recv 0x%x packet (%d bytes)\n",
! 365: DEVNAME(sc), sc->sc_buf[0], m->m_pkthdr.len));
! 366: sc->sc_unit.hci_stats.err_rx++;
! 367: m_free(m);
! 368: break;
! 369: }
! 370: } else
! 371: sc->sc_unit.hci_stats.err_rx++;
! 372:
! 373: splx(s);
! 374:
! 375: /* Claim this interrupt. */
! 376: return 1;
! 377: }
! 378:
! 379:
! 380: /*
! 381: * Bluetooth HCI unit functions
! 382: */
! 383:
! 384: int
! 385: sbt_enable(struct hci_unit *unit)
! 386: {
! 387: if (unit->hci_flags & BTF_RUNNING)
! 388: return 0;
! 389:
! 390: unit->hci_flags |= BTF_RUNNING;
! 391: unit->hci_flags &= ~BTF_XMIT;
! 392: return 0;
! 393: }
! 394:
! 395: void
! 396: sbt_disable(struct hci_unit *unit)
! 397: {
! 398: if (!(unit->hci_flags & BTF_RUNNING))
! 399: return;
! 400:
! 401: #ifdef notyet /* XXX */
! 402: if (sc->sc_rxp) {
! 403: m_freem(sc->sc_rxp);
! 404: sc->sc_rxp = NULL;
! 405: }
! 406:
! 407: if (sc->sc_txp) {
! 408: m_freem(sc->sc_txp);
! 409: sc->sc_txp = NULL;
! 410: }
! 411: #endif
! 412:
! 413: unit->hci_flags &= ~BTF_RUNNING;
! 414: }
! 415:
! 416: void
! 417: sbt_start(struct hci_unit *unit, struct ifqueue *q, int xmit)
! 418: {
! 419: struct sbt_softc *sc = (struct sbt_softc *)unit->hci_softc;
! 420: struct mbuf *m;
! 421: int len;
! 422: #ifdef SBT_DEBUG
! 423: const char *what;
! 424: #endif
! 425:
! 426: if (sc->sc_dying || IF_IS_EMPTY(q))
! 427: return;
! 428:
! 429: IF_DEQUEUE(q, m);
! 430:
! 431: #ifdef SBT_DEBUG
! 432: switch (xmit) {
! 433: case BTF_XMIT_CMD:
! 434: what = "CMD";
! 435: break;
! 436: case BTF_XMIT_ACL:
! 437: what = "ACL";
! 438: break;
! 439: case BTF_XMIT_SCO:
! 440: what = "SCO";
! 441: break;
! 442: }
! 443: DNPRINTF(1,("%s: xmit %s packet (%d bytes)\n", DEVNAME(sc),
! 444: what, m->m_pkthdr.len));
! 445: #endif
! 446:
! 447: unit->hci_flags |= xmit;
! 448:
! 449: len = m->m_pkthdr.len;
! 450: m_copydata(m, 0, len, sc->sc_buf);
! 451: m_freem(m);
! 452:
! 453: if (sbt_write_packet(sc, sc->sc_buf, len))
! 454: DPRINTF(("%s: sbt_write_packet failed\n", DEVNAME(sc)));
! 455:
! 456: unit->hci_flags &= ~xmit;
! 457: }
! 458:
! 459: void
! 460: sbt_start_cmd(struct hci_unit *unit)
! 461: {
! 462: sbt_start(unit, &unit->hci_cmdq, BTF_XMIT_CMD);
! 463: }
! 464:
! 465: void
! 466: sbt_start_acl(struct hci_unit *unit)
! 467: {
! 468: sbt_start(unit, &unit->hci_acltxq, BTF_XMIT_ACL);
! 469: }
! 470:
! 471: void
! 472: sbt_start_sco(struct hci_unit *unit)
! 473: {
! 474: sbt_start(unit, &unit->hci_scotxq, BTF_XMIT_SCO);
! 475: }
CVSweb