1/*	$NetBSD: hci_ioctl.c,v 1.15 2021/09/21 15:03:08 christos Exp $	*/
2
3/*-
4 * Copyright (c) 2005 Iain Hibbert.
5 * Copyright (c) 2006 Itronix Inc.
6 * All rights reserved.
7 *
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions
10 * are met:
11 * 1. Redistributions of source code must retain the above copyright
12 *    notice, this list of conditions and the following disclaimer.
13 * 2. Redistributions in binary form must reproduce the above copyright
14 *    notice, this list of conditions and the following disclaimer in the
15 *    documentation and/or other materials provided with the distribution.
16 * 3. The name of Itronix Inc. may not be used to endorse
17 *    or promote products derived from this software without specific
18 *    prior written permission.
19 *
20 * THIS SOFTWARE IS PROVIDED BY ITRONIX INC. ``AS IS'' AND
21 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
22 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
23 * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL ITRONIX INC. BE LIABLE FOR ANY
24 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
25 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
26 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
27 * ON ANY THEORY OF LIABILITY, WHETHER IN
28 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
29 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
30 * POSSIBILITY OF SUCH DAMAGE.
31 */
32
33#include <sys/cdefs.h>
34__KERNEL_RCSID(0, "$NetBSD: hci_ioctl.c,v 1.15 2021/09/21 15:03:08 christos Exp $");
35
36#include <sys/param.h>
37#include <sys/domain.h>
38#include <sys/ioctl.h>
39#include <sys/kauth.h>
40#include <sys/kernel.h>
41#include <sys/mbuf.h>
42#include <sys/proc.h>
43#include <sys/systm.h>
44
45#include <netbt/bluetooth.h>
46#include <netbt/hci.h>
47#include <netbt/l2cap.h>
48#include <netbt/rfcomm.h>
49
50#ifdef BLUETOOTH_DEBUG
51#define BDADDR(bd)	(bd).b[5], (bd).b[4], (bd).b[3],	\
52			(bd).b[2], (bd).b[1], (bd).b[0]
53
54static void
55hci_dump(void)
56{
57	struct hci_unit *unit;
58	struct hci_link *link;
59	struct l2cap_channel *chan;
60	struct rfcomm_session *rs;
61	struct rfcomm_dlc *dlc;
62
63	uprintf("HCI:\n");
64	SIMPLEQ_FOREACH(unit, &hci_unit_list, hci_next) {
65		uprintf("UNIT %s: flags 0x%4.4x, "
66			"num_cmd=%d, num_acl=%d, num_sco=%d\n",
67			device_xname(unit->hci_dev), unit->hci_flags,
68			unit->hci_num_cmd_pkts,
69			unit->hci_num_acl_pkts,
70			unit->hci_num_sco_pkts);
71		TAILQ_FOREACH(link, &unit->hci_links, hl_next) {
72			uprintf("+HANDLE #%d: %s "
73			    "raddr=%2.2x:%2.2x:%2.2x:%2.2x:%2.2x:%2.2x, "
74			    "state %d, refcnt %d\n",
75			    link->hl_handle,
76			    (link->hl_type == HCI_LINK_ACL ? "ACL":"SCO"),
77			    BDADDR(link->hl_bdaddr),
78			    link->hl_state, link->hl_refcnt);
79		}
80	}
81
82	uprintf("L2CAP:\n");
83	LIST_FOREACH(chan, &l2cap_active_list, lc_ncid) {
84		uprintf("CID #%d state %d, psm=0x%4.4x, "
85		    "laddr=%2.2x:%2.2x:%2.2x:%2.2x:%2.2x:%2.2x, "
86		    "raddr=%2.2x:%2.2x:%2.2x:%2.2x:%2.2x:%2.2x\n",
87		    chan->lc_lcid, chan->lc_state, chan->lc_raddr.bt_psm,
88		    BDADDR(chan->lc_laddr.bt_bdaddr),
89		    BDADDR(chan->lc_raddr.bt_bdaddr));
90	}
91
92	LIST_FOREACH(chan, &l2cap_listen_list, lc_ncid) {
93		uprintf("LISTEN psm=0x%4.4x, "
94		    "laddr=%2.2x:%2.2x:%2.2x:%2.2x:%2.2x:%2.2x\n",
95		    chan->lc_laddr.bt_psm,
96		    BDADDR(chan->lc_laddr.bt_bdaddr));
97	}
98
99	uprintf("RFCOMM:\n");
100	LIST_FOREACH(rs, &rfcomm_session_active, rs_next) {
101		chan = rs->rs_l2cap;
102		uprintf("SESSION: state=%d, flags=0x%4.4x, psm 0x%4.4x "
103		    "laddr=%2.2x:%2.2x:%2.2x:%2.2x:%2.2x:%2.2x, "
104		    "raddr=%2.2x:%2.2x:%2.2x:%2.2x:%2.2x:%2.2x\n",
105		    rs->rs_state, rs->rs_flags, chan->lc_raddr.bt_psm,
106		    BDADDR(chan->lc_laddr.bt_bdaddr),
107		    BDADDR(chan->lc_raddr.bt_bdaddr));
108		LIST_FOREACH(dlc, &rs->rs_dlcs, rd_next) {
109			uprintf("+DLC channel=%d, dlci=%d, "
110			    "state=%d, flags=0x%4.4x, rxcred=%d, rxsize=%ld, "
111			    "txcred=%d, pending=%d, txqlen=%d\n",
112			    dlc->rd_raddr.bt_channel, dlc->rd_dlci,
113			    dlc->rd_state, dlc->rd_flags,
114			    dlc->rd_rxcred, (unsigned long)dlc->rd_rxsize,
115			    dlc->rd_txcred, dlc->rd_pending,
116			    (dlc->rd_txbuf ? dlc->rd_txbuf->m_pkthdr.len : 0));
117		}
118	}
119
120	LIST_FOREACH(rs, &rfcomm_session_listen, rs_next) {
121		chan = rs->rs_l2cap;
122		uprintf("LISTEN: psm 0x%4.4x, "
123		    "laddr=%2.2x:%2.2x:%2.2x:%2.2x:%2.2x:%2.2x\n",
124		    chan->lc_laddr.bt_psm,
125		    BDADDR(chan->lc_laddr.bt_bdaddr));
126		LIST_FOREACH(dlc, &rs->rs_dlcs, rd_next)
127			uprintf("+DLC channel=%d\n", dlc->rd_laddr.bt_channel);
128	}
129}
130
131#undef BDADDR
132#endif
133
134int
135hci_ioctl_pcb(unsigned long cmd, void *data)
136{
137	struct btreq *btr = data;
138	struct hci_unit *unit;
139	int err = 0;
140
141	DPRINTFN(1, "cmd %#lx\n", cmd);
142
143	switch(cmd) {
144#ifdef BLUETOOTH_DEBUG
145	case SIOCBTDUMP:
146		hci_dump();
147		return 0;
148#endif
149	/*
150	 * Get unit info based on address rather than name
151	 */
152	case SIOCGBTINFOA:
153		unit = hci_unit_lookup(&btr->btr_bdaddr);
154		if (unit == NULL)
155			return ENXIO;
156
157		break;
158
159	/*
160	 * The remaining ioctl's all use the same btreq structure and
161	 * index on the name of the device, so we look that up first.
162	 */
163	case SIOCNBTINFO:
164		/* empty name means give the first unit */
165		if (btr->btr_name[0] == '\0') {
166			unit = NULL;
167			break;
168		}
169
170		/* else fall through and look it up */
171		/* FALLTHROUGH */
172	case SIOCGBTINFO:
173	case SIOCSBTFLAGS:
174	case SIOCSBTPOLICY:
175	case SIOCSBTPTYPE:
176	case SIOCGBTSTATS:
177	case SIOCZBTSTATS:
178	case SIOCSBTSCOMTU:
179	case SIOCGBTFEAT:
180		SIMPLEQ_FOREACH(unit, &hci_unit_list, hci_next) {
181			if (strncmp(device_xname(unit->hci_dev),
182			    btr->btr_name, HCI_DEVNAME_SIZE) == 0)
183				break;
184		}
185
186		if (unit == NULL)
187			return ENXIO;
188
189		break;
190
191	default:	/* not one of mine */
192		return EPASSTHROUGH;
193	}
194
195	switch(cmd) {
196	case SIOCNBTINFO:	/* get next info */
197		if (unit)
198			unit = SIMPLEQ_NEXT(unit, hci_next);
199		else
200			unit = SIMPLEQ_FIRST(&hci_unit_list);
201
202		if (unit == NULL) {
203			err = ENXIO;
204			break;
205		}
206
207		/* FALLTHROUGH */
208	case SIOCGBTINFO:	/* get unit info */
209		/* FALLTHROUGH */
210	case SIOCGBTINFOA:	/* get info by address */
211		memset(btr, 0, sizeof(struct btreq));
212		strlcpy(btr->btr_name, device_xname(unit->hci_dev), HCI_DEVNAME_SIZE);
213		bdaddr_copy(&btr->btr_bdaddr, &unit->hci_bdaddr);
214
215		btr->btr_flags = unit->hci_flags;
216
217		btr->btr_num_cmd = unit->hci_num_cmd_pkts;
218		btr->btr_num_acl = unit->hci_num_acl_pkts;
219		btr->btr_num_sco = unit->hci_num_sco_pkts;
220		btr->btr_acl_mtu = unit->hci_max_acl_size;
221		btr->btr_sco_mtu = unit->hci_max_sco_size;
222		btr->btr_max_acl = unit->hci_max_acl_pkts;
223		btr->btr_max_sco = unit->hci_max_sco_pkts;
224
225		btr->btr_packet_type = unit->hci_packet_type;
226		btr->btr_link_policy = unit->hci_link_policy;
227		break;
228
229	case SIOCSBTFLAGS:	/* set unit flags (privileged) */
230		err = kauth_authorize_device(kauth_cred_get(),
231		    KAUTH_DEVICE_BLUETOOTH_SETPRIV, unit, KAUTH_ARG(cmd),
232		    btr, NULL);
233		if (err)
234			break;
235
236		if ((unit->hci_flags & BTF_UP)
237		    && (btr->btr_flags & BTF_UP) == 0) {
238			hci_disable(unit);
239			unit->hci_flags &= ~BTF_UP;
240		}
241
242		unit->hci_flags &= ~BTF_MASTER;
243		unit->hci_flags |= (btr->btr_flags & (BTF_INIT | BTF_MASTER));
244
245		if ((unit->hci_flags & BTF_UP) == 0
246		    && (btr->btr_flags & BTF_UP)) {
247			err = hci_enable(unit);
248			if (err)
249				break;
250
251			unit->hci_flags |= BTF_UP;
252		}
253
254		btr->btr_flags = unit->hci_flags;
255		break;
256
257	case SIOCSBTPOLICY:	/* set unit link policy (privileged) */
258		err = kauth_authorize_device(kauth_cred_get(),
259		    KAUTH_DEVICE_BLUETOOTH_SETPRIV, unit, KAUTH_ARG(cmd),
260		    btr, NULL);
261		if (err)
262			break;
263
264		unit->hci_link_policy = btr->btr_link_policy;
265		unit->hci_link_policy &= unit->hci_lmp_mask;
266		btr->btr_link_policy = unit->hci_link_policy;
267		break;
268
269	case SIOCSBTPTYPE:	/* set unit packet types (privileged) */
270		err = kauth_authorize_device(kauth_cred_get(),
271		    KAUTH_DEVICE_BLUETOOTH_SETPRIV, unit, KAUTH_ARG(cmd),
272		    btr, NULL);
273		if (err)
274			break;
275
276		unit->hci_packet_type = btr->btr_packet_type;
277		unit->hci_packet_type &= unit->hci_acl_mask;
278		btr->btr_packet_type = unit->hci_packet_type;
279		break;
280
281	case SIOCGBTSTATS:	/* get unit statistics */
282		(*unit->hci_if->get_stats)(unit->hci_dev, &btr->btr_stats, 0);
283		break;
284
285	case SIOCZBTSTATS:	/* get & reset unit statistics */
286		err = kauth_authorize_device(kauth_cred_get(),
287		    KAUTH_DEVICE_BLUETOOTH_SETPRIV, unit, KAUTH_ARG(cmd),
288		    btr, NULL);
289		if (err)
290			break;
291
292		(*unit->hci_if->get_stats)(unit->hci_dev, &btr->btr_stats, 1);
293		break;
294
295	case SIOCSBTSCOMTU:	/* set sco_mtu value for unit */
296		/*
297		 * This is a temporary ioctl and may not be supported
298		 * in the future. The need is that if SCO packets are
299		 * sent to USB bluetooth controllers that are not an
300		 * integer number of frame sizes, the USB bus locks up.
301		 */
302		err = kauth_authorize_device(kauth_cred_get(),
303		    KAUTH_DEVICE_BLUETOOTH_SETPRIV, unit, KAUTH_ARG(cmd),
304		    btr, NULL);
305		if (err)
306			break;
307
308		unit->hci_max_sco_size = btr->btr_sco_mtu;
309		break;
310
311	case SIOCGBTFEAT:	/* get unit features */
312		memset(btr, 0, sizeof(struct btreq));
313		strlcpy(btr->btr_name, device_xname(unit->hci_dev), HCI_DEVNAME_SIZE);
314		memcpy(btr->btr_features0, unit->hci_feat0, HCI_FEATURES_SIZE);
315		memcpy(btr->btr_features1, unit->hci_feat1, HCI_FEATURES_SIZE);
316		memcpy(btr->btr_features2, unit->hci_feat2, HCI_FEATURES_SIZE);
317		break;
318
319	default:
320		err = EFAULT;
321		break;
322	}
323
324	return err;
325}
326