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