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