1 /* 2 * ng_hci_ulpi.c 3 * 4 * Copyright (c) Maksim Yevmenkin <m_evmenkin@yahoo.com> 5 * All rights reserved. 6 * 7 * Redistribution and use in source and binary forms, with or without 8 * modification, are permitted provided that the following conditions 9 * are met: 10 * 1. Redistributions of source code must retain the above copyright 11 * notice, this list of conditions and the following disclaimer. 12 * 2. Redistributions in binary form must reproduce the above copyright 13 * notice, this list of conditions and the following disclaimer in the 14 * documentation and/or other materials provided with the distribution. 15 * 16 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 17 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 18 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 19 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 20 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 21 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 22 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 23 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 24 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 25 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 26 * SUCH DAMAGE. 27 * 28 * $Id: ng_hci_ulpi.c,v 1.14 2002/11/12 22:35:40 max Exp $ 29 * $FreeBSD$ 30 */ 31 32 #include <sys/param.h> 33 #include <sys/systm.h> 34 #include <sys/kernel.h> 35 #include <sys/endian.h> 36 #include <sys/malloc.h> 37 #include <sys/mbuf.h> 38 #include <sys/queue.h> 39 #include <netgraph/ng_message.h> 40 #include <netgraph/netgraph.h> 41 #include "ng_bluetooth.h" 42 #include "ng_hci.h" 43 #include "ng_hci_var.h" 44 #include "ng_hci_cmds.h" 45 #include "ng_hci_evnt.h" 46 #include "ng_hci_ulpi.h" 47 #include "ng_hci_misc.h" 48 49 /****************************************************************************** 50 ****************************************************************************** 51 ** Upper Layer Protocol Interface module 52 ****************************************************************************** 53 ******************************************************************************/ 54 55 static int ng_hci_lp_acl_con_req (ng_hci_unit_p, item_p, hook_p); 56 static int ng_hci_lp_sco_con_req (ng_hci_unit_p, item_p, hook_p); 57 58 /* 59 * Process LP_ConnectReq event from the upper layer protocol 60 */ 61 62 int 63 ng_hci_lp_con_req(ng_hci_unit_p unit, item_p item, hook_p hook) 64 { 65 if ((unit->state & NG_HCI_UNIT_READY) != NG_HCI_UNIT_READY) { 66 NG_HCI_WARN( 67 "%s: %s - unit is not ready, state=%#x\n", 68 __func__, NG_NODE_NAME(unit->node), unit->state); 69 70 NG_FREE_ITEM(item); 71 72 return (ENXIO); 73 } 74 75 if (NGI_MSG(item)->header.arglen != sizeof(ng_hci_lp_con_req_ep)) { 76 NG_HCI_ALERT( 77 "%s: %s - invalid LP_ConnectReq message size=%d\n", 78 __func__, NG_NODE_NAME(unit->node), 79 NGI_MSG(item)->header.arglen); 80 81 NG_FREE_ITEM(item); 82 83 return (EMSGSIZE); 84 } 85 86 if (((ng_hci_lp_con_req_ep *)(NGI_MSG(item)->data))->link_type == NG_HCI_LINK_ACL) 87 return (ng_hci_lp_acl_con_req(unit, item, hook)); 88 89 if (hook != unit->sco) { 90 NG_HCI_WARN( 91 "%s: %s - LP_ConnectReq for SCO connection came from wrong hook=%p\n", 92 __func__, NG_NODE_NAME(unit->node), hook); 93 94 NG_FREE_ITEM(item); 95 96 return (EINVAL); 97 } 98 99 return (ng_hci_lp_sco_con_req(unit, item, hook)); 100 } /* ng_hci_lp_con_req */ 101 102 /* 103 * Request to create new ACL connection 104 */ 105 106 static int 107 ng_hci_lp_acl_con_req(ng_hci_unit_p unit, item_p item, hook_p hook) 108 { 109 struct acl_con_req { 110 ng_hci_cmd_pkt_t hdr; 111 ng_hci_create_con_cp cp; 112 } __attribute__ ((packed)) *req = NULL; 113 ng_hci_lp_con_req_ep *ep = NULL; 114 ng_hci_unit_con_p con = NULL; 115 ng_hci_neighbor_t *n = NULL; 116 struct mbuf *m = NULL; 117 int error = 0; 118 119 ep = (ng_hci_lp_con_req_ep *)(NGI_MSG(item)->data); 120 121 /* 122 * Only one ACL connection can exist between each pair of units. 123 * So try to find ACL connection descriptor (in any state) that 124 * has requested remote BD_ADDR. 125 * 126 * Two cases: 127 * 128 * 1) We do not have connection to the remote unit. This is simple. 129 * Just create new connection descriptor and send HCI command to 130 * create new connection. 131 * 132 * 2) We do have connection descriptor. We need to check connection 133 * state: 134 * 135 * 2.1) NG_HCI_CON_CLOSED mean we are in the process of closing 136 * connection to the remote unit. We will reject connection 137 * request until connection is closed. 138 * 139 * 2.2) NG_HCI_CON_W4_LP_CON_RSP means that we are in the middle of 140 * accepting connection from the remote unit. This is a race 141 * condition. We will ignore this message. 142 * 143 * 2.3) NG_HCI_CON_W4_CONN_COMPLETE means that upper layer already 144 * requested connection or we just accepted it. In any case 145 * all we need to do here is set appropriate notification bit 146 * and wait. 147 * 148 * 2.4) NG_HCI_CON_OPEN means connection is open. Just reply back 149 * and let upper layer know that we have connection already. 150 */ 151 152 con = ng_hci_con_by_bdaddr(unit, &ep->bdaddr, NG_HCI_LINK_ACL); 153 if (con != NULL) { 154 switch (con->state) { 155 case NG_HCI_CON_CLOSED: 156 error = EBUSY; 157 break; 158 159 case NG_HCI_CON_W4_LP_CON_RSP: /* XXX */ 160 error = EALREADY; 161 break; 162 163 case NG_HCI_CON_W4_CONN_COMPLETE: 164 if (hook == unit->acl) 165 con->flags |= NG_HCI_CON_NOTIFY_ACL; 166 else 167 con->flags |= NG_HCI_CON_NOTIFY_SCO; 168 break; 169 170 case NG_HCI_CON_OPEN: { 171 struct ng_mesg *msg = NULL; 172 ng_hci_lp_con_cfm_ep *cfm = NULL; 173 174 if (hook != NULL && NG_HOOK_IS_VALID(hook)) { 175 NGI_GET_MSG(item, msg); 176 NG_FREE_MSG(msg); 177 178 NG_MKMESSAGE(msg, NGM_HCI_COOKIE, 179 NGM_HCI_LP_CON_CFM, sizeof(*cfm), 180 M_NOWAIT); 181 if (msg != NULL) { 182 cfm = (ng_hci_lp_con_cfm_ep *)msg->data; 183 cfm->status = 0; 184 cfm->link_type = con->link_type; 185 cfm->con_handle = con->con_handle; 186 bcopy(&con->bdaddr, &cfm->bdaddr, 187 sizeof(cfm->bdaddr)); 188 189 /* 190 * This will forward item back to 191 * sender and set item to NULL 192 */ 193 194 _NGI_MSG(item) = msg; 195 NG_FWD_ITEM_HOOK(error, item, hook); 196 } else 197 error = ENOMEM; 198 } else 199 NG_HCI_INFO( 200 "%s: %s - Source hook is not valid, hook=%p\n", 201 __func__, NG_NODE_NAME(unit->node), 202 hook); 203 } break; 204 205 default: 206 KASSERT(0, 207 ("%s: %s - Invalid connection state=%d\n", 208 __func__, NG_NODE_NAME(unit->node),con->state)); 209 210 error = EINVAL; 211 break; 212 } 213 214 goto out; 215 } 216 217 /* 218 * If we got here then we need to create new ACL connection descriptor 219 * and submit HCI command. First create new connection desriptor, set 220 * bdaddr and notification flags. 221 */ 222 223 con = ng_hci_new_con(unit, NG_HCI_LINK_ACL); 224 if (con == NULL) { 225 error = ENOMEM; 226 goto out; 227 } 228 229 bcopy(&ep->bdaddr, &con->bdaddr, sizeof(con->bdaddr)); 230 231 /* 232 * Create HCI command 233 */ 234 235 MGETHDR(m, M_DONTWAIT, MT_DATA); 236 if (m == NULL) { 237 ng_hci_free_con(con); 238 error = ENOBUFS; 239 goto out; 240 } 241 242 m->m_pkthdr.len = m->m_len = sizeof(*req); 243 req = mtod(m, struct acl_con_req *); 244 req->hdr.type = NG_HCI_CMD_PKT; 245 req->hdr.length = sizeof(req->cp); 246 req->hdr.opcode = htole16(NG_HCI_OPCODE(NG_HCI_OGF_LINK_CONTROL, 247 NG_HCI_OCF_CREATE_CON)); 248 249 bcopy(&ep->bdaddr, &req->cp.bdaddr, sizeof(req->cp.bdaddr)); 250 251 req->cp.pkt_type = (NG_HCI_PKT_DM1|NG_HCI_PKT_DH1); 252 if (unit->features[0] & NG_HCI_LMP_3SLOT) 253 req->cp.pkt_type |= (NG_HCI_PKT_DM3|NG_HCI_PKT_DH3); 254 if (unit->features[0] & NG_HCI_LMP_5SLOT) 255 req->cp.pkt_type |= (NG_HCI_PKT_DM5|NG_HCI_PKT_DH5); 256 257 req->cp.pkt_type &= unit->packet_mask; 258 if (req->cp.pkt_type == 0) 259 req->cp.pkt_type = (NG_HCI_PKT_DM1|NG_HCI_PKT_DH1); 260 261 req->cp.pkt_type = htole16(req->cp.pkt_type); 262 263 if (unit->features[0] & NG_HCI_LMP_SWITCH) 264 req->cp.accept_role_switch = 1; 265 else 266 req->cp.accept_role_switch = 0; 267 268 /* 269 * We may speed up connect by specifying valid parameters. 270 * So check the neighbor cache. 271 */ 272 273 n = ng_hci_get_neighbor(unit, &ep->bdaddr); 274 if (n == NULL) { 275 req->cp.page_scan_rep_mode = 0; 276 req->cp.page_scan_mode = 0; 277 req->cp.clock_offset = 0; 278 } else { 279 req->cp.page_scan_rep_mode = n->page_scan_rep_mode; 280 req->cp.page_scan_mode = n->page_scan_mode; 281 req->cp.clock_offset = htole16(n->clock_offset); 282 } 283 284 /* 285 * Adust connection state 286 */ 287 288 if (hook == unit->acl) 289 con->flags |= NG_HCI_CON_NOTIFY_ACL; 290 else 291 con->flags |= NG_HCI_CON_NOTIFY_SCO; 292 293 con->state = NG_HCI_CON_W4_CONN_COMPLETE; 294 ng_hci_con_timeout(con); 295 296 /* 297 * Queue and send HCI command 298 */ 299 300 NG_BT_MBUFQ_ENQUEUE(&unit->cmdq, m); 301 if (!(unit->state & NG_HCI_UNIT_COMMAND_PENDING)) 302 error = ng_hci_send_command(unit); 303 out: 304 if (item != NULL) 305 NG_FREE_ITEM(item); 306 307 return (error); 308 } /* ng_hci_lp_acl_con_req */ 309 310 /* 311 * Request to create new SCO connection 312 */ 313 314 static int 315 ng_hci_lp_sco_con_req(ng_hci_unit_p unit, item_p item, hook_p hook) 316 { 317 struct sco_con_req { 318 ng_hci_cmd_pkt_t hdr; 319 ng_hci_add_sco_con_cp cp; 320 } __attribute__ ((packed)) *req = NULL; 321 ng_hci_lp_con_req_ep *ep = NULL; 322 ng_hci_unit_con_p acl_con = NULL, sco_con = NULL; 323 struct mbuf *m = NULL; 324 int error = 0; 325 326 ep = (ng_hci_lp_con_req_ep *)(NGI_MSG(item)->data); 327 328 /* 329 * SCO connection without ACL link 330 * 331 * If upper layer requests SCO connection and there is no open ACL 332 * connection to the desired remote unit, we will reject the request. 333 */ 334 335 LIST_FOREACH(acl_con, &unit->con_list, next) 336 if (acl_con->link_type == NG_HCI_LINK_ACL && 337 acl_con->state == NG_HCI_CON_OPEN && 338 bcmp(&acl_con->bdaddr, &ep->bdaddr, sizeof(bdaddr_t)) == 0) 339 break; 340 341 if (acl_con == NULL) { 342 NG_HCI_INFO( 343 "%s: %s - No open ACL connection to bdaddr=%x:%x:%x:%x:%x:%x\n", 344 __func__, NG_NODE_NAME(unit->node), 345 ep->bdaddr.b[5], ep->bdaddr.b[4], ep->bdaddr.b[3], 346 ep->bdaddr.b[2], ep->bdaddr.b[1], ep->bdaddr.b[0]); 347 348 error = ENOENT; 349 goto out; 350 } 351 352 /* 353 * Multiple SCO connections can exist between the same pair of units. 354 * We assume that multiple SCO connections have to be opened one after 355 * another. 356 * 357 * Try to find SCO connection descriptor that matches the following: 358 * 359 * 1) sco_con->link_type == NG_HCI_LINK_SCO 360 * 361 * 2) sco_con->state == NG_HCI_CON_W4_LP_CON_RSP || 362 * sco_con->state == NG_HCI_CON_W4_CONN_COMPLETE 363 * 364 * 3) sco_con->bdaddr == ep->bdaddr 365 * 366 * Two cases: 367 * 368 * 1) We do not have connection descriptor. This is simple. Just 369 * create new connection and submit Add_SCO_Connection command. 370 * 371 * 2) We do have connection descriptor. We need to check the state. 372 * 373 * 2.1) NG_HCI_CON_W4_LP_CON_RSP means we in the middle of accepting 374 * connection from the remote unit. This is a race condition and 375 * we will ignore the request. 376 * 377 * 2.2) NG_HCI_CON_W4_CONN_COMPLETE means upper layer already requested 378 * connection or we just accepted it. 379 * 380 * XXX FIXME what to do with connection(s) in CLOSED state? 381 */ 382 383 LIST_FOREACH(sco_con, &unit->con_list, next) 384 if (sco_con->link_type == NG_HCI_LINK_SCO && 385 (sco_con->state == NG_HCI_CON_W4_LP_CON_RSP || 386 sco_con->state == NG_HCI_CON_W4_CONN_COMPLETE) && 387 bcmp(&sco_con->bdaddr, &ep->bdaddr, sizeof(bdaddr_t)) == 0) 388 break; 389 390 if (sco_con != NULL) { 391 switch (sco_con->state) { 392 case NG_HCI_CON_W4_LP_CON_RSP: /* XXX */ 393 error = EALREADY; 394 break; 395 396 case NG_HCI_CON_W4_CONN_COMPLETE: 397 sco_con->flags |= NG_HCI_CON_NOTIFY_SCO; 398 break; 399 400 default: 401 KASSERT(0, 402 ("%s: %s - Inavalid connection state=%d\n", 403 __func__, NG_NODE_NAME(unit->node), 404 sco_con->state)); 405 406 error = EINVAL; 407 break; 408 } 409 410 goto out; 411 } 412 413 /* 414 * If we got here then we need to create new SCO connection descriptor 415 * and submit HCI command. 416 */ 417 418 sco_con = ng_hci_new_con(unit, NG_HCI_LINK_SCO); 419 if (sco_con == NULL) { 420 error = ENOMEM; 421 goto out; 422 } 423 424 bcopy(&ep->bdaddr, &sco_con->bdaddr, sizeof(sco_con->bdaddr)); 425 426 /* 427 * Create HCI command 428 */ 429 430 MGETHDR(m, M_DONTWAIT, MT_DATA); 431 if (m == NULL) { 432 ng_hci_free_con(sco_con); 433 error = ENOBUFS; 434 goto out; 435 } 436 437 m->m_pkthdr.len = m->m_len = sizeof(*req); 438 req = mtod(m, struct sco_con_req *); 439 req->hdr.type = NG_HCI_CMD_PKT; 440 req->hdr.length = sizeof(req->cp); 441 req->hdr.opcode = htole16(NG_HCI_OPCODE(NG_HCI_OGF_LINK_CONTROL, 442 NG_HCI_OCF_ADD_SCO_CON)); 443 444 req->cp.con_handle = htole16(acl_con->con_handle); 445 446 req->cp.pkt_type = NG_HCI_PKT_HV1; 447 if (unit->features[1] & NG_HCI_LMP_HV2_PKT) 448 req->cp.pkt_type |= NG_HCI_PKT_HV2; 449 if (unit->features[1] & NG_HCI_LMP_HV3_PKT) 450 req->cp.pkt_type |= NG_HCI_PKT_HV3; 451 452 req->cp.pkt_type &= unit->packet_mask; 453 if (req->cp.pkt_type == 0) 454 req->cp.pkt_type = NG_HCI_PKT_HV1; 455 456 req->cp.pkt_type = htole16(req->cp.pkt_type); 457 458 /* 459 * Adust connection state 460 */ 461 462 sco_con->flags |= NG_HCI_CON_NOTIFY_SCO; 463 464 sco_con->state = NG_HCI_CON_W4_CONN_COMPLETE; 465 ng_hci_con_timeout(sco_con); 466 467 /* 468 * Queue and send HCI command 469 */ 470 471 NG_BT_MBUFQ_ENQUEUE(&unit->cmdq, m); 472 if (!(unit->state & NG_HCI_UNIT_COMMAND_PENDING)) 473 error = ng_hci_send_command(unit); 474 out: 475 NG_FREE_ITEM(item); 476 477 return (error); 478 } /* ng_hci_lp_sco_con_req */ 479 480 /* 481 * Process LP_DisconnectReq event from the upper layer protocol 482 * 483 * XXX XXX XXX 484 * 485 * NOTE: This is NOT defined by Bluetooth specification (why?) But i think 486 * this might be useful (at least for testing), so please do not depend on 487 * this interface. 488 */ 489 490 int 491 ng_hci_lp_discon_req(ng_hci_unit_p unit, item_p item, hook_p hook) 492 { 493 struct discon_req { 494 ng_hci_cmd_pkt_t hdr; 495 ng_hci_discon_cp cp; 496 } __attribute__ ((packed)) *req = NULL; 497 ng_hci_lp_discon_req_ep *ep = NULL; 498 ng_hci_unit_con_p con = NULL; 499 struct mbuf *m = NULL; 500 int error = 0; 501 502 /* Check if unit is ready */ 503 if ((unit->state & NG_HCI_UNIT_READY) != NG_HCI_UNIT_READY) { 504 NG_HCI_WARN( 505 "%s: %s - unit is not ready, state=%#x\n", 506 __func__, NG_NODE_NAME(unit->node), unit->state); 507 508 error = ENXIO; 509 goto out; 510 } 511 512 if (NGI_MSG(item)->header.arglen != sizeof(*ep)) { 513 NG_HCI_ALERT( 514 "%s: %s - invalid LP_DisconnectReq message size=%d\n", 515 __func__, NG_NODE_NAME(unit->node), 516 NGI_MSG(item)->header.arglen); 517 518 error = EMSGSIZE; 519 goto out; 520 } 521 522 ep = (ng_hci_lp_discon_req_ep *)(NGI_MSG(item)->data); 523 524 con = ng_hci_con_by_handle(unit, ep->con_handle); 525 if (con == NULL) { 526 NG_HCI_ERR( 527 "%s: %s - invalid connection handle=%d\n", 528 __func__, NG_NODE_NAME(unit->node), ep->con_handle); 529 530 error = ENOENT; 531 goto out; 532 } 533 534 if (con->state != NG_HCI_CON_OPEN) { 535 NG_HCI_ERR( 536 "%s: %s - invalid connection state=%d, handle=%d\n", 537 __func__, NG_NODE_NAME(unit->node), con->state, 538 ep->con_handle); 539 540 error = EINVAL; 541 goto out; 542 } 543 544 /* 545 * Create HCI command 546 */ 547 548 MGETHDR(m, M_DONTWAIT, MT_DATA); 549 if (m == NULL) { 550 error = ENOBUFS; 551 goto out; 552 } 553 554 m->m_pkthdr.len = m->m_len = sizeof(*req); 555 req = mtod(m, struct discon_req *); 556 req->hdr.type = NG_HCI_CMD_PKT; 557 req->hdr.length = sizeof(req->cp); 558 req->hdr.opcode = htole16(NG_HCI_OPCODE(NG_HCI_OGF_LINK_CONTROL, 559 NG_HCI_OCF_DISCON)); 560 561 req->cp.con_handle = htole16(ep->con_handle); 562 req->cp.reason = ep->reason; 563 564 /* 565 * Adjust connection state 566 */ 567 568 con->state = NG_HCI_CON_CLOSED; 569 ng_hci_con_timeout(con); 570 571 /* 572 * Queue and send HCI command 573 */ 574 575 NG_BT_MBUFQ_ENQUEUE(&unit->cmdq, m); 576 if (!(unit->state & NG_HCI_UNIT_COMMAND_PENDING)) 577 error = ng_hci_send_command(unit); 578 out: 579 NG_FREE_ITEM(item); 580 581 return (error); 582 } /* ng_hci_lp_discon_req */ 583 584 /* 585 * Send LP_ConnectCfm event to the upper layer protocol 586 */ 587 588 int 589 ng_hci_lp_con_cfm(ng_hci_unit_con_p con, int status) 590 { 591 ng_hci_unit_p unit = con->unit; 592 struct ng_mesg *msg = NULL; 593 ng_hci_lp_con_cfm_ep *ep = NULL; 594 int error; 595 596 /* 597 * Check who wants to be notified. For ACL links both ACL and SCO 598 * upstream hooks will be notified (if required). For SCO links 599 * only SCO upstream hook will receive notification 600 */ 601 602 if (con->link_type == NG_HCI_LINK_ACL && 603 con->flags & NG_HCI_CON_NOTIFY_ACL) { 604 if (unit->acl != NULL && NG_HOOK_IS_VALID(unit->acl)) { 605 NG_MKMESSAGE(msg, NGM_HCI_COOKIE, NGM_HCI_LP_CON_CFM, 606 sizeof(*ep), M_NOWAIT); 607 if (msg != NULL) { 608 ep = (ng_hci_lp_con_cfm_ep *) msg->data; 609 ep->status = status; 610 ep->link_type = con->link_type; 611 ep->con_handle = con->con_handle; 612 bcopy(&con->bdaddr, &ep->bdaddr, 613 sizeof(ep->bdaddr)); 614 615 NG_SEND_MSG_HOOK(error, unit->node, msg, 616 unit->acl, NULL); 617 } 618 } else 619 NG_HCI_INFO( 620 "%s: %s - ACL hook not valid, hook=%p\n", 621 __func__, NG_NODE_NAME(unit->node), unit->acl); 622 623 con->flags &= ~NG_HCI_CON_NOTIFY_ACL; 624 } 625 626 if (con->flags & NG_HCI_CON_NOTIFY_SCO) { 627 if (unit->sco != NULL && NG_HOOK_IS_VALID(unit->sco)) { 628 NG_MKMESSAGE(msg, NGM_HCI_COOKIE, NGM_HCI_LP_CON_CFM, 629 sizeof(*ep), M_NOWAIT); 630 if (msg != NULL) { 631 ep = (ng_hci_lp_con_cfm_ep *) msg->data; 632 ep->status = status; 633 ep->link_type = con->link_type; 634 ep->con_handle = con->con_handle; 635 bcopy(&con->bdaddr, &ep->bdaddr, 636 sizeof(ep->bdaddr)); 637 638 NG_SEND_MSG_HOOK(error, unit->node, msg, 639 unit->sco, NULL); 640 } 641 } else 642 NG_HCI_INFO( 643 "%s: %s - SCO hook not valid, hook=%p\n", 644 __func__, NG_NODE_NAME(unit->node), unit->acl); 645 646 con->flags &= ~NG_HCI_CON_NOTIFY_SCO; 647 } 648 649 return (0); 650 } /* ng_hci_lp_con_cfm */ 651 652 /* 653 * Send LP_ConnectInd event to the upper layer protocol 654 */ 655 656 int 657 ng_hci_lp_con_ind(ng_hci_unit_con_p con, u_int8_t *uclass) 658 { 659 ng_hci_unit_p unit = con->unit; 660 struct ng_mesg *msg = NULL; 661 ng_hci_lp_con_ind_ep *ep = NULL; 662 hook_p hook = NULL; 663 int error = 0; 664 665 /* 666 * Connection_Request event is generated for specific link type. 667 * Use link_type to select upstream hook. 668 */ 669 670 if (con->link_type == NG_HCI_LINK_ACL) 671 hook = unit->acl; 672 else 673 hook = unit->sco; 674 675 if (hook != NULL && NG_HOOK_IS_VALID(hook)) { 676 NG_MKMESSAGE(msg, NGM_HCI_COOKIE, NGM_HCI_LP_CON_IND, 677 sizeof(*ep), M_NOWAIT); 678 if (msg == NULL) 679 return (ENOMEM); 680 681 ep = (ng_hci_lp_con_ind_ep *)(msg->data); 682 ep->link_type = con->link_type; 683 bcopy(uclass, ep->uclass, sizeof(ep->uclass)); 684 bcopy(&con->bdaddr, &ep->bdaddr, sizeof(ep->bdaddr)); 685 686 NG_SEND_MSG_HOOK(error, unit->node, msg, hook, NULL); 687 } else { 688 NG_HCI_WARN( 689 "%s: %s - Upstream hook is not connected or not valid, hook=%p\n", 690 __func__, NG_NODE_NAME(unit->node), hook); 691 692 error = ENOTCONN; 693 } 694 695 return (error); 696 } /* ng_hci_lp_con_ind */ 697 698 /* 699 * Process LP_ConnectRsp event from the upper layer protocol 700 */ 701 702 int 703 ng_hci_lp_con_rsp(ng_hci_unit_p unit, item_p item, hook_p hook) 704 { 705 struct con_rsp_req { 706 ng_hci_cmd_pkt_t hdr; 707 union { 708 ng_hci_accept_con_cp acc; 709 ng_hci_reject_con_cp rej; 710 } __attribute__ ((packed)) cp; 711 } __attribute__ ((packed)) *req = NULL; 712 ng_hci_lp_con_rsp_ep *ep = NULL; 713 ng_hci_unit_con_p con = NULL; 714 struct mbuf *m = NULL; 715 int error = 0; 716 717 /* Check if unit is ready */ 718 if ((unit->state & NG_HCI_UNIT_READY) != NG_HCI_UNIT_READY) { 719 NG_HCI_WARN( 720 "%s: %s - unit is not ready, state=%#x\n", 721 __func__, NG_NODE_NAME(unit->node), unit->state); 722 723 error = ENXIO; 724 goto out; 725 } 726 727 if (NGI_MSG(item)->header.arglen != sizeof(*ep)) { 728 NG_HCI_ALERT( 729 "%s: %s - invalid LP_ConnectRsp message size=%d\n", 730 __func__, NG_NODE_NAME(unit->node), 731 NGI_MSG(item)->header.arglen); 732 733 error = EMSGSIZE; 734 goto out; 735 } 736 737 ep = (ng_hci_lp_con_rsp_ep *)(NGI_MSG(item)->data); 738 739 /* 740 * Here we have to deal with race. Upper layers might send conflicting 741 * requests. One might send Accept and other Reject. We will not try 742 * to solve all the problems, so first request will always win. 743 * 744 * Try to find connection that matches the following: 745 * 746 * 1) con->link_type == ep->link_type 747 * 748 * 2) con->state == NG_HCI_CON_W4_LP_CON_RSP || 749 * con->state == NG_HCI_CON_W4_CONN_COMPLETE 750 * 751 * 3) con->bdaddr == ep->bdaddr 752 * 753 * Two cases: 754 * 755 * 1) We do not have connection descriptor. Could be bogus request or 756 * we have rejected connection already. 757 * 758 * 2) We do have connection descriptor. Then we need to check state: 759 * 760 * 2.1) NG_HCI_CON_W4_LP_CON_RSP means upper layer has requested 761 * connection and it is a first response from the upper layer. 762 * if "status == 0" (Accept) then we will send Accept_Connection 763 * command and change connection state to W4_CONN_COMPLETE, else 764 * send reject and delete connection. 765 * 766 * 2.2) NG_HCI_CON_W4_CONN_COMPLETE means that we already accepted 767 * connection. If "status == 0" we just need to link request 768 * and wait, else ignore Reject request. 769 */ 770 771 LIST_FOREACH(con, &unit->con_list, next) 772 if (con->link_type == ep->link_type && 773 (con->state == NG_HCI_CON_W4_LP_CON_RSP || 774 con->state == NG_HCI_CON_W4_CONN_COMPLETE) && 775 bcmp(&con->bdaddr, &ep->bdaddr, sizeof(bdaddr_t)) == 0) 776 break; 777 778 if (con == NULL) { 779 /* Reject for non-existing connection is fine */ 780 error = (ep->status == 0)? ENOENT : 0; 781 goto out; 782 } 783 784 /* 785 * Remove connection timeout and check connection state 786 */ 787 788 ng_hci_con_untimeout(con); 789 790 switch (con->state) { 791 case NG_HCI_CON_W4_LP_CON_RSP: 792 793 /* 794 * Create HCI command 795 */ 796 797 MGETHDR(m, M_DONTWAIT, MT_DATA); 798 if (m == NULL) { 799 error = ENOBUFS; 800 goto out; 801 } 802 803 req = mtod(m, struct con_rsp_req *); 804 req->hdr.type = NG_HCI_CMD_PKT; 805 806 if (ep->status == 0) { 807 req->hdr.length = sizeof(req->cp.acc); 808 req->hdr.opcode = htole16(NG_HCI_OPCODE( 809 NG_HCI_OGF_LINK_CONTROL, 810 NG_HCI_OCF_ACCEPT_CON)); 811 812 bcopy(&ep->bdaddr, &req->cp.acc.bdaddr, 813 sizeof(req->cp.acc.bdaddr)); 814 815 /* 816 * XXX should be configurable? 817 * 818 * We are accepting connection, so if we support role 819 * switch then set role to NG_HCI_ROLE_MASTER and let 820 * LM peform role switch. Otherwise it is probably 821 * makes sense to remain slave. In this case LM WILL 822 * NOT perform role switch. 823 */ 824 825 if (unit->features[0] & NG_HCI_LMP_SWITCH) 826 req->cp.acc.role = NG_HCI_ROLE_MASTER; 827 else 828 req->cp.acc.role = NG_HCI_ROLE_SLAVE; 829 830 /* 831 * Adjust connection state 832 */ 833 834 if (hook == unit->acl) 835 con->flags |= NG_HCI_CON_NOTIFY_ACL; 836 else 837 con->flags |= NG_HCI_CON_NOTIFY_SCO; 838 839 con->state = NG_HCI_CON_W4_CONN_COMPLETE; 840 ng_hci_con_timeout(con); 841 } else { 842 req->hdr.length = sizeof(req->cp.rej); 843 req->hdr.opcode = htole16(NG_HCI_OPCODE( 844 NG_HCI_OGF_LINK_CONTROL, 845 NG_HCI_OCF_REJECT_CON)); 846 847 bcopy(&ep->bdaddr, &req->cp.rej.bdaddr, 848 sizeof(req->cp.rej.bdaddr)); 849 850 req->cp.rej.reason = ep->status; 851 852 /* 853 * Free connection descritor 854 * Item will be deleted just before return. 855 */ 856 857 ng_hci_free_con(con); 858 } 859 860 m->m_pkthdr.len = m->m_len = sizeof(req->hdr) + req->hdr.length; 861 862 /* Queue and send HCI command */ 863 NG_BT_MBUFQ_ENQUEUE(&unit->cmdq, m); 864 if (!(unit->state & NG_HCI_UNIT_COMMAND_PENDING)) 865 error = ng_hci_send_command(unit); 866 break; 867 868 case NG_HCI_CON_W4_CONN_COMPLETE: 869 if (ep->status == 0) { 870 if (hook == unit->acl) 871 con->flags |= NG_HCI_CON_NOTIFY_ACL; 872 else 873 con->flags |= NG_HCI_CON_NOTIFY_SCO; 874 } else 875 error = EPERM; 876 break; 877 878 default: 879 KASSERT(0, 880 ("%s: %s - Invalid connection state=%d\n", 881 __func__, NG_NODE_NAME(unit->node), con->state)); 882 883 error = EINVAL; 884 break; 885 } 886 out: 887 NG_FREE_ITEM(item); 888 889 return (error); 890 } /* ng_hci_lp_con_rsp */ 891 892 /* 893 * Send LP_DisconnectInd to the upper layer protocol 894 */ 895 896 int 897 ng_hci_lp_discon_ind(ng_hci_unit_con_p con, int reason) 898 { 899 ng_hci_unit_p unit = con->unit; 900 struct ng_mesg *msg = NULL; 901 ng_hci_lp_discon_ind_ep *ep = NULL; 902 int error = 0; 903 904 /* 905 * Disconnect_Complete event is generated for specific connection 906 * handle. For ACL connection handles both ACL and SCO upstream 907 * hooks will receive notification. For SCO connection handles 908 * only SCO upstream hook will receive notification. 909 */ 910 911 if (con->link_type == NG_HCI_LINK_ACL) { 912 if (unit->acl != NULL && NG_HOOK_IS_VALID(unit->acl)) { 913 NG_MKMESSAGE(msg, NGM_HCI_COOKIE, 914 NGM_HCI_LP_DISCON_IND, sizeof(*ep), M_NOWAIT); 915 if (msg == NULL) 916 return (ENOMEM); 917 918 ep = (ng_hci_lp_discon_ind_ep *) msg->data; 919 ep->reason = reason; 920 ep->link_type = con->link_type; 921 ep->con_handle = con->con_handle; 922 923 NG_SEND_MSG_HOOK(error,unit->node,msg,unit->acl,NULL); 924 } else 925 NG_HCI_INFO( 926 "%s: %s - ACL hook is not connected or not valid, hook=%p\n", 927 __func__, NG_NODE_NAME(unit->node), unit->acl); 928 } 929 930 if (unit->sco != NULL && NG_HOOK_IS_VALID(unit->sco)) { 931 NG_MKMESSAGE(msg, NGM_HCI_COOKIE, NGM_HCI_LP_DISCON_IND, 932 sizeof(*ep), M_NOWAIT); 933 if (msg == NULL) 934 return (ENOMEM); 935 936 ep = (ng_hci_lp_discon_ind_ep *) msg->data; 937 ep->reason = reason; 938 ep->link_type = con->link_type; 939 ep->con_handle = con->con_handle; 940 941 NG_SEND_MSG_HOOK(error, unit->node, msg, unit->sco, NULL); 942 } else 943 NG_HCI_INFO( 944 "%s: %s - SCO hook is not connected or not valid, hook=%p\n", 945 __func__, NG_NODE_NAME(unit->node), unit->sco); 946 947 return (0); 948 } /* ng_hci_lp_discon_ind */ 949 950 /* 951 * Process LP_QoSReq action from the upper layer protocol 952 */ 953 954 int 955 ng_hci_lp_qos_req(ng_hci_unit_p unit, item_p item, hook_p hook) 956 { 957 struct qos_setup_req { 958 ng_hci_cmd_pkt_t hdr; 959 ng_hci_qos_setup_cp cp; 960 } __attribute__ ((packed)) *req = NULL; 961 ng_hci_lp_qos_req_ep *ep = NULL; 962 ng_hci_unit_con_p con = NULL; 963 struct mbuf *m = NULL; 964 int error = 0; 965 966 /* Check if unit is ready */ 967 if ((unit->state & NG_HCI_UNIT_READY) != NG_HCI_UNIT_READY) { 968 NG_HCI_WARN( 969 "%s: %s - unit is not ready, state=%#x\n", 970 __func__, NG_NODE_NAME(unit->node), unit->state); 971 972 error = ENXIO; 973 goto out; 974 } 975 976 if (NGI_MSG(item)->header.arglen != sizeof(*ep)) { 977 NG_HCI_ALERT( 978 "%s: %s - invalid LP_QoSSetupReq message size=%d\n", 979 __func__, NG_NODE_NAME(unit->node), 980 NGI_MSG(item)->header.arglen); 981 982 error = EMSGSIZE; 983 goto out; 984 } 985 986 ep = (ng_hci_lp_qos_req_ep *)(NGI_MSG(item)->data); 987 988 con = ng_hci_con_by_handle(unit, ep->con_handle); 989 if (con == NULL) { 990 NG_HCI_ERR( 991 "%s: %s - invalid connection handle=%d\n", 992 __func__, NG_NODE_NAME(unit->node), ep->con_handle); 993 994 error = EINVAL; 995 goto out; 996 } 997 998 if (con->link_type != NG_HCI_LINK_ACL) { 999 NG_HCI_ERR("%s: %s - invalid link type=%d\n", 1000 __func__, NG_NODE_NAME(unit->node), con->link_type); 1001 1002 error = EINVAL; 1003 goto out; 1004 } 1005 1006 if (con->state != NG_HCI_CON_OPEN) { 1007 NG_HCI_ERR( 1008 "%s: %s - invalid connection state=%d, handle=%d\n", 1009 __func__, NG_NODE_NAME(unit->node), con->state, 1010 con->con_handle); 1011 1012 error = EINVAL; 1013 goto out; 1014 } 1015 1016 /* 1017 * Create HCI command 1018 */ 1019 1020 MGETHDR(m, M_DONTWAIT, MT_DATA); 1021 if (m == NULL) { 1022 error = ENOBUFS; 1023 goto out; 1024 } 1025 1026 m->m_pkthdr.len = m->m_len = sizeof(*req); 1027 req = mtod(m, struct qos_setup_req *); 1028 req->hdr.type = NG_HCI_CMD_PKT; 1029 req->hdr.length = sizeof(req->cp); 1030 req->hdr.opcode = htole16(NG_HCI_OPCODE(NG_HCI_OGF_LINK_POLICY, 1031 NG_HCI_OCF_QOS_SETUP)); 1032 1033 req->cp.con_handle = htole16(ep->con_handle); 1034 req->cp.flags = ep->flags; 1035 req->cp.service_type = ep->service_type; 1036 req->cp.token_rate = htole32(ep->token_rate); 1037 req->cp.peak_bandwidth = htole32(ep->peak_bandwidth); 1038 req->cp.latency = htole32(ep->latency); 1039 req->cp.delay_variation = htole32(ep->delay_variation); 1040 1041 /* 1042 * Adjust connection state 1043 */ 1044 1045 if (hook == unit->acl) 1046 con->flags |= NG_HCI_CON_NOTIFY_ACL; 1047 else 1048 con->flags |= NG_HCI_CON_NOTIFY_SCO; 1049 1050 /* 1051 * Queue and send HCI command 1052 */ 1053 1054 NG_BT_MBUFQ_ENQUEUE(&unit->cmdq, m); 1055 if (!(unit->state & NG_HCI_UNIT_COMMAND_PENDING)) 1056 error = ng_hci_send_command(unit); 1057 out: 1058 NG_FREE_ITEM(item); 1059 1060 return (error); 1061 } /* ng_hci_lp_qos_req */ 1062 1063 /* 1064 * Send LP_QoSCfm event to the upper layer protocol 1065 */ 1066 1067 int 1068 ng_hci_lp_qos_cfm(ng_hci_unit_con_p con, int status) 1069 { 1070 ng_hci_unit_p unit = con->unit; 1071 struct ng_mesg *msg = NULL; 1072 ng_hci_lp_qos_cfm_ep *ep = NULL; 1073 int error; 1074 1075 if (con->flags & NG_HCI_CON_NOTIFY_ACL) { 1076 if (unit->acl != NULL && NG_HOOK_IS_VALID(unit->acl)) { 1077 NG_MKMESSAGE(msg, NGM_HCI_COOKIE, NGM_HCI_LP_QOS_CFM, 1078 sizeof(*ep), M_NOWAIT); 1079 if (msg != NULL) { 1080 ep = (ng_hci_lp_qos_cfm_ep *) msg->data; 1081 ep->status = status; 1082 ep->con_handle = con->con_handle; 1083 1084 NG_SEND_MSG_HOOK(error, unit->node, msg, 1085 unit->acl, NULL); 1086 } 1087 } else 1088 NG_HCI_INFO( 1089 "%s: %s - ACL hook not valid, hook=%p\n", 1090 __func__, NG_NODE_NAME(unit->node), unit->acl); 1091 1092 con->flags &= ~NG_HCI_CON_NOTIFY_ACL; 1093 } 1094 1095 if (con->flags & NG_HCI_CON_NOTIFY_SCO) { 1096 if (unit->sco != NULL && NG_HOOK_IS_VALID(unit->sco)) { 1097 NG_MKMESSAGE(msg, NGM_HCI_COOKIE, NGM_HCI_LP_QOS_CFM, 1098 sizeof(*ep), M_NOWAIT); 1099 if (msg != NULL) { 1100 ep = (ng_hci_lp_qos_cfm_ep *) msg->data; 1101 ep->status = status; 1102 ep->con_handle = con->con_handle; 1103 1104 NG_SEND_MSG_HOOK(error, unit->node, msg, 1105 unit->sco, NULL); 1106 } 1107 } else 1108 NG_HCI_INFO( 1109 "%s: %s - SCO hook not valid, hook=%p\n", 1110 __func__, NG_NODE_NAME(unit->node), unit->sco); 1111 1112 con->flags &= ~NG_HCI_CON_NOTIFY_SCO; 1113 } 1114 1115 return (0); 1116 } /* ng_hci_lp_qos_cfm */ 1117 1118 /* 1119 * Send LP_QoSViolationInd event to the upper layer protocol 1120 */ 1121 1122 int 1123 ng_hci_lp_qos_ind(ng_hci_unit_con_p con) 1124 { 1125 ng_hci_unit_p unit = con->unit; 1126 struct ng_mesg *msg = NULL; 1127 ng_hci_lp_qos_ind_ep *ep = NULL; 1128 int error; 1129 1130 /* 1131 * QoS Violation can only be generated for ACL connection handles. 1132 * Both ACL and SCO upstream hooks will receive notification. 1133 */ 1134 1135 if (unit->acl != NULL && NG_HOOK_IS_VALID(unit->acl)) { 1136 NG_MKMESSAGE(msg, NGM_HCI_COOKIE, NGM_HCI_LP_QOS_IND, 1137 sizeof(*ep), M_NOWAIT); 1138 if (msg == NULL) 1139 return (ENOMEM); 1140 1141 ep = (ng_hci_lp_qos_ind_ep *) msg->data; 1142 ep->con_handle = con->con_handle; 1143 1144 NG_SEND_MSG_HOOK(error, unit->node, msg, unit->acl, NULL); 1145 } else 1146 NG_HCI_INFO( 1147 "%s: %s - ACL hook is not connected or not valid, hook=%p\n", 1148 __func__, NG_NODE_NAME(unit->node), unit->acl); 1149 1150 if (unit->sco != NULL && NG_HOOK_IS_VALID(unit->sco)) { 1151 NG_MKMESSAGE(msg, NGM_HCI_COOKIE, NGM_HCI_LP_QOS_IND, 1152 sizeof(*ep), M_NOWAIT); 1153 if (msg == NULL) 1154 return (ENOMEM); 1155 1156 ep = (ng_hci_lp_qos_ind_ep *) msg->data; 1157 ep->con_handle = con->con_handle; 1158 1159 NG_SEND_MSG_HOOK(error, unit->node, msg, unit->sco, NULL); 1160 } else 1161 NG_HCI_INFO( 1162 "%s: %s - SCO hook is not connected or not valid, hook=%p\n", 1163 __func__, NG_NODE_NAME(unit->node), unit->sco); 1164 1165 return (0); 1166 } /* ng_hci_lp_qos_ind */ 1167 1168 /* 1169 * Process connection timeout 1170 */ 1171 1172 void 1173 ng_hci_process_con_timeout(node_p node, hook_p hook, void *arg1, int arg2) 1174 { 1175 ng_hci_unit_con_p con = (ng_hci_unit_con_p) arg1; 1176 1177 KASSERT((con->flags & NG_HCI_CON_TIMEOUT_PENDING), 1178 ("%s: %s - No connection timeout!\n", __func__, NG_NODE_NAME(node))); 1179 1180 con->flags &= ~NG_HCI_CON_TIMEOUT_PENDING; 1181 1182 /* 1183 * We expect to receive connection timeout in one of the following 1184 * states: 1185 * 1186 * 1) NG_HCI_CON_CLOSED means that upper layer has requested disconnect 1187 * via LP_DISCON_REQ and we have not received Disconnect_Complete 1188 * event. In this case we will send LP_DISCON_IND to upper layer. 1189 * 1190 * 2) NG_HCI_CON_W4_LP_CON_RSP means that upper layer has not responded 1191 * to our LP_CON_IND. Do nothing and destroy connection. Remote peer 1192 * most likely already gave up on us. 1193 * 1194 * 3) NG_HCI_CON_W4_CONN_COMPLETE means upper layer requested connection 1195 * (or we in the process of accepting it) and baseband has timedout 1196 * on us. Inform upper layers and send LP_CON_CFM. 1197 */ 1198 1199 switch (con->state) { 1200 case NG_HCI_CON_CLOSED: 1201 ng_hci_lp_discon_ind(con, 0x16); 1202 break; 1203 1204 case NG_HCI_CON_W4_LP_CON_RSP: 1205 break; 1206 1207 case NG_HCI_CON_W4_CONN_COMPLETE: 1208 ng_hci_lp_con_cfm(con, 0xee); 1209 break; 1210 1211 default: 1212 KASSERT(0, 1213 ("%s: %s - Invalid connection state=%d\n", 1214 __func__, NG_NODE_NAME(node), con->state)); 1215 break; 1216 } 1217 1218 ng_hci_free_con(con); 1219 } /* ng_hci_process_con_timeout */ 1220 1221 /* 1222 * Process connection watchdog timeout 1223 */ 1224 1225 void 1226 ng_hci_process_con_watchdog_timeout(node_p node, hook_p hook, 1227 void *arg1, int arg2) 1228 { 1229 ng_hci_unit_con_p con = (ng_hci_unit_con_p) arg1; 1230 struct discon_req { 1231 ng_hci_cmd_pkt_t hdr; 1232 ng_hci_discon_cp cp; 1233 } __attribute__ ((packed)) *req = NULL; 1234 struct mbuf *m = NULL; 1235 1236 KASSERT((con->state == NG_HCI_CON_OPEN), 1237 ("%s: %s - invalid connection state=%d, handle=%d\n", 1238 __func__, NG_NODE_NAME(node), con->state, con->con_handle)); 1239 1240 KASSERT((con->flags & NG_HCI_CON_WATCHDOG_TIMEOUT_PENDING), 1241 ("%s: %s - No connection watchdog timeout!\n", 1242 __func__, NG_NODE_NAME(node))); 1243 1244 con->flags &= ~NG_HCI_CON_WATCHDOG_TIMEOUT_PENDING; 1245 1246 /* 1247 * Create HCI command 1248 */ 1249 1250 MGETHDR(m, M_DONTWAIT, MT_DATA); 1251 if (m == NULL) 1252 return; /* XXX this is bad */ 1253 1254 m->m_pkthdr.len = m->m_len = sizeof(*req); 1255 req = mtod(m, struct discon_req *); 1256 req->hdr.type = NG_HCI_CMD_PKT; 1257 req->hdr.length = sizeof(req->cp); 1258 req->hdr.opcode = htole16(NG_HCI_OPCODE(NG_HCI_OGF_LINK_CONTROL, 1259 NG_HCI_OCF_DISCON)); 1260 1261 req->cp.con_handle = htole16(con->con_handle); 1262 req->cp.reason = 0x13; /* User ended connection */ 1263 1264 /* 1265 * Queue and send HCI command 1266 */ 1267 1268 NG_BT_MBUFQ_ENQUEUE(&con->unit->cmdq, m); 1269 if (!(con->unit->state & NG_HCI_UNIT_COMMAND_PENDING)) 1270 ng_hci_send_command(con->unit); 1271 1272 /* 1273 * Send LP_DISCON_IND to the upper layers 1274 * Connection terminated by local host 1275 */ 1276 1277 ng_hci_lp_discon_ind(con, 0x16); 1278 ng_hci_free_con(con); 1279 } /* ng_hci_process_con_watchdog_timeout */ 1280 1281