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