Annotation of sys/netbt/hci_unit.c, Revision 1.1
1.1 ! nbrk 1: /* $OpenBSD: hci_unit.c,v 1.7 2007/06/24 20:55:27 uwe Exp $ */
! 2: /* $NetBSD: hci_unit.c,v 1.4 2007/03/30 20:47:03 plunky Exp $ */
! 3:
! 4: /*-
! 5: * Copyright (c) 2005 Iain Hibbert.
! 6: * Copyright (c) 2006 Itronix Inc.
! 7: * All rights reserved.
! 8: *
! 9: * Redistribution and use in source and binary forms, with or without
! 10: * modification, are permitted provided that the following conditions
! 11: * are met:
! 12: * 1. Redistributions of source code must retain the above copyright
! 13: * notice, this list of conditions and the following disclaimer.
! 14: * 2. Redistributions in binary form must reproduce the above copyright
! 15: * notice, this list of conditions and the following disclaimer in the
! 16: * documentation and/or other materials provided with the distribution.
! 17: * 3. The name of Itronix Inc. may not be used to endorse
! 18: * or promote products derived from this software without specific
! 19: * prior written permission.
! 20: *
! 21: * THIS SOFTWARE IS PROVIDED BY ITRONIX INC. ``AS IS'' AND
! 22: * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
! 23: * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
! 24: * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL ITRONIX INC. BE LIABLE FOR ANY
! 25: * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
! 26: * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
! 27: * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
! 28: * ON ANY THEORY OF LIABILITY, WHETHER IN
! 29: * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
! 30: * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
! 31: * POSSIBILITY OF SUCH DAMAGE.
! 32: */
! 33:
! 34: #include <sys/cdefs.h>
! 35:
! 36: #include <sys/param.h>
! 37: #include <sys/conf.h>
! 38: #include <sys/device.h>
! 39: #include <sys/kernel.h>
! 40: #include <sys/malloc.h>
! 41: #include <sys/mbuf.h>
! 42: #include <sys/proc.h>
! 43: #include <sys/queue.h>
! 44: #include <sys/systm.h>
! 45:
! 46: #include <net/netisr.h>
! 47:
! 48: #include <netbt/bluetooth.h>
! 49: #include <netbt/hci.h>
! 50:
! 51: struct hci_unit_list hci_unit_list = TAILQ_HEAD_INITIALIZER(hci_unit_list);
! 52:
! 53: /*
! 54: * HCI Input Queue max lengths.
! 55: */
! 56: int hci_eventq_max = 20;
! 57: int hci_aclrxq_max = 50;
! 58: int hci_scorxq_max = 50;
! 59:
! 60: /*
! 61: * bluetooth unit functions
! 62: */
! 63:
! 64: void
! 65: hci_attach(struct hci_unit *unit)
! 66: {
! 67: KASSERT(unit->hci_softc != NULL);
! 68: KASSERT(unit->hci_devname != NULL);
! 69: KASSERT(unit->hci_enable != NULL);
! 70: KASSERT(unit->hci_disable != NULL);
! 71: KASSERT(unit->hci_start_cmd != NULL);
! 72: KASSERT(unit->hci_start_acl != NULL);
! 73: KASSERT(unit->hci_start_sco != NULL);
! 74:
! 75: unit->hci_eventq.ifq_maxlen = hci_eventq_max;
! 76: unit->hci_aclrxq.ifq_maxlen = hci_aclrxq_max;
! 77: unit->hci_scorxq.ifq_maxlen = hci_scorxq_max;
! 78:
! 79: TAILQ_INIT(&unit->hci_links);
! 80: LIST_INIT(&unit->hci_memos);
! 81:
! 82: TAILQ_INSERT_TAIL(&hci_unit_list, unit, hci_next);
! 83: }
! 84:
! 85: void
! 86: hci_detach(struct hci_unit *unit)
! 87: {
! 88: hci_disable(unit);
! 89:
! 90: TAILQ_REMOVE(&hci_unit_list, unit, hci_next);
! 91: }
! 92:
! 93: int
! 94: hci_enable(struct hci_unit *unit)
! 95: {
! 96: int s, err;
! 97:
! 98: /*
! 99: * Bluetooth spec says that a device can accept one
! 100: * command on power up until they send a Command Status
! 101: * or Command Complete event with more information, but
! 102: * it seems that some devices cant and prefer to send a
! 103: * No-op Command Status packet when they are ready, so
! 104: * we set this here and allow the driver (bt3c) to zero
! 105: * it.
! 106: */
! 107: unit->hci_num_cmd_pkts = 1;
! 108: unit->hci_num_acl_pkts = 0;
! 109: unit->hci_num_sco_pkts = 0;
! 110:
! 111: /*
! 112: * only allow the basic packet types until
! 113: * the features report is in
! 114: */
! 115: unit->hci_acl_mask = HCI_PKT_DM1 | HCI_PKT_DH1;
! 116: unit->hci_packet_type = unit->hci_acl_mask;
! 117:
! 118: s = splraiseipl(unit->hci_ipl);
! 119: err = (*unit->hci_enable)(unit);
! 120: splx(s);
! 121: if (err)
! 122: goto bad1;
! 123:
! 124: /*
! 125: * Reset the device, this will trigger initialisation
! 126: * and wake us up.
! 127: */
! 128: unit->hci_flags |= BTF_INIT;
! 129:
! 130: err = hci_send_cmd(unit, HCI_CMD_RESET, NULL, 0);
! 131: if (err)
! 132: goto bad2;
! 133:
! 134: while (unit->hci_flags & BTF_INIT) {
! 135: err = tsleep(unit, PWAIT | PCATCH, __func__, 5 * hz);
! 136: if (err)
! 137: goto bad2;
! 138:
! 139: /* XXX
! 140: * "What If", while we were sleeping, the device
! 141: * was removed and detached? Ho Hum.
! 142: */
! 143: }
! 144:
! 145: /*
! 146: * Attach Bluetooth Device Hub
! 147: */
! 148: unit->hci_bthub = config_found(unit->hci_softc,
! 149: &unit->hci_bdaddr, NULL);
! 150:
! 151: return 0;
! 152:
! 153: bad2:
! 154: s = splraiseipl(unit->hci_ipl);
! 155: (*unit->hci_disable)(unit);
! 156: splx(s);
! 157:
! 158: bad1:
! 159: return err;
! 160: }
! 161:
! 162: void
! 163: hci_disable(struct hci_unit *unit)
! 164: {
! 165: struct hci_link *link, *next;
! 166: struct hci_memo *memo;
! 167: int s, acl;
! 168:
! 169: if (unit->hci_bthub) {
! 170: config_detach(unit->hci_bthub, DETACH_FORCE);
! 171: unit->hci_bthub = NULL;
! 172: }
! 173:
! 174: s = splraiseipl(unit->hci_ipl);
! 175: (*unit->hci_disable)(unit);
! 176: splx(s);
! 177:
! 178: /*
! 179: * close down any links, take care to close SCO first since
! 180: * they may depend on ACL links.
! 181: */
! 182: for (acl = 0 ; acl < 2 ; acl++) {
! 183: next = TAILQ_FIRST(&unit->hci_links);
! 184: while ((link = next) != NULL) {
! 185: next = TAILQ_NEXT(link, hl_next);
! 186: if (acl || link->hl_type != HCI_LINK_ACL)
! 187: hci_link_free(link, ECONNABORTED);
! 188: }
! 189: }
! 190:
! 191: while ((memo = LIST_FIRST(&unit->hci_memos)) != NULL)
! 192: hci_memo_free(memo);
! 193:
! 194: IF_PURGE(&unit->hci_eventq);
! 195: unit->hci_eventqlen = 0;
! 196:
! 197: IF_PURGE(&unit->hci_aclrxq);
! 198: unit->hci_aclrxqlen = 0;
! 199:
! 200: IF_PURGE(&unit->hci_scorxq);
! 201: unit->hci_scorxqlen = 0;
! 202:
! 203: IF_PURGE(&unit->hci_cmdq);
! 204: IF_PURGE(&unit->hci_cmdwait);
! 205: IF_PURGE(&unit->hci_acltxq);
! 206: IF_PURGE(&unit->hci_scotxq);
! 207: IF_PURGE(&unit->hci_scodone);
! 208: }
! 209:
! 210: struct hci_unit *
! 211: hci_unit_lookup(bdaddr_t *addr)
! 212: {
! 213: struct hci_unit *unit;
! 214:
! 215: TAILQ_FOREACH(unit, &hci_unit_list, hci_next) {
! 216: if ((unit->hci_flags & BTF_UP) == 0)
! 217: continue;
! 218:
! 219: if (bdaddr_same(&unit->hci_bdaddr, addr))
! 220: break;
! 221: }
! 222:
! 223: return unit;
! 224: }
! 225:
! 226: /*
! 227: * construct and queue a HCI command packet
! 228: */
! 229: int
! 230: hci_send_cmd(struct hci_unit *unit, uint16_t opcode, void *buf, uint8_t len)
! 231: {
! 232: struct mbuf *m;
! 233: hci_cmd_hdr_t *p;
! 234:
! 235: KASSERT(unit != NULL);
! 236:
! 237: m = m_gethdr(M_DONTWAIT, MT_DATA);
! 238: if (m == NULL)
! 239: return ENOMEM;
! 240:
! 241: p = mtod(m, hci_cmd_hdr_t *);
! 242: p->type = HCI_CMD_PKT;
! 243: p->opcode = htole16(opcode);
! 244: p->length = len;
! 245: m->m_pkthdr.len = m->m_len = sizeof(hci_cmd_hdr_t);
! 246: M_SETCTX(m, NULL);
! 247:
! 248: if (len) {
! 249: KASSERT(buf != NULL);
! 250:
! 251: m_copyback(m, sizeof(hci_cmd_hdr_t), len, buf);
! 252: if (m->m_pkthdr.len != (sizeof(hci_cmd_hdr_t) + len)) {
! 253: m_freem(m);
! 254: return ENOMEM;
! 255: }
! 256: }
! 257:
! 258: DPRINTFN(2, "(%s) opcode (%3.3x|%4.4x)\n", unit->hci_devname,
! 259: HCI_OGF(opcode), HCI_OCF(opcode));
! 260:
! 261: /* and send it on */
! 262: if (unit->hci_num_cmd_pkts == 0)
! 263: IF_ENQUEUE(&unit->hci_cmdwait, m);
! 264: else
! 265: hci_output_cmd(unit, m);
! 266:
! 267: return 0;
! 268: }
! 269:
! 270: /*
! 271: * Incoming packet processing. Since the code is single threaded
! 272: * in any case (IPL_SOFTNET), we handle it all in one interrupt function
! 273: * picking our way through more important packets first so that hopefully
! 274: * we will never get clogged up with bulk data.
! 275: */
! 276: void
! 277: hci_intr(void *arg)
! 278: {
! 279: struct hci_unit *unit = arg;
! 280: struct mbuf *m;
! 281: int s;
! 282:
! 283: another:
! 284: s = splraiseipl(unit->hci_ipl);
! 285:
! 286: if (unit->hci_eventqlen > 0) {
! 287: IF_DEQUEUE(&unit->hci_eventq, m);
! 288: unit->hci_eventqlen--;
! 289: KASSERT(m != NULL);
! 290: splx(s);
! 291:
! 292: DPRINTFN(10, "(%s) recv event, len = %d\n",
! 293: unit->hci_devname, m->m_pkthdr.len);
! 294:
! 295: m->m_flags |= M_LINK0; /* mark incoming packet */
! 296: hci_mtap(m, unit);
! 297: hci_event(m, unit);
! 298:
! 299: goto another;
! 300: }
! 301:
! 302: if (unit->hci_scorxqlen > 0) {
! 303: IF_DEQUEUE(&unit->hci_scorxq, m);
! 304: unit->hci_scorxqlen--;
! 305: KASSERT(m != NULL);
! 306: splx(s);
! 307:
! 308: DPRINTFN(10, "(%s) recv SCO, len = %d\n",
! 309: unit->hci_devname, m->m_pkthdr.len);
! 310:
! 311: m->m_flags |= M_LINK0; /* mark incoming packet */
! 312: hci_mtap(m, unit);
! 313: hci_sco_recv(m, unit);
! 314:
! 315: goto another;
! 316: }
! 317:
! 318: if (unit->hci_aclrxqlen > 0) {
! 319: IF_DEQUEUE(&unit->hci_aclrxq, m);
! 320: unit->hci_aclrxqlen--;
! 321: KASSERT(m != NULL);
! 322: splx(s);
! 323:
! 324: DPRINTFN(10, "(%s) recv ACL, len = %d\n",
! 325: unit->hci_devname, m->m_pkthdr.len);
! 326:
! 327: m->m_flags |= M_LINK0; /* mark incoming packet */
! 328: hci_mtap(m, unit);
! 329: hci_acl_recv(m, unit);
! 330:
! 331: goto another;
! 332: }
! 333:
! 334: IF_DEQUEUE(&unit->hci_scodone, m);
! 335: if (m != NULL) {
! 336: struct hci_link *link;
! 337: splx(s);
! 338:
! 339: DPRINTFN(11, "(%s) complete SCO\n",
! 340: unit->hci_devname);
! 341:
! 342: TAILQ_FOREACH(link, &unit->hci_links, hl_next) {
! 343: if (link == M_GETCTX(m, struct hci_link *)) {
! 344: hci_sco_complete(link, 1);
! 345: break;
! 346: }
! 347: }
! 348:
! 349: unit->hci_num_sco_pkts++;
! 350: m_freem(m);
! 351:
! 352: goto another;
! 353: }
! 354:
! 355: splx(s);
! 356:
! 357: DPRINTFN(10, "done\n");
! 358: }
! 359:
! 360: /**********************************************************************
! 361: *
! 362: * IO routines
! 363: *
! 364: * input & complete routines will be called from device driver
! 365: * (at unit->hci_ipl)
! 366: */
! 367:
! 368: void
! 369: hci_input_event(struct hci_unit *unit, struct mbuf *m)
! 370: {
! 371: if (unit->hci_eventqlen > hci_eventq_max) {
! 372: DPRINTF("(%s) dropped event packet.\n", unit->hci_devname);
! 373: unit->hci_stats.err_rx++;
! 374: m_freem(m);
! 375: } else {
! 376: unit->hci_eventqlen++;
! 377: IF_ENQUEUE(&unit->hci_eventq, m);
! 378: schednetisr(NETISR_BT);
! 379: }
! 380: }
! 381:
! 382: void
! 383: hci_input_acl(struct hci_unit *unit, struct mbuf *m)
! 384: {
! 385: if (unit->hci_aclrxqlen > hci_aclrxq_max) {
! 386: DPRINTF("(%s) dropped ACL packet.\n", unit->hci_devname);
! 387: unit->hci_stats.err_rx++;
! 388: m_freem(m);
! 389: } else {
! 390: unit->hci_aclrxqlen++;
! 391: IF_ENQUEUE(&unit->hci_aclrxq, m);
! 392: schednetisr(NETISR_BT);
! 393: }
! 394: }
! 395:
! 396: void
! 397: hci_input_sco(struct hci_unit *unit, struct mbuf *m)
! 398: {
! 399: if (unit->hci_scorxqlen > hci_scorxq_max) {
! 400: DPRINTF("(%s) dropped SCO packet.\n", unit->hci_devname);
! 401: unit->hci_stats.err_rx++;
! 402: m_freem(m);
! 403: } else {
! 404: unit->hci_scorxqlen++;
! 405: IF_ENQUEUE(&unit->hci_scorxq, m);
! 406: schednetisr(NETISR_BT);
! 407: }
! 408: }
! 409:
! 410: void
! 411: hci_output_cmd(struct hci_unit *unit, struct mbuf *m)
! 412: {
! 413: void *arg;
! 414: int s;
! 415:
! 416: hci_mtap(m, unit);
! 417:
! 418: DPRINTFN(10, "(%s) num_cmd_pkts=%d\n", unit->hci_devname,
! 419: unit->hci_num_cmd_pkts);
! 420:
! 421: unit->hci_num_cmd_pkts--;
! 422:
! 423: /*
! 424: * If context is set, this was from a HCI raw socket
! 425: * and a record needs to be dropped from the sockbuf.
! 426: */
! 427: arg = M_GETCTX(m, void *);
! 428: if (arg != NULL)
! 429: hci_drop(arg);
! 430:
! 431: s = splraiseipl(unit->hci_ipl);
! 432: IF_ENQUEUE(&unit->hci_cmdq, m);
! 433: if ((unit->hci_flags & BTF_XMIT_CMD) == 0)
! 434: (*unit->hci_start_cmd)(unit);
! 435:
! 436: splx(s);
! 437: }
! 438:
! 439: void
! 440: hci_output_acl(struct hci_unit *unit, struct mbuf *m)
! 441: {
! 442: int s;
! 443:
! 444: hci_mtap(m, unit);
! 445:
! 446: DPRINTFN(10, "(%s) num_acl_pkts=%d\n", unit->hci_devname,
! 447: unit->hci_num_acl_pkts);
! 448:
! 449: unit->hci_num_acl_pkts--;
! 450:
! 451: s = splraiseipl(unit->hci_ipl);
! 452: IF_ENQUEUE(&unit->hci_acltxq, m);
! 453: if ((unit->hci_flags & BTF_XMIT_ACL) == 0)
! 454: (*unit->hci_start_acl)(unit);
! 455:
! 456: splx(s);
! 457: }
! 458:
! 459: void
! 460: hci_output_sco(struct hci_unit *unit, struct mbuf *m)
! 461: {
! 462: int s;
! 463:
! 464: hci_mtap(m, unit);
! 465:
! 466: DPRINTFN(10, "(%s) num_sco_pkts=%d\n", unit->hci_devname,
! 467: unit->hci_num_sco_pkts);
! 468:
! 469: unit->hci_num_sco_pkts--;
! 470:
! 471: s = splraiseipl(unit->hci_ipl);
! 472: IF_ENQUEUE(&unit->hci_scotxq, m);
! 473: if ((unit->hci_flags & BTF_XMIT_SCO) == 0)
! 474: (*unit->hci_start_sco)(unit);
! 475:
! 476: splx(s);
! 477: }
! 478:
! 479: void
! 480: hci_complete_sco(struct hci_unit *unit, struct mbuf *m)
! 481: {
! 482: IF_ENQUEUE(&unit->hci_scodone, m);
! 483: schednetisr(NETISR_BT);
! 484: }
CVSweb