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