1 /* 2 * ng_l2cap_cmds.c 3 */ 4 5 /*- 6 * SPDX-License-Identifier: BSD-2-Clause 7 * 8 * Copyright (c) Maksim Yevmenkin <m_evmenkin@yahoo.com> 9 * All rights reserved. 10 * 11 * Redistribution and use in source and binary forms, with or without 12 * modification, are permitted provided that the following conditions 13 * are met: 14 * 1. Redistributions of source code must retain the above copyright 15 * notice, this list of conditions and the following disclaimer. 16 * 2. Redistributions in binary form must reproduce the above copyright 17 * notice, this list of conditions and the following disclaimer in the 18 * documentation and/or other materials provided with the distribution. 19 * 20 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 21 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 22 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 23 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 24 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 25 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 26 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 27 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 28 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 29 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 30 * SUCH DAMAGE. 31 * 32 * $Id: ng_l2cap_cmds.c,v 1.2 2003/09/08 19:11:45 max Exp $ 33 * $FreeBSD$ 34 */ 35 36 #include <sys/param.h> 37 #include <sys/systm.h> 38 #include <sys/kernel.h> 39 #include <sys/endian.h> 40 #include <sys/malloc.h> 41 #include <sys/mbuf.h> 42 #include <sys/queue.h> 43 #include <netgraph/ng_message.h> 44 #include <netgraph/netgraph.h> 45 #include <netgraph/bluetooth/include/ng_bluetooth.h> 46 #include <netgraph/bluetooth/include/ng_hci.h> 47 #include <netgraph/bluetooth/include/ng_l2cap.h> 48 #include <netgraph/bluetooth/l2cap/ng_l2cap_var.h> 49 #include <netgraph/bluetooth/l2cap/ng_l2cap_cmds.h> 50 #include <netgraph/bluetooth/l2cap/ng_l2cap_evnt.h> 51 #include <netgraph/bluetooth/l2cap/ng_l2cap_llpi.h> 52 #include <netgraph/bluetooth/l2cap/ng_l2cap_ulpi.h> 53 #include <netgraph/bluetooth/l2cap/ng_l2cap_misc.h> 54 55 /****************************************************************************** 56 ****************************************************************************** 57 ** L2CAP commands processing module 58 ****************************************************************************** 59 ******************************************************************************/ 60 61 /* 62 * Process L2CAP command queue on connection 63 */ 64 65 void 66 ng_l2cap_con_wakeup(ng_l2cap_con_p con) 67 { 68 ng_l2cap_cmd_p cmd = NULL; 69 struct mbuf *m = NULL; 70 int error = 0; 71 72 /* Find first non-pending command in the queue */ 73 TAILQ_FOREACH(cmd, &con->cmd_list, next) { 74 KASSERT((cmd->con == con), 75 ("%s: %s - invalid connection pointer!\n", 76 __func__, NG_NODE_NAME(con->l2cap->node))); 77 78 if (!(cmd->flags & NG_L2CAP_CMD_PENDING)) 79 break; 80 } 81 82 if (cmd == NULL) 83 return; 84 85 /* Detach command packet */ 86 m = cmd->aux; 87 cmd->aux = NULL; 88 89 /* Process command */ 90 switch (cmd->code) { 91 case NG_L2CAP_DISCON_RSP: 92 case NG_L2CAP_ECHO_RSP: 93 case NG_L2CAP_INFO_RSP: 94 /* 95 * Do not check return ng_l2cap_lp_send() value, because 96 * in these cases we do not really have a graceful way out. 97 * ECHO and INFO responses are internal to the stack and not 98 * visible to user. REJect is just being nice to remote end 99 * (otherwise remote end will timeout anyway). DISCON is 100 * probably most interesting here, however, if it fails 101 * there is nothing we can do anyway. 102 */ 103 104 (void) ng_l2cap_lp_send(con, NG_L2CAP_SIGNAL_CID, m); 105 ng_l2cap_unlink_cmd(cmd); 106 ng_l2cap_free_cmd(cmd); 107 break; 108 case NG_L2CAP_CMD_REJ: 109 (void) ng_l2cap_lp_send(con, 110 (con->linktype == NG_HCI_LINK_ACL)? 111 NG_L2CAP_SIGNAL_CID: 112 NG_L2CAP_LESIGNAL_CID 113 , m); 114 ng_l2cap_unlink_cmd(cmd); 115 ng_l2cap_free_cmd(cmd); 116 break; 117 118 case NG_L2CAP_CON_REQ: 119 error = ng_l2cap_lp_send(con, NG_L2CAP_SIGNAL_CID, m); 120 if (error != 0) { 121 ng_l2cap_l2ca_con_rsp(cmd->ch, cmd->token, 122 NG_L2CAP_NO_RESOURCES, 0); 123 ng_l2cap_free_chan(cmd->ch); /* will free commands */ 124 } else 125 ng_l2cap_command_timeout(cmd, 126 bluetooth_l2cap_rtx_timeout()); 127 break; 128 case NG_L2CAP_CON_RSP: 129 error = ng_l2cap_lp_send(con, NG_L2CAP_SIGNAL_CID, m); 130 ng_l2cap_unlink_cmd(cmd); 131 if (cmd->ch != NULL) { 132 ng_l2cap_l2ca_con_rsp_rsp(cmd->ch, cmd->token, 133 (error == 0)? NG_L2CAP_SUCCESS : 134 NG_L2CAP_NO_RESOURCES); 135 if (error != 0) 136 ng_l2cap_free_chan(cmd->ch); 137 } 138 ng_l2cap_free_cmd(cmd); 139 break; 140 141 case NG_L2CAP_CFG_REQ: 142 error = ng_l2cap_lp_send(con, NG_L2CAP_SIGNAL_CID, m); 143 if (error != 0) { 144 ng_l2cap_l2ca_cfg_rsp(cmd->ch, cmd->token, 145 NG_L2CAP_NO_RESOURCES); 146 ng_l2cap_unlink_cmd(cmd); 147 ng_l2cap_free_cmd(cmd); 148 } else 149 ng_l2cap_command_timeout(cmd, 150 bluetooth_l2cap_rtx_timeout()); 151 break; 152 153 case NG_L2CAP_CFG_RSP: 154 error = ng_l2cap_lp_send(con, NG_L2CAP_SIGNAL_CID, m); 155 ng_l2cap_unlink_cmd(cmd); 156 if (cmd->ch != NULL) 157 ng_l2cap_l2ca_cfg_rsp_rsp(cmd->ch, cmd->token, 158 (error == 0)? NG_L2CAP_SUCCESS : 159 NG_L2CAP_NO_RESOURCES); 160 ng_l2cap_free_cmd(cmd); 161 break; 162 163 case NG_L2CAP_DISCON_REQ: 164 error = ng_l2cap_lp_send(con, NG_L2CAP_SIGNAL_CID, m); 165 ng_l2cap_l2ca_discon_rsp(cmd->ch, cmd->token, 166 (error == 0)? NG_L2CAP_SUCCESS : NG_L2CAP_NO_RESOURCES); 167 if (error != 0) 168 ng_l2cap_free_chan(cmd->ch); /* XXX free channel */ 169 else 170 ng_l2cap_command_timeout(cmd, 171 bluetooth_l2cap_rtx_timeout()); 172 break; 173 174 case NG_L2CAP_ECHO_REQ: 175 error = ng_l2cap_lp_send(con, NG_L2CAP_SIGNAL_CID, m); 176 if (error != 0) { 177 ng_l2cap_l2ca_ping_rsp(con, cmd->token, 178 NG_L2CAP_NO_RESOURCES, NULL); 179 ng_l2cap_unlink_cmd(cmd); 180 ng_l2cap_free_cmd(cmd); 181 } else 182 ng_l2cap_command_timeout(cmd, 183 bluetooth_l2cap_rtx_timeout()); 184 break; 185 186 case NG_L2CAP_INFO_REQ: 187 error = ng_l2cap_lp_send(con, NG_L2CAP_SIGNAL_CID, m); 188 if (error != 0) { 189 ng_l2cap_l2ca_get_info_rsp(con, cmd->token, 190 NG_L2CAP_NO_RESOURCES, NULL); 191 ng_l2cap_unlink_cmd(cmd); 192 ng_l2cap_free_cmd(cmd); 193 } else 194 ng_l2cap_command_timeout(cmd, 195 bluetooth_l2cap_rtx_timeout()); 196 break; 197 198 case NGM_L2CAP_L2CA_WRITE: { 199 int length = m->m_pkthdr.len; 200 201 if (cmd->ch->dcid == NG_L2CAP_CLT_CID) { 202 m = ng_l2cap_prepend(m, sizeof(ng_l2cap_clt_hdr_t)); 203 if (m == NULL) 204 error = ENOBUFS; 205 else 206 mtod(m, ng_l2cap_clt_hdr_t *)->psm = 207 htole16(cmd->ch->psm); 208 } 209 210 if (error == 0) 211 error = ng_l2cap_lp_send(con, cmd->ch->dcid, m); 212 213 ng_l2cap_l2ca_write_rsp(cmd->ch, cmd->token, 214 (error == 0)? NG_L2CAP_SUCCESS : NG_L2CAP_NO_RESOURCES, 215 length); 216 217 ng_l2cap_unlink_cmd(cmd); 218 ng_l2cap_free_cmd(cmd); 219 } break; 220 case NG_L2CAP_CMD_PARAM_UPDATE_RESPONSE: 221 error = ng_l2cap_lp_send(con, NG_L2CAP_LESIGNAL_CID, m); 222 ng_l2cap_unlink_cmd(cmd); 223 ng_l2cap_free_cmd(cmd); 224 break; 225 case NG_L2CAP_CMD_PARAM_UPDATE_REQUEST: 226 /*TBD.*/ 227 /* XXX FIXME add other commands */ 228 default: 229 panic( 230 "%s: %s - unknown command code=%d\n", 231 __func__, NG_NODE_NAME(con->l2cap->node), cmd->code); 232 break; 233 } 234 } /* ng_l2cap_con_wakeup */ 235 236 /* 237 * We have failed to open ACL connection to the remote unit. Could be negative 238 * confirmation or timeout. So fail any "delayed" commands, notify upper layer, 239 * remove all channels and remove connection descriptor. 240 */ 241 242 void 243 ng_l2cap_con_fail(ng_l2cap_con_p con, u_int16_t result) 244 { 245 ng_l2cap_p l2cap = con->l2cap; 246 ng_l2cap_cmd_p cmd = NULL; 247 ng_l2cap_chan_p ch = NULL; 248 249 NG_L2CAP_INFO( 250 "%s: %s - ACL connection failed, result=%d\n", 251 __func__, NG_NODE_NAME(l2cap->node), result); 252 253 /* Connection is dying */ 254 con->flags |= NG_L2CAP_CON_DYING; 255 256 /* Clean command queue */ 257 while (!TAILQ_EMPTY(&con->cmd_list)) { 258 cmd = TAILQ_FIRST(&con->cmd_list); 259 260 ng_l2cap_unlink_cmd(cmd); 261 if(cmd->flags & NG_L2CAP_CMD_PENDING) 262 ng_l2cap_command_untimeout(cmd); 263 264 KASSERT((cmd->con == con), 265 ("%s: %s - invalid connection pointer!\n", 266 __func__, NG_NODE_NAME(l2cap->node))); 267 268 switch (cmd->code) { 269 case NG_L2CAP_CMD_REJ: 270 case NG_L2CAP_DISCON_RSP: 271 case NG_L2CAP_ECHO_RSP: 272 case NG_L2CAP_INFO_RSP: 273 case NG_L2CAP_CMD_PARAM_UPDATE_RESPONSE: 274 break; 275 276 case NG_L2CAP_CON_REQ: 277 ng_l2cap_l2ca_con_rsp(cmd->ch, cmd->token, result, 0); 278 break; 279 280 case NG_L2CAP_CON_RSP: 281 if (cmd->ch != NULL) 282 ng_l2cap_l2ca_con_rsp_rsp(cmd->ch, cmd->token, 283 result); 284 break; 285 286 case NG_L2CAP_CFG_REQ: 287 case NG_L2CAP_CFG_RSP: 288 case NGM_L2CAP_L2CA_WRITE: 289 ng_l2cap_l2ca_discon_ind(cmd->ch); 290 break; 291 292 case NG_L2CAP_DISCON_REQ: 293 ng_l2cap_l2ca_discon_rsp(cmd->ch, cmd->token, 294 NG_L2CAP_SUCCESS); 295 break; 296 297 case NG_L2CAP_ECHO_REQ: 298 ng_l2cap_l2ca_ping_rsp(cmd->con, cmd->token, 299 result, NULL); 300 break; 301 302 case NG_L2CAP_INFO_REQ: 303 ng_l2cap_l2ca_get_info_rsp(cmd->con, cmd->token, 304 result, NULL); 305 break; 306 307 /* XXX FIXME add other commands */ 308 309 default: 310 panic( 311 "%s: %s - unexpected command code=%d\n", 312 __func__, NG_NODE_NAME(l2cap->node), cmd->code); 313 break; 314 } 315 316 if (cmd->ch != NULL) 317 ng_l2cap_free_chan(cmd->ch); 318 319 ng_l2cap_free_cmd(cmd); 320 } 321 322 /* 323 * There still might be channels (in OPEN state?) that 324 * did not submit any commands, so disconnect them 325 */ 326 327 LIST_FOREACH(ch, &l2cap->chan_list, next) 328 if (ch->con == con) 329 ng_l2cap_l2ca_discon_ind(ch); 330 331 /* Free connection descriptor */ 332 ng_l2cap_free_con(con); 333 } /* ng_l2cap_con_fail */ 334 335 /* 336 * Process L2CAP command timeout. In general - notify upper layer and destroy 337 * channel. Do not pay much attention to return code, just do our best. 338 */ 339 340 void 341 ng_l2cap_process_command_timeout(node_p node, hook_p hook, void *arg1, int arg2) 342 { 343 ng_l2cap_p l2cap = NULL; 344 ng_l2cap_con_p con = NULL; 345 ng_l2cap_cmd_p cmd = NULL; 346 u_int16_t con_handle = (arg2 & 0x0ffff); 347 u_int8_t ident = ((arg2 >> 16) & 0xff); 348 349 if (NG_NODE_NOT_VALID(node)) { 350 printf("%s: Netgraph node is not valid\n", __func__); 351 return; 352 } 353 354 l2cap = (ng_l2cap_p) NG_NODE_PRIVATE(node); 355 356 con = ng_l2cap_con_by_handle(l2cap, con_handle); 357 if (con == NULL) { 358 NG_L2CAP_ALERT( 359 "%s: %s - could not find connection, con_handle=%d\n", 360 __func__, NG_NODE_NAME(node), con_handle); 361 return; 362 } 363 364 cmd = ng_l2cap_cmd_by_ident(con, ident); 365 if (cmd == NULL) { 366 NG_L2CAP_ALERT( 367 "%s: %s - could not find command, con_handle=%d, ident=%d\n", 368 __func__, NG_NODE_NAME(node), con_handle, ident); 369 return; 370 } 371 372 cmd->flags &= ~NG_L2CAP_CMD_PENDING; 373 ng_l2cap_unlink_cmd(cmd); 374 375 switch (cmd->code) { 376 case NG_L2CAP_CON_REQ: 377 ng_l2cap_l2ca_con_rsp(cmd->ch, cmd->token, NG_L2CAP_TIMEOUT, 0); 378 ng_l2cap_free_chan(cmd->ch); 379 break; 380 381 case NG_L2CAP_CFG_REQ: 382 ng_l2cap_l2ca_cfg_rsp(cmd->ch, cmd->token, NG_L2CAP_TIMEOUT); 383 break; 384 385 case NG_L2CAP_DISCON_REQ: 386 ng_l2cap_l2ca_discon_rsp(cmd->ch, cmd->token, NG_L2CAP_TIMEOUT); 387 ng_l2cap_free_chan(cmd->ch); /* XXX free channel */ 388 break; 389 390 case NG_L2CAP_ECHO_REQ: 391 /* Echo request timed out. Let the upper layer know */ 392 ng_l2cap_l2ca_ping_rsp(cmd->con, cmd->token, 393 NG_L2CAP_TIMEOUT, NULL); 394 break; 395 396 case NG_L2CAP_INFO_REQ: 397 /* Info request timed out. Let the upper layer know */ 398 ng_l2cap_l2ca_get_info_rsp(cmd->con, cmd->token, 399 NG_L2CAP_TIMEOUT, NULL); 400 break; 401 402 /* XXX FIXME add other commands */ 403 404 default: 405 panic( 406 "%s: %s - unexpected command code=%d\n", 407 __func__, NG_NODE_NAME(l2cap->node), cmd->code); 408 break; 409 } 410 411 ng_l2cap_free_cmd(cmd); 412 } /* ng_l2cap_process_command_timeout */ 413