hci_unit.c revision 1.1.2.2 1 /* $NetBSD: hci_unit.c,v 1.1.2.2 2006/06/21 15:10:51 yamt 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.2.2 2006/06/21 15:10:51 yamt 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