1 | /* $NetBSD: btbc.c,v 1.17 2014/11/16 16:20:00 ozaki-r Exp $ */ |
2 | /* |
3 | * Copyright (c) 2007 KIYOHARA Takashi |
4 | * All rights reserved. |
5 | * |
6 | * Redistribution and use in source and binary forms, with or without |
7 | * modification, are permitted provided that the following conditions |
8 | * are met: |
9 | * 1. Redistributions of source code must retain the above copyright |
10 | * notice, this list of conditions and the following disclaimer. |
11 | * 2. Redistributions in binary form must reproduce the above copyright |
12 | * notice, this list of conditions and the following disclaimer in the |
13 | * documentation and/or other materials provided with the distribution. |
14 | * |
15 | * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR |
16 | * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED |
17 | * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE |
18 | * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, |
19 | * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES |
20 | * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR |
21 | * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) |
22 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, |
23 | * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN |
24 | * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE |
25 | * POSSIBILITY OF SUCH DAMAGE. |
26 | */ |
27 | /* |
28 | * This driver is support to the AnyCom BlueCard. written with reference to |
29 | * Linux driver: (drivers/bluetooth/bluecard_cs.c) |
30 | */ |
31 | |
32 | #include <sys/cdefs.h> |
33 | __KERNEL_RCSID(0, "$NetBSD: btbc.c,v 1.17 2014/11/16 16:20:00 ozaki-r Exp $" ); |
34 | |
35 | #include <sys/param.h> |
36 | #include <sys/callout.h> |
37 | #include <sys/device.h> |
38 | #include <sys/errno.h> |
39 | #include <sys/kernel.h> |
40 | #include <sys/mbuf.h> |
41 | #include <sys/proc.h> |
42 | |
43 | #include <sys/bus.h> |
44 | #include <sys/intr.h> |
45 | |
46 | #include <dev/pcmcia/pcmciareg.h> |
47 | #include <dev/pcmcia/pcmciavar.h> |
48 | #include <dev/pcmcia/pcmciadevs.h> |
49 | |
50 | #include <netbt/bluetooth.h> |
51 | #include <netbt/hci.h> |
52 | |
53 | #include <dev/pcmcia/bluecardreg.h> |
54 | |
55 | |
56 | /* sc_state */ /* receiving */ |
57 | #define BTBC_RECV_PKT_TYPE 0 /* packet type */ |
58 | #define BTBC_RECV_ACL_HDR 1 /* acl header */ |
59 | #define BTBC_RECV_SCO_HDR 2 /* sco header */ |
60 | #define BTBC_RECV_EVENT_HDR 3 /* event header */ |
61 | #define BTBC_RECV_ACL_DATA 4 /* acl packet data */ |
62 | #define BTBC_RECV_SCO_DATA 5 /* sco packet data */ |
63 | #define BTBC_RECV_EVENT_DATA 6 /* event packet data */ |
64 | |
65 | /* sc_flags */ |
66 | #define BTBC_XMIT (1 << 1) /* transmit active */ |
67 | #define BTBC_ENABLED (1 << 2) /* is enabled */ |
68 | |
69 | /* Default baud rate: 57600, 115200, 230400 or 460800 */ |
70 | #ifndef BTBC_DEFAULT_BAUDRATE |
71 | #define BTBC_DEFAULT_BAUDRATE 57600 |
72 | #endif |
73 | |
74 | struct btbc_softc { |
75 | device_t sc_dev; |
76 | |
77 | struct pcmcia_function *sc_pf; /* our PCMCIA function */ |
78 | struct pcmcia_io_handle sc_pcioh; /* PCMCIA i/o space info */ |
79 | int sc_flags; /* flags */ |
80 | |
81 | struct hci_unit *sc_unit; /* Bluetooth HCI Unit */ |
82 | struct bt_stats sc_stats; /* HCI stats */ |
83 | |
84 | /* hardware interrupt */ |
85 | void *sc_intr; /* cookie */ |
86 | int sc_state; /* receive state */ |
87 | int sc_want; /* how much we want */ |
88 | struct mbuf *sc_rxp; /* incoming packet */ |
89 | struct mbuf *sc_txp; /* outgoing packet */ |
90 | int sc_txstate; |
91 | #define TXBUF1_EMPTY (1 << 0) |
92 | #define TXBUF2_EMPTY (1 << 1) |
93 | #define TXBUF_MASK (1 << 2) |
94 | |
95 | /* output queues */ |
96 | MBUFQ_HEAD() sc_cmdq; |
97 | MBUFQ_HEAD() sc_aclq; |
98 | MBUFQ_HEAD() sc_scoq; |
99 | |
100 | callout_t sc_ledch; /* callout handler for LED */ |
101 | uint8_t sc_ctrlreg; /* value for control register */ |
102 | }; |
103 | |
104 | static int btbc_match(device_t, cfdata_t, void *); |
105 | static void btbc_attach(device_t, device_t, void *); |
106 | static int btbc_detach(device_t, int); |
107 | static bool btbc_suspend(device_t, const pmf_qual_t *); |
108 | static bool btbc_resume(device_t, const pmf_qual_t *); |
109 | |
110 | static void btbc_activity_led_timeout(void *); |
111 | static void btbc_enable_activity_led(struct btbc_softc *); |
112 | static int btbc_read(struct btbc_softc *, uint32_t, uint8_t *, int); |
113 | static int btbc_write(struct btbc_softc *, uint32_t, uint8_t *, int); |
114 | static int btbc_set_baudrate(struct btbc_softc *, int); |
115 | static void btbc_receive(struct btbc_softc *, uint32_t); |
116 | static void btbc_transmit(struct btbc_softc *); |
117 | static int btbc_intr(void *); |
118 | static void btbc_start(struct btbc_softc *); |
119 | |
120 | static int btbc_enable(device_t); |
121 | static void btbc_disable(device_t); |
122 | static void btbc_output_cmd(device_t, struct mbuf *); |
123 | static void btbc_output_acl(device_t, struct mbuf *); |
124 | static void btbc_output_sco(device_t, struct mbuf *); |
125 | static void btbc_stats(device_t, struct bt_stats *, int); |
126 | |
127 | CFATTACH_DECL_NEW(btbc, sizeof(struct btbc_softc), |
128 | btbc_match, btbc_attach, btbc_detach, NULL); |
129 | |
130 | static const struct hci_if btbc_hci = { |
131 | .enable = btbc_enable, |
132 | .disable = btbc_disable, |
133 | .output_cmd = btbc_output_cmd, |
134 | .output_acl = btbc_output_acl, |
135 | .output_sco = btbc_output_sco, |
136 | .get_stats = btbc_stats, |
137 | .ipl = IPL_TTY, |
138 | }; |
139 | |
140 | /* ARGSUSED */ |
141 | static int |
142 | btbc_match(device_t parent, cfdata_t match, void *aux) |
143 | { |
144 | struct pcmcia_attach_args *pa = aux; |
145 | |
146 | if (pa->manufacturer == PCMCIA_VENDOR_ANYCOM) |
147 | if ((pa->product == PCMCIA_PRODUCT_ANYCOM_LSE041) || |
148 | (pa->product == PCMCIA_PRODUCT_ANYCOM_LSE039) || |
149 | (pa->product == PCMCIA_PRODUCT_ANYCOM_LSE139)) |
150 | return 1; |
151 | return 0; |
152 | } |
153 | |
154 | static int |
155 | btbc_pcmcia_validate_config(struct pcmcia_config_entry *cfe) |
156 | { |
157 | |
158 | if (cfe->iftype != PCMCIA_IFTYPE_IO || |
159 | cfe->num_iospace < 1 || cfe->num_iospace > 2) |
160 | return EINVAL; |
161 | return 0; |
162 | } |
163 | |
164 | /* ARGSUSED */ |
165 | static void |
166 | btbc_attach(device_t parent, device_t self, void *aux) |
167 | { |
168 | struct btbc_softc *sc = device_private(self); |
169 | struct pcmcia_attach_args *pa = aux; |
170 | struct pcmcia_config_entry *cfe; |
171 | int error; |
172 | |
173 | sc->sc_dev = self; |
174 | sc->sc_pf = pa->pf; |
175 | |
176 | MBUFQ_INIT(&sc->sc_cmdq); |
177 | MBUFQ_INIT(&sc->sc_aclq); |
178 | MBUFQ_INIT(&sc->sc_scoq); |
179 | |
180 | if ((error = pcmcia_function_configure(pa->pf, |
181 | btbc_pcmcia_validate_config)) != 0) { |
182 | aprint_error_dev(self, "configure failed, error=%d\n" , error); |
183 | return; |
184 | } |
185 | |
186 | cfe = pa->pf->cfe; |
187 | sc->sc_pcioh = cfe->iospace[0].handle; |
188 | |
189 | /* Attach Bluetooth unit */ |
190 | sc->sc_unit = hci_attach_pcb(&btbc_hci, self, 0); |
191 | if (sc->sc_unit == NULL) |
192 | aprint_error_dev(self, "HCI attach failed\n" ); |
193 | |
194 | if (!pmf_device_register(self, btbc_suspend, btbc_resume)) |
195 | aprint_error_dev(self, "couldn't establish power handler\n" ); |
196 | |
197 | callout_init(&sc->sc_ledch, 0); |
198 | callout_setfunc(&sc->sc_ledch, btbc_activity_led_timeout, sc); |
199 | |
200 | return; |
201 | } |
202 | |
203 | /* ARGSUSED */ |
204 | static int |
205 | btbc_detach(device_t self, int flags) |
206 | { |
207 | struct btbc_softc *sc = device_private(self); |
208 | int err = 0; |
209 | |
210 | pmf_device_deregister(self); |
211 | btbc_disable(sc->sc_dev); |
212 | |
213 | callout_halt(&sc->sc_ledch, NULL); |
214 | callout_destroy(&sc->sc_ledch); |
215 | |
216 | if (sc->sc_unit) { |
217 | hci_detach_pcb(sc->sc_unit); |
218 | sc->sc_unit = NULL; |
219 | } |
220 | |
221 | pcmcia_function_unconfigure(sc->sc_pf); |
222 | |
223 | return err; |
224 | } |
225 | |
226 | static bool |
227 | btbc_suspend(device_t self, const pmf_qual_t *qual) |
228 | { |
229 | struct btbc_softc *sc = device_private(self); |
230 | |
231 | if (sc->sc_unit) { |
232 | hci_detach_pcb(sc->sc_unit); |
233 | sc->sc_unit = NULL; |
234 | } |
235 | |
236 | return true; |
237 | } |
238 | |
239 | |
240 | static bool |
241 | btbc_resume(device_t self, const pmf_qual_t *qual) |
242 | { |
243 | struct btbc_softc *sc = device_private(self); |
244 | |
245 | KASSERT(sc->sc_unit == NULL); |
246 | |
247 | sc->sc_unit = hci_attach_pcb(&btbc_hci, sc->sc_dev, 0); |
248 | if (sc->sc_unit == NULL) |
249 | return false; |
250 | |
251 | return true; |
252 | } |
253 | |
254 | static void |
255 | btbc_activity_led_timeout(void *arg) |
256 | { |
257 | struct btbc_softc *sc = arg; |
258 | uint8_t id; |
259 | |
260 | id = bus_space_read_1(sc->sc_pcioh.iot, sc->sc_pcioh.ioh, |
261 | BLUECARD_LEDCONTROL); |
262 | if (id & 0x20) |
263 | /* Disable activity LED */ |
264 | bus_space_write_1(sc->sc_pcioh.iot, sc->sc_pcioh.ioh, |
265 | BLUECARD_LEDCONTROL, 0x08 | 0x20); |
266 | else |
267 | /* Disable power LED */ |
268 | bus_space_write_1(sc->sc_pcioh.iot, sc->sc_pcioh.ioh, |
269 | BLUECARD_LEDCONTROL, 0x00); |
270 | } |
271 | |
272 | static void |
273 | btbc_enable_activity_led(struct btbc_softc *sc) |
274 | { |
275 | uint8_t id; |
276 | |
277 | id = bus_space_read_1(sc->sc_pcioh.iot, sc->sc_pcioh.ioh, |
278 | BLUECARD_LEDCONTROL); |
279 | if (id & 0x20) { |
280 | /* Enable activity LED */ |
281 | bus_space_write_1(sc->sc_pcioh.iot, sc->sc_pcioh.ioh, |
282 | BLUECARD_LEDCONTROL, 0x10 | 0x40); |
283 | |
284 | /* Stop the LED after hz/4 */ |
285 | callout_schedule(&sc->sc_ledch, hz / 4); |
286 | } else { |
287 | /* Enable power LED */ |
288 | bus_space_write_1(sc->sc_pcioh.iot, sc->sc_pcioh.ioh, |
289 | BLUECARD_LEDCONTROL, 0x08 | 0x20); |
290 | |
291 | /* Stop the LED after HZ/2 */ |
292 | callout_schedule(&sc->sc_ledch, hz / 2); |
293 | } |
294 | } |
295 | |
296 | static int |
297 | btbc_read(struct btbc_softc *sc, uint32_t offset, uint8_t *buf, int buflen) |
298 | { |
299 | int i, n, len; |
300 | |
301 | bus_space_write_1(sc->sc_pcioh.iot, sc->sc_pcioh.ioh, |
302 | BLUECARD_COMMAND, BLUECARD_COMMAND_RXWIN1); |
303 | len = bus_space_read_1(sc->sc_pcioh.iot, sc->sc_pcioh.ioh, offset); |
304 | |
305 | n = 0; |
306 | i = 1; |
307 | while (n < len) { |
308 | if (i == 16) { |
309 | bus_space_write_1(sc->sc_pcioh.iot, sc->sc_pcioh.ioh, |
310 | BLUECARD_COMMAND, BLUECARD_COMMAND_RXWIN2); |
311 | i = 0; |
312 | } |
313 | |
314 | buf[n] = bus_space_read_1(sc->sc_pcioh.iot, sc->sc_pcioh.ioh, |
315 | offset + i); |
316 | i++; |
317 | if (++n > buflen) |
318 | break; |
319 | } |
320 | return len; |
321 | } |
322 | |
323 | static int |
324 | btbc_write(struct btbc_softc *sc, uint32_t offset, uint8_t *buf, int buflen) |
325 | { |
326 | int i, actual; |
327 | |
328 | actual = (buflen > 15) ? 15 : buflen; |
329 | bus_space_write_1(sc->sc_pcioh.iot, sc->sc_pcioh.ioh, offset, actual); |
330 | for (i = 0; i < actual; i++) |
331 | bus_space_write_1(sc->sc_pcioh.iot, sc->sc_pcioh.ioh, |
332 | offset + i + 1, buf[i]); |
333 | return actual; |
334 | } |
335 | |
336 | /* |
337 | * send Ericsson baud rate command |
338 | */ |
339 | static int |
340 | btbc_set_baudrate(struct btbc_softc *sc, int baud) |
341 | { |
342 | hci_cmd_hdr_t *p; |
343 | struct mbuf *m; |
344 | const uint16_t opcode = htole16(HCI_CMD_ERICSSON_SET_UART_BAUD_RATE); |
345 | uint8_t param; |
346 | |
347 | m = m_gethdr(M_WAIT, MT_DATA); |
348 | |
349 | switch (baud) { |
350 | case 460800: |
351 | param = 0x00; |
352 | break; |
353 | |
354 | case 230400: |
355 | param = 0x01; |
356 | break; |
357 | |
358 | case 115200: |
359 | param = 0x02; |
360 | break; |
361 | |
362 | case 57600: |
363 | default: |
364 | param = 0x03; |
365 | break; |
366 | } |
367 | |
368 | p = mtod(m, hci_cmd_hdr_t *); |
369 | p->type = HCI_CMD_PKT; |
370 | p->opcode = opcode; |
371 | p->length = sizeof(param); |
372 | m->m_pkthdr.len = m->m_len = sizeof(hci_cmd_hdr_t); |
373 | m_copyback(m, sizeof(hci_cmd_hdr_t), p->length, ¶m); |
374 | |
375 | btbc_output_cmd(sc->sc_dev, m); |
376 | return 0; |
377 | } |
378 | |
379 | static void |
380 | btbc_receive(struct btbc_softc *sc, uint32_t offset) |
381 | { |
382 | struct mbuf *m = sc->sc_rxp; |
383 | int count, space = 0, i; |
384 | uint8_t buf[31]; |
385 | |
386 | btbc_enable_activity_led(sc); |
387 | |
388 | /* |
389 | * If we already started a packet, find the |
390 | * trailing end of it. |
391 | */ |
392 | if (m) { |
393 | while (m->m_next) |
394 | m = m->m_next; |
395 | |
396 | space = M_TRAILINGSPACE(m); |
397 | } |
398 | |
399 | count = btbc_read(sc, offset, buf, sizeof(buf)); |
400 | i = 0; |
401 | |
402 | while (i < count) { |
403 | if (space == 0) { |
404 | if (m == NULL) { |
405 | /* new packet */ |
406 | MGETHDR(m, M_DONTWAIT, MT_DATA); |
407 | if (m == NULL) { |
408 | aprint_error_dev(sc->sc_dev, |
409 | "out of memory\n" ); |
410 | sc->sc_stats.err_rx++; |
411 | return; /* (lost sync) */ |
412 | } |
413 | |
414 | sc->sc_rxp = m; |
415 | m->m_pkthdr.len = m->m_len = 0; |
416 | space = MHLEN; |
417 | |
418 | sc->sc_state = BTBC_RECV_PKT_TYPE; |
419 | sc->sc_want = 1; |
420 | } else { |
421 | /* extend mbuf */ |
422 | MGET(m->m_next, M_DONTWAIT, MT_DATA); |
423 | if (m->m_next == NULL) { |
424 | aprint_error_dev(sc->sc_dev, |
425 | "out of memory\n" ); |
426 | sc->sc_stats.err_rx++; |
427 | return; /* (lost sync) */ |
428 | } |
429 | |
430 | m = m->m_next; |
431 | m->m_len = 0; |
432 | space = MLEN; |
433 | |
434 | if (sc->sc_want > MINCLSIZE) { |
435 | MCLGET(m, M_DONTWAIT); |
436 | if (m->m_flags & M_EXT) |
437 | space = MCLBYTES; |
438 | } |
439 | } |
440 | } |
441 | |
442 | mtod(m, uint8_t *)[m->m_len++] = buf[i]; |
443 | space--; |
444 | sc->sc_rxp->m_pkthdr.len++; |
445 | sc->sc_stats.byte_rx++; |
446 | |
447 | sc->sc_want--; |
448 | if (sc->sc_want > 0) { |
449 | i++; |
450 | continue; /* want more */ |
451 | } |
452 | |
453 | switch (sc->sc_state) { |
454 | case BTBC_RECV_PKT_TYPE: /* Got packet type */ |
455 | switch (buf[i]) { |
456 | case 0x00: /* init packet */ |
457 | m_freem(sc->sc_rxp); |
458 | sc->sc_rxp = NULL; |
459 | break; |
460 | |
461 | case HCI_ACL_DATA_PKT: |
462 | sc->sc_state = BTBC_RECV_ACL_HDR; |
463 | sc->sc_want = sizeof(hci_acldata_hdr_t) - 1; |
464 | break; |
465 | |
466 | case HCI_SCO_DATA_PKT: |
467 | sc->sc_state = BTBC_RECV_SCO_HDR; |
468 | sc->sc_want = sizeof(hci_scodata_hdr_t) - 1; |
469 | break; |
470 | |
471 | case HCI_EVENT_PKT: |
472 | sc->sc_state = BTBC_RECV_EVENT_HDR; |
473 | sc->sc_want = sizeof(hci_event_hdr_t) - 1; |
474 | break; |
475 | |
476 | default: |
477 | aprint_error_dev(sc->sc_dev, |
478 | "Unknown packet type=%#x!\n" , buf[i]); |
479 | sc->sc_stats.err_rx++; |
480 | m_freem(sc->sc_rxp); |
481 | sc->sc_rxp = NULL; |
482 | return; /* (lost sync) */ |
483 | } |
484 | |
485 | break; |
486 | |
487 | /* |
488 | * we assume (correctly of course :) that the packet headers |
489 | * all fit into a single pkthdr mbuf |
490 | */ |
491 | case BTBC_RECV_ACL_HDR: /* Got ACL Header */ |
492 | sc->sc_state = BTBC_RECV_ACL_DATA; |
493 | sc->sc_want = mtod(m, hci_acldata_hdr_t *)->length; |
494 | sc->sc_want = le16toh(sc->sc_want); |
495 | break; |
496 | |
497 | case BTBC_RECV_SCO_HDR: /* Got SCO Header */ |
498 | sc->sc_state = BTBC_RECV_SCO_DATA; |
499 | sc->sc_want = mtod(m, hci_scodata_hdr_t *)->length; |
500 | break; |
501 | |
502 | case BTBC_RECV_EVENT_HDR: /* Got Event Header */ |
503 | sc->sc_state = BTBC_RECV_EVENT_DATA; |
504 | sc->sc_want = mtod(m, hci_event_hdr_t *)->length; |
505 | break; |
506 | |
507 | case BTBC_RECV_ACL_DATA: /* ACL Packet Complete */ |
508 | if (!hci_input_acl(sc->sc_unit, sc->sc_rxp)) |
509 | sc->sc_stats.err_rx++; |
510 | |
511 | sc->sc_stats.acl_rx++; |
512 | sc->sc_rxp = m = NULL; |
513 | space = 0; |
514 | break; |
515 | |
516 | case BTBC_RECV_SCO_DATA: /* SCO Packet Complete */ |
517 | if (!hci_input_sco(sc->sc_unit, sc->sc_rxp)) |
518 | sc->sc_stats.err_rx++; |
519 | |
520 | sc->sc_stats.sco_rx++; |
521 | sc->sc_rxp = m = NULL; |
522 | space = 0; |
523 | break; |
524 | |
525 | case BTBC_RECV_EVENT_DATA: /* Event Packet Complete */ |
526 | if (!hci_input_event(sc->sc_unit, sc->sc_rxp)) |
527 | sc->sc_stats.err_rx++; |
528 | |
529 | sc->sc_stats.evt_rx++; |
530 | sc->sc_rxp = m = NULL; |
531 | space = 0; |
532 | break; |
533 | |
534 | default: |
535 | panic("%s: invalid state %d!\n" , |
536 | device_xname(sc->sc_dev), sc->sc_state); |
537 | } |
538 | i++; |
539 | } |
540 | } |
541 | |
542 | /* |
543 | * write data from current packet to Transmit FIFO. |
544 | * restart when done. |
545 | */ |
546 | static void |
547 | btbc_transmit(struct btbc_softc *sc) |
548 | { |
549 | hci_cmd_hdr_t *p; |
550 | struct mbuf *m; |
551 | int count, set_baudrate, n, s; |
552 | uint32_t offset, command; |
553 | uint8_t *rptr; |
554 | |
555 | m = sc->sc_txp; |
556 | if (m == NULL) { |
557 | sc->sc_flags &= ~BTBC_XMIT; |
558 | btbc_start(sc); |
559 | return; |
560 | } |
561 | |
562 | set_baudrate = 0; |
563 | p = mtod(m, hci_cmd_hdr_t *); |
564 | if ((void *)m->m_pktdat == (void *)p) { |
565 | const uint16_t opcode = |
566 | htole16(HCI_CMD_ERICSSON_SET_UART_BAUD_RATE); |
567 | |
568 | if (p->type == HCI_CMD_PKT && |
569 | p->opcode == opcode && |
570 | p->length == 1) { |
571 | set_baudrate = 1; |
572 | sc->sc_txp = NULL; /* safe reentrant */ |
573 | } |
574 | } |
575 | |
576 | count = 0; |
577 | rptr = mtod(m, uint8_t *); |
578 | for(;;) { |
579 | if (m->m_len == 0) { |
580 | m = m->m_next; |
581 | if (m == NULL) { |
582 | m = sc->sc_txp; |
583 | sc->sc_txp = NULL; |
584 | |
585 | if (M_GETCTX(m, void *) == NULL) |
586 | m_freem(m); |
587 | else if (!hci_complete_sco(sc->sc_unit, m)) |
588 | sc->sc_stats.err_tx++; |
589 | |
590 | break; |
591 | } |
592 | |
593 | rptr = mtod(m, uint8_t *); |
594 | continue; |
595 | } |
596 | |
597 | s = splhigh(); |
598 | if (sc->sc_txstate & TXBUF_MASK) { |
599 | if (sc->sc_txstate & TXBUF2_EMPTY) { |
600 | offset = BLUECARD_BUF2; |
601 | command = BLUECARD_COMMAND_TXBUF2; |
602 | sc->sc_txstate &= ~(TXBUF2_EMPTY | TXBUF_MASK); |
603 | } else { |
604 | splx(s); |
605 | break; |
606 | } |
607 | } else { |
608 | if (sc->sc_txstate & TXBUF1_EMPTY) { |
609 | offset = BLUECARD_BUF1; |
610 | command = BLUECARD_COMMAND_TXBUF1; |
611 | sc->sc_txstate &= ~TXBUF1_EMPTY; |
612 | sc->sc_txstate |= TXBUF_MASK; |
613 | } else { |
614 | splx(s); |
615 | break; |
616 | } |
617 | } |
618 | splx(s); |
619 | |
620 | if (set_baudrate) { |
621 | /* Disable RTS */ |
622 | sc->sc_ctrlreg |= BLUECARD_CONTROL_RTS; |
623 | bus_space_write_1(sc->sc_pcioh.iot, sc->sc_pcioh.ioh, |
624 | BLUECARD_CONTROL, sc->sc_ctrlreg); |
625 | } |
626 | |
627 | /* Activate LED */ |
628 | btbc_enable_activity_led(sc); |
629 | |
630 | /* Send frame */ |
631 | n = btbc_write(sc, offset, rptr, m->m_len); |
632 | count += n; |
633 | rptr += n; |
634 | m_adj(m, n); |
635 | |
636 | /* Tell the FPGA to send the data */ |
637 | bus_space_write_1(sc->sc_pcioh.iot, sc->sc_pcioh.ioh, |
638 | BLUECARD_COMMAND, command); |
639 | |
640 | if (set_baudrate) { |
641 | unsigned char baud_reg; |
642 | |
643 | switch (*(uint8_t *)(p + 1)) { |
644 | case 0x00: /* baud rate 460800 */ |
645 | baud_reg = BLUECARD_CONTROL_BAUDRATE_460800; |
646 | break; |
647 | case 0x01: /* baud rate 230400 */ |
648 | baud_reg = BLUECARD_CONTROL_BAUDRATE_230400; |
649 | break; |
650 | case 0x02: /* baud rate 115200 */ |
651 | baud_reg = BLUECARD_CONTROL_BAUDRATE_115200; |
652 | break; |
653 | case 0x03: /* baud rate 57600 */ |
654 | default: |
655 | baud_reg = BLUECARD_CONTROL_BAUDRATE_57600; |
656 | break; |
657 | } |
658 | |
659 | /* Wait until the command reaches the baseband */ |
660 | tsleep(sc, PCATCH, "btbc_wait" , hz / 5); |
661 | |
662 | /* Set baud on baseband */ |
663 | sc->sc_ctrlreg &= ~BLUECARD_CONTROL_BAUDRATE_MASK; |
664 | sc->sc_ctrlreg |= baud_reg; |
665 | bus_space_write_1(sc->sc_pcioh.iot, sc->sc_pcioh.ioh, |
666 | BLUECARD_CONTROL, sc->sc_ctrlreg); |
667 | |
668 | /* Enable RTS */ |
669 | sc->sc_ctrlreg &= ~BLUECARD_CONTROL_RTS; |
670 | bus_space_write_1(sc->sc_pcioh.iot, sc->sc_pcioh.ioh, |
671 | BLUECARD_CONTROL, sc->sc_ctrlreg); |
672 | |
673 | /* Wait before the next HCI packet can be send */ |
674 | tsleep(sc, PCATCH, "btbc_wait" , hz); |
675 | |
676 | m_freem(m); |
677 | break; |
678 | } |
679 | } |
680 | sc->sc_stats.byte_tx += count; |
681 | } |
682 | |
683 | static int |
684 | btbc_intr(void *arg) |
685 | { |
686 | struct btbc_softc *sc = arg; |
687 | int handled = 0; |
688 | uint8_t isr; |
689 | |
690 | isr = bus_space_read_1(sc->sc_pcioh.iot, sc->sc_pcioh.ioh, |
691 | BLUECARD_INTERRUPT); |
692 | if (isr != 0x00 && isr != 0xff) { |
693 | if (isr & BLUECARD_INTERRUPT_RXBUF1) { |
694 | isr &= ~BLUECARD_INTERRUPT_RXBUF1; |
695 | handled = 1; |
696 | btbc_receive(sc, BLUECARD_BUF1); |
697 | bus_space_write_1(sc->sc_pcioh.iot, sc->sc_pcioh.ioh, |
698 | BLUECARD_INTERRUPT, BLUECARD_INTERRUPT_RXBUF1); |
699 | bus_space_write_1(sc->sc_pcioh.iot, sc->sc_pcioh.ioh, |
700 | BLUECARD_COMMAND, BLUECARD_COMMAND_RXBUF1); |
701 | } |
702 | if (isr & BLUECARD_INTERRUPT_RXBUF2) { |
703 | isr &= ~BLUECARD_INTERRUPT_RXBUF2; |
704 | handled = 1; |
705 | btbc_receive(sc, BLUECARD_BUF2); |
706 | bus_space_write_1(sc->sc_pcioh.iot, sc->sc_pcioh.ioh, |
707 | BLUECARD_INTERRUPT, BLUECARD_INTERRUPT_RXBUF2); |
708 | bus_space_write_1(sc->sc_pcioh.iot, sc->sc_pcioh.ioh, |
709 | BLUECARD_COMMAND, BLUECARD_COMMAND_RXBUF2); |
710 | } |
711 | if (isr & BLUECARD_INTERRUPT_TXBUF1) { |
712 | isr &= ~BLUECARD_INTERRUPT_TXBUF1; |
713 | handled = 1; |
714 | sc->sc_txstate |= TXBUF1_EMPTY; |
715 | bus_space_write_1(sc->sc_pcioh.iot, sc->sc_pcioh.ioh, |
716 | BLUECARD_INTERRUPT, BLUECARD_INTERRUPT_TXBUF1); |
717 | btbc_transmit(sc); |
718 | } |
719 | if (isr & BLUECARD_INTERRUPT_TXBUF2) { |
720 | isr &= ~BLUECARD_INTERRUPT_TXBUF2; |
721 | handled = 1; |
722 | sc->sc_txstate |= TXBUF2_EMPTY; |
723 | bus_space_write_1(sc->sc_pcioh.iot, sc->sc_pcioh.ioh, |
724 | BLUECARD_INTERRUPT, BLUECARD_INTERRUPT_TXBUF2); |
725 | btbc_transmit(sc); |
726 | } |
727 | |
728 | if (isr & 0x40) { /* card eject ? */ |
729 | aprint_normal_dev(sc->sc_dev, "card eject?\n" ); |
730 | isr &= ~0x40; |
731 | bus_space_write_1(sc->sc_pcioh.iot, sc->sc_pcioh.ioh, |
732 | BLUECARD_INTERRUPT, 0x40); |
733 | } |
734 | if (isr != 0x00) { |
735 | aprint_error_dev(sc->sc_dev, |
736 | "unknown interrupt: isr=0x%x\n" , isr); |
737 | bus_space_write_1(sc->sc_pcioh.iot, sc->sc_pcioh.ioh, |
738 | BLUECARD_INTERRUPT, isr); |
739 | } |
740 | } |
741 | |
742 | return handled; |
743 | } |
744 | |
745 | /* |
746 | * start sending on btbc |
747 | * |
748 | * should be called at spltty() and when BTBC_XMIT is not set |
749 | */ |
750 | static void |
751 | btbc_start(struct btbc_softc *sc) |
752 | { |
753 | struct mbuf *m; |
754 | |
755 | KASSERT((sc->sc_flags & BTBC_XMIT) == 0); |
756 | KASSERT(sc->sc_txp == NULL); |
757 | |
758 | if (MBUFQ_FIRST(&sc->sc_cmdq)) { |
759 | MBUFQ_DEQUEUE(&sc->sc_cmdq, m); |
760 | sc->sc_stats.cmd_tx++; |
761 | goto start; |
762 | } |
763 | |
764 | if (MBUFQ_FIRST(&sc->sc_scoq)) { |
765 | MBUFQ_DEQUEUE(&sc->sc_scoq, m); |
766 | sc->sc_stats.sco_tx++; |
767 | goto start; |
768 | } |
769 | |
770 | if (MBUFQ_FIRST(&sc->sc_aclq)) { |
771 | MBUFQ_DEQUEUE(&sc->sc_aclq, m); |
772 | sc->sc_stats.acl_tx++; |
773 | goto start; |
774 | } |
775 | |
776 | /* Nothing to send */ |
777 | return; |
778 | |
779 | start: |
780 | sc->sc_txp = m; |
781 | sc->sc_flags |= BTBC_XMIT; |
782 | btbc_transmit(sc); |
783 | } |
784 | |
785 | static int |
786 | btbc_enable(device_t self) |
787 | { |
788 | struct btbc_softc *sc = device_private(self); |
789 | int err, s; |
790 | uint8_t id, ctrl; |
791 | |
792 | if (sc->sc_flags & BTBC_ENABLED) |
793 | return 0; |
794 | |
795 | s = spltty(); |
796 | |
797 | sc->sc_txstate = TXBUF1_EMPTY | TXBUF2_EMPTY; |
798 | sc->sc_intr = pcmcia_intr_establish(sc->sc_pf, IPL_TTY, btbc_intr, sc); |
799 | if (sc->sc_intr == NULL) { |
800 | err = EIO; |
801 | goto fail1; |
802 | } |
803 | |
804 | err = pcmcia_function_enable(sc->sc_pf); |
805 | if (err) |
806 | goto fail2; |
807 | |
808 | sc->sc_flags |= BTBC_ENABLED; |
809 | sc->sc_flags &= ~BTBC_XMIT; |
810 | |
811 | /* Reset card */ |
812 | ctrl = BLUECARD_CONTROL_RESET | BLUECARD_CONTROL_CARDRESET; |
813 | bus_space_write_1(sc->sc_pcioh.iot, sc->sc_pcioh.ioh, BLUECARD_CONTROL, |
814 | ctrl); |
815 | |
816 | /* Turn FPGA off */ |
817 | bus_space_write_1(sc->sc_pcioh.iot, sc->sc_pcioh.ioh, |
818 | BLUECARD_CARDRESET, 0x80); |
819 | |
820 | /* Wait some time */ |
821 | tsleep(sc, PCATCH, "btbc_reset" , 1); |
822 | |
823 | /* Turn FPGA on */ |
824 | bus_space_write_1(sc->sc_pcioh.iot, sc->sc_pcioh.ioh, |
825 | BLUECARD_CARDRESET, 0x00); |
826 | |
827 | /* Activate card */ |
828 | ctrl = BLUECARD_CONTROL_ON | BLUECARD_CONTROL_RESPU; |
829 | bus_space_write_1(sc->sc_pcioh.iot, sc->sc_pcioh.ioh, BLUECARD_CONTROL, |
830 | ctrl); |
831 | |
832 | tsleep(sc, PCATCH, "btbc_enable" , 1); |
833 | sc->sc_ctrlreg = ctrl; |
834 | |
835 | /* Enable interrupt */ |
836 | bus_space_write_1(sc->sc_pcioh.iot, sc->sc_pcioh.ioh, |
837 | BLUECARD_INTERRUPT, 0xff); |
838 | sc->sc_ctrlreg |= BLUECARD_CONTROL_INTERRUPT; |
839 | bus_space_write_1(sc->sc_pcioh.iot, sc->sc_pcioh.ioh, BLUECARD_CONTROL, |
840 | sc->sc_ctrlreg); |
841 | |
842 | id = bus_space_read_1(sc->sc_pcioh.iot, |
843 | sc->sc_pcioh.ioh, BLUECARD_LEDCONTROL); |
844 | switch (id & 0x0f) { |
845 | case 0x02: |
846 | /* Enable LED */ |
847 | bus_space_write_1(sc->sc_pcioh.iot, sc->sc_pcioh.ioh, |
848 | BLUECARD_LEDCONTROL, 0x08 | 0x20); |
849 | break; |
850 | |
851 | case 0x03: |
852 | /* Disable RTS */ |
853 | ctrl |= BLUECARD_CONTROL_RTS; |
854 | bus_space_write_1(sc->sc_pcioh.iot, sc->sc_pcioh.ioh, |
855 | BLUECARD_CONTROL, ctrl); |
856 | |
857 | /* Set baud rate */ |
858 | ctrl |= BLUECARD_CONTROL_BAUDRATE_460800; |
859 | bus_space_write_1(sc->sc_pcioh.iot, sc->sc_pcioh.ioh, |
860 | BLUECARD_CONTROL, ctrl); |
861 | |
862 | /* Enable RTS */ |
863 | ctrl &= ~BLUECARD_CONTROL_RTS; |
864 | bus_space_write_1(sc->sc_pcioh.iot, sc->sc_pcioh.ioh, |
865 | BLUECARD_CONTROL, ctrl); |
866 | break; |
867 | } |
868 | |
869 | /* Start the RX buffers */ |
870 | bus_space_write_1(sc->sc_pcioh.iot, sc->sc_pcioh.ioh, |
871 | BLUECARD_COMMAND, BLUECARD_COMMAND_RXBUF1); |
872 | bus_space_write_1(sc->sc_pcioh.iot, sc->sc_pcioh.ioh, |
873 | BLUECARD_COMMAND, BLUECARD_COMMAND_RXBUF2); |
874 | |
875 | /* XXX: Control the point at which RTS is enabled */ |
876 | bus_space_write_1(sc->sc_pcioh.iot, sc->sc_pcioh.ioh, |
877 | BLUECARD_RXCONTROL, BLUECARD_RXCONTROL_RTSLEVEL(0x0f) | 1); |
878 | |
879 | /* Timeout before it is safe to send the first HCI packet */ |
880 | tsleep(sc, PCATCH, "btbc_enable" , hz * 2); |
881 | |
882 | btbc_set_baudrate(sc, BTBC_DEFAULT_BAUDRATE); |
883 | |
884 | splx(s); |
885 | return 0; |
886 | |
887 | fail2: |
888 | pcmcia_intr_disestablish(sc->sc_pf, sc->sc_intr); |
889 | sc->sc_intr = NULL; |
890 | fail1: |
891 | splx(s); |
892 | return err; |
893 | } |
894 | |
895 | static void |
896 | btbc_disable(device_t self) |
897 | { |
898 | struct btbc_softc *sc = device_private(self); |
899 | int s; |
900 | |
901 | if ((sc->sc_flags & BTBC_ENABLED) == 0) |
902 | return; |
903 | |
904 | s = spltty(); |
905 | |
906 | pcmcia_function_disable(sc->sc_pf); |
907 | |
908 | if (sc->sc_intr) { |
909 | pcmcia_intr_disestablish(sc->sc_pf, sc->sc_intr); |
910 | sc->sc_intr = NULL; |
911 | } |
912 | |
913 | if (sc->sc_rxp) { |
914 | m_freem(sc->sc_rxp); |
915 | sc->sc_rxp = NULL; |
916 | } |
917 | |
918 | if (sc->sc_txp) { |
919 | m_freem(sc->sc_txp); |
920 | sc->sc_txp = NULL; |
921 | } |
922 | |
923 | MBUFQ_DRAIN(&sc->sc_cmdq); |
924 | MBUFQ_DRAIN(&sc->sc_aclq); |
925 | MBUFQ_DRAIN(&sc->sc_scoq); |
926 | |
927 | sc->sc_flags &= ~BTBC_ENABLED; |
928 | |
929 | /* Disable LED */ |
930 | bus_space_write_1(sc->sc_pcioh.iot, sc->sc_pcioh.ioh, |
931 | BLUECARD_LEDCONTROL, 0x00); |
932 | |
933 | /* Reset card */ |
934 | sc->sc_ctrlreg = BLUECARD_CONTROL_RESET | BLUECARD_CONTROL_CARDRESET; |
935 | bus_space_write_1(sc->sc_pcioh.iot, sc->sc_pcioh.ioh, BLUECARD_CONTROL, |
936 | sc->sc_ctrlreg); |
937 | |
938 | /* Turn FPGA off */ |
939 | bus_space_write_1(sc->sc_pcioh.iot, sc->sc_pcioh.ioh, |
940 | BLUECARD_CARDRESET, 0x80); |
941 | |
942 | splx(s); |
943 | } |
944 | |
945 | static void |
946 | btbc_output_cmd(device_t self, struct mbuf *m) |
947 | { |
948 | struct btbc_softc *sc = device_private(self); |
949 | int s; |
950 | |
951 | KASSERT(sc->sc_flags & BTBC_ENABLED); |
952 | |
953 | M_SETCTX(m, NULL); |
954 | |
955 | s = spltty(); |
956 | MBUFQ_ENQUEUE(&sc->sc_cmdq, m); |
957 | if ((sc->sc_flags & BTBC_XMIT) == 0) |
958 | btbc_start(sc); |
959 | |
960 | splx(s); |
961 | } |
962 | |
963 | static void |
964 | btbc_output_acl(device_t self, struct mbuf *m) |
965 | { |
966 | struct btbc_softc *sc = device_private(self); |
967 | int s; |
968 | |
969 | KASSERT(sc->sc_flags & BTBC_ENABLED); |
970 | |
971 | M_SETCTX(m, NULL); |
972 | |
973 | s = spltty(); |
974 | MBUFQ_ENQUEUE(&sc->sc_aclq, m); |
975 | if ((sc->sc_flags & BTBC_XMIT) == 0) |
976 | btbc_start(sc); |
977 | |
978 | splx(s); |
979 | } |
980 | |
981 | static void |
982 | btbc_output_sco(device_t self, struct mbuf *m) |
983 | { |
984 | struct btbc_softc *sc = device_private(self); |
985 | int s; |
986 | |
987 | KASSERT(sc->sc_flags & BTBC_ENABLED); |
988 | |
989 | s = spltty(); |
990 | MBUFQ_ENQUEUE(&sc->sc_scoq, m); |
991 | if ((sc->sc_flags & BTBC_XMIT) == 0) |
992 | btbc_start(sc); |
993 | |
994 | splx(s); |
995 | } |
996 | |
997 | static void |
998 | btbc_stats(device_t self, struct bt_stats *dest, int flush) |
999 | { |
1000 | struct btbc_softc *sc = device_private(self); |
1001 | int s; |
1002 | |
1003 | s = spltty(); |
1004 | memcpy(dest, &sc->sc_stats, sizeof(struct bt_stats)); |
1005 | |
1006 | if (flush) |
1007 | memset(&sc->sc_stats, 0, sizeof(struct bt_stats)); |
1008 | |
1009 | splx(s); |
1010 | } |
1011 | |