1b06ebda0SMatthew Dillon /* 2b06ebda0SMatthew Dillon * ng_l2cap_llpi.c 3b06ebda0SMatthew Dillon */ 4b06ebda0SMatthew Dillon 5b06ebda0SMatthew Dillon /*- 6b06ebda0SMatthew Dillon * Copyright (c) Maksim Yevmenkin <m_evmenkin@yahoo.com> 7b06ebda0SMatthew Dillon * All rights reserved. 8b06ebda0SMatthew Dillon * 9b06ebda0SMatthew Dillon * Redistribution and use in source and binary forms, with or without 10b06ebda0SMatthew Dillon * modification, are permitted provided that the following conditions 11b06ebda0SMatthew Dillon * are met: 12b06ebda0SMatthew Dillon * 1. Redistributions of source code must retain the above copyright 13b06ebda0SMatthew Dillon * notice, this list of conditions and the following disclaimer. 14b06ebda0SMatthew Dillon * 2. Redistributions in binary form must reproduce the above copyright 15b06ebda0SMatthew Dillon * notice, this list of conditions and the following disclaimer in the 16b06ebda0SMatthew Dillon * documentation and/or other materials provided with the distribution. 17b06ebda0SMatthew Dillon * 18b06ebda0SMatthew Dillon * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 19b06ebda0SMatthew Dillon * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 20b06ebda0SMatthew Dillon * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 21b06ebda0SMatthew Dillon * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 22b06ebda0SMatthew Dillon * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 23b06ebda0SMatthew Dillon * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 24b06ebda0SMatthew Dillon * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 25b06ebda0SMatthew Dillon * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 26b06ebda0SMatthew Dillon * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 27b06ebda0SMatthew Dillon * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 28b06ebda0SMatthew Dillon * SUCH DAMAGE. 29b06ebda0SMatthew Dillon * 30b06ebda0SMatthew Dillon * $Id: ng_l2cap_llpi.c,v 1.5 2003/09/08 19:11:45 max Exp $ 31b06ebda0SMatthew Dillon * $FreeBSD: src/sys/netgraph/bluetooth/l2cap/ng_l2cap_llpi.c,v 1.9 2005/07/29 14:44:17 emax Exp $ 32*5a975a3dSMatthew Dillon * $DragonFly: src/sys/netgraph7/bluetooth/l2cap/ng_l2cap_llpi.c,v 1.2 2008/06/26 23:05:40 dillon Exp $ 33b06ebda0SMatthew Dillon */ 34b06ebda0SMatthew Dillon 35b06ebda0SMatthew Dillon #include <sys/param.h> 36b06ebda0SMatthew Dillon #include <sys/systm.h> 37b06ebda0SMatthew Dillon #include <sys/kernel.h> 38b06ebda0SMatthew Dillon #include <sys/endian.h> 39b06ebda0SMatthew Dillon #include <sys/malloc.h> 40b06ebda0SMatthew Dillon #include <sys/mbuf.h> 41b06ebda0SMatthew Dillon #include <sys/queue.h> 42*5a975a3dSMatthew Dillon #include "ng_message.h" 43*5a975a3dSMatthew Dillon #include "netgraph.h" 44*5a975a3dSMatthew Dillon #include "bluetooth/include/ng_bluetooth.h" 45*5a975a3dSMatthew Dillon #include "bluetooth/include/ng_hci.h" 46*5a975a3dSMatthew Dillon #include "bluetooth/include/ng_l2cap.h" 47*5a975a3dSMatthew Dillon #include "bluetooth/l2cap/ng_l2cap_var.h" 48*5a975a3dSMatthew Dillon #include "bluetooth/l2cap/ng_l2cap_cmds.h" 49*5a975a3dSMatthew Dillon #include "bluetooth/l2cap/ng_l2cap_evnt.h" 50*5a975a3dSMatthew Dillon #include "bluetooth/l2cap/ng_l2cap_llpi.h" 51*5a975a3dSMatthew Dillon #include "bluetooth/l2cap/ng_l2cap_ulpi.h" 52*5a975a3dSMatthew Dillon #include "bluetooth/l2cap/ng_l2cap_misc.h" 53b06ebda0SMatthew Dillon 54b06ebda0SMatthew Dillon /****************************************************************************** 55b06ebda0SMatthew Dillon ****************************************************************************** 56b06ebda0SMatthew Dillon ** Lower Layer Protocol (HCI) Interface module 57b06ebda0SMatthew Dillon ****************************************************************************** 58b06ebda0SMatthew Dillon ******************************************************************************/ 59b06ebda0SMatthew Dillon 60b06ebda0SMatthew Dillon /* 61b06ebda0SMatthew Dillon * Send LP_ConnectReq event to the lower layer protocol. Create new connection 62b06ebda0SMatthew Dillon * descriptor and initialize it. Create LP_ConnectReq event and send it to the 63b06ebda0SMatthew Dillon * lower layer, then adjust connection state and start timer. The function WILL 64b06ebda0SMatthew Dillon * FAIL if connection to the remote unit already exists. 65b06ebda0SMatthew Dillon */ 66b06ebda0SMatthew Dillon 67b06ebda0SMatthew Dillon int 68b06ebda0SMatthew Dillon ng_l2cap_lp_con_req(ng_l2cap_p l2cap, bdaddr_p bdaddr) 69b06ebda0SMatthew Dillon { 70b06ebda0SMatthew Dillon struct ng_mesg *msg = NULL; 71b06ebda0SMatthew Dillon ng_hci_lp_con_req_ep *ep = NULL; 72b06ebda0SMatthew Dillon ng_l2cap_con_p con = NULL; 73b06ebda0SMatthew Dillon int error = 0; 74b06ebda0SMatthew Dillon 75b06ebda0SMatthew Dillon /* Verify that we DO NOT have connection to the remote unit */ 76b06ebda0SMatthew Dillon con = ng_l2cap_con_by_addr(l2cap, bdaddr); 77b06ebda0SMatthew Dillon if (con != NULL) { 78b06ebda0SMatthew Dillon NG_L2CAP_ALERT( 79b06ebda0SMatthew Dillon "%s: %s - unexpected LP_ConnectReq event. " \ 80b06ebda0SMatthew Dillon "Connection already exists, state=%d, con_handle=%d\n", 81b06ebda0SMatthew Dillon __func__, NG_NODE_NAME(l2cap->node), con->state, 82b06ebda0SMatthew Dillon con->con_handle); 83b06ebda0SMatthew Dillon 84b06ebda0SMatthew Dillon return (EEXIST); 85b06ebda0SMatthew Dillon } 86b06ebda0SMatthew Dillon 87b06ebda0SMatthew Dillon /* Check if lower layer protocol is still connected */ 88b06ebda0SMatthew Dillon if (l2cap->hci == NULL || NG_HOOK_NOT_VALID(l2cap->hci)) { 89b06ebda0SMatthew Dillon NG_L2CAP_ERR( 90b06ebda0SMatthew Dillon "%s: %s - hook \"%s\" is not connected or valid\n", 91b06ebda0SMatthew Dillon __func__, NG_NODE_NAME(l2cap->node), NG_L2CAP_HOOK_HCI); 92b06ebda0SMatthew Dillon 93b06ebda0SMatthew Dillon return (ENOTCONN); 94b06ebda0SMatthew Dillon } 95b06ebda0SMatthew Dillon 96b06ebda0SMatthew Dillon /* Create and intialize new connection descriptor */ 97b06ebda0SMatthew Dillon con = ng_l2cap_new_con(l2cap, bdaddr); 98b06ebda0SMatthew Dillon if (con == NULL) 99b06ebda0SMatthew Dillon return (ENOMEM); 100b06ebda0SMatthew Dillon 101b06ebda0SMatthew Dillon /* Create and send LP_ConnectReq event */ 102b06ebda0SMatthew Dillon NG_MKMESSAGE(msg, NGM_HCI_COOKIE, NGM_HCI_LP_CON_REQ, 103*5a975a3dSMatthew Dillon sizeof(*ep), M_WAITOK | M_NULLOK); 104b06ebda0SMatthew Dillon if (msg == NULL) { 105b06ebda0SMatthew Dillon ng_l2cap_free_con(con); 106b06ebda0SMatthew Dillon 107b06ebda0SMatthew Dillon return (ENOMEM); 108b06ebda0SMatthew Dillon } 109b06ebda0SMatthew Dillon 110b06ebda0SMatthew Dillon ep = (ng_hci_lp_con_req_ep *) (msg->data); 111b06ebda0SMatthew Dillon bcopy(bdaddr, &ep->bdaddr, sizeof(ep->bdaddr)); 112b06ebda0SMatthew Dillon ep->link_type = NG_HCI_LINK_ACL; 113b06ebda0SMatthew Dillon 114b06ebda0SMatthew Dillon con->flags |= NG_L2CAP_CON_OUTGOING; 115b06ebda0SMatthew Dillon con->state = NG_L2CAP_W4_LP_CON_CFM; 116b06ebda0SMatthew Dillon ng_l2cap_lp_timeout(con); 117b06ebda0SMatthew Dillon 118b06ebda0SMatthew Dillon NG_SEND_MSG_HOOK(error, l2cap->node, msg, l2cap->hci, 0); 119b06ebda0SMatthew Dillon if (error != 0) { 120b06ebda0SMatthew Dillon if ((error = ng_l2cap_lp_untimeout(con)) != 0) 121b06ebda0SMatthew Dillon return (error); 122b06ebda0SMatthew Dillon 123b06ebda0SMatthew Dillon ng_l2cap_free_con(con); 124b06ebda0SMatthew Dillon } 125b06ebda0SMatthew Dillon 126b06ebda0SMatthew Dillon return (error); 127b06ebda0SMatthew Dillon } /* ng_l2cap_lp_con_req */ 128b06ebda0SMatthew Dillon 129b06ebda0SMatthew Dillon /* 130b06ebda0SMatthew Dillon * Process LP_ConnectCfm event from the lower layer protocol. It could be 131b06ebda0SMatthew Dillon * positive or negative. Verify remote unit address then stop the timer and 132b06ebda0SMatthew Dillon * process event. 133b06ebda0SMatthew Dillon */ 134b06ebda0SMatthew Dillon 135b06ebda0SMatthew Dillon int 136b06ebda0SMatthew Dillon ng_l2cap_lp_con_cfm(ng_l2cap_p l2cap, struct ng_mesg *msg) 137b06ebda0SMatthew Dillon { 138b06ebda0SMatthew Dillon ng_hci_lp_con_cfm_ep *ep = NULL; 139b06ebda0SMatthew Dillon ng_l2cap_con_p con = NULL; 140b06ebda0SMatthew Dillon int error = 0; 141b06ebda0SMatthew Dillon 142b06ebda0SMatthew Dillon /* Check message */ 143b06ebda0SMatthew Dillon if (msg->header.arglen != sizeof(*ep)) { 144b06ebda0SMatthew Dillon NG_L2CAP_ALERT( 145b06ebda0SMatthew Dillon "%s: %s - invalid LP_ConnectCfm[Neg] message size\n", 146b06ebda0SMatthew Dillon __func__, NG_NODE_NAME(l2cap->node)); 147b06ebda0SMatthew Dillon error = EMSGSIZE; 148b06ebda0SMatthew Dillon goto out; 149b06ebda0SMatthew Dillon } 150b06ebda0SMatthew Dillon 151b06ebda0SMatthew Dillon ep = (ng_hci_lp_con_cfm_ep *) (msg->data); 152b06ebda0SMatthew Dillon 153b06ebda0SMatthew Dillon /* Check if we have requested/accepted this connection */ 154b06ebda0SMatthew Dillon con = ng_l2cap_con_by_addr(l2cap, &ep->bdaddr); 155b06ebda0SMatthew Dillon if (con == NULL) { 156b06ebda0SMatthew Dillon NG_L2CAP_ERR( 157b06ebda0SMatthew Dillon "%s: %s - unexpected LP_ConnectCfm event. Connection does not exist\n", 158b06ebda0SMatthew Dillon __func__, NG_NODE_NAME(l2cap->node)); 159b06ebda0SMatthew Dillon error = ENOENT; 160b06ebda0SMatthew Dillon goto out; 161b06ebda0SMatthew Dillon } 162b06ebda0SMatthew Dillon 163b06ebda0SMatthew Dillon /* Check connection state */ 164b06ebda0SMatthew Dillon if (con->state != NG_L2CAP_W4_LP_CON_CFM) { 165b06ebda0SMatthew Dillon NG_L2CAP_ALERT( 166b06ebda0SMatthew Dillon "%s: %s - unexpected LP_ConnectCfm event. " \ 167b06ebda0SMatthew Dillon "Invalid connection state, state=%d, con_handle=%d\n", 168b06ebda0SMatthew Dillon __func__, NG_NODE_NAME(l2cap->node), con->state, 169b06ebda0SMatthew Dillon con->con_handle); 170b06ebda0SMatthew Dillon error = EINVAL; 171b06ebda0SMatthew Dillon goto out; 172b06ebda0SMatthew Dillon } 173b06ebda0SMatthew Dillon 174b06ebda0SMatthew Dillon /* 175b06ebda0SMatthew Dillon * Looks like it is our confirmation. It is safe now to cancel 176b06ebda0SMatthew Dillon * connection timer and notify upper layer. If timeout already 177b06ebda0SMatthew Dillon * happened then ignore connection confirmation and let timeout 178b06ebda0SMatthew Dillon * handle that. 179b06ebda0SMatthew Dillon */ 180b06ebda0SMatthew Dillon 181b06ebda0SMatthew Dillon if ((error = ng_l2cap_lp_untimeout(con)) != 0) 182b06ebda0SMatthew Dillon goto out; 183b06ebda0SMatthew Dillon 184b06ebda0SMatthew Dillon if (ep->status == 0) { 185b06ebda0SMatthew Dillon con->state = NG_L2CAP_CON_OPEN; 186b06ebda0SMatthew Dillon con->con_handle = ep->con_handle; 187b06ebda0SMatthew Dillon ng_l2cap_lp_deliver(con); 188b06ebda0SMatthew Dillon } else /* Negative confirmation - remove connection descriptor */ 189b06ebda0SMatthew Dillon ng_l2cap_con_fail(con, ep->status); 190b06ebda0SMatthew Dillon out: 191b06ebda0SMatthew Dillon return (error); 192b06ebda0SMatthew Dillon } /* ng_l2cap_lp_con_cfm */ 193b06ebda0SMatthew Dillon 194b06ebda0SMatthew Dillon /* 195b06ebda0SMatthew Dillon * Process LP_ConnectInd event from the lower layer protocol. This is a good 196b06ebda0SMatthew Dillon * place to put some extra check on remote unit address and/or class. We could 197b06ebda0SMatthew Dillon * even forward this information to control hook (or check against internal 198b06ebda0SMatthew Dillon * black list) and thus implement some kind of firewall. But for now be simple 199b06ebda0SMatthew Dillon * and create new connection descriptor, start timer and send LP_ConnectRsp 200b06ebda0SMatthew Dillon * event (i.e. accept connection). 201b06ebda0SMatthew Dillon */ 202b06ebda0SMatthew Dillon 203b06ebda0SMatthew Dillon int 204b06ebda0SMatthew Dillon ng_l2cap_lp_con_ind(ng_l2cap_p l2cap, struct ng_mesg *msg) 205b06ebda0SMatthew Dillon { 206b06ebda0SMatthew Dillon ng_hci_lp_con_ind_ep *ep = NULL; 207b06ebda0SMatthew Dillon ng_hci_lp_con_rsp_ep *rp = NULL; 208b06ebda0SMatthew Dillon struct ng_mesg *rsp = NULL; 209b06ebda0SMatthew Dillon ng_l2cap_con_p con = NULL; 210b06ebda0SMatthew Dillon int error = 0; 211b06ebda0SMatthew Dillon 212b06ebda0SMatthew Dillon /* Check message */ 213b06ebda0SMatthew Dillon if (msg->header.arglen != sizeof(*ep)) { 214b06ebda0SMatthew Dillon NG_L2CAP_ALERT( 215b06ebda0SMatthew Dillon "%s: %s - invalid LP_ConnectInd message size\n", 216b06ebda0SMatthew Dillon __func__, NG_NODE_NAME(l2cap->node)); 217b06ebda0SMatthew Dillon error = EMSGSIZE; 218b06ebda0SMatthew Dillon goto out; 219b06ebda0SMatthew Dillon } 220b06ebda0SMatthew Dillon 221b06ebda0SMatthew Dillon ep = (ng_hci_lp_con_ind_ep *) (msg->data); 222b06ebda0SMatthew Dillon 223b06ebda0SMatthew Dillon /* Make sure we have only one connection to the remote unit */ 224b06ebda0SMatthew Dillon con = ng_l2cap_con_by_addr(l2cap, &ep->bdaddr); 225b06ebda0SMatthew Dillon if (con != NULL) { 226b06ebda0SMatthew Dillon NG_L2CAP_ALERT( 227b06ebda0SMatthew Dillon "%s: %s - unexpected LP_ConnectInd event. " \ 228b06ebda0SMatthew Dillon "Connection already exists, state=%d, con_handle=%d\n", 229b06ebda0SMatthew Dillon __func__, NG_NODE_NAME(l2cap->node), con->state, 230b06ebda0SMatthew Dillon con->con_handle); 231b06ebda0SMatthew Dillon error = EEXIST; 232b06ebda0SMatthew Dillon goto out; 233b06ebda0SMatthew Dillon } 234b06ebda0SMatthew Dillon 235b06ebda0SMatthew Dillon /* Check if lower layer protocol is still connected */ 236b06ebda0SMatthew Dillon if (l2cap->hci == NULL || NG_HOOK_NOT_VALID(l2cap->hci)) { 237b06ebda0SMatthew Dillon NG_L2CAP_ERR( 238b06ebda0SMatthew Dillon "%s: %s - hook \"%s\" is not connected or valid", 239b06ebda0SMatthew Dillon __func__, NG_NODE_NAME(l2cap->node), NG_L2CAP_HOOK_HCI); 240b06ebda0SMatthew Dillon error = ENOTCONN; 241b06ebda0SMatthew Dillon goto out; 242b06ebda0SMatthew Dillon } 243b06ebda0SMatthew Dillon 244b06ebda0SMatthew Dillon /* Create and intialize new connection descriptor */ 245b06ebda0SMatthew Dillon con = ng_l2cap_new_con(l2cap, &ep->bdaddr); 246b06ebda0SMatthew Dillon if (con == NULL) { 247b06ebda0SMatthew Dillon error = ENOMEM; 248b06ebda0SMatthew Dillon goto out; 249b06ebda0SMatthew Dillon } 250b06ebda0SMatthew Dillon 251b06ebda0SMatthew Dillon /* Create and send LP_ConnectRsp event */ 252b06ebda0SMatthew Dillon NG_MKMESSAGE(rsp, NGM_HCI_COOKIE, NGM_HCI_LP_CON_RSP, 253*5a975a3dSMatthew Dillon sizeof(*rp), M_WAITOK | M_NULLOK); 254b06ebda0SMatthew Dillon if (rsp == NULL) { 255b06ebda0SMatthew Dillon ng_l2cap_free_con(con); 256b06ebda0SMatthew Dillon error = ENOMEM; 257b06ebda0SMatthew Dillon goto out; 258b06ebda0SMatthew Dillon } 259b06ebda0SMatthew Dillon 260b06ebda0SMatthew Dillon rp = (ng_hci_lp_con_rsp_ep *)(rsp->data); 261b06ebda0SMatthew Dillon rp->status = 0x00; /* accept connection */ 262b06ebda0SMatthew Dillon rp->link_type = NG_HCI_LINK_ACL; 263b06ebda0SMatthew Dillon bcopy(&ep->bdaddr, &rp->bdaddr, sizeof(rp->bdaddr)); 264b06ebda0SMatthew Dillon 265b06ebda0SMatthew Dillon con->state = NG_L2CAP_W4_LP_CON_CFM; 266b06ebda0SMatthew Dillon ng_l2cap_lp_timeout(con); 267b06ebda0SMatthew Dillon 268b06ebda0SMatthew Dillon NG_SEND_MSG_HOOK(error, l2cap->node, rsp, l2cap->hci, 0); 269b06ebda0SMatthew Dillon if (error != 0) { 270b06ebda0SMatthew Dillon if ((error = ng_l2cap_lp_untimeout(con)) != 0) 271b06ebda0SMatthew Dillon goto out; 272b06ebda0SMatthew Dillon 273b06ebda0SMatthew Dillon ng_l2cap_free_con(con); 274b06ebda0SMatthew Dillon } 275b06ebda0SMatthew Dillon out: 276b06ebda0SMatthew Dillon return (error); 277b06ebda0SMatthew Dillon } /* ng_hci_lp_con_ind */ 278b06ebda0SMatthew Dillon 279b06ebda0SMatthew Dillon /* 280b06ebda0SMatthew Dillon * Process LP_DisconnectInd event from the lower layer protocol. We have been 281b06ebda0SMatthew Dillon * disconnected from the remote unit. So notify the upper layer protocol. 282b06ebda0SMatthew Dillon */ 283b06ebda0SMatthew Dillon 284b06ebda0SMatthew Dillon int 285b06ebda0SMatthew Dillon ng_l2cap_lp_discon_ind(ng_l2cap_p l2cap, struct ng_mesg *msg) 286b06ebda0SMatthew Dillon { 287b06ebda0SMatthew Dillon ng_hci_lp_discon_ind_ep *ep = NULL; 288b06ebda0SMatthew Dillon ng_l2cap_con_p con = NULL; 289b06ebda0SMatthew Dillon int error = 0; 290b06ebda0SMatthew Dillon 291b06ebda0SMatthew Dillon /* Check message */ 292b06ebda0SMatthew Dillon if (msg->header.arglen != sizeof(*ep)) { 293b06ebda0SMatthew Dillon NG_L2CAP_ALERT( 294b06ebda0SMatthew Dillon "%s: %s - invalid LP_DisconnectInd message size\n", 295b06ebda0SMatthew Dillon __func__, NG_NODE_NAME(l2cap->node)); 296b06ebda0SMatthew Dillon error = EMSGSIZE; 297b06ebda0SMatthew Dillon goto out; 298b06ebda0SMatthew Dillon } 299b06ebda0SMatthew Dillon 300b06ebda0SMatthew Dillon ep = (ng_hci_lp_discon_ind_ep *) (msg->data); 301b06ebda0SMatthew Dillon 302b06ebda0SMatthew Dillon /* Check if we have this connection */ 303b06ebda0SMatthew Dillon con = ng_l2cap_con_by_handle(l2cap, ep->con_handle); 304b06ebda0SMatthew Dillon if (con == NULL) { 305b06ebda0SMatthew Dillon NG_L2CAP_ERR( 306b06ebda0SMatthew Dillon "%s: %s - unexpected LP_DisconnectInd event. " \ 307b06ebda0SMatthew Dillon "Connection does not exist, con_handle=%d\n", 308b06ebda0SMatthew Dillon __func__, NG_NODE_NAME(l2cap->node), ep->con_handle); 309b06ebda0SMatthew Dillon error = ENOENT; 310b06ebda0SMatthew Dillon goto out; 311b06ebda0SMatthew Dillon } 312b06ebda0SMatthew Dillon 313b06ebda0SMatthew Dillon /* XXX Verify connection state -- do we need to check this? */ 314b06ebda0SMatthew Dillon if (con->state != NG_L2CAP_CON_OPEN) { 315b06ebda0SMatthew Dillon NG_L2CAP_ERR( 316b06ebda0SMatthew Dillon "%s: %s - unexpected LP_DisconnectInd event. " \ 317b06ebda0SMatthew Dillon "Invalid connection state, state=%d, con_handle=%d\n", 318b06ebda0SMatthew Dillon __func__, NG_NODE_NAME(l2cap->node), con->state, 319b06ebda0SMatthew Dillon con->con_handle); 320b06ebda0SMatthew Dillon error = EINVAL; 321b06ebda0SMatthew Dillon goto out; 322b06ebda0SMatthew Dillon } 323b06ebda0SMatthew Dillon 324b06ebda0SMatthew Dillon /* 325b06ebda0SMatthew Dillon * Notify upper layer and remove connection 326b06ebda0SMatthew Dillon * Note: The connection could have auto disconnect timeout set. Try 327b06ebda0SMatthew Dillon * to remove it. If auto disconnect timeout happened then ignore 328b06ebda0SMatthew Dillon * disconnect indication and let timeout handle that. 329b06ebda0SMatthew Dillon */ 330b06ebda0SMatthew Dillon 331b06ebda0SMatthew Dillon if (con->flags & NG_L2CAP_CON_AUTO_DISCON_TIMO) 332b06ebda0SMatthew Dillon if ((error = ng_l2cap_discon_untimeout(con)) != 0) 333b06ebda0SMatthew Dillon return (error); 334b06ebda0SMatthew Dillon 335b06ebda0SMatthew Dillon ng_l2cap_con_fail(con, ep->reason); 336b06ebda0SMatthew Dillon out: 337b06ebda0SMatthew Dillon return (error); 338b06ebda0SMatthew Dillon } /* ng_l2cap_lp_discon_ind */ 339b06ebda0SMatthew Dillon 340b06ebda0SMatthew Dillon /* 341b06ebda0SMatthew Dillon * Send LP_QoSSetupReq event to the lower layer protocol 342b06ebda0SMatthew Dillon */ 343b06ebda0SMatthew Dillon 344b06ebda0SMatthew Dillon int 345b06ebda0SMatthew Dillon ng_l2cap_lp_qos_req(ng_l2cap_p l2cap, u_int16_t con_handle, 346b06ebda0SMatthew Dillon ng_l2cap_flow_p flow) 347b06ebda0SMatthew Dillon { 348b06ebda0SMatthew Dillon struct ng_mesg *msg = NULL; 349b06ebda0SMatthew Dillon ng_hci_lp_qos_req_ep *ep = NULL; 350b06ebda0SMatthew Dillon ng_l2cap_con_p con = NULL; 351b06ebda0SMatthew Dillon int error = 0; 352b06ebda0SMatthew Dillon 353b06ebda0SMatthew Dillon /* Verify that we have this connection */ 354b06ebda0SMatthew Dillon con = ng_l2cap_con_by_handle(l2cap, con_handle); 355b06ebda0SMatthew Dillon if (con == NULL) { 356b06ebda0SMatthew Dillon NG_L2CAP_ERR( 357b06ebda0SMatthew Dillon "%s: %s - unexpected LP_QoSSetupReq event. " \ 358b06ebda0SMatthew Dillon "Connection does not exist, con_handle=%d\n", 359b06ebda0SMatthew Dillon __func__, NG_NODE_NAME(l2cap->node), con_handle); 360b06ebda0SMatthew Dillon 361b06ebda0SMatthew Dillon return (ENOENT); 362b06ebda0SMatthew Dillon } 363b06ebda0SMatthew Dillon 364b06ebda0SMatthew Dillon /* Verify connection state */ 365b06ebda0SMatthew Dillon if (con->state != NG_L2CAP_CON_OPEN) { 366b06ebda0SMatthew Dillon NG_L2CAP_ERR( 367b06ebda0SMatthew Dillon "%s: %s - unexpected LP_QoSSetupReq event. " \ 368b06ebda0SMatthew Dillon "Invalid connection state, state=%d, con_handle=%d\n", 369b06ebda0SMatthew Dillon __func__, NG_NODE_NAME(l2cap->node), con->state, 370b06ebda0SMatthew Dillon con->con_handle); 371b06ebda0SMatthew Dillon 372b06ebda0SMatthew Dillon return (EINVAL); 373b06ebda0SMatthew Dillon } 374b06ebda0SMatthew Dillon 375b06ebda0SMatthew Dillon /* Check if lower layer protocol is still connected */ 376b06ebda0SMatthew Dillon if (l2cap->hci == NULL || NG_HOOK_NOT_VALID(l2cap->hci)) { 377b06ebda0SMatthew Dillon NG_L2CAP_ERR( 378b06ebda0SMatthew Dillon "%s: %s - hook \"%s\" is not connected or valid", 379b06ebda0SMatthew Dillon __func__, NG_NODE_NAME(l2cap->node), NG_L2CAP_HOOK_HCI); 380b06ebda0SMatthew Dillon 381b06ebda0SMatthew Dillon return (ENOTCONN); 382b06ebda0SMatthew Dillon } 383b06ebda0SMatthew Dillon 384b06ebda0SMatthew Dillon /* Create and send LP_QoSSetupReq event */ 385b06ebda0SMatthew Dillon NG_MKMESSAGE(msg, NGM_HCI_COOKIE, NGM_HCI_LP_QOS_REQ, 386*5a975a3dSMatthew Dillon sizeof(*ep), M_WAITOK | M_NULLOK); 387b06ebda0SMatthew Dillon if (msg == NULL) 388b06ebda0SMatthew Dillon return (ENOMEM); 389b06ebda0SMatthew Dillon 390b06ebda0SMatthew Dillon ep = (ng_hci_lp_qos_req_ep *) (msg->data); 391b06ebda0SMatthew Dillon ep->con_handle = con_handle; 392b06ebda0SMatthew Dillon ep->flags = flow->flags; 393b06ebda0SMatthew Dillon ep->service_type = flow->service_type; 394b06ebda0SMatthew Dillon ep->token_rate = flow->token_rate; 395b06ebda0SMatthew Dillon ep->peak_bandwidth = flow->peak_bandwidth; 396b06ebda0SMatthew Dillon ep->latency = flow->latency; 397b06ebda0SMatthew Dillon ep->delay_variation = flow->delay_variation; 398b06ebda0SMatthew Dillon 399b06ebda0SMatthew Dillon NG_SEND_MSG_HOOK(error, l2cap->node, msg, l2cap->hci, 0); 400b06ebda0SMatthew Dillon 401b06ebda0SMatthew Dillon return (error); 402b06ebda0SMatthew Dillon } /* ng_l2cap_lp_con_req */ 403b06ebda0SMatthew Dillon 404b06ebda0SMatthew Dillon /* 405b06ebda0SMatthew Dillon * Process LP_QoSSetupCfm from the lower layer protocol 406b06ebda0SMatthew Dillon */ 407b06ebda0SMatthew Dillon 408b06ebda0SMatthew Dillon int 409b06ebda0SMatthew Dillon ng_l2cap_lp_qos_cfm(ng_l2cap_p l2cap, struct ng_mesg *msg) 410b06ebda0SMatthew Dillon { 411b06ebda0SMatthew Dillon ng_hci_lp_qos_cfm_ep *ep = NULL; 412b06ebda0SMatthew Dillon int error = 0; 413b06ebda0SMatthew Dillon 414b06ebda0SMatthew Dillon /* Check message */ 415b06ebda0SMatthew Dillon if (msg->header.arglen != sizeof(*ep)) { 416b06ebda0SMatthew Dillon NG_L2CAP_ALERT( 417b06ebda0SMatthew Dillon "%s: %s - invalid LP_QoSSetupCfm[Neg] message size\n", 418b06ebda0SMatthew Dillon __func__, NG_NODE_NAME(l2cap->node)); 419b06ebda0SMatthew Dillon error = EMSGSIZE; 420b06ebda0SMatthew Dillon goto out; 421b06ebda0SMatthew Dillon } 422b06ebda0SMatthew Dillon 423b06ebda0SMatthew Dillon ep = (ng_hci_lp_qos_cfm_ep *) (msg->data); 424b06ebda0SMatthew Dillon /* XXX FIXME do something */ 425b06ebda0SMatthew Dillon out: 426b06ebda0SMatthew Dillon return (error); 427b06ebda0SMatthew Dillon } /* ng_l2cap_lp_qos_cfm */ 428b06ebda0SMatthew Dillon 429b06ebda0SMatthew Dillon /* 430b06ebda0SMatthew Dillon * Process LP_QoSViolationInd event from the lower layer protocol. Lower 431b06ebda0SMatthew Dillon * layer protocol has detected QoS Violation, so we MUST notify the 432b06ebda0SMatthew Dillon * upper layer. 433b06ebda0SMatthew Dillon */ 434b06ebda0SMatthew Dillon 435b06ebda0SMatthew Dillon int 436b06ebda0SMatthew Dillon ng_l2cap_lp_qos_ind(ng_l2cap_p l2cap, struct ng_mesg *msg) 437b06ebda0SMatthew Dillon { 438b06ebda0SMatthew Dillon ng_hci_lp_qos_ind_ep *ep = NULL; 439b06ebda0SMatthew Dillon ng_l2cap_con_p con = NULL; 440b06ebda0SMatthew Dillon int error = 0; 441b06ebda0SMatthew Dillon 442b06ebda0SMatthew Dillon /* Check message */ 443b06ebda0SMatthew Dillon if (msg->header.arglen != sizeof(*ep)) { 444b06ebda0SMatthew Dillon NG_L2CAP_ALERT( 445b06ebda0SMatthew Dillon "%s: %s - invalid LP_QoSViolation message size\n", 446b06ebda0SMatthew Dillon __func__, NG_NODE_NAME(l2cap->node)); 447b06ebda0SMatthew Dillon error = EMSGSIZE; 448b06ebda0SMatthew Dillon goto out; 449b06ebda0SMatthew Dillon } 450b06ebda0SMatthew Dillon 451b06ebda0SMatthew Dillon ep = (ng_hci_lp_qos_ind_ep *) (msg->data); 452b06ebda0SMatthew Dillon 453b06ebda0SMatthew Dillon /* Check if we have this connection */ 454b06ebda0SMatthew Dillon con = ng_l2cap_con_by_handle(l2cap, ep->con_handle); 455b06ebda0SMatthew Dillon if (con == NULL) { 456b06ebda0SMatthew Dillon NG_L2CAP_ERR( 457b06ebda0SMatthew Dillon "%s: %s - unexpected LP_QoSViolationInd event. " \ 458b06ebda0SMatthew Dillon "Connection does not exist, con_handle=%d\n", 459b06ebda0SMatthew Dillon __func__, NG_NODE_NAME(l2cap->node), ep->con_handle); 460b06ebda0SMatthew Dillon error = ENOENT; 461b06ebda0SMatthew Dillon goto out; 462b06ebda0SMatthew Dillon } 463b06ebda0SMatthew Dillon 464b06ebda0SMatthew Dillon /* Verify connection state */ 465b06ebda0SMatthew Dillon if (con->state != NG_L2CAP_CON_OPEN) { 466b06ebda0SMatthew Dillon NG_L2CAP_ERR( 467b06ebda0SMatthew Dillon "%s: %s - unexpected LP_QoSViolationInd event. " \ 468b06ebda0SMatthew Dillon "Invalid connection state, state=%d, con_handle=%d\n", 469b06ebda0SMatthew Dillon __func__, NG_NODE_NAME(l2cap->node), con->state, 470b06ebda0SMatthew Dillon con->con_handle); 471b06ebda0SMatthew Dillon error = EINVAL; 472b06ebda0SMatthew Dillon goto out; 473b06ebda0SMatthew Dillon } 474b06ebda0SMatthew Dillon 475b06ebda0SMatthew Dillon /* XXX FIXME Notify upper layer and terminate channels if required */ 476b06ebda0SMatthew Dillon out: 477b06ebda0SMatthew Dillon return (error); 478b06ebda0SMatthew Dillon } /* ng_l2cap_qos_ind */ 479b06ebda0SMatthew Dillon 480b06ebda0SMatthew Dillon /* 481b06ebda0SMatthew Dillon * Prepare L2CAP packet. Prepend packet with L2CAP packet header and then 482b06ebda0SMatthew Dillon * segment it according to HCI MTU. 483b06ebda0SMatthew Dillon */ 484b06ebda0SMatthew Dillon 485b06ebda0SMatthew Dillon int 486b06ebda0SMatthew Dillon ng_l2cap_lp_send(ng_l2cap_con_p con, u_int16_t dcid, struct mbuf *m0) 487b06ebda0SMatthew Dillon { 488b06ebda0SMatthew Dillon ng_l2cap_p l2cap = con->l2cap; 489b06ebda0SMatthew Dillon ng_l2cap_hdr_t *l2cap_hdr = NULL; 490b06ebda0SMatthew Dillon ng_hci_acldata_pkt_t *acl_hdr = NULL; 491b06ebda0SMatthew Dillon struct mbuf *m_last = NULL, *m = NULL; 492b06ebda0SMatthew Dillon int len, flag = NG_HCI_PACKET_START; 493b06ebda0SMatthew Dillon 494b06ebda0SMatthew Dillon KASSERT((con->tx_pkt == NULL), 495b06ebda0SMatthew Dillon ("%s: %s - another packet pending?!\n", __func__, NG_NODE_NAME(l2cap->node))); 496b06ebda0SMatthew Dillon KASSERT((l2cap->pkt_size > 0), 497b06ebda0SMatthew Dillon ("%s: %s - invalid l2cap->pkt_size?!\n", __func__, NG_NODE_NAME(l2cap->node))); 498b06ebda0SMatthew Dillon 499b06ebda0SMatthew Dillon /* Prepend mbuf with L2CAP header */ 500b06ebda0SMatthew Dillon m0 = ng_l2cap_prepend(m0, sizeof(*l2cap_hdr)); 501b06ebda0SMatthew Dillon if (m0 == NULL) { 502b06ebda0SMatthew Dillon NG_L2CAP_ALERT( 503b06ebda0SMatthew Dillon "%s: %s - ng_l2cap_prepend(%zd) failed\n", 504b06ebda0SMatthew Dillon __func__, NG_NODE_NAME(l2cap->node), 505b06ebda0SMatthew Dillon sizeof(*l2cap_hdr)); 506b06ebda0SMatthew Dillon 507b06ebda0SMatthew Dillon goto fail; 508b06ebda0SMatthew Dillon } 509b06ebda0SMatthew Dillon 510b06ebda0SMatthew Dillon l2cap_hdr = mtod(m0, ng_l2cap_hdr_t *); 511b06ebda0SMatthew Dillon l2cap_hdr->length = htole16(m0->m_pkthdr.len - sizeof(*l2cap_hdr)); 512b06ebda0SMatthew Dillon l2cap_hdr->dcid = htole16(dcid); 513b06ebda0SMatthew Dillon 514b06ebda0SMatthew Dillon /* 515b06ebda0SMatthew Dillon * Segment single L2CAP packet according to the HCI layer MTU. Convert 516b06ebda0SMatthew Dillon * each segment into ACL data packet and prepend it with ACL data packet 517b06ebda0SMatthew Dillon * header. Link all segments together via m_nextpkt link. 518b06ebda0SMatthew Dillon * 519b06ebda0SMatthew Dillon * XXX BC (Broadcast flag) will always be 0 (zero). 520b06ebda0SMatthew Dillon */ 521b06ebda0SMatthew Dillon 522b06ebda0SMatthew Dillon while (m0 != NULL) { 523b06ebda0SMatthew Dillon /* Check length of the packet against HCI MTU */ 524b06ebda0SMatthew Dillon len = m0->m_pkthdr.len; 525b06ebda0SMatthew Dillon if (len > l2cap->pkt_size) { 526*5a975a3dSMatthew Dillon m = m_split(m0, l2cap->pkt_size, MB_DONTWAIT); 527b06ebda0SMatthew Dillon if (m == NULL) { 528b06ebda0SMatthew Dillon NG_L2CAP_ALERT( 529b06ebda0SMatthew Dillon "%s: %s - m_split(%d) failed\n", __func__, NG_NODE_NAME(l2cap->node), 530b06ebda0SMatthew Dillon l2cap->pkt_size); 531b06ebda0SMatthew Dillon goto fail; 532b06ebda0SMatthew Dillon } 533b06ebda0SMatthew Dillon 534b06ebda0SMatthew Dillon len = l2cap->pkt_size; 535b06ebda0SMatthew Dillon } 536b06ebda0SMatthew Dillon 537b06ebda0SMatthew Dillon /* Convert packet fragment into ACL data packet */ 538b06ebda0SMatthew Dillon m0 = ng_l2cap_prepend(m0, sizeof(*acl_hdr)); 539b06ebda0SMatthew Dillon if (m0 == NULL) { 540b06ebda0SMatthew Dillon NG_L2CAP_ALERT( 541b06ebda0SMatthew Dillon "%s: %s - ng_l2cap_prepend(%zd) failed\n", 542b06ebda0SMatthew Dillon __func__, NG_NODE_NAME(l2cap->node), 543b06ebda0SMatthew Dillon sizeof(*acl_hdr)); 544b06ebda0SMatthew Dillon goto fail; 545b06ebda0SMatthew Dillon } 546b06ebda0SMatthew Dillon 547b06ebda0SMatthew Dillon acl_hdr = mtod(m0, ng_hci_acldata_pkt_t *); 548b06ebda0SMatthew Dillon acl_hdr->type = NG_HCI_ACL_DATA_PKT; 549b06ebda0SMatthew Dillon acl_hdr->length = htole16(len); 550b06ebda0SMatthew Dillon acl_hdr->con_handle = htole16(NG_HCI_MK_CON_HANDLE( 551b06ebda0SMatthew Dillon con->con_handle, flag, 0)); 552b06ebda0SMatthew Dillon 553b06ebda0SMatthew Dillon /* Add fragment to the chain */ 554b06ebda0SMatthew Dillon m0->m_nextpkt = NULL; 555b06ebda0SMatthew Dillon 556b06ebda0SMatthew Dillon if (con->tx_pkt == NULL) 557b06ebda0SMatthew Dillon con->tx_pkt = m_last = m0; 558b06ebda0SMatthew Dillon else { 559b06ebda0SMatthew Dillon m_last->m_nextpkt = m0; 560b06ebda0SMatthew Dillon m_last = m0; 561b06ebda0SMatthew Dillon } 562b06ebda0SMatthew Dillon 563b06ebda0SMatthew Dillon NG_L2CAP_INFO( 564b06ebda0SMatthew Dillon "%s: %s - attaching ACL packet, con_handle=%d, PB=%#x, length=%d\n", 565b06ebda0SMatthew Dillon __func__, NG_NODE_NAME(l2cap->node), con->con_handle, 566b06ebda0SMatthew Dillon flag, len); 567b06ebda0SMatthew Dillon 568b06ebda0SMatthew Dillon m0 = m; 569b06ebda0SMatthew Dillon m = NULL; 570b06ebda0SMatthew Dillon flag = NG_HCI_PACKET_FRAGMENT; 571b06ebda0SMatthew Dillon } 572b06ebda0SMatthew Dillon 573b06ebda0SMatthew Dillon return (0); 574b06ebda0SMatthew Dillon fail: 575b06ebda0SMatthew Dillon NG_FREE_M(m0); 576b06ebda0SMatthew Dillon NG_FREE_M(m); 577b06ebda0SMatthew Dillon 578b06ebda0SMatthew Dillon while (con->tx_pkt != NULL) { 579b06ebda0SMatthew Dillon m = con->tx_pkt->m_nextpkt; 580b06ebda0SMatthew Dillon m_freem(con->tx_pkt); 581b06ebda0SMatthew Dillon con->tx_pkt = m; 582b06ebda0SMatthew Dillon } 583b06ebda0SMatthew Dillon 584b06ebda0SMatthew Dillon return (ENOBUFS); 585b06ebda0SMatthew Dillon } /* ng_l2cap_lp_send */ 586b06ebda0SMatthew Dillon 587b06ebda0SMatthew Dillon /* 588b06ebda0SMatthew Dillon * Receive ACL data packet from the HCI layer. First strip ACL packet header 589b06ebda0SMatthew Dillon * and get connection handle, PB (Packet Boundary) flag and payload length. 590b06ebda0SMatthew Dillon * Then find connection descriptor and verify its state. Then process ACL 591b06ebda0SMatthew Dillon * packet as follows. 592b06ebda0SMatthew Dillon * 593b06ebda0SMatthew Dillon * 1) If we got first segment (pb == NG_HCI_PACKET_START) then extract L2CAP 594b06ebda0SMatthew Dillon * header and get total length of the L2CAP packet. Then start new L2CAP 595b06ebda0SMatthew Dillon * packet. 596b06ebda0SMatthew Dillon * 597b06ebda0SMatthew Dillon * 2) If we got other (then first :) segment (pb == NG_HCI_PACKET_FRAGMENT) 598b06ebda0SMatthew Dillon * then add segment to the packet. 599b06ebda0SMatthew Dillon */ 600b06ebda0SMatthew Dillon 601b06ebda0SMatthew Dillon int 602b06ebda0SMatthew Dillon ng_l2cap_lp_receive(ng_l2cap_p l2cap, struct mbuf *m) 603b06ebda0SMatthew Dillon { 604b06ebda0SMatthew Dillon ng_hci_acldata_pkt_t *acl_hdr = NULL; 605b06ebda0SMatthew Dillon ng_l2cap_hdr_t *l2cap_hdr = NULL; 606b06ebda0SMatthew Dillon ng_l2cap_con_p con = NULL; 607b06ebda0SMatthew Dillon u_int16_t con_handle, length, pb; 608b06ebda0SMatthew Dillon int error = 0; 609b06ebda0SMatthew Dillon 610b06ebda0SMatthew Dillon /* Check ACL data packet */ 611b06ebda0SMatthew Dillon if (m->m_pkthdr.len < sizeof(*acl_hdr)) { 612b06ebda0SMatthew Dillon NG_L2CAP_ERR( 613b06ebda0SMatthew Dillon "%s: %s - invalid ACL data packet. Packet too small, length=%d\n", 614b06ebda0SMatthew Dillon __func__, NG_NODE_NAME(l2cap->node), m->m_pkthdr.len); 615b06ebda0SMatthew Dillon error = EMSGSIZE; 616b06ebda0SMatthew Dillon goto drop; 617b06ebda0SMatthew Dillon } 618b06ebda0SMatthew Dillon 619b06ebda0SMatthew Dillon /* Strip ACL data packet header */ 620b06ebda0SMatthew Dillon NG_L2CAP_M_PULLUP(m, sizeof(*acl_hdr)); 621b06ebda0SMatthew Dillon if (m == NULL) 622b06ebda0SMatthew Dillon return (ENOBUFS); 623b06ebda0SMatthew Dillon 624b06ebda0SMatthew Dillon acl_hdr = mtod(m, ng_hci_acldata_pkt_t *); 625b06ebda0SMatthew Dillon m_adj(m, sizeof(*acl_hdr)); 626b06ebda0SMatthew Dillon 627b06ebda0SMatthew Dillon /* Get ACL connection handle, PB flag and payload length */ 628b06ebda0SMatthew Dillon acl_hdr->con_handle = le16toh(acl_hdr->con_handle); 629b06ebda0SMatthew Dillon con_handle = NG_HCI_CON_HANDLE(acl_hdr->con_handle); 630b06ebda0SMatthew Dillon pb = NG_HCI_PB_FLAG(acl_hdr->con_handle); 631b06ebda0SMatthew Dillon length = le16toh(acl_hdr->length); 632b06ebda0SMatthew Dillon 633b06ebda0SMatthew Dillon NG_L2CAP_INFO( 634b06ebda0SMatthew Dillon "%s: %s - got ACL data packet, con_handle=%d, PB=%#x, length=%d\n", 635b06ebda0SMatthew Dillon __func__, NG_NODE_NAME(l2cap->node), con_handle, pb, length); 636b06ebda0SMatthew Dillon 637b06ebda0SMatthew Dillon /* Get connection descriptor */ 638b06ebda0SMatthew Dillon con = ng_l2cap_con_by_handle(l2cap, con_handle); 639b06ebda0SMatthew Dillon if (con == NULL) { 640b06ebda0SMatthew Dillon NG_L2CAP_ERR( 641b06ebda0SMatthew Dillon "%s: %s - unexpected ACL data packet. " \ 642b06ebda0SMatthew Dillon "Connection does not exist, con_handle=%d\n", 643b06ebda0SMatthew Dillon __func__, NG_NODE_NAME(l2cap->node), con_handle); 644b06ebda0SMatthew Dillon error = ENOENT; 645b06ebda0SMatthew Dillon goto drop; 646b06ebda0SMatthew Dillon } 647b06ebda0SMatthew Dillon 648b06ebda0SMatthew Dillon /* Verify connection state */ 649b06ebda0SMatthew Dillon if (con->state != NG_L2CAP_CON_OPEN) { 650b06ebda0SMatthew Dillon NG_L2CAP_ERR( 651b06ebda0SMatthew Dillon "%s: %s - unexpected ACL data packet. Invalid connection state=%d\n", 652b06ebda0SMatthew Dillon __func__, NG_NODE_NAME(l2cap->node), con->state); 653b06ebda0SMatthew Dillon error = EHOSTDOWN; 654b06ebda0SMatthew Dillon goto drop; 655b06ebda0SMatthew Dillon } 656b06ebda0SMatthew Dillon 657b06ebda0SMatthew Dillon /* Process packet */ 658b06ebda0SMatthew Dillon if (pb == NG_HCI_PACKET_START) { 659b06ebda0SMatthew Dillon if (con->rx_pkt != NULL) { 660b06ebda0SMatthew Dillon NG_L2CAP_ERR( 661b06ebda0SMatthew Dillon "%s: %s - dropping incomplete L2CAP packet, got %d bytes, want %d bytes\n", 662b06ebda0SMatthew Dillon __func__, NG_NODE_NAME(l2cap->node), 663b06ebda0SMatthew Dillon con->rx_pkt->m_pkthdr.len, con->rx_pkt_len); 664b06ebda0SMatthew Dillon NG_FREE_M(con->rx_pkt); 665b06ebda0SMatthew Dillon con->rx_pkt_len = 0; 666b06ebda0SMatthew Dillon } 667b06ebda0SMatthew Dillon 668b06ebda0SMatthew Dillon /* Get L2CAP header */ 669b06ebda0SMatthew Dillon if (m->m_pkthdr.len < sizeof(*l2cap_hdr)) { 670b06ebda0SMatthew Dillon NG_L2CAP_ERR( 671b06ebda0SMatthew Dillon "%s: %s - invalid L2CAP packet start fragment. Packet too small, length=%d\n", 672b06ebda0SMatthew Dillon __func__, NG_NODE_NAME(l2cap->node), 673b06ebda0SMatthew Dillon m->m_pkthdr.len); 674b06ebda0SMatthew Dillon error = EMSGSIZE; 675b06ebda0SMatthew Dillon goto drop; 676b06ebda0SMatthew Dillon } 677b06ebda0SMatthew Dillon 678b06ebda0SMatthew Dillon NG_L2CAP_M_PULLUP(m, sizeof(*l2cap_hdr)); 679b06ebda0SMatthew Dillon if (m == NULL) 680b06ebda0SMatthew Dillon return (ENOBUFS); 681b06ebda0SMatthew Dillon 682b06ebda0SMatthew Dillon l2cap_hdr = mtod(m, ng_l2cap_hdr_t *); 683b06ebda0SMatthew Dillon 684b06ebda0SMatthew Dillon NG_L2CAP_INFO( 685b06ebda0SMatthew Dillon "%s: %s - staring new L2CAP packet, con_handle=%d, length=%d\n", 686b06ebda0SMatthew Dillon __func__, NG_NODE_NAME(l2cap->node), con_handle, 687b06ebda0SMatthew Dillon le16toh(l2cap_hdr->length)); 688b06ebda0SMatthew Dillon 689b06ebda0SMatthew Dillon /* Start new L2CAP packet */ 690b06ebda0SMatthew Dillon con->rx_pkt = m; 691b06ebda0SMatthew Dillon con->rx_pkt_len = le16toh(l2cap_hdr->length)+sizeof(*l2cap_hdr); 692b06ebda0SMatthew Dillon } else if (pb == NG_HCI_PACKET_FRAGMENT) { 693b06ebda0SMatthew Dillon if (con->rx_pkt == NULL) { 694b06ebda0SMatthew Dillon NG_L2CAP_ERR( 695b06ebda0SMatthew Dillon "%s: %s - unexpected ACL data packet fragment, con_handle=%d\n", 696b06ebda0SMatthew Dillon __func__, NG_NODE_NAME(l2cap->node), 697b06ebda0SMatthew Dillon con->con_handle); 698b06ebda0SMatthew Dillon goto drop; 699b06ebda0SMatthew Dillon } 700b06ebda0SMatthew Dillon 701b06ebda0SMatthew Dillon /* Add fragment to the L2CAP packet */ 702b06ebda0SMatthew Dillon m_cat(con->rx_pkt, m); 703b06ebda0SMatthew Dillon con->rx_pkt->m_pkthdr.len += length; 704b06ebda0SMatthew Dillon } else { 705b06ebda0SMatthew Dillon NG_L2CAP_ERR( 706b06ebda0SMatthew Dillon "%s: %s - invalid ACL data packet. Invalid PB flag=%#x\n", 707b06ebda0SMatthew Dillon __func__, NG_NODE_NAME(l2cap->node), pb); 708b06ebda0SMatthew Dillon error = EINVAL; 709b06ebda0SMatthew Dillon goto drop; 710b06ebda0SMatthew Dillon } 711b06ebda0SMatthew Dillon 712b06ebda0SMatthew Dillon con->rx_pkt_len -= length; 713b06ebda0SMatthew Dillon if (con->rx_pkt_len < 0) { 714b06ebda0SMatthew Dillon NG_L2CAP_ALERT( 715b06ebda0SMatthew Dillon "%s: %s - packet length mismatch. Got %d bytes, offset %d bytes\n", 716b06ebda0SMatthew Dillon __func__, NG_NODE_NAME(l2cap->node), 717b06ebda0SMatthew Dillon con->rx_pkt->m_pkthdr.len, con->rx_pkt_len); 718b06ebda0SMatthew Dillon NG_FREE_M(con->rx_pkt); 719b06ebda0SMatthew Dillon con->rx_pkt_len = 0; 720b06ebda0SMatthew Dillon } else if (con->rx_pkt_len == 0) { 721b06ebda0SMatthew Dillon /* OK, we have got complete L2CAP packet, so process it */ 722b06ebda0SMatthew Dillon error = ng_l2cap_receive(con); 723b06ebda0SMatthew Dillon con->rx_pkt = NULL; 724b06ebda0SMatthew Dillon con->rx_pkt_len = 0; 725b06ebda0SMatthew Dillon } 726b06ebda0SMatthew Dillon 727b06ebda0SMatthew Dillon return (error); 728b06ebda0SMatthew Dillon 729b06ebda0SMatthew Dillon drop: 730b06ebda0SMatthew Dillon NG_FREE_M(m); 731b06ebda0SMatthew Dillon 732b06ebda0SMatthew Dillon return (error); 733b06ebda0SMatthew Dillon } /* ng_l2cap_lp_receive */ 734b06ebda0SMatthew Dillon 735b06ebda0SMatthew Dillon /* 736b06ebda0SMatthew Dillon * Send queued ACL packets to the HCI layer 737b06ebda0SMatthew Dillon */ 738b06ebda0SMatthew Dillon 739b06ebda0SMatthew Dillon void 740b06ebda0SMatthew Dillon ng_l2cap_lp_deliver(ng_l2cap_con_p con) 741b06ebda0SMatthew Dillon { 742b06ebda0SMatthew Dillon ng_l2cap_p l2cap = con->l2cap; 743b06ebda0SMatthew Dillon struct mbuf *m = NULL; 744b06ebda0SMatthew Dillon int error; 745b06ebda0SMatthew Dillon 746b06ebda0SMatthew Dillon /* Check connection */ 747b06ebda0SMatthew Dillon if (con->state != NG_L2CAP_CON_OPEN) 748b06ebda0SMatthew Dillon return; 749b06ebda0SMatthew Dillon 750b06ebda0SMatthew Dillon if (con->tx_pkt == NULL) 751b06ebda0SMatthew Dillon ng_l2cap_con_wakeup(con); 752b06ebda0SMatthew Dillon 753b06ebda0SMatthew Dillon if (con->tx_pkt == NULL) 754b06ebda0SMatthew Dillon return; 755b06ebda0SMatthew Dillon 756b06ebda0SMatthew Dillon /* Check if lower layer protocol is still connected */ 757b06ebda0SMatthew Dillon if (l2cap->hci == NULL || NG_HOOK_NOT_VALID(l2cap->hci)) { 758b06ebda0SMatthew Dillon NG_L2CAP_ERR( 759b06ebda0SMatthew Dillon "%s: %s - hook \"%s\" is not connected or valid", 760b06ebda0SMatthew Dillon __func__, NG_NODE_NAME(l2cap->node), NG_L2CAP_HOOK_HCI); 761b06ebda0SMatthew Dillon 762b06ebda0SMatthew Dillon goto drop; /* XXX what to do with "pending"? */ 763b06ebda0SMatthew Dillon } 764b06ebda0SMatthew Dillon 765b06ebda0SMatthew Dillon /* Send ACL data packets */ 766b06ebda0SMatthew Dillon while (con->pending < con->l2cap->num_pkts && con->tx_pkt != NULL) { 767b06ebda0SMatthew Dillon m = con->tx_pkt; 768b06ebda0SMatthew Dillon con->tx_pkt = con->tx_pkt->m_nextpkt; 769b06ebda0SMatthew Dillon m->m_nextpkt = NULL; 770b06ebda0SMatthew Dillon 771b06ebda0SMatthew Dillon NG_L2CAP_INFO( 772b06ebda0SMatthew Dillon "%s: %s - sending ACL packet, con_handle=%d, len=%d\n", 773b06ebda0SMatthew Dillon __func__, NG_NODE_NAME(l2cap->node), con->con_handle, 774b06ebda0SMatthew Dillon m->m_pkthdr.len); 775b06ebda0SMatthew Dillon 776b06ebda0SMatthew Dillon NG_SEND_DATA_ONLY(error, l2cap->hci, m); 777b06ebda0SMatthew Dillon if (error != 0) { 778b06ebda0SMatthew Dillon NG_L2CAP_ERR( 779b06ebda0SMatthew Dillon "%s: %s - could not send ACL data packet, con_handle=%d, error=%d\n", 780b06ebda0SMatthew Dillon __func__, NG_NODE_NAME(l2cap->node), 781b06ebda0SMatthew Dillon con->con_handle, error); 782b06ebda0SMatthew Dillon 783b06ebda0SMatthew Dillon goto drop; /* XXX what to do with "pending"? */ 784b06ebda0SMatthew Dillon } 785b06ebda0SMatthew Dillon 786b06ebda0SMatthew Dillon con->pending ++; 787b06ebda0SMatthew Dillon } 788b06ebda0SMatthew Dillon 789b06ebda0SMatthew Dillon NG_L2CAP_INFO( 790b06ebda0SMatthew Dillon "%s: %s - %d ACL packets have been sent, con_handle=%d\n", 791b06ebda0SMatthew Dillon __func__, NG_NODE_NAME(l2cap->node), con->pending, 792b06ebda0SMatthew Dillon con->con_handle); 793b06ebda0SMatthew Dillon 794b06ebda0SMatthew Dillon return; 795b06ebda0SMatthew Dillon 796b06ebda0SMatthew Dillon drop: 797b06ebda0SMatthew Dillon while (con->tx_pkt != NULL) { 798b06ebda0SMatthew Dillon m = con->tx_pkt->m_nextpkt; 799b06ebda0SMatthew Dillon m_freem(con->tx_pkt); 800b06ebda0SMatthew Dillon con->tx_pkt = m; 801b06ebda0SMatthew Dillon } 802b06ebda0SMatthew Dillon } /* ng_l2cap_lp_deliver */ 803b06ebda0SMatthew Dillon 804b06ebda0SMatthew Dillon /* 805b06ebda0SMatthew Dillon * Process connection timeout. Remove connection from the list. If there 806b06ebda0SMatthew Dillon * are any channels that wait for the connection then notify them. Free 807b06ebda0SMatthew Dillon * connection descriptor. 808b06ebda0SMatthew Dillon */ 809b06ebda0SMatthew Dillon 810b06ebda0SMatthew Dillon void 811b06ebda0SMatthew Dillon ng_l2cap_process_lp_timeout(node_p node, hook_p hook, void *arg1, int con_handle) 812b06ebda0SMatthew Dillon { 813b06ebda0SMatthew Dillon ng_l2cap_p l2cap = NULL; 814b06ebda0SMatthew Dillon ng_l2cap_con_p con = NULL; 815b06ebda0SMatthew Dillon 816b06ebda0SMatthew Dillon if (NG_NODE_NOT_VALID(node)) { 817b06ebda0SMatthew Dillon printf("%s: Netgraph node is not valid\n", __func__); 818b06ebda0SMatthew Dillon return; 819b06ebda0SMatthew Dillon } 820b06ebda0SMatthew Dillon 821b06ebda0SMatthew Dillon l2cap = (ng_l2cap_p) NG_NODE_PRIVATE(node); 822b06ebda0SMatthew Dillon con = ng_l2cap_con_by_handle(l2cap, con_handle); 823b06ebda0SMatthew Dillon 824b06ebda0SMatthew Dillon if (con == NULL) { 825b06ebda0SMatthew Dillon NG_L2CAP_ALERT( 826b06ebda0SMatthew Dillon "%s: %s - could not find connection, con_handle=%d\n", 827b06ebda0SMatthew Dillon __func__, NG_NODE_NAME(node), con_handle); 828b06ebda0SMatthew Dillon return; 829b06ebda0SMatthew Dillon } 830b06ebda0SMatthew Dillon 831b06ebda0SMatthew Dillon if (!(con->flags & NG_L2CAP_CON_LP_TIMO)) { 832b06ebda0SMatthew Dillon NG_L2CAP_ALERT( 833b06ebda0SMatthew Dillon "%s: %s - no pending LP timeout, con_handle=%d, state=%d, flags=%#x\n", 834b06ebda0SMatthew Dillon __func__, NG_NODE_NAME(node), con_handle, con->state, 835b06ebda0SMatthew Dillon con->flags); 836b06ebda0SMatthew Dillon return; 837b06ebda0SMatthew Dillon } 838b06ebda0SMatthew Dillon 839b06ebda0SMatthew Dillon /* 840b06ebda0SMatthew Dillon * Notify channels that connection has timed out. This will remove 841b06ebda0SMatthew Dillon * connection, channels and pending commands. 842b06ebda0SMatthew Dillon */ 843b06ebda0SMatthew Dillon 844b06ebda0SMatthew Dillon con->flags &= ~NG_L2CAP_CON_LP_TIMO; 845b06ebda0SMatthew Dillon ng_l2cap_con_fail(con, NG_L2CAP_TIMEOUT); 846b06ebda0SMatthew Dillon } /* ng_l2cap_process_lp_timeout */ 847b06ebda0SMatthew Dillon 848b06ebda0SMatthew Dillon /* 849b06ebda0SMatthew Dillon * Process auto disconnect timeout and send LP_DisconReq event to the 850b06ebda0SMatthew Dillon * lower layer protocol 851b06ebda0SMatthew Dillon */ 852b06ebda0SMatthew Dillon 853b06ebda0SMatthew Dillon void 854b06ebda0SMatthew Dillon ng_l2cap_process_discon_timeout(node_p node, hook_p hook, void *arg1, int con_handle) 855b06ebda0SMatthew Dillon { 856b06ebda0SMatthew Dillon ng_l2cap_p l2cap = NULL; 857b06ebda0SMatthew Dillon ng_l2cap_con_p con = NULL; 858b06ebda0SMatthew Dillon struct ng_mesg *msg = NULL; 859b06ebda0SMatthew Dillon ng_hci_lp_discon_req_ep *ep = NULL; 860b06ebda0SMatthew Dillon int error; 861b06ebda0SMatthew Dillon 862b06ebda0SMatthew Dillon if (NG_NODE_NOT_VALID(node)) { 863b06ebda0SMatthew Dillon printf("%s: Netgraph node is not valid\n", __func__); 864b06ebda0SMatthew Dillon return; 865b06ebda0SMatthew Dillon } 866b06ebda0SMatthew Dillon 867b06ebda0SMatthew Dillon l2cap = (ng_l2cap_p) NG_NODE_PRIVATE(node); 868b06ebda0SMatthew Dillon con = ng_l2cap_con_by_handle(l2cap, con_handle); 869b06ebda0SMatthew Dillon 870b06ebda0SMatthew Dillon if (con == NULL) { 871b06ebda0SMatthew Dillon NG_L2CAP_ALERT( 872b06ebda0SMatthew Dillon "%s: %s - could not find connection, con_handle=%d\n", 873b06ebda0SMatthew Dillon __func__, NG_NODE_NAME(node), con_handle); 874b06ebda0SMatthew Dillon return; 875b06ebda0SMatthew Dillon } 876b06ebda0SMatthew Dillon 877b06ebda0SMatthew Dillon if (!(con->flags & NG_L2CAP_CON_AUTO_DISCON_TIMO)) { 878b06ebda0SMatthew Dillon NG_L2CAP_ALERT( 879b06ebda0SMatthew Dillon "%s: %s - no pending disconnect timeout, con_handle=%d, state=%d, flags=%#x\n", 880b06ebda0SMatthew Dillon __func__, NG_NODE_NAME(node), con_handle, con->state, 881b06ebda0SMatthew Dillon con->flags); 882b06ebda0SMatthew Dillon return; 883b06ebda0SMatthew Dillon } 884b06ebda0SMatthew Dillon 885b06ebda0SMatthew Dillon con->flags &= ~NG_L2CAP_CON_AUTO_DISCON_TIMO; 886b06ebda0SMatthew Dillon 887b06ebda0SMatthew Dillon /* Check if lower layer protocol is still connected */ 888b06ebda0SMatthew Dillon if (l2cap->hci == NULL || NG_HOOK_NOT_VALID(l2cap->hci)) { 889b06ebda0SMatthew Dillon NG_L2CAP_ERR( 890b06ebda0SMatthew Dillon "%s: %s - hook \"%s\" is not connected or valid\n", 891b06ebda0SMatthew Dillon __func__, NG_NODE_NAME(l2cap->node), NG_L2CAP_HOOK_HCI); 892b06ebda0SMatthew Dillon return; 893b06ebda0SMatthew Dillon } 894b06ebda0SMatthew Dillon 895b06ebda0SMatthew Dillon /* Create and send LP_DisconReq event */ 896b06ebda0SMatthew Dillon NG_MKMESSAGE(msg, NGM_HCI_COOKIE, NGM_HCI_LP_DISCON_REQ, 897*5a975a3dSMatthew Dillon sizeof(*ep), M_WAITOK | M_NULLOK); 898b06ebda0SMatthew Dillon if (msg == NULL) 899b06ebda0SMatthew Dillon return; 900b06ebda0SMatthew Dillon 901b06ebda0SMatthew Dillon ep = (ng_hci_lp_discon_req_ep *) (msg->data); 902b06ebda0SMatthew Dillon ep->con_handle = con->con_handle; 903b06ebda0SMatthew Dillon ep->reason = 0x13; /* User Ended Connection */ 904b06ebda0SMatthew Dillon 905b06ebda0SMatthew Dillon NG_SEND_MSG_HOOK(error, l2cap->node, msg, l2cap->hci, 0); 906b06ebda0SMatthew Dillon } /* ng_l2cap_process_discon_timeout */ 907b06ebda0SMatthew Dillon 908