1 | /* $NetBSD: bt3c.c,v 1.23 2014/05/20 18:25:54 rmind Exp $ */ |
2 | |
3 | /*- |
4 | * Copyright (c) 2005 Iain D. Hibbert, |
5 | * All rights reserved. |
6 | * |
7 | * Redistribution and use in source and binary forms, with or without |
8 | * modification, are permitted provided that the following conditions |
9 | * are met: |
10 | * 1. Redistributions of source code must retain the above copyright |
11 | * notice, this list of conditions and the following disclaimer. |
12 | * 2. Redistributions in binary form must reproduce the above copyright |
13 | * notice, this list of conditions and the following disclaimer in the |
14 | * documentation and/or other materials provided with the distribution. |
15 | * 3. The name of the author may not be used to endorse or promote products |
16 | * derived from this software without specific prior written permission. |
17 | * |
18 | * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR |
19 | * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES |
20 | * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. |
21 | * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, |
22 | * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT |
23 | * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
24 | * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
25 | * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
26 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF |
27 | * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
28 | */ |
29 | |
30 | /* |
31 | * Driver for the 3Com Bluetooth PC Card 3CRWB6096, written with reference to |
32 | * FreeBSD and BlueZ drivers for same, with credit for those going to: |
33 | * |
34 | * Maksim Yevmenkin <m_evmenkin@yahoo.com> (FreeBSD) |
35 | * Marcel Holtmann <marcel@holtmann.org> (BlueZ) |
36 | * Jose Orlando Pereira <jop@di.uminho.pt> (BlueZ) |
37 | * David Hinds <dahinds@users.sourceforge.net> (Original Code) |
38 | */ |
39 | |
40 | /* |
41 | * The CIS info from my card: |
42 | * |
43 | * pcmcia1: CIS tuple chain: |
44 | * CISTPL_DEVICE type=null speed=null |
45 | * 01 03 00 00 ff |
46 | * CISTPL_VERS_1 |
47 | * 15 24 05 00 33 43 4f 4d 00 33 43 52 57 42 36 30 |
48 | * 2d 41 00 42 6c 75 65 74 6f 6f 74 68 20 50 43 20 |
49 | * 43 61 72 64 00 ff |
50 | * CISTPL_MANFID |
51 | * 20 04 01 01 40 00 |
52 | * CISTPL_FUNCID |
53 | * 21 02 02 01 |
54 | * CISTPL_CONFIG |
55 | * 1a 06 05 30 20 03 17 00 |
56 | * CISTPL_CFTABLE_ENTRY |
57 | * 1b 09 f0 41 18 a0 40 07 30 ff ff |
58 | * unhandled CISTPL 80 |
59 | * 80 0a 02 01 40 00 2d 00 00 00 00 ff |
60 | * CISTPL_NO_LINK |
61 | * 14 00 |
62 | * CISTPL_END |
63 | * ff |
64 | * pcmcia1: CIS version PC Card Standard 5.0 |
65 | * pcmcia1: CIS info: 3COM, 3CRWB60-A, Bluetooth PC Card |
66 | * pcmcia1: Manufacturer code 0x101, product 0x40 |
67 | * pcmcia1: function 0: serial port, ccr addr 320 mask 17 |
68 | * pcmcia1: function 0, config table entry 48: I/O card; irq mask ffff; iomask 0, iospace 0-7; rdybsy_active io8 irqlevel |
69 | */ |
70 | |
71 | #include <sys/cdefs.h> |
72 | __KERNEL_RCSID(0, "$NetBSD: bt3c.c,v 1.23 2014/05/20 18:25:54 rmind Exp $" ); |
73 | |
74 | #include <sys/param.h> |
75 | #include <sys/device.h> |
76 | #include <sys/mbuf.h> |
77 | #include <sys/systm.h> |
78 | |
79 | #include <sys/cpu.h> |
80 | #include <sys/bus.h> |
81 | #include <sys/intr.h> |
82 | |
83 | #include <dev/pcmcia/pcmciareg.h> |
84 | #include <dev/pcmcia/pcmciavar.h> |
85 | #include <dev/pcmcia/pcmciadevs.h> |
86 | |
87 | #include <netbt/bluetooth.h> |
88 | #include <netbt/hci.h> |
89 | |
90 | #include <dev/firmload.h> |
91 | #define BT3C_FIRMWARE_FILE "BT3CPCC.bin" |
92 | |
93 | /************************************************************************** |
94 | * |
95 | * bt3c autoconfig glue |
96 | */ |
97 | |
98 | struct bt3c_softc { |
99 | device_t sc_dev; |
100 | |
101 | struct pcmcia_function *sc_pf; /* our PCMCIA function */ |
102 | struct pcmcia_io_handle sc_pcioh; /* PCMCIA i/o space info */ |
103 | int sc_iow; /* our i/o window */ |
104 | int sc_flags; /* flags */ |
105 | |
106 | struct hci_unit *sc_unit; /* Bluetooth HCI Unit */ |
107 | struct bt_stats sc_stats; /* HCI stats */ |
108 | |
109 | /* hardware interrupt */ |
110 | void *sc_intr; /* cookie */ |
111 | int sc_state; /* receive state */ |
112 | int sc_want; /* how much we want */ |
113 | struct mbuf *sc_rxp; /* incoming packet */ |
114 | struct mbuf *sc_txp; /* outgoing packet */ |
115 | |
116 | /* transmit queues */ |
117 | MBUFQ_HEAD() sc_cmdq; /* commands */ |
118 | MBUFQ_HEAD() sc_aclq; /* ACL data */ |
119 | MBUFQ_HEAD() sc_scoq; /* SCO data */ |
120 | }; |
121 | |
122 | /* sc_state */ /* receiving */ |
123 | #define BT3C_RECV_PKT_TYPE 0 /* packet type */ |
124 | #define BT3C_RECV_ACL_HDR 1 /* acl header */ |
125 | #define BT3C_RECV_SCO_HDR 2 /* sco header */ |
126 | #define BT3C_RECV_EVENT_HDR 3 /* event header */ |
127 | #define BT3C_RECV_ACL_DATA 4 /* acl packet data */ |
128 | #define BT3C_RECV_SCO_DATA 5 /* sco packet data */ |
129 | #define BT3C_RECV_EVENT_DATA 6 /* event packet data */ |
130 | |
131 | /* sc_flags */ |
132 | #define BT3C_XMIT (1 << 1) /* transmit active */ |
133 | #define BT3C_ENABLED (1 << 2) /* enabled */ |
134 | |
135 | static int bt3c_match(device_t, cfdata_t, void *); |
136 | static void bt3c_attach(device_t, device_t, void *); |
137 | static int bt3c_detach(device_t, int); |
138 | static bool bt3c_suspend(device_t, const pmf_qual_t *); |
139 | static bool bt3c_resume(device_t, const pmf_qual_t *); |
140 | |
141 | CFATTACH_DECL_NEW(bt3c, sizeof(struct bt3c_softc), |
142 | bt3c_match, bt3c_attach, bt3c_detach, NULL); |
143 | |
144 | static int bt3c_enable(device_t); |
145 | static void bt3c_disable(device_t); |
146 | static void bt3c_output_cmd(device_t, struct mbuf *); |
147 | static void bt3c_output_acl(device_t, struct mbuf *); |
148 | static void bt3c_output_sco(device_t, struct mbuf *); |
149 | static void bt3c_stats(device_t, struct bt_stats *, int); |
150 | |
151 | static const struct hci_if bt3c_hci = { |
152 | .enable = bt3c_enable, |
153 | .disable = bt3c_disable, |
154 | .output_cmd = bt3c_output_cmd, |
155 | .output_acl = bt3c_output_acl, |
156 | .output_sco = bt3c_output_sco, |
157 | .get_stats = bt3c_stats, |
158 | .ipl = IPL_TTY, |
159 | }; |
160 | |
161 | static void bt3c_start(struct bt3c_softc *); |
162 | |
163 | /************************************************************************** |
164 | * |
165 | * Hardware Definitions & IO routines |
166 | * |
167 | * I made up the names for most of these defs since we dont have |
168 | * manufacturers recommendations, but I dont like raw numbers.. |
169 | * |
170 | * all hardware routines are running at IPL_TTY |
171 | * |
172 | */ |
173 | #define BT3C_ISR 0x7001 /* Interrupt Status Register */ |
174 | #define BT3C_ISR_RXRDY (1<<0) /* Device has data */ |
175 | #define BT3C_ISR_TXRDY (1<<1) /* Finished sending data */ |
176 | #define BT3C_ISR_ANTENNA (1<<5) /* Antenna position changed */ |
177 | |
178 | #define BT3C_CSR 0x7002 /* Card Status Register */ |
179 | #define BT3C_CSR_ANTENNA (1<<4) /* Antenna position */ |
180 | |
181 | #define BT3C_TX_COUNT 0x7005 /* Tx fifo contents */ |
182 | #define BT3C_TX_FIFO 0x7080 /* Transmit Fifo */ |
183 | #define BT3C_RX_COUNT 0x7006 /* Rx fifo contents */ |
184 | #define BT3C_RX_FIFO 0x7480 /* Receive Fifo */ |
185 | #define BT3C_FIFO_SIZE 256 |
186 | |
187 | /* IO Registers */ |
188 | #define BT3C_IOR_DATA_L 0x00 /* data low byte */ |
189 | #define BT3C_IOR_DATA_H 0x01 /* data high byte */ |
190 | #define BT3C_IOR_ADDR_L 0x02 /* address low byte */ |
191 | #define BT3C_IOR_ADDR_H 0x03 /* address high byte */ |
192 | #define BT3C_IOR_CNTL 0x04 /* control byte */ |
193 | #define BT3C_IOR_CNTL_BOOT (1<<6) /* Boot Card */ |
194 | #define BT3C_IOR_CNTL_INTR (1<<7) /* Interrupt Requested */ |
195 | #define BT3C_IOR_LEN 0x05 |
196 | |
197 | static inline uint16_t |
198 | bt3c_get(struct bt3c_softc *sc) |
199 | { |
200 | uint16_t data; |
201 | |
202 | bus_space_barrier(sc->sc_pcioh.iot, sc->sc_pcioh.ioh, |
203 | 0, BT3C_IOR_LEN, BUS_SPACE_BARRIER_READ); |
204 | data = bus_space_read_1(sc->sc_pcioh.iot, sc->sc_pcioh.ioh, |
205 | BT3C_IOR_DATA_L); |
206 | data |= bus_space_read_1(sc->sc_pcioh.iot, sc->sc_pcioh.ioh, |
207 | BT3C_IOR_DATA_H) << 8; |
208 | |
209 | return data; |
210 | } |
211 | |
212 | static inline void |
213 | bt3c_put(struct bt3c_softc *sc, uint16_t data) |
214 | { |
215 | |
216 | bus_space_barrier(sc->sc_pcioh.iot, sc->sc_pcioh.ioh, |
217 | 0, BT3C_IOR_LEN, BUS_SPACE_BARRIER_WRITE); |
218 | bus_space_write_1(sc->sc_pcioh.iot, sc->sc_pcioh.ioh, |
219 | BT3C_IOR_DATA_L, data & 0xff); |
220 | bus_space_write_1(sc->sc_pcioh.iot, sc->sc_pcioh.ioh, |
221 | BT3C_IOR_DATA_H, (data >> 8) & 0xff); |
222 | } |
223 | |
224 | static inline uint8_t |
225 | bt3c_read_control(struct bt3c_softc *sc) |
226 | { |
227 | |
228 | bus_space_barrier(sc->sc_pcioh.iot, sc->sc_pcioh.ioh, |
229 | 0, BT3C_IOR_LEN, BUS_SPACE_BARRIER_READ); |
230 | return bus_space_read_1(sc->sc_pcioh.iot, sc->sc_pcioh.ioh, |
231 | BT3C_IOR_CNTL); |
232 | } |
233 | |
234 | static inline void |
235 | bt3c_write_control(struct bt3c_softc *sc, uint8_t data) |
236 | { |
237 | |
238 | bus_space_barrier(sc->sc_pcioh.iot, sc->sc_pcioh.ioh, |
239 | 0, BT3C_IOR_LEN, BUS_SPACE_BARRIER_WRITE); |
240 | bus_space_write_1(sc->sc_pcioh.iot, sc->sc_pcioh.ioh, |
241 | BT3C_IOR_CNTL, data); |
242 | } |
243 | |
244 | static inline void |
245 | bt3c_set_address(struct bt3c_softc *sc, uint16_t addr) |
246 | { |
247 | |
248 | bus_space_barrier(sc->sc_pcioh.iot, sc->sc_pcioh.ioh, |
249 | 0, BT3C_IOR_LEN, BUS_SPACE_BARRIER_WRITE); |
250 | bus_space_write_1(sc->sc_pcioh.iot, sc->sc_pcioh.ioh, |
251 | BT3C_IOR_ADDR_L, addr & 0xff); |
252 | bus_space_write_1(sc->sc_pcioh.iot, sc->sc_pcioh.ioh, |
253 | BT3C_IOR_ADDR_H, (addr >> 8) & 0xff); |
254 | } |
255 | |
256 | static inline uint16_t |
257 | bt3c_read(struct bt3c_softc *sc, uint16_t addr) |
258 | { |
259 | |
260 | bt3c_set_address(sc, addr); |
261 | return bt3c_get(sc); |
262 | } |
263 | |
264 | static inline void |
265 | bt3c_write(struct bt3c_softc *sc, uint16_t addr, uint16_t data) |
266 | { |
267 | |
268 | bt3c_set_address(sc, addr); |
269 | bt3c_put(sc, data); |
270 | } |
271 | |
272 | /* |
273 | * receive incoming data from device, store in mbuf chain and |
274 | * pass on complete packets to bt device |
275 | */ |
276 | static void |
277 | bt3c_receive(struct bt3c_softc *sc) |
278 | { |
279 | struct mbuf *m = sc->sc_rxp; |
280 | int space = 0; |
281 | uint16_t count; |
282 | uint8_t b; |
283 | |
284 | /* |
285 | * If we already started a packet, find the |
286 | * trailing end of it. |
287 | */ |
288 | if (m) { |
289 | while (m->m_next) |
290 | m = m->m_next; |
291 | |
292 | space = M_TRAILINGSPACE(m); |
293 | } |
294 | |
295 | count = bt3c_read(sc, BT3C_RX_COUNT); |
296 | bt3c_set_address(sc, BT3C_RX_FIFO); |
297 | |
298 | while (count > 0) { |
299 | if (space == 0) { |
300 | if (m == NULL) { |
301 | /* new packet */ |
302 | MGETHDR(m, M_DONTWAIT, MT_DATA); |
303 | if (m == NULL) { |
304 | aprint_error_dev(sc->sc_dev, |
305 | "out of memory\n" ); |
306 | sc->sc_stats.err_rx++; |
307 | goto out; /* (lost sync) */ |
308 | } |
309 | |
310 | sc->sc_rxp = m; |
311 | m->m_pkthdr.len = m->m_len = 0; |
312 | space = MHLEN; |
313 | |
314 | sc->sc_state = BT3C_RECV_PKT_TYPE; |
315 | sc->sc_want = 1; |
316 | } else { |
317 | /* extend mbuf */ |
318 | MGET(m->m_next, M_DONTWAIT, MT_DATA); |
319 | if (m->m_next == NULL) { |
320 | aprint_error_dev(sc->sc_dev, |
321 | "out of memory\n" ); |
322 | sc->sc_stats.err_rx++; |
323 | goto out; /* (lost sync) */ |
324 | } |
325 | |
326 | m = m->m_next; |
327 | m->m_len = 0; |
328 | space = MLEN; |
329 | |
330 | if (sc->sc_want > MINCLSIZE) { |
331 | MCLGET(m, M_DONTWAIT); |
332 | if (m->m_flags & M_EXT) |
333 | space = MCLBYTES; |
334 | } |
335 | } |
336 | } |
337 | |
338 | b = bt3c_get(sc); |
339 | mtod(m, uint8_t *)[m->m_len++] = b; |
340 | count--; |
341 | space--; |
342 | sc->sc_rxp->m_pkthdr.len++; |
343 | sc->sc_stats.byte_rx++; |
344 | |
345 | sc->sc_want--; |
346 | if (sc->sc_want > 0) |
347 | continue; /* want more */ |
348 | |
349 | switch (sc->sc_state) { |
350 | case BT3C_RECV_PKT_TYPE: /* Got packet type */ |
351 | |
352 | switch (b) { |
353 | case HCI_ACL_DATA_PKT: |
354 | sc->sc_state = BT3C_RECV_ACL_HDR; |
355 | sc->sc_want = sizeof(hci_acldata_hdr_t) - 1; |
356 | break; |
357 | |
358 | case HCI_SCO_DATA_PKT: |
359 | sc->sc_state = BT3C_RECV_SCO_HDR; |
360 | sc->sc_want = sizeof(hci_scodata_hdr_t) - 1; |
361 | break; |
362 | |
363 | case HCI_EVENT_PKT: |
364 | sc->sc_state = BT3C_RECV_EVENT_HDR; |
365 | sc->sc_want = sizeof(hci_event_hdr_t) - 1; |
366 | break; |
367 | |
368 | default: |
369 | aprint_error_dev(sc->sc_dev, |
370 | "Unknown packet type=%#x!\n" , b); |
371 | sc->sc_stats.err_rx++; |
372 | m_freem(sc->sc_rxp); |
373 | sc->sc_rxp = NULL; |
374 | goto out; /* (lost sync) */ |
375 | } |
376 | |
377 | break; |
378 | |
379 | /* |
380 | * we assume (correctly of course :) that the packet headers |
381 | * all fit into a single pkthdr mbuf |
382 | */ |
383 | case BT3C_RECV_ACL_HDR: /* Got ACL Header */ |
384 | sc->sc_state = BT3C_RECV_ACL_DATA; |
385 | sc->sc_want = mtod(m, hci_acldata_hdr_t *)->length; |
386 | sc->sc_want = le16toh(sc->sc_want); |
387 | break; |
388 | |
389 | case BT3C_RECV_SCO_HDR: /* Got SCO Header */ |
390 | sc->sc_state = BT3C_RECV_SCO_DATA; |
391 | sc->sc_want = mtod(m, hci_scodata_hdr_t *)->length; |
392 | break; |
393 | |
394 | case BT3C_RECV_EVENT_HDR: /* Got Event Header */ |
395 | sc->sc_state = BT3C_RECV_EVENT_DATA; |
396 | sc->sc_want = mtod(m, hci_event_hdr_t *)->length; |
397 | break; |
398 | |
399 | case BT3C_RECV_ACL_DATA: /* ACL Packet Complete */ |
400 | if (!hci_input_acl(sc->sc_unit, sc->sc_rxp)) |
401 | sc->sc_stats.err_rx++; |
402 | |
403 | sc->sc_stats.acl_rx++; |
404 | sc->sc_rxp = m = NULL; |
405 | space = 0; |
406 | break; |
407 | |
408 | case BT3C_RECV_SCO_DATA: /* SCO Packet Complete */ |
409 | if (!hci_input_sco(sc->sc_unit, sc->sc_rxp)) |
410 | sc->sc_stats.err_rx++; |
411 | |
412 | sc->sc_stats.sco_rx++; |
413 | sc->sc_rxp = m = NULL; |
414 | space = 0; |
415 | break; |
416 | |
417 | case BT3C_RECV_EVENT_DATA: /* Event Packet Complete */ |
418 | if (!hci_input_event(sc->sc_unit, sc->sc_rxp)) |
419 | sc->sc_stats.err_rx++; |
420 | |
421 | sc->sc_stats.evt_rx++; |
422 | sc->sc_rxp = m = NULL; |
423 | space = 0; |
424 | break; |
425 | |
426 | default: |
427 | panic("%s: invalid state %d!\n" , |
428 | device_xname(sc->sc_dev), sc->sc_state); |
429 | } |
430 | } |
431 | |
432 | out: |
433 | bt3c_write(sc, BT3C_RX_COUNT, 0x0000); |
434 | } |
435 | |
436 | /* |
437 | * write data from current packet to Transmit FIFO. |
438 | * restart when done. |
439 | */ |
440 | static void |
441 | bt3c_transmit(struct bt3c_softc *sc) |
442 | { |
443 | struct mbuf *m; |
444 | int count, rlen; |
445 | uint8_t *rptr; |
446 | |
447 | m = sc->sc_txp; |
448 | if (m == NULL) { |
449 | sc->sc_flags &= ~BT3C_XMIT; |
450 | bt3c_start(sc); |
451 | return; |
452 | } |
453 | |
454 | count = 0; |
455 | rlen = 0; |
456 | rptr = mtod(m, uint8_t *); |
457 | |
458 | bt3c_set_address(sc, BT3C_TX_FIFO); |
459 | |
460 | for(;;) { |
461 | if (rlen >= m->m_len) { |
462 | m = m->m_next; |
463 | if (m == NULL) { |
464 | m = sc->sc_txp; |
465 | sc->sc_txp = NULL; |
466 | |
467 | if (M_GETCTX(m, void *) == NULL) |
468 | m_freem(m); |
469 | else if (!hci_complete_sco(sc->sc_unit, m)) |
470 | sc->sc_stats.err_tx++; |
471 | |
472 | break; |
473 | } |
474 | |
475 | rlen = 0; |
476 | rptr = mtod(m, uint8_t *); |
477 | continue; |
478 | } |
479 | |
480 | if (count >= BT3C_FIFO_SIZE) { |
481 | m_adj(m, rlen); |
482 | break; |
483 | } |
484 | |
485 | bt3c_put(sc, *rptr++); |
486 | rlen++; |
487 | count++; |
488 | } |
489 | |
490 | bt3c_write(sc, BT3C_TX_COUNT, count); |
491 | sc->sc_stats.byte_tx += count; |
492 | } |
493 | |
494 | /* |
495 | * interrupt routine |
496 | */ |
497 | static int |
498 | bt3c_intr(void *arg) |
499 | { |
500 | struct bt3c_softc *sc = arg; |
501 | uint16_t control, isr; |
502 | |
503 | control = bt3c_read_control(sc); |
504 | if (control & BT3C_IOR_CNTL_INTR) { |
505 | isr = bt3c_read(sc, BT3C_ISR); |
506 | if ((isr & 0xff) == 0x7f) { |
507 | aprint_error_dev(sc->sc_dev, "strange ISR=%04x\n" , isr); |
508 | } else if ((isr & 0xff) != 0xff) { |
509 | |
510 | if (isr & BT3C_ISR_RXRDY) |
511 | bt3c_receive(sc); |
512 | |
513 | if (isr & BT3C_ISR_TXRDY) |
514 | bt3c_transmit(sc); |
515 | |
516 | #ifdef DIAGNOSTIC |
517 | if (isr & BT3C_ISR_ANTENNA) { |
518 | if (bt3c_read(sc, BT3C_CSR) & BT3C_CSR_ANTENNA) |
519 | aprint_verbose_dev(sc->sc_dev, |
520 | "Antenna Out\n" ); |
521 | else |
522 | aprint_verbose_dev(sc->sc_dev, |
523 | "Antenna In\n" ); |
524 | } |
525 | #endif |
526 | |
527 | bt3c_write(sc, BT3C_ISR, 0x0000); |
528 | bt3c_write_control(sc, control); |
529 | |
530 | return 1; /* handled */ |
531 | } |
532 | } |
533 | |
534 | return 0; /* not handled */ |
535 | } |
536 | |
537 | /* |
538 | * load firmware for the device |
539 | * |
540 | * The firmware file is a plain ASCII file in the Motorola S-Record format, |
541 | * with lines in the format: |
542 | * |
543 | * S<Digit><Len><Address><Data1><Data2>...<DataN><Checksum> |
544 | * |
545 | * <Digit>: 0 header |
546 | * 3 data record (4 byte address) |
547 | * 7 boot record (4 byte address) |
548 | * |
549 | * <Len>: 1 byte, and is the number of bytes in the rest of the line |
550 | * <Address>: 4 byte address (only 2 bytes are valid for bt3c I think) |
551 | * <Data>: 2 byte data word to be written to the address |
552 | * <Checksum>: checksum of all bytes in the line including <Len> |
553 | * |
554 | * all bytes are in hexadecimal |
555 | */ |
556 | static inline int32_t |
557 | hex(const uint8_t *p, int n) |
558 | { |
559 | uint32_t val = 0; |
560 | |
561 | while (n > 0) { |
562 | val <<= 4; |
563 | |
564 | if ('0' <= *p && *p <= '9') |
565 | val += (*p - '0'); |
566 | else if ('a' <= *p && *p <= 'f') |
567 | val += (*p - 'a' + 0xa); |
568 | else if ('A' <= *p && *p <= 'F') |
569 | val += (*p - 'A' + 0xa); |
570 | else |
571 | return -1; |
572 | |
573 | p++; |
574 | n--; |
575 | } |
576 | |
577 | return val; |
578 | } |
579 | |
580 | static int |
581 | bt3c_load_firmware(struct bt3c_softc *sc) |
582 | { |
583 | uint8_t *buf, *line, *next, *p; |
584 | int32_t addr, data; |
585 | int err, sum, len; |
586 | firmware_handle_t fh; |
587 | cfdata_t cf = device_cfdata(sc->sc_dev); |
588 | size_t size; |
589 | |
590 | err = firmware_open(cf->cf_name, |
591 | BT3C_FIRMWARE_FILE, &fh); |
592 | if (err) { |
593 | aprint_error_dev(sc->sc_dev, "Cannot open firmware %s/%s\n" , |
594 | cf->cf_name, BT3C_FIRMWARE_FILE); |
595 | return err; |
596 | } |
597 | |
598 | size = (size_t)firmware_get_size(fh); |
599 | if (size > 10 * 1024) { /* sanity check */ |
600 | aprint_error_dev(sc->sc_dev, "insane firmware file size!\n" ); |
601 | firmware_close(fh); |
602 | return EFBIG; |
603 | } |
604 | |
605 | buf = firmware_malloc(size); |
606 | KASSERT(buf != NULL); |
607 | |
608 | err = firmware_read(fh, 0, buf, size); |
609 | if (err) { |
610 | aprint_error_dev(sc->sc_dev, "Firmware read failed (%d)\n" , err); |
611 | goto out; |
612 | } |
613 | |
614 | /* Reset */ |
615 | bt3c_write(sc, 0x8040, 0x0404); |
616 | bt3c_write(sc, 0x8040, 0x0400); |
617 | DELAY(1); |
618 | bt3c_write(sc, 0x8040, 0x0404); |
619 | DELAY(17); |
620 | |
621 | next = buf; |
622 | err = EFTYPE; |
623 | |
624 | while (next < buf + size) { |
625 | line = next; |
626 | |
627 | while (*next != '\r' && *next != '\n') { |
628 | if (next >= buf + size) |
629 | goto out; |
630 | |
631 | next++; |
632 | } |
633 | |
634 | /* 14 covers address and checksum minimum */ |
635 | if (next - line < 14) |
636 | goto out; |
637 | |
638 | if (line[0] != 'S') |
639 | goto out; |
640 | |
641 | /* verify line length */ |
642 | len = hex(line + 2, 2); |
643 | if (len < 0 || next - line != len * 2 + 4) |
644 | goto out; |
645 | |
646 | /* checksum the line */ |
647 | sum = 0; |
648 | for (p = line + 2 ; p < next ; p += 2) |
649 | sum += hex(p, 2); |
650 | |
651 | if ((sum & 0xff) != 0xff) |
652 | goto out; |
653 | |
654 | /* extract relevant data */ |
655 | switch (line[1]) { |
656 | case '0': |
657 | /* we ignore the header */ |
658 | break; |
659 | |
660 | case '3': |
661 | /* find number of data words */ |
662 | len = (len - 5) / 2; |
663 | if (len > 15) |
664 | goto out; |
665 | |
666 | addr = hex(line + 8, 4); |
667 | if (addr < 0) |
668 | goto out; |
669 | |
670 | bt3c_set_address(sc, addr); |
671 | |
672 | for (p = line + 12 ; p + 4 < next ; p += 4) { |
673 | data = hex(p, 4); |
674 | if (data < 0) |
675 | goto out; |
676 | |
677 | bt3c_put(sc, data); |
678 | } |
679 | break; |
680 | |
681 | case '7': |
682 | /* |
683 | * for some reason we ignore this record |
684 | * and boot from 0x3000 which happens to |
685 | * be the first record in the file. |
686 | */ |
687 | break; |
688 | |
689 | default: |
690 | goto out; |
691 | } |
692 | |
693 | /* skip to start of next line */ |
694 | while (next < buf + size && (*next == '\r' || *next == '\n')) |
695 | next++; |
696 | } |
697 | |
698 | err = 0; |
699 | DELAY(17); |
700 | |
701 | /* Boot */ |
702 | bt3c_set_address(sc, 0x3000); |
703 | bt3c_write_control(sc, (bt3c_read_control(sc) | BT3C_IOR_CNTL_BOOT)); |
704 | DELAY(17); |
705 | |
706 | /* Clear Registers */ |
707 | bt3c_write(sc, BT3C_RX_COUNT, 0x0000); |
708 | bt3c_write(sc, BT3C_TX_COUNT, 0x0000); |
709 | bt3c_write(sc, BT3C_ISR, 0x0000); |
710 | DELAY(1000); |
711 | |
712 | out: |
713 | firmware_free(buf, size); |
714 | firmware_close(fh); |
715 | return err; |
716 | } |
717 | |
718 | /************************************************************************** |
719 | * |
720 | * bt device callbacks |
721 | */ |
722 | |
723 | /* |
724 | * start sending on bt3c |
725 | * should be called at spltty() when BT3C_XMIT is not set |
726 | */ |
727 | static void |
728 | bt3c_start(struct bt3c_softc *sc) |
729 | { |
730 | struct mbuf *m; |
731 | |
732 | KASSERT((sc->sc_flags & BT3C_XMIT) == 0); |
733 | KASSERT(sc->sc_txp == NULL); |
734 | |
735 | if (MBUFQ_FIRST(&sc->sc_cmdq)) { |
736 | MBUFQ_DEQUEUE(&sc->sc_cmdq, m); |
737 | sc->sc_stats.cmd_tx++; |
738 | goto start; |
739 | } |
740 | |
741 | if (MBUFQ_FIRST(&sc->sc_scoq)) { |
742 | MBUFQ_DEQUEUE(&sc->sc_scoq, m); |
743 | sc->sc_stats.sco_tx++; |
744 | goto start; |
745 | } |
746 | |
747 | if (MBUFQ_FIRST(&sc->sc_aclq)) { |
748 | MBUFQ_DEQUEUE(&sc->sc_aclq, m); |
749 | sc->sc_stats.acl_tx++; |
750 | goto start; |
751 | } |
752 | |
753 | /* Nothing to send */ |
754 | return; |
755 | |
756 | start: |
757 | sc->sc_txp = m; |
758 | sc->sc_flags |= BT3C_XMIT; |
759 | bt3c_transmit(sc); |
760 | } |
761 | |
762 | static void |
763 | bt3c_output_cmd(device_t self, struct mbuf *m) |
764 | { |
765 | struct bt3c_softc *sc = device_private(self); |
766 | int s; |
767 | |
768 | KASSERT(sc->sc_flags & BT3C_ENABLED); |
769 | |
770 | M_SETCTX(m, NULL); |
771 | |
772 | s = spltty(); |
773 | MBUFQ_ENQUEUE(&sc->sc_cmdq, m); |
774 | if ((sc->sc_flags & BT3C_XMIT) == 0) |
775 | bt3c_start(sc); |
776 | |
777 | splx(s); |
778 | } |
779 | |
780 | static void |
781 | bt3c_output_acl(device_t self, struct mbuf *m) |
782 | { |
783 | struct bt3c_softc *sc = device_private(self); |
784 | int s; |
785 | |
786 | KASSERT(sc->sc_flags & BT3C_ENABLED); |
787 | |
788 | M_SETCTX(m, NULL); |
789 | |
790 | s = spltty(); |
791 | MBUFQ_ENQUEUE(&sc->sc_aclq, m); |
792 | if ((sc->sc_flags & BT3C_XMIT) == 0) |
793 | bt3c_start(sc); |
794 | |
795 | splx(s); |
796 | } |
797 | |
798 | static void |
799 | bt3c_output_sco(device_t self, struct mbuf *m) |
800 | { |
801 | struct bt3c_softc *sc = device_private(self); |
802 | int s; |
803 | |
804 | KASSERT(sc->sc_flags & BT3C_ENABLED); |
805 | |
806 | s = spltty(); |
807 | MBUFQ_ENQUEUE(&sc->sc_scoq, m); |
808 | if ((sc->sc_flags & BT3C_XMIT) == 0) |
809 | bt3c_start(sc); |
810 | |
811 | splx(s); |
812 | } |
813 | |
814 | /* |
815 | * enable device |
816 | * turn on card |
817 | * load firmware |
818 | * establish interrupts |
819 | */ |
820 | static int |
821 | bt3c_enable(device_t self) |
822 | { |
823 | struct bt3c_softc *sc = device_private(self); |
824 | int err, s; |
825 | |
826 | if (sc->sc_flags & BT3C_ENABLED) |
827 | return 0; |
828 | |
829 | s = spltty(); |
830 | |
831 | sc->sc_intr = pcmcia_intr_establish(sc->sc_pf, IPL_TTY, bt3c_intr, sc); |
832 | if (sc->sc_intr == NULL) { |
833 | err = EIO; |
834 | goto bad; |
835 | } |
836 | |
837 | err = pcmcia_function_enable(sc->sc_pf); |
838 | if (err) |
839 | goto bad1; |
840 | |
841 | err = bt3c_load_firmware(sc); |
842 | if (err) |
843 | goto bad2; |
844 | |
845 | sc->sc_flags |= BT3C_ENABLED; |
846 | sc->sc_flags &= ~BT3C_XMIT; |
847 | |
848 | splx(s); |
849 | |
850 | return 0; |
851 | |
852 | bad2: |
853 | pcmcia_function_disable(sc->sc_pf); |
854 | bad1: |
855 | pcmcia_intr_disestablish(sc->sc_pf, sc->sc_intr); |
856 | sc->sc_intr = NULL; |
857 | bad: |
858 | splx(s); |
859 | return err; |
860 | } |
861 | |
862 | /* |
863 | * disable device |
864 | * shut down card |
865 | * disestablish interrupts |
866 | * free held packets |
867 | */ |
868 | static void |
869 | bt3c_disable(device_t self) |
870 | { |
871 | struct bt3c_softc *sc = device_private(self); |
872 | int s; |
873 | |
874 | if ((sc->sc_flags & BT3C_ENABLED) == 0) |
875 | return; |
876 | |
877 | s = spltty(); |
878 | |
879 | pcmcia_function_disable(sc->sc_pf); |
880 | |
881 | if (sc->sc_intr) { |
882 | pcmcia_intr_disestablish(sc->sc_pf, sc->sc_intr); |
883 | sc->sc_intr = NULL; |
884 | } |
885 | |
886 | if (sc->sc_rxp) { |
887 | m_freem(sc->sc_rxp); |
888 | sc->sc_rxp = NULL; |
889 | } |
890 | |
891 | if (sc->sc_txp) { |
892 | m_freem(sc->sc_txp); |
893 | sc->sc_txp = NULL; |
894 | } |
895 | |
896 | MBUFQ_DRAIN(&sc->sc_cmdq); |
897 | MBUFQ_DRAIN(&sc->sc_aclq); |
898 | MBUFQ_DRAIN(&sc->sc_scoq); |
899 | |
900 | sc->sc_flags &= ~BT3C_ENABLED; |
901 | splx(s); |
902 | } |
903 | |
904 | void |
905 | bt3c_stats(device_t self, struct bt_stats *dest, int flush) |
906 | { |
907 | struct bt3c_softc *sc = device_private(self); |
908 | int s; |
909 | |
910 | s = spltty(); |
911 | memcpy(dest, &sc->sc_stats, sizeof(struct bt_stats)); |
912 | |
913 | if (flush) |
914 | memset(&sc->sc_stats, 0, sizeof(struct bt_stats)); |
915 | |
916 | splx(s); |
917 | } |
918 | |
919 | /************************************************************************** |
920 | * |
921 | * bt3c PCMCIA autoconfig glue |
922 | */ |
923 | |
924 | static int |
925 | bt3c_match(device_t parent, cfdata_t match, void *aux) |
926 | { |
927 | struct pcmcia_attach_args *pa = aux; |
928 | |
929 | if (pa->manufacturer == PCMCIA_VENDOR_3COM && |
930 | pa->product == PCMCIA_PRODUCT_3COM_3CRWB6096) |
931 | return 10; /* 'com' also claims this, so trump them */ |
932 | |
933 | return 0; |
934 | } |
935 | |
936 | static void |
937 | bt3c_attach(device_t parent, device_t self, void *aux) |
938 | { |
939 | struct bt3c_softc *sc = device_private(self); |
940 | struct pcmcia_attach_args *pa = aux; |
941 | struct pcmcia_config_entry *cfe; |
942 | |
943 | sc->sc_dev = self; |
944 | sc->sc_pf = pa->pf; |
945 | |
946 | MBUFQ_INIT(&sc->sc_cmdq); |
947 | MBUFQ_INIT(&sc->sc_aclq); |
948 | MBUFQ_INIT(&sc->sc_scoq); |
949 | |
950 | /* Find a PCMCIA config entry we can use */ |
951 | SIMPLEQ_FOREACH(cfe, &pa->pf->cfe_head, cfe_list) { |
952 | if (cfe->num_memspace != 0) |
953 | continue; |
954 | |
955 | if (cfe->num_iospace != 1) |
956 | continue; |
957 | |
958 | if (pcmcia_io_alloc(pa->pf, cfe->iospace[0].start, |
959 | cfe->iospace[0].length, 0, &sc->sc_pcioh) == 0) |
960 | break; |
961 | } |
962 | |
963 | if (cfe == 0) { |
964 | aprint_error_dev(self, "cannot allocate io space\n" ); |
965 | goto no_config_entry; |
966 | } |
967 | |
968 | /* Initialise it */ |
969 | pcmcia_function_init(pa->pf, cfe); |
970 | |
971 | /* Map in the io space */ |
972 | if (pcmcia_io_map(pa->pf, PCMCIA_WIDTH_AUTO, |
973 | &sc->sc_pcioh, &sc->sc_iow)) { |
974 | aprint_error_dev(self, "cannot map io space\n" ); |
975 | goto iomap_failed; |
976 | } |
977 | |
978 | /* Attach Bluetooth unit */ |
979 | sc->sc_unit = hci_attach_pcb(&bt3c_hci, self, BTF_POWER_UP_NOOP); |
980 | if (sc->sc_unit == NULL) |
981 | aprint_error_dev(self, "HCI attach failed\n" ); |
982 | |
983 | if (!pmf_device_register(self, bt3c_suspend, bt3c_resume)) |
984 | aprint_error_dev(self, "couldn't establish power handler\n" ); |
985 | |
986 | return; |
987 | |
988 | iomap_failed: |
989 | /* unmap io space */ |
990 | pcmcia_io_free(pa->pf, &sc->sc_pcioh); |
991 | |
992 | no_config_entry: |
993 | sc->sc_iow = -1; |
994 | } |
995 | |
996 | static int |
997 | bt3c_detach(device_t self, int flags) |
998 | { |
999 | struct bt3c_softc *sc = device_private(self); |
1000 | int err = 0; |
1001 | |
1002 | pmf_device_deregister(self); |
1003 | bt3c_disable(self); |
1004 | |
1005 | if (sc->sc_unit) { |
1006 | hci_detach_pcb(sc->sc_unit); |
1007 | sc->sc_unit = NULL; |
1008 | } |
1009 | |
1010 | if (sc->sc_iow != -1) { |
1011 | pcmcia_io_unmap(sc->sc_pf, sc->sc_iow); |
1012 | pcmcia_io_free(sc->sc_pf, &sc->sc_pcioh); |
1013 | sc->sc_iow = -1; |
1014 | } |
1015 | |
1016 | return err; |
1017 | } |
1018 | |
1019 | static bool |
1020 | bt3c_suspend(device_t self, const pmf_qual_t *qual) |
1021 | { |
1022 | struct bt3c_softc *sc = device_private(self); |
1023 | |
1024 | if (sc->sc_unit) { |
1025 | hci_detach_pcb(sc->sc_unit); |
1026 | sc->sc_unit = NULL; |
1027 | } |
1028 | |
1029 | return true; |
1030 | } |
1031 | |
1032 | static bool |
1033 | bt3c_resume(device_t self, const pmf_qual_t *qual) |
1034 | { |
1035 | struct bt3c_softc *sc = device_private(self); |
1036 | |
1037 | KASSERT(sc->sc_unit == NULL); |
1038 | |
1039 | sc->sc_unit = hci_attach_pcb(&bt3c_hci, self, BTF_POWER_UP_NOOP); |
1040 | if (sc->sc_unit == NULL) |
1041 | return false; |
1042 | |
1043 | return true; |
1044 | } |
1045 | |