1 /*- 2 * l2cap.c 3 * 4 * SPDX-License-Identifier: BSD-2-Clause-FreeBSD 5 * 6 * Copyright (c) 2001-2002 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: l2cap.c,v 1.5 2003/05/16 19:52:37 max Exp $ 31 * $FreeBSD$ 32 */ 33 34 #include <sys/ioctl.h> 35 #define L2CAP_SOCKET_CHECKED 36 #include <bluetooth.h> 37 #include <errno.h> 38 #include <stdio.h> 39 #include <stdlib.h> 40 #include <string.h> 41 #include "l2control.h" 42 43 #define SIZE(x) (sizeof((x))/sizeof((x)[0])) 44 45 /* Print BDADDR */ 46 static char * 47 bdaddrpr(bdaddr_t const *ba) 48 { 49 extern int numeric_bdaddr; 50 static char str[24]; 51 struct hostent *he = NULL; 52 53 if (memcmp(ba, NG_HCI_BDADDR_ANY, sizeof(*ba)) == 0) { 54 str[0] = '*'; 55 str[1] = 0; 56 57 return (str); 58 } 59 60 if (!numeric_bdaddr && 61 (he = bt_gethostbyaddr((char *)ba, sizeof(*ba), AF_BLUETOOTH)) != NULL) { 62 strlcpy(str, he->h_name, sizeof(str)); 63 64 return (str); 65 } 66 67 bt_ntoa(ba, str); 68 69 return (str); 70 } /* bdaddrpr */ 71 72 /* Send read_node_flags command to the node */ 73 static int 74 l2cap_read_node_flags(int s, int argc, char **argv) 75 { 76 struct ng_btsocket_l2cap_raw_node_flags r; 77 78 memset(&r, 0, sizeof(r)); 79 if (ioctl(s, SIOC_L2CAP_NODE_GET_FLAGS, &r, sizeof(r)) < 0) 80 return (ERROR); 81 82 fprintf(stdout, "Connectionless traffic flags:\n"); 83 fprintf(stdout, "\tSDP: %s\n", 84 (r.flags & NG_L2CAP_CLT_SDP_DISABLED)? "disabled" : "enabled"); 85 fprintf(stdout, "\tRFCOMM: %s\n", 86 (r.flags & NG_L2CAP_CLT_RFCOMM_DISABLED)? "disabled":"enabled"); 87 fprintf(stdout, "\tTCP: %s\n", 88 (r.flags & NG_L2CAP_CLT_TCP_DISABLED)? "disabled" : "enabled"); 89 90 return (OK); 91 } /* l2cap_read_node_flags */ 92 93 /* Send read_debug_level command to the node */ 94 static int 95 l2cap_read_debug_level(int s, int argc, char **argv) 96 { 97 struct ng_btsocket_l2cap_raw_node_debug r; 98 99 memset(&r, 0, sizeof(r)); 100 if (ioctl(s, SIOC_L2CAP_NODE_GET_DEBUG, &r, sizeof(r)) < 0) 101 return (ERROR); 102 103 fprintf(stdout, "Debug level: %d\n", r.debug); 104 105 return (OK); 106 } /* l2cap_read_debug_level */ 107 108 /* Send write_debug_level command to the node */ 109 static int 110 l2cap_write_debug_level(int s, int argc, char **argv) 111 { 112 struct ng_btsocket_l2cap_raw_node_debug r; 113 114 memset(&r, 0, sizeof(r)); 115 switch (argc) { 116 case 1: 117 r.debug = atoi(argv[0]); 118 break; 119 120 default: 121 return (USAGE); 122 } 123 124 if (ioctl(s, SIOC_L2CAP_NODE_SET_DEBUG, &r, sizeof(r)) < 0) 125 return (ERROR); 126 127 return (OK); 128 } /* l2cap_write_debug_level */ 129 130 /* Send read_connection_list command to the node */ 131 static int 132 l2cap_read_connection_list(int s, int argc, char **argv) 133 { 134 static char const * const state[] = { 135 /* NG_L2CAP_CON_CLOSED */ "CLOSED", 136 /* NG_L2CAP_W4_LP_CON_CFM */ "W4_LP_CON_CFM", 137 /* NG_L2CAP_CON_OPEN */ "OPEN" 138 }; 139 #define con_state2str(x) ((x) >= SIZE(state)? "UNKNOWN" : state[(x)]) 140 141 struct ng_btsocket_l2cap_raw_con_list r; 142 int n, error = OK; 143 144 memset(&r, 0, sizeof(r)); 145 r.num_connections = NG_L2CAP_MAX_CON_NUM; 146 r.connections = calloc(NG_L2CAP_MAX_CON_NUM, 147 sizeof(ng_l2cap_node_con_ep)); 148 if (r.connections == NULL) { 149 errno = ENOMEM; 150 return (ERROR); 151 } 152 153 if (ioctl(s, SIOC_L2CAP_NODE_GET_CON_LIST, &r, sizeof(r)) < 0) { 154 error = ERROR; 155 goto out; 156 } 157 158 fprintf(stdout, "L2CAP connections:\n"); 159 fprintf(stdout, 160 "Remote BD_ADDR Handle Flags Pending State\n"); 161 for (n = 0; n < r.num_connections; n++) { 162 fprintf(stdout, 163 "%-17.17s " \ 164 "%6d " \ 165 "%c%c%c%c%c " \ 166 "%7d " \ 167 "%s\n", 168 bdaddrpr(&r.connections[n].remote), 169 r.connections[n].con_handle, 170 ((r.connections[n].flags & NG_L2CAP_CON_OUTGOING)? 'O' : 'I'), 171 ((r.connections[n].flags & NG_L2CAP_CON_LP_TIMO)? 'L' : ' '), 172 ((r.connections[n].flags & NG_L2CAP_CON_AUTO_DISCON_TIMO)? 'D' : ' '), 173 ((r.connections[n].flags & NG_L2CAP_CON_TX)? 'T' : ' '), 174 ((r.connections[n].flags & NG_L2CAP_CON_RX)? 'R' : ' '), 175 r.connections[n].pending, 176 con_state2str(r.connections[n].state)); 177 } 178 out: 179 free(r.connections); 180 181 return (error); 182 } /* l2cap_read_connection_list */ 183 184 /* Send read_channel_list command to the node */ 185 static int 186 l2cap_read_channel_list(int s, int argc, char **argv) 187 { 188 static char const * const state[] = { 189 /* NG_L2CAP_CLOSED */ "CLOSED", 190 /* NG_L2CAP_W4_L2CAP_CON_RSP */ "W4_L2CAP_CON_RSP", 191 /* NG_L2CAP_W4_L2CA_CON_RSP */ "W4_L2CA_CON_RSP", 192 /* NG_L2CAP_CONFIG */ "CONFIG", 193 /* NG_L2CAP_OPEN */ "OPEN", 194 /* NG_L2CAP_W4_L2CAP_DISCON_RSP */ "W4_L2CAP_DISCON_RSP", 195 /* NG_L2CAP_W4_L2CA_DISCON_RSP */ "W4_L2CA_DISCON_RSP" 196 }; 197 #define ch_state2str(x) ((x) >= SIZE(state)? "UNKNOWN" : state[(x)]) 198 199 struct ng_btsocket_l2cap_raw_chan_list r; 200 int n, error = OK; 201 202 memset(&r, 0, sizeof(r)); 203 r.num_channels = NG_L2CAP_MAX_CHAN_NUM; 204 r.channels = calloc(NG_L2CAP_MAX_CHAN_NUM, 205 sizeof(ng_l2cap_node_chan_ep)); 206 if (r.channels == NULL) { 207 errno = ENOMEM; 208 return (ERROR); 209 } 210 211 if (ioctl(s, SIOC_L2CAP_NODE_GET_CHAN_LIST, &r, sizeof(r)) < 0) { 212 error = ERROR; 213 goto out; 214 } 215 216 fprintf(stdout, "L2CAP channels:\n"); 217 fprintf(stdout, 218 "Remote BD_ADDR SCID/ DCID PSM IMTU/ OMTU State\n"); 219 for (n = 0; n < r.num_channels; n++) { 220 fprintf(stdout, 221 "%-17.17s " \ 222 "%5d/%5d %5d " \ 223 "%5d/%5d " \ 224 "%s\n", 225 bdaddrpr(&r.channels[n].remote), 226 r.channels[n].scid, r.channels[n].dcid, 227 r.channels[n].psm, r.channels[n].imtu, 228 r.channels[n].omtu, 229 ch_state2str(r.channels[n].state)); 230 } 231 out: 232 free(r.channels); 233 234 return (error); 235 } /* l2cap_read_channel_list */ 236 237 /* Send read_auto_disconnect_timeout command to the node */ 238 static int 239 l2cap_read_auto_disconnect_timeout(int s, int argc, char **argv) 240 { 241 struct ng_btsocket_l2cap_raw_auto_discon_timo r; 242 243 memset(&r, 0, sizeof(r)); 244 if (ioctl(s, SIOC_L2CAP_NODE_GET_AUTO_DISCON_TIMO, &r, sizeof(r)) < 0) 245 return (ERROR); 246 247 if (r.timeout != 0) 248 fprintf(stdout, "Auto disconnect timeout: %d sec\n", r.timeout); 249 else 250 fprintf(stdout, "Auto disconnect disabled\n"); 251 252 return (OK); 253 } /* l2cap_read_auto_disconnect_timeout */ 254 255 /* Send write_auto_disconnect_timeout command to the node */ 256 static int 257 l2cap_write_auto_disconnect_timeout(int s, int argc, char **argv) 258 { 259 struct ng_btsocket_l2cap_raw_auto_discon_timo r; 260 261 memset(&r, 0, sizeof(r)); 262 switch (argc) { 263 case 1: 264 r.timeout = atoi(argv[0]); 265 break; 266 267 default: 268 return (USAGE); 269 } 270 271 if (ioctl(s, SIOC_L2CAP_NODE_SET_AUTO_DISCON_TIMO, &r, sizeof(r)) < 0) 272 return (ERROR); 273 274 return (OK); 275 } /* l2cap_write_auto_disconnect_timeout */ 276 277 struct l2cap_command l2cap_commands[] = { 278 { 279 "read_node_flags", 280 "Get L2CAP node flags", 281 &l2cap_read_node_flags 282 }, 283 { 284 "read_debug_level", 285 "Get L2CAP node debug level", 286 &l2cap_read_debug_level 287 }, 288 { 289 "write_debug_level <level>", 290 "Set L2CAP node debug level", 291 &l2cap_write_debug_level 292 }, 293 { 294 "read_connection_list", 295 "Read list of the L2CAP connections", 296 &l2cap_read_connection_list 297 }, 298 { 299 "read_channel_list", 300 "Read list of the L2CAP channels", 301 &l2cap_read_channel_list 302 }, 303 { 304 "read_auto_disconnect_timeout", 305 "Get L2CAP node auto disconnect timeout (in sec)", 306 &l2cap_read_auto_disconnect_timeout 307 }, 308 { 309 "write_auto_disconnect_timeout <timeout>", 310 "Set L2CAP node auto disconnect timeout (in sec)", 311 &l2cap_write_auto_disconnect_timeout 312 }, 313 { 314 NULL, 315 }}; 316 317