1107120Sjulian/*
2107120Sjulian * l2cap.c
3107120Sjulian *
4107120Sjulian * Copyright (c) 2001-2002 Maksim Yevmenkin <m_evmenkin@yahoo.com>
5107120Sjulian * All rights reserved.
6107120Sjulian *
7107120Sjulian * Redistribution and use in source and binary forms, with or without
8107120Sjulian * modification, are permitted provided that the following conditions
9107120Sjulian * are met:
10107120Sjulian * 1. Redistributions of source code must retain the above copyright
11107120Sjulian *    notice, this list of conditions and the following disclaimer.
12107120Sjulian * 2. Redistributions in binary form must reproduce the above copyright
13107120Sjulian *    notice, this list of conditions and the following disclaimer in the
14107120Sjulian *    documentation and/or other materials provided with the distribution.
15107120Sjulian *
16107120Sjulian * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
17107120Sjulian * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18107120Sjulian * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19107120Sjulian * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
20107120Sjulian * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21107120Sjulian * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
22107120Sjulian * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
23107120Sjulian * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
24107120Sjulian * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25107120Sjulian * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26107120Sjulian * SUCH DAMAGE.
27107120Sjulian *
28121054Semax * $Id: l2cap.c,v 1.5 2003/05/16 19:52:37 max Exp $
29107120Sjulian * $FreeBSD$
30107120Sjulian */
31107120Sjulian
32107120Sjulian#include <sys/ioctl.h>
33121054Semax#include <bluetooth.h>
34107120Sjulian#include <errno.h>
35107120Sjulian#include <stdio.h>
36107120Sjulian#include <stdlib.h>
37107120Sjulian#include <string.h>
38107120Sjulian#include "l2control.h"
39107120Sjulian
40107120Sjulian#define	SIZE(x)	(sizeof((x))/sizeof((x)[0]))
41107120Sjulian
42121054Semax/* Print BDADDR */
43121054Semaxstatic char *
44121054Semaxbdaddrpr(bdaddr_t const *ba)
45121054Semax{
46121054Semax	extern int	 numeric_bdaddr;
47121054Semax	static char	 str[24];
48121054Semax	struct hostent	*he = NULL;
49121054Semax
50121054Semax	if (memcmp(ba, NG_HCI_BDADDR_ANY, sizeof(*ba)) == 0) {
51121054Semax		str[0] = '*';
52121054Semax		str[1] = 0;
53121054Semax
54121054Semax		return (str);
55121054Semax	}
56121054Semax
57121054Semax	if (!numeric_bdaddr &&
58121054Semax	    (he = bt_gethostbyaddr((char *)ba, sizeof(*ba), AF_BLUETOOTH)) != NULL) {
59121054Semax		strlcpy(str, he->h_name, sizeof(str));
60121054Semax
61121054Semax		return (str);
62121054Semax	}
63121054Semax
64121054Semax	bt_ntoa(ba, str);
65121054Semax
66121054Semax	return (str);
67121054Semax} /* bdaddrpr */
68121054Semax
69107120Sjulian/* Send read_node_flags command to the node */
70107120Sjulianstatic int
71107120Sjulianl2cap_read_node_flags(int s, int argc, char **argv)
72107120Sjulian{
73107120Sjulian	struct ng_btsocket_l2cap_raw_node_flags	r;
74107120Sjulian
75107120Sjulian	memset(&r, 0, sizeof(r));
76107120Sjulian	if (ioctl(s, SIOC_L2CAP_NODE_GET_FLAGS, &r, sizeof(r)) < 0)
77107120Sjulian		return (ERROR);
78107120Sjulian
79107120Sjulian	fprintf(stdout, "Connectionless traffic flags:\n");
80107120Sjulian	fprintf(stdout, "\tSDP: %s\n",
81107120Sjulian		(r.flags & NG_L2CAP_CLT_SDP_DISABLED)? "disabled" : "enabled");
82107120Sjulian	fprintf(stdout, "\tRFCOMM: %s\n",
83107120Sjulian		(r.flags & NG_L2CAP_CLT_RFCOMM_DISABLED)? "disabled":"enabled");
84107120Sjulian	fprintf(stdout, "\tTCP: %s\n",
85107120Sjulian		(r.flags & NG_L2CAP_CLT_TCP_DISABLED)? "disabled" : "enabled");
86107120Sjulian
87107120Sjulian	return (OK);
88107120Sjulian} /* l2cap_read_node_flags */
89107120Sjulian
90107120Sjulian/* Send read_debug_level command to the node */
91107120Sjulianstatic int
92107120Sjulianl2cap_read_debug_level(int s, int argc, char **argv)
93107120Sjulian{
94107120Sjulian	struct ng_btsocket_l2cap_raw_node_debug	r;
95107120Sjulian
96107120Sjulian	memset(&r, 0, sizeof(r));
97107120Sjulian	if (ioctl(s, SIOC_L2CAP_NODE_GET_DEBUG, &r, sizeof(r)) < 0)
98107120Sjulian		return (ERROR);
99107120Sjulian
100107120Sjulian	fprintf(stdout, "Debug level: %d\n", r.debug);
101107120Sjulian
102107120Sjulian	return (OK);
103107120Sjulian} /* l2cap_read_debug_level */
104107120Sjulian
105107120Sjulian/* Send write_debug_level command to the node */
106107120Sjulianstatic int
107107120Sjulianl2cap_write_debug_level(int s, int argc, char **argv)
108107120Sjulian{
109107120Sjulian	struct ng_btsocket_l2cap_raw_node_debug	r;
110107120Sjulian
111107120Sjulian	memset(&r, 0, sizeof(r));
112107120Sjulian	switch (argc) {
113107120Sjulian	case 1:
114107120Sjulian		r.debug = atoi(argv[0]);
115107120Sjulian		break;
116107120Sjulian
117107120Sjulian	default:
118107120Sjulian		return (USAGE);
119107120Sjulian	}
120107120Sjulian
121107120Sjulian	if (ioctl(s, SIOC_L2CAP_NODE_SET_DEBUG, &r, sizeof(r)) < 0)
122107120Sjulian		return (ERROR);
123107120Sjulian
124107120Sjulian	return (OK);
125107120Sjulian} /* l2cap_write_debug_level */
126107120Sjulian
127107120Sjulian/* Send read_connection_list command to the node */
128107120Sjulianstatic int
129107120Sjulianl2cap_read_connection_list(int s, int argc, char **argv)
130107120Sjulian{
131107120Sjulian	static char const * const	state[] = {
132107120Sjulian		/* NG_L2CAP_CON_CLOSED */	"CLOSED",
133107120Sjulian		/* NG_L2CAP_W4_LP_CON_CFM */	"W4_LP_CON_CFM",
134107120Sjulian		/* NG_L2CAP_CON_OPEN */		"OPEN"
135107120Sjulian	};
136107120Sjulian#define con_state2str(x)	((x) >= SIZE(state)? "UNKNOWN" : state[(x)])
137107120Sjulian
138107120Sjulian	struct ng_btsocket_l2cap_raw_con_list	r;
139107120Sjulian	int					n, error = OK;
140107120Sjulian
141107120Sjulian	memset(&r, 0, sizeof(r));
142107120Sjulian	r.num_connections = NG_L2CAP_MAX_CON_NUM;
143107120Sjulian	r.connections = calloc(NG_L2CAP_MAX_CON_NUM,
144107120Sjulian				sizeof(ng_l2cap_node_con_ep));
145107120Sjulian	if (r.connections == NULL) {
146107120Sjulian		errno = ENOMEM;
147107120Sjulian		return (ERROR);
148107120Sjulian	}
149107120Sjulian
150107120Sjulian	if (ioctl(s, SIOC_L2CAP_NODE_GET_CON_LIST, &r, sizeof(r)) < 0) {
151107120Sjulian		error = ERROR;
152107120Sjulian		goto out;
153107120Sjulian	}
154107120Sjulian
155107120Sjulian	fprintf(stdout, "L2CAP connections:\n");
156107120Sjulian	fprintf(stdout,
157107120Sjulian"Remote BD_ADDR    Handle Flags Pending State\n");
158107120Sjulian	for (n = 0; n < r.num_connections; n++) {
159107120Sjulian		fprintf(stdout,
160121054Semax			"%-17.17s " \
161114879Sjulian			"%6d " \
162114879Sjulian			"%c%c%c%c%c " \
163107120Sjulian			"%7d " \
164107120Sjulian			"%s\n",
165121054Semax			bdaddrpr(&r.connections[n].remote),
166107120Sjulian			r.connections[n].con_handle,
167114879Sjulian			((r.connections[n].flags & NG_L2CAP_CON_OUTGOING)? 'O' : 'I'),
168114879Sjulian			((r.connections[n].flags & NG_L2CAP_CON_LP_TIMO)? 'L' : ' '),
169114879Sjulian			((r.connections[n].flags & NG_L2CAP_CON_AUTO_DISCON_TIMO)? 'D' : ' '),
170114879Sjulian			((r.connections[n].flags & NG_L2CAP_CON_TX)? 'T' : ' '),
171114879Sjulian			((r.connections[n].flags & NG_L2CAP_CON_RX)? 'R' : ' '),
172107120Sjulian			r.connections[n].pending,
173107120Sjulian			con_state2str(r.connections[n].state));
174107120Sjulian	}
175107120Sjulianout:
176107120Sjulian	free(r.connections);
177107120Sjulian
178107120Sjulian	return (error);
179107120Sjulian} /* l2cap_read_connection_list */
180107120Sjulian
181107120Sjulian/* Send read_channel_list command to the node */
182107120Sjulianstatic int
183107120Sjulianl2cap_read_channel_list(int s, int argc, char **argv)
184107120Sjulian{
185107120Sjulian	static char const * const	state[] = {
186107120Sjulian		/* NG_L2CAP_CLOSED */			"CLOSED",
187107120Sjulian		/* NG_L2CAP_W4_L2CAP_CON_RSP */		"W4_L2CAP_CON_RSP",
188107120Sjulian		/* NG_L2CAP_W4_L2CA_CON_RSP */		"W4_L2CA_CON_RSP",
189107120Sjulian		/* NG_L2CAP_CONFIG */			"CONFIG",
190107120Sjulian		/* NG_L2CAP_OPEN */			"OPEN",
191107120Sjulian		/* NG_L2CAP_W4_L2CAP_DISCON_RSP */	"W4_L2CAP_DISCON_RSP",
192107120Sjulian		/* NG_L2CAP_W4_L2CA_DISCON_RSP */	"W4_L2CA_DISCON_RSP"
193107120Sjulian	};
194107120Sjulian#define ch_state2str(x)	((x) >= SIZE(state)? "UNKNOWN" : state[(x)])
195107120Sjulian
196107120Sjulian	struct ng_btsocket_l2cap_raw_chan_list	r;
197107120Sjulian	int					n, error = OK;
198107120Sjulian
199107120Sjulian	memset(&r, 0, sizeof(r));
200107120Sjulian	r.num_channels = NG_L2CAP_MAX_CHAN_NUM;
201107120Sjulian	r.channels = calloc(NG_L2CAP_MAX_CHAN_NUM,
202107120Sjulian				sizeof(ng_l2cap_node_chan_ep));
203107120Sjulian	if (r.channels == NULL) {
204107120Sjulian		errno = ENOMEM;
205107120Sjulian		return (ERROR);
206107120Sjulian	}
207107120Sjulian
208107120Sjulian	if (ioctl(s, SIOC_L2CAP_NODE_GET_CHAN_LIST, &r, sizeof(r)) < 0) {
209107120Sjulian		error = ERROR;
210107120Sjulian		goto out;
211107120Sjulian	}
212107120Sjulian
213107120Sjulian	fprintf(stdout, "L2CAP channels:\n");
214107120Sjulian	fprintf(stdout,
215107120Sjulian"Remote BD_ADDR     SCID/ DCID   PSM  IMTU/ OMTU State\n");
216107120Sjulian	for (n = 0; n < r.num_channels; n++) {
217107120Sjulian		fprintf(stdout,
218121054Semax			"%-17.17s " \
219107120Sjulian			"%5d/%5d %5d " \
220107120Sjulian			"%5d/%5d " \
221107120Sjulian			"%s\n",
222121054Semax			bdaddrpr(&r.channels[n].remote),
223107120Sjulian			r.channels[n].scid, r.channels[n].dcid,
224107120Sjulian			r.channels[n].psm, r.channels[n].imtu,
225107120Sjulian			r.channels[n].omtu,
226107120Sjulian			ch_state2str(r.channels[n].state));
227107120Sjulian	}
228107120Sjulianout:
229107120Sjulian	free(r.channels);
230107120Sjulian
231107120Sjulian	return (error);
232107120Sjulian} /* l2cap_read_channel_list */
233107120Sjulian
234114879Sjulian/* Send read_auto_disconnect_timeout command to the node */
235114879Sjulianstatic int
236114879Sjulianl2cap_read_auto_disconnect_timeout(int s, int argc, char **argv)
237114879Sjulian{
238114879Sjulian	struct ng_btsocket_l2cap_raw_auto_discon_timo	r;
239114879Sjulian
240114879Sjulian	memset(&r, 0, sizeof(r));
241114879Sjulian	if (ioctl(s, SIOC_L2CAP_NODE_GET_AUTO_DISCON_TIMO, &r, sizeof(r)) < 0)
242114879Sjulian		return (ERROR);
243114879Sjulian
244114879Sjulian	if (r.timeout != 0)
245114879Sjulian		fprintf(stdout, "Auto disconnect timeout: %d sec\n", r.timeout);
246114879Sjulian	else
247114879Sjulian		fprintf(stdout, "Auto disconnect disabled\n");
248114879Sjulian
249114879Sjulian	return (OK);
250114879Sjulian} /* l2cap_read_auto_disconnect_timeout */
251114879Sjulian
252114879Sjulian/* Send write_auto_disconnect_timeout command to the node */
253114879Sjulianstatic int
254114879Sjulianl2cap_write_auto_disconnect_timeout(int s, int argc, char **argv)
255114879Sjulian{
256114879Sjulian	struct ng_btsocket_l2cap_raw_auto_discon_timo	r;
257114879Sjulian
258114879Sjulian	memset(&r, 0, sizeof(r));
259114879Sjulian	switch (argc) {
260114879Sjulian	case 1:
261114879Sjulian		r.timeout = atoi(argv[0]);
262114879Sjulian		break;
263114879Sjulian
264114879Sjulian	default:
265114879Sjulian		return (USAGE);
266114879Sjulian	}
267114879Sjulian
268114879Sjulian	if (ioctl(s, SIOC_L2CAP_NODE_SET_AUTO_DISCON_TIMO, &r, sizeof(r)) < 0)
269114879Sjulian		return (ERROR);
270114879Sjulian
271114879Sjulian	return (OK);
272114879Sjulian} /* l2cap_write_auto_disconnect_timeout */
273114879Sjulian
274107120Sjulianstruct l2cap_command	l2cap_commands[] = {
275107120Sjulian{
276107120Sjulian"read_node_flags",
277107120Sjulian"Get L2CAP node flags",
278107120Sjulian&l2cap_read_node_flags
279107120Sjulian},
280107120Sjulian{
281107120Sjulian"read_debug_level",
282107120Sjulian"Get L2CAP node debug level",
283107120Sjulian&l2cap_read_debug_level
284107120Sjulian},
285107120Sjulian{
286107120Sjulian"write_debug_level <level>",
287107120Sjulian"Set L2CAP node debug level",
288107120Sjulian&l2cap_write_debug_level
289107120Sjulian},
290107120Sjulian{
291107120Sjulian"read_connection_list",
292107120Sjulian"Read list of the L2CAP connections",
293107120Sjulian&l2cap_read_connection_list
294107120Sjulian},
295107120Sjulian{
296107120Sjulian"read_channel_list",
297107120Sjulian"Read list of the L2CAP channels",
298107120Sjulian&l2cap_read_channel_list
299107120Sjulian},
300107120Sjulian{
301114879Sjulian"read_auto_disconnect_timeout",
302114879Sjulian"Get L2CAP node auto disconnect timeout (in sec)",
303114879Sjulian&l2cap_read_auto_disconnect_timeout
304114879Sjulian},
305114879Sjulian{
306114879Sjulian"write_auto_disconnect_timeout <timeout>",
307114879Sjulian"Set L2CAP node auto disconnect timeout (in sec)",
308114879Sjulian&l2cap_write_auto_disconnect_timeout
309114879Sjulian},
310114879Sjulian{
311107120SjulianNULL,
312107120Sjulian}};
313107120Sjulian
314