xref: /netbsd-src/sys/netbt/hci_unit.c (revision c7fb772b85b2b5d4cfb282f868f454b4701534fd)
1*c7fb772bSthorpej /*	$NetBSD: hci_unit.c,v 1.16 2021/08/07 16:19:18 thorpej Exp $	*/
2a5c89047Sgdamore 
3a5c89047Sgdamore /*-
4a5c89047Sgdamore  * Copyright (c) 2005 Iain Hibbert.
5a5c89047Sgdamore  * Copyright (c) 2006 Itronix Inc.
6a5c89047Sgdamore  * All rights reserved.
7a5c89047Sgdamore  *
8a5c89047Sgdamore  * Redistribution and use in source and binary forms, with or without
9a5c89047Sgdamore  * modification, are permitted provided that the following conditions
10a5c89047Sgdamore  * are met:
11a5c89047Sgdamore  * 1. Redistributions of source code must retain the above copyright
12a5c89047Sgdamore  *    notice, this list of conditions and the following disclaimer.
13a5c89047Sgdamore  * 2. Redistributions in binary form must reproduce the above copyright
14a5c89047Sgdamore  *    notice, this list of conditions and the following disclaimer in the
15a5c89047Sgdamore  *    documentation and/or other materials provided with the distribution.
16a5c89047Sgdamore  * 3. The name of Itronix Inc. may not be used to endorse
17a5c89047Sgdamore  *    or promote products derived from this software without specific
18a5c89047Sgdamore  *    prior written permission.
19a5c89047Sgdamore  *
20a5c89047Sgdamore  * THIS SOFTWARE IS PROVIDED BY ITRONIX INC. ``AS IS'' AND
21a5c89047Sgdamore  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
22a5c89047Sgdamore  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
23a5c89047Sgdamore  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL ITRONIX INC. BE LIABLE FOR ANY
24a5c89047Sgdamore  * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
25a5c89047Sgdamore  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
26a5c89047Sgdamore  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
27a5c89047Sgdamore  * ON ANY THEORY OF LIABILITY, WHETHER IN
28a5c89047Sgdamore  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
29a5c89047Sgdamore  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
30a5c89047Sgdamore  * POSSIBILITY OF SUCH DAMAGE.
31a5c89047Sgdamore  */
32a5c89047Sgdamore 
33a5c89047Sgdamore #include <sys/cdefs.h>
34*c7fb772bSthorpej __KERNEL_RCSID(0, "$NetBSD: hci_unit.c,v 1.16 2021/08/07 16:19:18 thorpej Exp $");
35a5c89047Sgdamore 
36a5c89047Sgdamore #include <sys/param.h>
37a5c89047Sgdamore #include <sys/conf.h>
38a5c89047Sgdamore #include <sys/device.h>
39a5c89047Sgdamore #include <sys/kernel.h>
40a5c89047Sgdamore #include <sys/malloc.h>
41a5c89047Sgdamore #include <sys/mbuf.h>
42a5c89047Sgdamore #include <sys/proc.h>
43a5c89047Sgdamore #include <sys/queue.h>
44a5c89047Sgdamore #include <sys/systm.h>
4546ed8f7dSad #include <sys/intr.h>
4615e29e98Sad #include <sys/socketvar.h>
47a5c89047Sgdamore 
48a5c89047Sgdamore #include <netbt/bluetooth.h>
49a5c89047Sgdamore #include <netbt/hci.h>
50a5c89047Sgdamore 
51a5c89047Sgdamore struct hci_unit_list hci_unit_list = SIMPLEQ_HEAD_INITIALIZER(hci_unit_list);
52a5c89047Sgdamore 
53a5c89047Sgdamore MALLOC_DEFINE(M_BLUETOOTH, "Bluetooth", "Bluetooth System Memory");
54a5c89047Sgdamore 
55a5c89047Sgdamore /*
56a5c89047Sgdamore  * HCI Input Queue max lengths.
57a5c89047Sgdamore  */
58a5c89047Sgdamore int hci_eventq_max = 20;
59a5c89047Sgdamore int hci_aclrxq_max = 50;
60a5c89047Sgdamore int hci_scorxq_max = 50;
61a5c89047Sgdamore 
62a5c89047Sgdamore /*
63aeab3db8Splunky  * This is the default minimum command set supported by older
64aeab3db8Splunky  * devices. Anything conforming to 1.2 spec or later will get
65aeab3db8Splunky  * updated during init.
66aeab3db8Splunky  */
67aeab3db8Splunky static const uint8_t hci_cmds_v10[HCI_COMMANDS_SIZE] = {
68aeab3db8Splunky 	0xff, 0xff, 0xff, 0x01, 0xfe, 0xff, 0xff, 0xff,
69aeab3db8Splunky 	0xff, 0xff, 0xff, 0x7f, 0x32, 0x03, 0xb8, 0xfe,
70aeab3db8Splunky 	0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
71aeab3db8Splunky 	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
72aeab3db8Splunky 	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
73aeab3db8Splunky 	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
74aeab3db8Splunky 	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
75aeab3db8Splunky 	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
76aeab3db8Splunky };
77aeab3db8Splunky 
78aeab3db8Splunky /*
79a5c89047Sgdamore  * bluetooth unit functions
80a5c89047Sgdamore  */
81a5c89047Sgdamore static void hci_intr (void *);
82a5c89047Sgdamore 
83736a9db0Splunky struct hci_unit *
hci_attach_pcb(const struct hci_if * hci_if,device_t dev,uint16_t flags)8456a73a7dSrmind hci_attach_pcb(const struct hci_if *hci_if, device_t dev, uint16_t flags)
85a5c89047Sgdamore {
86736a9db0Splunky 	struct hci_unit *unit;
87a5c89047Sgdamore 
88736a9db0Splunky 	KASSERT(dev != NULL);
89736a9db0Splunky 	KASSERT(hci_if->enable != NULL);
90736a9db0Splunky 	KASSERT(hci_if->disable != NULL);
91736a9db0Splunky 	KASSERT(hci_if->output_cmd != NULL);
92736a9db0Splunky 	KASSERT(hci_if->output_acl != NULL);
93736a9db0Splunky 	KASSERT(hci_if->output_sco != NULL);
94736a9db0Splunky 	KASSERT(hci_if->get_stats != NULL);
95736a9db0Splunky 
96736a9db0Splunky 	unit = malloc(sizeof(struct hci_unit), M_BLUETOOTH, M_ZERO | M_WAITOK);
97736a9db0Splunky 	KASSERT(unit != NULL);
98736a9db0Splunky 
99736a9db0Splunky 	unit->hci_dev = dev;
100736a9db0Splunky 	unit->hci_if = hci_if;
101736a9db0Splunky 	unit->hci_flags = flags;
102736a9db0Splunky 
103736a9db0Splunky 	mutex_init(&unit->hci_devlock, MUTEX_DRIVER, hci_if->ipl);
10415e29e98Sad 	cv_init(&unit->hci_init, "hci_init");
105a5c89047Sgdamore 
106a5c89047Sgdamore 	MBUFQ_INIT(&unit->hci_eventq);
107a5c89047Sgdamore 	MBUFQ_INIT(&unit->hci_aclrxq);
108a5c89047Sgdamore 	MBUFQ_INIT(&unit->hci_scorxq);
109a5c89047Sgdamore 	MBUFQ_INIT(&unit->hci_cmdwait);
110a5c89047Sgdamore 	MBUFQ_INIT(&unit->hci_scodone);
111a5c89047Sgdamore 
112a5c89047Sgdamore 	TAILQ_INIT(&unit->hci_links);
113a5c89047Sgdamore 	LIST_INIT(&unit->hci_memos);
114a5c89047Sgdamore 
11515e29e98Sad 	mutex_enter(bt_lock);
116a5c89047Sgdamore 	SIMPLEQ_INSERT_TAIL(&hci_unit_list, unit, hci_next);
11715e29e98Sad 	mutex_exit(bt_lock);
118736a9db0Splunky 
119736a9db0Splunky 	return unit;
120a5c89047Sgdamore }
121a5c89047Sgdamore 
122a5c89047Sgdamore void
hci_detach_pcb(struct hci_unit * unit)12356a73a7dSrmind hci_detach_pcb(struct hci_unit *unit)
124a5c89047Sgdamore {
125a5c89047Sgdamore 
12615e29e98Sad 	mutex_enter(bt_lock);
127a5c89047Sgdamore 	hci_disable(unit);
128a5c89047Sgdamore 
129a5c89047Sgdamore 	SIMPLEQ_REMOVE(&hci_unit_list, unit, hci_unit, hci_next);
13015e29e98Sad 	mutex_exit(bt_lock);
131736a9db0Splunky 
13215e29e98Sad 	cv_destroy(&unit->hci_init);
133736a9db0Splunky 	mutex_destroy(&unit->hci_devlock);
134736a9db0Splunky 	free(unit, M_BLUETOOTH);
135a5c89047Sgdamore }
136a5c89047Sgdamore 
137a5c89047Sgdamore int
hci_enable(struct hci_unit * unit)138a5c89047Sgdamore hci_enable(struct hci_unit *unit)
139a5c89047Sgdamore {
140736a9db0Splunky 	int err;
141a5c89047Sgdamore 
142a5c89047Sgdamore 	/*
143a5c89047Sgdamore 	 * Bluetooth spec says that a device can accept one
144a5c89047Sgdamore 	 * command on power up until they send a Command Status
145a5c89047Sgdamore 	 * or Command Complete event with more information, but
146a5c89047Sgdamore 	 * it seems that some devices cant and prefer to send a
147736a9db0Splunky 	 * No-op Command Status packet when they are ready.
148a5c89047Sgdamore 	 */
149736a9db0Splunky 	unit->hci_num_cmd_pkts = (unit->hci_flags & BTF_POWER_UP_NOOP) ? 0 : 1;
150a5c89047Sgdamore 	unit->hci_num_acl_pkts = 0;
151a5c89047Sgdamore 	unit->hci_num_sco_pkts = 0;
152a5c89047Sgdamore 
153a5c89047Sgdamore 	/*
154a5c89047Sgdamore 	 * only allow the basic packet types until
155a5c89047Sgdamore 	 * the features report is in
156a5c89047Sgdamore 	 */
157a5c89047Sgdamore 	unit->hci_acl_mask = HCI_PKT_DM1 | HCI_PKT_DH1;
158a5c89047Sgdamore 	unit->hci_packet_type = unit->hci_acl_mask;
159a5c89047Sgdamore 
160aeab3db8Splunky 	memcpy(unit->hci_cmds, hci_cmds_v10, HCI_COMMANDS_SIZE);
161aeab3db8Splunky 
16246ed8f7dSad 	unit->hci_rxint = softint_establish(SOFTINT_NET, &hci_intr, unit);
163a5c89047Sgdamore 	if (unit->hci_rxint == NULL)
164a5c89047Sgdamore 		return EIO;
165a5c89047Sgdamore 
166736a9db0Splunky 	err = (*unit->hci_if->enable)(unit->hci_dev);
167a5c89047Sgdamore 	if (err)
168a5c89047Sgdamore 		goto bad1;
169a5c89047Sgdamore 
170736a9db0Splunky 	unit->hci_flags |= BTF_RUNNING;
171736a9db0Splunky 
172a5c89047Sgdamore 	/*
173a5c89047Sgdamore 	 * Reset the device, this will trigger initialisation
174a5c89047Sgdamore 	 * and wake us up.
175a5c89047Sgdamore 	 */
176a5c89047Sgdamore 	unit->hci_flags |= BTF_INIT;
177a5c89047Sgdamore 
178a5c89047Sgdamore 	err = hci_send_cmd(unit, HCI_CMD_RESET, NULL, 0);
179a5c89047Sgdamore 	if (err)
180a5c89047Sgdamore 		goto bad2;
181a5c89047Sgdamore 
182a5c89047Sgdamore 	while (unit->hci_flags & BTF_INIT) {
18315e29e98Sad 		err = cv_timedwait_sig(&unit->hci_init, bt_lock, 5 * hz);
184a5c89047Sgdamore 		if (err)
185a5c89047Sgdamore 			goto bad2;
186a5c89047Sgdamore 
187a5c89047Sgdamore 		/* XXX
188a5c89047Sgdamore 		 * "What If", while we were sleeping, the device
189a5c89047Sgdamore 		 * was removed and detached? Ho Hum.
190a5c89047Sgdamore 		 */
191a5c89047Sgdamore 	}
192a5c89047Sgdamore 
1934f1cbddcSplunky 	/*
1944f1cbddcSplunky 	 * Attach Bluetooth Device Hub
1954f1cbddcSplunky 	 */
1962685996bSthorpej 	unit->hci_bthub = config_found(unit->hci_dev, &unit->hci_bdaddr, NULL,
197*c7fb772bSthorpej 	    CFARGS(.iattr = "btbus"));
1984f1cbddcSplunky 
199a5c89047Sgdamore 	return 0;
200a5c89047Sgdamore 
201a5c89047Sgdamore bad2:
202736a9db0Splunky 	(*unit->hci_if->disable)(unit->hci_dev);
203736a9db0Splunky 	unit->hci_flags &= ~BTF_RUNNING;
204a5c89047Sgdamore bad1:
20546ed8f7dSad 	softint_disestablish(unit->hci_rxint);
206a5c89047Sgdamore 	unit->hci_rxint = NULL;
207a5c89047Sgdamore 
208a5c89047Sgdamore 	return err;
209a5c89047Sgdamore }
210a5c89047Sgdamore 
211a5c89047Sgdamore void
hci_disable(struct hci_unit * unit)212a5c89047Sgdamore hci_disable(struct hci_unit *unit)
213a5c89047Sgdamore {
214a5c89047Sgdamore 	struct hci_link *link, *next;
215a5c89047Sgdamore 	struct hci_memo *memo;
216736a9db0Splunky 	int acl;
217a5c89047Sgdamore 
2184f1cbddcSplunky 	if (unit->hci_bthub) {
219d7fdc240Splunky 		device_t hub;
220d7fdc240Splunky 
221d7fdc240Splunky 		hub = unit->hci_bthub;
2224f1cbddcSplunky 		unit->hci_bthub = NULL;
223d7fdc240Splunky 
224d7fdc240Splunky 		mutex_exit(bt_lock);
225d7fdc240Splunky 		config_detach(hub, DETACH_FORCE);
226d7fdc240Splunky 		mutex_enter(bt_lock);
2274f1cbddcSplunky 	}
2284f1cbddcSplunky 
229a5c89047Sgdamore 	if (unit->hci_rxint) {
23046ed8f7dSad 		softint_disestablish(unit->hci_rxint);
231a5c89047Sgdamore 		unit->hci_rxint = NULL;
232a5c89047Sgdamore 	}
233a5c89047Sgdamore 
234736a9db0Splunky 	(*unit->hci_if->disable)(unit->hci_dev);
235736a9db0Splunky 	unit->hci_flags &= ~BTF_RUNNING;
236a5c89047Sgdamore 
237a5c89047Sgdamore 	/*
238a5c89047Sgdamore 	 * close down any links, take care to close SCO first since
239a5c89047Sgdamore 	 * they may depend on ACL links.
240a5c89047Sgdamore 	 */
241a5c89047Sgdamore 	for (acl = 0 ; acl < 2 ; acl++) {
242a5c89047Sgdamore 		next = TAILQ_FIRST(&unit->hci_links);
243a5c89047Sgdamore 		while ((link = next) != NULL) {
244a5c89047Sgdamore 			next = TAILQ_NEXT(link, hl_next);
245a5c89047Sgdamore 			if (acl || link->hl_type != HCI_LINK_ACL)
246a5c89047Sgdamore 				hci_link_free(link, ECONNABORTED);
247a5c89047Sgdamore 		}
248a5c89047Sgdamore 	}
249a5c89047Sgdamore 
250a5c89047Sgdamore 	while ((memo = LIST_FIRST(&unit->hci_memos)) != NULL)
251a5c89047Sgdamore 		hci_memo_free(memo);
252a5c89047Sgdamore 
253736a9db0Splunky 	/* (no need to hold hci_devlock, the driver is disabled) */
254736a9db0Splunky 
255a5c89047Sgdamore 	MBUFQ_DRAIN(&unit->hci_eventq);
256a5c89047Sgdamore 	unit->hci_eventqlen = 0;
257a5c89047Sgdamore 
258a5c89047Sgdamore 	MBUFQ_DRAIN(&unit->hci_aclrxq);
259a5c89047Sgdamore 	unit->hci_aclrxqlen = 0;
260a5c89047Sgdamore 
261a5c89047Sgdamore 	MBUFQ_DRAIN(&unit->hci_scorxq);
262a5c89047Sgdamore 	unit->hci_scorxqlen = 0;
263a5c89047Sgdamore 
264a5c89047Sgdamore 	MBUFQ_DRAIN(&unit->hci_cmdwait);
265a5c89047Sgdamore 	MBUFQ_DRAIN(&unit->hci_scodone);
266a5c89047Sgdamore }
267a5c89047Sgdamore 
268a5c89047Sgdamore struct hci_unit *
hci_unit_lookup(const bdaddr_t * addr)269e27c48bfSplunky hci_unit_lookup(const bdaddr_t *addr)
270a5c89047Sgdamore {
271a5c89047Sgdamore 	struct hci_unit *unit;
272a5c89047Sgdamore 
273a5c89047Sgdamore 	SIMPLEQ_FOREACH(unit, &hci_unit_list, hci_next) {
274a5c89047Sgdamore 		if ((unit->hci_flags & BTF_UP) == 0)
275a5c89047Sgdamore 			continue;
276a5c89047Sgdamore 
277a5c89047Sgdamore 		if (bdaddr_same(&unit->hci_bdaddr, addr))
278a5c89047Sgdamore 			break;
279a5c89047Sgdamore 	}
280a5c89047Sgdamore 
281a5c89047Sgdamore 	return unit;
282a5c89047Sgdamore }
283a5c89047Sgdamore 
284a5c89047Sgdamore /*
285a09c132dSplunky  * update num_cmd_pkts and push on pending commands queue
286a09c132dSplunky  */
287a09c132dSplunky void
hci_num_cmds(struct hci_unit * unit,uint8_t num)288a09c132dSplunky hci_num_cmds(struct hci_unit *unit, uint8_t num)
289a09c132dSplunky {
290a09c132dSplunky 	struct mbuf *m;
291a09c132dSplunky 
292a09c132dSplunky 	unit->hci_num_cmd_pkts = num;
293a09c132dSplunky 
294a09c132dSplunky 	while (unit->hci_num_cmd_pkts > 0 && MBUFQ_FIRST(&unit->hci_cmdwait)) {
295a09c132dSplunky 		MBUFQ_DEQUEUE(&unit->hci_cmdwait, m);
296a09c132dSplunky 		hci_output_cmd(unit, m);
297a09c132dSplunky 	}
298a09c132dSplunky }
299a09c132dSplunky 
300a09c132dSplunky /*
301a5c89047Sgdamore  * construct and queue a HCI command packet
302a5c89047Sgdamore  */
303a5c89047Sgdamore int
hci_send_cmd(struct hci_unit * unit,uint16_t opcode,void * buf,uint8_t len)304a5c89047Sgdamore hci_send_cmd(struct hci_unit *unit, uint16_t opcode, void *buf, uint8_t len)
305a5c89047Sgdamore {
306a5c89047Sgdamore 	struct mbuf *m;
307a5c89047Sgdamore 	hci_cmd_hdr_t *p;
308a5c89047Sgdamore 
309f21bb768Splunky 	KASSERT(unit != NULL);
310a5c89047Sgdamore 
311a5c89047Sgdamore 	m = m_gethdr(M_DONTWAIT, MT_DATA);
312a5c89047Sgdamore 	if (m == NULL)
313a5c89047Sgdamore 		return ENOMEM;
314a5c89047Sgdamore 
315a5c89047Sgdamore 	p = mtod(m, hci_cmd_hdr_t *);
316a5c89047Sgdamore 	p->type = HCI_CMD_PKT;
317a5c89047Sgdamore 	p->opcode = htole16(opcode);
318a5c89047Sgdamore 	p->length = len;
319a5c89047Sgdamore 	m->m_pkthdr.len = m->m_len = sizeof(hci_cmd_hdr_t);
320a5c89047Sgdamore 
321a5c89047Sgdamore 	if (len) {
322f21bb768Splunky 		KASSERT(buf != NULL);
323a5c89047Sgdamore 
324a5c89047Sgdamore 		m_copyback(m, sizeof(hci_cmd_hdr_t), len, buf);
325a5c89047Sgdamore 		if (m->m_pkthdr.len != (sizeof(hci_cmd_hdr_t) + len)) {
326a5c89047Sgdamore 			m_freem(m);
327a5c89047Sgdamore 			return ENOMEM;
328a5c89047Sgdamore 		}
329a5c89047Sgdamore 	}
330a5c89047Sgdamore 
3310b799668Splunky 	DPRINTFN(2, "(%s) opcode (%3.3x|%4.4x)\n", device_xname(unit->hci_dev),
332a5c89047Sgdamore 		HCI_OGF(opcode), HCI_OCF(opcode));
333a5c89047Sgdamore 
334a5c89047Sgdamore 	/* and send it on */
335a5c89047Sgdamore 	if (unit->hci_num_cmd_pkts == 0)
336a5c89047Sgdamore 		MBUFQ_ENQUEUE(&unit->hci_cmdwait, m);
337a5c89047Sgdamore 	else
338a5c89047Sgdamore 		hci_output_cmd(unit, m);
339a5c89047Sgdamore 
340a5c89047Sgdamore 	return 0;
341a5c89047Sgdamore }
342a5c89047Sgdamore 
343a5c89047Sgdamore /*
344a5c89047Sgdamore  * Incoming packet processing. Since the code is single threaded
345a5c89047Sgdamore  * in any case (IPL_SOFTNET), we handle it all in one interrupt function
346a5c89047Sgdamore  * picking our way through more important packets first so that hopefully
347a5c89047Sgdamore  * we will never get clogged up with bulk data.
348a5c89047Sgdamore  */
349a5c89047Sgdamore static void
hci_intr(void * arg)350a5c89047Sgdamore hci_intr(void *arg)
351a5c89047Sgdamore {
352a5c89047Sgdamore 	struct hci_unit *unit = arg;
353a5c89047Sgdamore 	struct mbuf *m;
354a5c89047Sgdamore 
35515e29e98Sad 	mutex_enter(bt_lock);
356a5c89047Sgdamore another:
357736a9db0Splunky 	mutex_enter(&unit->hci_devlock);
358a5c89047Sgdamore 
359a5c89047Sgdamore 	if (unit->hci_eventqlen > 0) {
360a5c89047Sgdamore 		MBUFQ_DEQUEUE(&unit->hci_eventq, m);
361a5c89047Sgdamore 		unit->hci_eventqlen--;
362736a9db0Splunky 		mutex_exit(&unit->hci_devlock);
363736a9db0Splunky 
364a5c89047Sgdamore 		KASSERT(m != NULL);
365a5c89047Sgdamore 
366a5c89047Sgdamore 		DPRINTFN(10, "(%s) recv event, len = %d\n",
3670b799668Splunky 				device_xname(unit->hci_dev), m->m_pkthdr.len);
368a5c89047Sgdamore 
369a5c89047Sgdamore 		m->m_flags |= M_LINK0;	/* mark incoming packet */
370a5c89047Sgdamore 		hci_mtap(m, unit);
371a5c89047Sgdamore 		hci_event(m, unit);
372a5c89047Sgdamore 
373a5c89047Sgdamore 		goto another;
374a5c89047Sgdamore 	}
375a5c89047Sgdamore 
376a5c89047Sgdamore 	if (unit->hci_scorxqlen > 0) {
377a5c89047Sgdamore 		MBUFQ_DEQUEUE(&unit->hci_scorxq, m);
378a5c89047Sgdamore 		unit->hci_scorxqlen--;
379736a9db0Splunky 		mutex_exit(&unit->hci_devlock);
380736a9db0Splunky 
381a5c89047Sgdamore 		KASSERT(m != NULL);
382a5c89047Sgdamore 
383a5c89047Sgdamore 		DPRINTFN(10, "(%s) recv SCO, len = %d\n",
3840b799668Splunky 				device_xname(unit->hci_dev), m->m_pkthdr.len);
385a5c89047Sgdamore 
386a5c89047Sgdamore 		m->m_flags |= M_LINK0;	/* mark incoming packet */
387a5c89047Sgdamore 		hci_mtap(m, unit);
388a5c89047Sgdamore 		hci_sco_recv(m, unit);
389a5c89047Sgdamore 
390a5c89047Sgdamore 		goto another;
391a5c89047Sgdamore 	}
392a5c89047Sgdamore 
393a5c89047Sgdamore 	if (unit->hci_aclrxqlen > 0) {
394a5c89047Sgdamore 		MBUFQ_DEQUEUE(&unit->hci_aclrxq, m);
395a5c89047Sgdamore 		unit->hci_aclrxqlen--;
396736a9db0Splunky 		mutex_exit(&unit->hci_devlock);
397736a9db0Splunky 
398a5c89047Sgdamore 		KASSERT(m != NULL);
399a5c89047Sgdamore 
400a5c89047Sgdamore 		DPRINTFN(10, "(%s) recv ACL, len = %d\n",
4010b799668Splunky 				device_xname(unit->hci_dev), m->m_pkthdr.len);
402a5c89047Sgdamore 
403a5c89047Sgdamore 		m->m_flags |= M_LINK0;	/* mark incoming packet */
404a5c89047Sgdamore 		hci_mtap(m, unit);
405a5c89047Sgdamore 		hci_acl_recv(m, unit);
406a5c89047Sgdamore 
407a5c89047Sgdamore 		goto another;
408a5c89047Sgdamore 	}
409a5c89047Sgdamore 
410a5c89047Sgdamore 	MBUFQ_DEQUEUE(&unit->hci_scodone, m);
411a5c89047Sgdamore 	if (m != NULL) {
412a5c89047Sgdamore 		struct hci_link *link;
413736a9db0Splunky 
414736a9db0Splunky 		mutex_exit(&unit->hci_devlock);
415a5c89047Sgdamore 
416a5c89047Sgdamore 		DPRINTFN(11, "(%s) complete SCO\n",
4170b799668Splunky 				device_xname(unit->hci_dev));
418a5c89047Sgdamore 
419a5c89047Sgdamore 		TAILQ_FOREACH(link, &unit->hci_links, hl_next) {
420a5c89047Sgdamore 			if (link == M_GETCTX(m, struct hci_link *)) {
421a5c89047Sgdamore 				hci_sco_complete(link, 1);
422a5c89047Sgdamore 				break;
423a5c89047Sgdamore 			}
424a5c89047Sgdamore 		}
425a5c89047Sgdamore 
426a5c89047Sgdamore 		unit->hci_num_sco_pkts++;
427a5c89047Sgdamore 		m_freem(m);
428a5c89047Sgdamore 
429a5c89047Sgdamore 		goto another;
430a5c89047Sgdamore 	}
431a5c89047Sgdamore 
432736a9db0Splunky 	mutex_exit(&unit->hci_devlock);
43315e29e98Sad 	mutex_exit(bt_lock);
434a5c89047Sgdamore 
435a5c89047Sgdamore 	DPRINTFN(10, "done\n");
436a5c89047Sgdamore }
437a5c89047Sgdamore 
438a5c89047Sgdamore /**********************************************************************
439a5c89047Sgdamore  *
440a5c89047Sgdamore  * IO routines
441a5c89047Sgdamore  *
442736a9db0Splunky  * input & complete routines will be called from device drivers,
443736a9db0Splunky  * possibly in interrupt context. We return success or failure to
444736a9db0Splunky  * enable proper accounting but we own the mbuf.
445a5c89047Sgdamore  */
446a5c89047Sgdamore 
447736a9db0Splunky bool
hci_input_event(struct hci_unit * unit,struct mbuf * m)448a5c89047Sgdamore hci_input_event(struct hci_unit *unit, struct mbuf *m)
449a5c89047Sgdamore {
450736a9db0Splunky 	bool rv;
451736a9db0Splunky 
452736a9db0Splunky 	mutex_enter(&unit->hci_devlock);
453a5c89047Sgdamore 
454a5c89047Sgdamore 	if (unit->hci_eventqlen > hci_eventq_max || unit->hci_rxint == NULL) {
4550b799668Splunky 		DPRINTF("(%s) dropped event packet.\n", device_xname(unit->hci_dev));
456a5c89047Sgdamore 		m_freem(m);
457736a9db0Splunky 		rv = false;
458a5c89047Sgdamore 	} else {
459a5c89047Sgdamore 		unit->hci_eventqlen++;
460a5c89047Sgdamore 		MBUFQ_ENQUEUE(&unit->hci_eventq, m);
46146ed8f7dSad 		softint_schedule(unit->hci_rxint);
462736a9db0Splunky 		rv = true;
463a5c89047Sgdamore 	}
464a5c89047Sgdamore 
465736a9db0Splunky 	mutex_exit(&unit->hci_devlock);
466736a9db0Splunky 	return rv;
467736a9db0Splunky }
468736a9db0Splunky 
469736a9db0Splunky bool
hci_input_acl(struct hci_unit * unit,struct mbuf * m)470a5c89047Sgdamore hci_input_acl(struct hci_unit *unit, struct mbuf *m)
471a5c89047Sgdamore {
472736a9db0Splunky 	bool rv;
473736a9db0Splunky 
474736a9db0Splunky 	mutex_enter(&unit->hci_devlock);
475a5c89047Sgdamore 
476a5c89047Sgdamore 	if (unit->hci_aclrxqlen > hci_aclrxq_max || unit->hci_rxint == NULL) {
4770b799668Splunky 		DPRINTF("(%s) dropped ACL packet.\n", device_xname(unit->hci_dev));
478a5c89047Sgdamore 		m_freem(m);
479736a9db0Splunky 		rv = false;
480a5c89047Sgdamore 	} else {
481a5c89047Sgdamore 		unit->hci_aclrxqlen++;
482a5c89047Sgdamore 		MBUFQ_ENQUEUE(&unit->hci_aclrxq, m);
48346ed8f7dSad 		softint_schedule(unit->hci_rxint);
484736a9db0Splunky 		rv = true;
485a5c89047Sgdamore 	}
486a5c89047Sgdamore 
487736a9db0Splunky 	mutex_exit(&unit->hci_devlock);
488736a9db0Splunky 	return rv;
489736a9db0Splunky }
490736a9db0Splunky 
491736a9db0Splunky bool
hci_input_sco(struct hci_unit * unit,struct mbuf * m)492a5c89047Sgdamore hci_input_sco(struct hci_unit *unit, struct mbuf *m)
493a5c89047Sgdamore {
494736a9db0Splunky 	bool rv;
495736a9db0Splunky 
496736a9db0Splunky 	mutex_enter(&unit->hci_devlock);
497a5c89047Sgdamore 
498a5c89047Sgdamore 	if (unit->hci_scorxqlen > hci_scorxq_max || unit->hci_rxint == NULL) {
4990b799668Splunky 		DPRINTF("(%s) dropped SCO packet.\n", device_xname(unit->hci_dev));
500a5c89047Sgdamore 		m_freem(m);
501736a9db0Splunky 		rv = false;
502a5c89047Sgdamore 	} else {
503a5c89047Sgdamore 		unit->hci_scorxqlen++;
504a5c89047Sgdamore 		MBUFQ_ENQUEUE(&unit->hci_scorxq, m);
50546ed8f7dSad 		softint_schedule(unit->hci_rxint);
506736a9db0Splunky 		rv = true;
507a5c89047Sgdamore 	}
508736a9db0Splunky 
509736a9db0Splunky 	mutex_exit(&unit->hci_devlock);
510736a9db0Splunky 	return rv;
511a5c89047Sgdamore }
512a5c89047Sgdamore 
513a5c89047Sgdamore void
hci_output_cmd(struct hci_unit * unit,struct mbuf * m)514a5c89047Sgdamore hci_output_cmd(struct hci_unit *unit, struct mbuf *m)
515a5c89047Sgdamore {
516a5c89047Sgdamore 	void *arg;
517a5c89047Sgdamore 
518a5c89047Sgdamore 	hci_mtap(m, unit);
519a5c89047Sgdamore 
5200b799668Splunky 	DPRINTFN(10, "(%s) num_cmd_pkts=%d\n", device_xname(unit->hci_dev),
521a5c89047Sgdamore 					       unit->hci_num_cmd_pkts);
522a5c89047Sgdamore 
523a5c89047Sgdamore 	unit->hci_num_cmd_pkts--;
524a5c89047Sgdamore 
525a5c89047Sgdamore 	/*
526a5c89047Sgdamore 	 * If context is set, this was from a HCI raw socket
527a5c89047Sgdamore 	 * and a record needs to be dropped from the sockbuf.
528a5c89047Sgdamore 	 */
529a5c89047Sgdamore 	arg = M_GETCTX(m, void *);
530a5c89047Sgdamore 	if (arg != NULL)
531a5c89047Sgdamore 		hci_drop(arg);
532a5c89047Sgdamore 
533736a9db0Splunky 	(*unit->hci_if->output_cmd)(unit->hci_dev, m);
534a5c89047Sgdamore }
535a5c89047Sgdamore 
536a5c89047Sgdamore void
hci_output_acl(struct hci_unit * unit,struct mbuf * m)537a5c89047Sgdamore hci_output_acl(struct hci_unit *unit, struct mbuf *m)
538a5c89047Sgdamore {
539a5c89047Sgdamore 
540a5c89047Sgdamore 	hci_mtap(m, unit);
541a5c89047Sgdamore 
5420b799668Splunky 	DPRINTFN(10, "(%s) num_acl_pkts=%d\n", device_xname(unit->hci_dev),
543a5c89047Sgdamore 					       unit->hci_num_acl_pkts);
544a5c89047Sgdamore 
545a5c89047Sgdamore 	unit->hci_num_acl_pkts--;
546736a9db0Splunky 	(*unit->hci_if->output_acl)(unit->hci_dev, m);
547a5c89047Sgdamore }
548a5c89047Sgdamore 
549a5c89047Sgdamore void
hci_output_sco(struct hci_unit * unit,struct mbuf * m)550a5c89047Sgdamore hci_output_sco(struct hci_unit *unit, struct mbuf *m)
551a5c89047Sgdamore {
552a5c89047Sgdamore 
553a5c89047Sgdamore 	hci_mtap(m, unit);
554a5c89047Sgdamore 
5550b799668Splunky 	DPRINTFN(10, "(%s) num_sco_pkts=%d\n", device_xname(unit->hci_dev),
556a5c89047Sgdamore 					       unit->hci_num_sco_pkts);
557a5c89047Sgdamore 
558a5c89047Sgdamore 	unit->hci_num_sco_pkts--;
559736a9db0Splunky 	(*unit->hci_if->output_sco)(unit->hci_dev, m);
560a5c89047Sgdamore }
561a5c89047Sgdamore 
562736a9db0Splunky bool
hci_complete_sco(struct hci_unit * unit,struct mbuf * m)563a5c89047Sgdamore hci_complete_sco(struct hci_unit *unit, struct mbuf *m)
564a5c89047Sgdamore {
565a5c89047Sgdamore 
566a5c89047Sgdamore 	if (unit->hci_rxint == NULL) {
5670b799668Splunky 		DPRINTFN(10, "(%s) complete SCO!\n", device_xname(unit->hci_dev));
568a5c89047Sgdamore 		m_freem(m);
569736a9db0Splunky 		return false;
570736a9db0Splunky 	}
571736a9db0Splunky 
572736a9db0Splunky 	mutex_enter(&unit->hci_devlock);
573736a9db0Splunky 
574a5c89047Sgdamore 	MBUFQ_ENQUEUE(&unit->hci_scodone, m);
57546ed8f7dSad 	softint_schedule(unit->hci_rxint);
576736a9db0Splunky 
577736a9db0Splunky 	mutex_exit(&unit->hci_devlock);
578736a9db0Splunky 	return true;
579a5c89047Sgdamore }
580