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