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