xref: /netbsd-src/sys/netbt/hci_unit.c (revision fad4c9f71477ae11cea2ee75ec82151ac770a534)
1 /*	$NetBSD: hci_unit.c,v 1.1 2006/06/19 15:44:45 gdamore 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_unit.c,v 1.1 2006/06/19 15:44:45 gdamore Exp $");
35 
36 #include <sys/param.h>
37 #include <sys/conf.h>
38 #include <sys/device.h>
39 #include <sys/kernel.h>
40 #include <sys/malloc.h>
41 #include <sys/mbuf.h>
42 #include <sys/proc.h>
43 #include <sys/queue.h>
44 #include <sys/systm.h>
45 
46 #include <netbt/bluetooth.h>
47 #include <netbt/hci.h>
48 
49 struct hci_unit_list hci_unit_list = SIMPLEQ_HEAD_INITIALIZER(hci_unit_list);
50 
51 MALLOC_DEFINE(M_BLUETOOTH, "Bluetooth", "Bluetooth System Memory");
52 
53 /*
54  * HCI Input Queue max lengths.
55  */
56 int hci_eventq_max = 20;
57 int hci_aclrxq_max = 50;
58 int hci_scorxq_max = 50;
59 
60 /*
61  * bluetooth unit functions
62  */
63 static void hci_intr (void *);
64 
65 void
66 hci_attach(struct hci_unit *unit)
67 {
68 
69 	KASSERT(unit->hci_softc);
70 	KASSERT(unit->hci_devname);
71 	KASSERT(unit->hci_enable);
72 	KASSERT(unit->hci_disable);
73 	KASSERT(unit->hci_start_cmd);
74 	KASSERT(unit->hci_start_acl);
75 	KASSERT(unit->hci_start_sco);
76 
77 	MBUFQ_INIT(&unit->hci_eventq);
78 	MBUFQ_INIT(&unit->hci_aclrxq);
79 	MBUFQ_INIT(&unit->hci_scorxq);
80 	MBUFQ_INIT(&unit->hci_cmdq);
81 	MBUFQ_INIT(&unit->hci_cmdwait);
82 	MBUFQ_INIT(&unit->hci_acltxq);
83 	MBUFQ_INIT(&unit->hci_scotxq);
84 	MBUFQ_INIT(&unit->hci_scodone);
85 
86 	TAILQ_INIT(&unit->hci_links);
87 	LIST_INIT(&unit->hci_memos);
88 
89 	SIMPLEQ_INSERT_TAIL(&hci_unit_list, unit, hci_next);
90 }
91 
92 void
93 hci_detach(struct hci_unit *unit)
94 {
95 
96 	hci_disable(unit);
97 
98 	SIMPLEQ_REMOVE(&hci_unit_list, unit, hci_unit, hci_next);
99 }
100 
101 int
102 hci_enable(struct hci_unit *unit)
103 {
104 	int s, err;
105 
106 	/*
107 	 * Bluetooth spec says that a device can accept one
108 	 * command on power up until they send a Command Status
109 	 * or Command Complete event with more information, but
110 	 * it seems that some devices cant and prefer to send a
111 	 * No-op Command Status packet when they are ready, so
112 	 * we set this here and allow the driver (bt3c) to zero
113 	 * it.
114 	 */
115 	unit->hci_num_cmd_pkts = 1;
116 	unit->hci_num_acl_pkts = 0;
117 	unit->hci_num_sco_pkts = 0;
118 
119 	/*
120 	 * only allow the basic packet types until
121 	 * the features report is in
122 	 */
123 	unit->hci_acl_mask = HCI_PKT_DM1 | HCI_PKT_DH1;
124 	unit->hci_packet_type = unit->hci_acl_mask;
125 
126 	unit->hci_rxint = softintr_establish(IPL_SOFTNET, &hci_intr, unit);
127 	if (unit->hci_rxint == NULL)
128 		return EIO;
129 
130 	s = splraiseipl(unit->hci_ipl);
131 	err = (*unit->hci_enable)(unit);
132 	splx(s);
133 	if (err)
134 		goto bad1;
135 
136 	/*
137 	 * Reset the device, this will trigger initialisation
138 	 * and wake us up.
139 	 */
140 	unit->hci_flags |= BTF_INIT;
141 
142 	err = hci_send_cmd(unit, HCI_CMD_RESET, NULL, 0);
143 	if (err)
144 		goto bad2;
145 
146 	while (unit->hci_flags & BTF_INIT) {
147 		err = tsleep(unit, PWAIT | PCATCH, __func__, hz);
148 		if (err)
149 			goto bad2;
150 
151 		/* XXX
152 		 * "What If", while we were sleeping, the device
153 		 * was removed and detached? Ho Hum.
154 		 */
155 	}
156 
157 	return 0;
158 
159 bad2:
160 	s = splraiseipl(unit->hci_ipl);
161 	(*unit->hci_disable)(unit);
162 	splx(s);
163 
164 bad1:
165 	softintr_disestablish(unit->hci_rxint);
166 	unit->hci_rxint = NULL;
167 
168 	return err;
169 }
170 
171 void
172 hci_disable(struct hci_unit *unit)
173 {
174 	struct hci_link *link, *next;
175 	struct hci_memo *memo;
176 	int s, acl;
177 
178 	if (unit->hci_rxint) {
179 		softintr_disestablish(unit->hci_rxint);
180 		unit->hci_rxint = NULL;
181 	}
182 
183 	s = splraiseipl(unit->hci_ipl);
184 	(*unit->hci_disable)(unit);
185 	splx(s);
186 
187 	/*
188 	 * close down any links, take care to close SCO first since
189 	 * they may depend on ACL links.
190 	 */
191 	for (acl = 0 ; acl < 2 ; acl++) {
192 		next = TAILQ_FIRST(&unit->hci_links);
193 		while ((link = next) != NULL) {
194 			next = TAILQ_NEXT(link, hl_next);
195 			if (acl || link->hl_type != HCI_LINK_ACL)
196 				hci_link_free(link, ECONNABORTED);
197 		}
198 	}
199 
200 	while ((memo = LIST_FIRST(&unit->hci_memos)) != NULL)
201 		hci_memo_free(memo);
202 
203 	MBUFQ_DRAIN(&unit->hci_eventq);
204 	unit->hci_eventqlen = 0;
205 
206 	MBUFQ_DRAIN(&unit->hci_aclrxq);
207 	unit->hci_aclrxqlen = 0;
208 
209 	MBUFQ_DRAIN(&unit->hci_scorxq);
210 	unit->hci_scorxqlen = 0;
211 
212 	MBUFQ_DRAIN(&unit->hci_cmdq);
213 	MBUFQ_DRAIN(&unit->hci_cmdwait);
214 	MBUFQ_DRAIN(&unit->hci_acltxq);
215 	MBUFQ_DRAIN(&unit->hci_scotxq);
216 	MBUFQ_DRAIN(&unit->hci_scodone);
217 }
218 
219 struct hci_unit *
220 hci_unit_lookup(bdaddr_t *addr)
221 {
222 	struct hci_unit *unit;
223 
224 	SIMPLEQ_FOREACH(unit, &hci_unit_list, hci_next) {
225 		if ((unit->hci_flags & BTF_UP) == 0)
226 			continue;
227 
228 		if (bdaddr_same(&unit->hci_bdaddr, addr))
229 			break;
230 	}
231 
232 	return unit;
233 }
234 
235 /*
236  * construct and queue a HCI command packet
237  */
238 int
239 hci_send_cmd(struct hci_unit *unit, uint16_t opcode, void *buf, uint8_t len)
240 {
241 	struct mbuf *m;
242 	hci_cmd_hdr_t *p;
243 
244 	KASSERT(unit);
245 
246 	m = m_gethdr(M_DONTWAIT, MT_DATA);
247 	if (m == NULL)
248 		return ENOMEM;
249 
250 	p = mtod(m, hci_cmd_hdr_t *);
251 	p->type = HCI_CMD_PKT;
252 	p->opcode = htole16(opcode);
253 	p->length = len;
254 	m->m_pkthdr.len = m->m_len = sizeof(hci_cmd_hdr_t);
255 
256 	if (len) {
257 		KASSERT(buf);
258 
259 		m_copyback(m, sizeof(hci_cmd_hdr_t), len, buf);
260 		if (m->m_pkthdr.len != (sizeof(hci_cmd_hdr_t) + len)) {
261 			m_freem(m);
262 			return ENOMEM;
263 		}
264 	}
265 
266 	DPRINTFN(2, "(%s) opcode (%3.3x|%4.4x)\n", unit->hci_devname,
267 		HCI_OGF(opcode), HCI_OCF(opcode));
268 
269 	/* and send it on */
270 	if (unit->hci_num_cmd_pkts == 0)
271 		MBUFQ_ENQUEUE(&unit->hci_cmdwait, m);
272 	else
273 		hci_output_cmd(unit, m);
274 
275 	return 0;
276 }
277 
278 /*
279  * Incoming packet processing. Since the code is single threaded
280  * in any case (IPL_SOFTNET), we handle it all in one interrupt function
281  * picking our way through more important packets first so that hopefully
282  * we will never get clogged up with bulk data.
283  */
284 static void
285 hci_intr(void *arg)
286 {
287 	struct hci_unit *unit = arg;
288 	struct mbuf *m;
289 	int s;
290 
291 another:
292 	s = splraiseipl(unit->hci_ipl);
293 
294 	if (unit->hci_eventqlen > 0) {
295 		MBUFQ_DEQUEUE(&unit->hci_eventq, m);
296 		unit->hci_eventqlen--;
297 		KASSERT(m != NULL);
298 		splx(s);
299 
300 		DPRINTFN(10, "(%s) recv event, len = %d\n",
301 				unit->hci_devname, m->m_pkthdr.len);
302 
303 		m->m_flags |= M_LINK0;	/* mark incoming packet */
304 		hci_mtap(m, unit);
305 		hci_event(m, unit);
306 
307 		goto another;
308 	}
309 
310 	if (unit->hci_scorxqlen > 0) {
311 		MBUFQ_DEQUEUE(&unit->hci_scorxq, m);
312 		unit->hci_scorxqlen--;
313 		KASSERT(m != NULL);
314 		splx(s);
315 
316 		DPRINTFN(10, "(%s) recv SCO, len = %d\n",
317 				unit->hci_devname, m->m_pkthdr.len);
318 
319 		m->m_flags |= M_LINK0;	/* mark incoming packet */
320 		hci_mtap(m, unit);
321 		hci_sco_recv(m, unit);
322 
323 		goto another;
324 	}
325 
326 	if (unit->hci_aclrxqlen > 0) {
327 		MBUFQ_DEQUEUE(&unit->hci_aclrxq, m);
328 		unit->hci_aclrxqlen--;
329 		KASSERT(m != NULL);
330 		splx(s);
331 
332 		DPRINTFN(10, "(%s) recv ACL, len = %d\n",
333 				unit->hci_devname, m->m_pkthdr.len);
334 
335 		m->m_flags |= M_LINK0;	/* mark incoming packet */
336 		hci_mtap(m, unit);
337 		hci_acl_recv(m, unit);
338 
339 		goto another;
340 	}
341 
342 	MBUFQ_DEQUEUE(&unit->hci_scodone, m);
343 	if (m != NULL) {
344 		struct hci_link *link;
345 		splx(s);
346 
347 		DPRINTFN(11, "(%s) complete SCO\n",
348 				unit->hci_devname);
349 
350 		TAILQ_FOREACH(link, &unit->hci_links, hl_next) {
351 			if (link == M_GETCTX(m, struct hci_link *)) {
352 				hci_sco_complete(link, 1);
353 				break;
354 			}
355 		}
356 
357 		unit->hci_num_sco_pkts++;
358 		m_freem(m);
359 
360 		goto another;
361 	}
362 
363 	splx(s);
364 
365 	DPRINTFN(10, "done\n");
366 }
367 
368 /**********************************************************************
369  *
370  * IO routines
371  *
372  * input & complete routines will be called from device driver
373  * (at unit->hci_ipl)
374  */
375 
376 void
377 hci_input_event(struct hci_unit *unit, struct mbuf *m)
378 {
379 
380 	if (unit->hci_eventqlen > hci_eventq_max || unit->hci_rxint == NULL) {
381 		DPRINTF("(%s) dropped event packet.\n", unit->hci_devname);
382 		unit->hci_stats.err_rx++;
383 		m_freem(m);
384 	} else {
385 		unit->hci_eventqlen++;
386 		MBUFQ_ENQUEUE(&unit->hci_eventq, m);
387 		softintr_schedule(unit->hci_rxint);
388 	}
389 }
390 
391 void
392 hci_input_acl(struct hci_unit *unit, struct mbuf *m)
393 {
394 
395 	if (unit->hci_aclrxqlen > hci_aclrxq_max || unit->hci_rxint == NULL) {
396 		DPRINTF("(%s) dropped ACL packet.\n", unit->hci_devname);
397 		unit->hci_stats.err_rx++;
398 		m_freem(m);
399 	} else {
400 		unit->hci_aclrxqlen++;
401 		MBUFQ_ENQUEUE(&unit->hci_aclrxq, m);
402 		softintr_schedule(unit->hci_rxint);
403 	}
404 }
405 
406 void
407 hci_input_sco(struct hci_unit *unit, struct mbuf *m)
408 {
409 
410 	if (unit->hci_scorxqlen > hci_scorxq_max || unit->hci_rxint == NULL) {
411 		DPRINTF("(%s) dropped SCO packet.\n", unit->hci_devname);
412 		unit->hci_stats.err_rx++;
413 		m_freem(m);
414 	} else {
415 		unit->hci_scorxqlen++;
416 		MBUFQ_ENQUEUE(&unit->hci_scorxq, m);
417 		softintr_schedule(unit->hci_rxint);
418 	}
419 }
420 
421 void
422 hci_output_cmd(struct hci_unit *unit, struct mbuf *m)
423 {
424 	void *arg;
425 	int s;
426 
427 	hci_mtap(m, unit);
428 
429 	DPRINTFN(10, "(%s) num_cmd_pkts=%d\n", unit->hci_devname,
430 					       unit->hci_num_cmd_pkts);
431 
432 	unit->hci_num_cmd_pkts--;
433 
434 	/*
435 	 * If context is set, this was from a HCI raw socket
436 	 * and a record needs to be dropped from the sockbuf.
437 	 */
438 	arg = M_GETCTX(m, void *);
439 	if (arg != NULL)
440 		hci_drop(arg);
441 
442 	s = splraiseipl(unit->hci_ipl);
443 	MBUFQ_ENQUEUE(&unit->hci_cmdq, m);
444 	if ((unit->hci_flags & BTF_XMIT_CMD) == 0)
445 		(*unit->hci_start_cmd)(unit);
446 
447 	splx(s);
448 }
449 
450 void
451 hci_output_acl(struct hci_unit *unit, struct mbuf *m)
452 {
453 	int s;
454 
455 	hci_mtap(m, unit);
456 
457 	DPRINTFN(10, "(%s) num_acl_pkts=%d\n", unit->hci_devname,
458 					       unit->hci_num_acl_pkts);
459 
460 	unit->hci_num_acl_pkts--;
461 
462 	s = splraiseipl(unit->hci_ipl);
463 	MBUFQ_ENQUEUE(&unit->hci_acltxq, m);
464 	if ((unit->hci_flags & BTF_XMIT_ACL) == 0)
465 		(*unit->hci_start_acl)(unit);
466 
467 	splx(s);
468 }
469 
470 void
471 hci_output_sco(struct hci_unit *unit, struct mbuf *m)
472 {
473 	int s;
474 
475 	hci_mtap(m, unit);
476 
477 	DPRINTFN(10, "(%s) num_sco_pkts=%d\n", unit->hci_devname,
478 					       unit->hci_num_sco_pkts);
479 
480 	unit->hci_num_sco_pkts--;
481 
482 	s = splraiseipl(unit->hci_ipl);
483 	MBUFQ_ENQUEUE(&unit->hci_scotxq, m);
484 	if ((unit->hci_flags & BTF_XMIT_SCO) == 0)
485 		(*unit->hci_start_sco)(unit);
486 
487 	splx(s);
488 }
489 
490 void
491 hci_complete_sco(struct hci_unit *unit, struct mbuf *m)
492 {
493 
494 	if (unit->hci_rxint == NULL) {
495 		DPRINTFN(10, "(%s) complete SCO!\n", unit->hci_devname);
496 		unit->hci_stats.err_rx++;
497 		m_freem(m);
498 	} else {
499 		MBUFQ_ENQUEUE(&unit->hci_scodone, m);
500 		softintr_schedule(unit->hci_rxint);
501 	}
502 }
503