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