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