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