Subject: misc/30036: device driver for 3Com Bluetooth PC Card
To: None <misc-bug-people@netbsd.org, gnats-admin@netbsd.org,>
From: Iain Hibbert <plunky@rya-online.net>
List: netbsd-bugs
Date: 04/22/2005 19:44:00
>Number: 30036
>Category: misc
>Synopsis: New device driver for 3Com Bluetooth PC Card, called bt3c
>Confidential: no
>Severity: non-critical
>Priority: medium
>Responsible: misc-bug-people
>State: open
>Class: change-request
>Submitter-Id: net
>Arrival-Date: Fri Apr 22 19:44:00 +0000 2005
>Originator: Iain Hibbert
>Release: NetBSD 2.0
>Organization:
>Environment:
System: NetBSD galant 2.0 NetBSD 2.0 (GALANT) #12: Tue Mar 15 22:08:38 GMT 2005 plunky@galant:/home/plunky/src/sys/arch/i386/compile/GALANT i386
Architecture: i386
Machine: i386
>Description:
Please find attached a device driver "bt3c" providing support for the
3Com Bluetooth PC Card to the bthci device layer which is currently
implemented as a character device. The included patch is against
NetBSD 2.0 source distribution, and creates or modifies the following
files:
sys/dev/DEVNAMES add the bt3c device name to the list
sys/dev/bluetooth/files.bluetooth fix a wrongness
sys/dev/bluetooth/bluetooth.h add some missing definitions
sys/dev/pcmcia/pcmciadevs add the bt3c device
sys/dev/pcmcia/files.pcmcia add the bt3c device
sys/dev/pcmcia/bt3cfw.h include file for firmware loader (new)
sys/dev/pcmcia/bt3c.c device driver (new)
sys/lkm/misc/bt3cpcc/Makefile firmware loader Makefile (new)
sys/lkm/misc/bt3cpcc/bt3cpcc.awk firmware conversion script (new)
sys/lkm/misc/bt3cpcc/lkminit_bt3cpcc.c firmware loader (new)
share/man/man4/bt3c.4 manual page for bt3c device (new)
share/man/man4/Makefile update the Makefile
I havent tested the patch against NetBSD-current but I think it should be Ok
apart from hunk #2 which is already in.
you will need to 'make -f Makefile.pcmciadevs' in sys/dev/pcmcia to update
the pcmciadevs.h and pcmciadevs_data.h files.
>How-To-Repeat:
>Fix:
--- /usr/src/sys/dev/DEVNAMES 2004-08-30 10:46:47.000000000 +0100
+++ sys/dev/DEVNAMES 2005-03-08 16:44:33.000000000 +0000
@@ -131,6 +131,7 @@
boca MI
bonito algor
bpp MI
+bt3c MI
bt_dac sparc Attribute
bt_dac sparc64 Attribute
btl arc
--- /usr/src/sys/dev/bluetooth/files.bluetooth 2003-01-11 05:46:11.000000000 +0000
+++ sys/dev/bluetooth/files.bluetooth 2005-03-03 00:03:40.000000000 +0000
@@ -7,6 +7,6 @@
device bthci { }: bthcidrv
attach bthci at btbus
-file dev/bluetooth/bluetooth.c bthcidrv
+file dev/bluetooth/bluetooth.c btbus
file dev/bluetooth/bthci.c bthcidrv needs-flag
file dev/bluetooth/bthci_util.c bthcidrv
--- /usr/src/sys/dev/bluetooth/bluetooth.h 2004-01-04 05:47:43.000000000 +0000
+++ sys/dev/bluetooth/bluetooth.h 2005-03-02 11:10:31.000000000 +0000
@@ -113,6 +113,12 @@
#define BTHCI_ACL_DATA_LEN_OFFT 2
#define BTHCI_ACL_DATA_LEN_LENGTH 2
+/* Maximum SCO data packet length, including header */
+#define BTHCI_SCO_DATA_MIN_LEN 3
+#define BTHCI_SCO_DATA_MAX_LEN (0xff + BTHCI_SCO_DATA_MIN_LEN)
+#define BTHCI_SCO_DATA_LEN_OFFT 2
+#define BTHCI_SCO_DATA_LEN_LENGTH 1
+
/* HCI consumer interface constants */
#define BTHCI_PKTID_COMMAND 1
#define BTHCI_PKTID_ACL_DATA 2
--- /usr/src/sys/dev/pcmcia/pcmciadevs 2004-02-01 12:35:59.000000000 +0000
+++ sys/dev/pcmcia/pcmciadevs 2005-02-10 18:09:28.000000000 +0000
@@ -121,6 +121,7 @@
product 3COM 3CXM056BNW 0x002f 3Com/NoteWorthy 3CXM056-BNW 56K Modem
product 3COM 3CXEM556 0x0035 3Com/Megahertz 3CXEM556 Ethernet/Modem
product 3COM 3CXEM556INT 0x003d 3Com/Megahertz 3CXEM556-INT Ethernet/Modem
+product 3COM 3CRWB6096 0x0040 3Com Bluetooth PC Card
product 3COM 3CCFEM556BI 0x0556 3Com/Megahertz 3CCFEM556BI Ethernet/Modem
product 3COM 3C562 0x0562 3Com 3c562 33.6 Modem/10Mbps Ethernet
product 3COM 3C589 0x0589 3Com 3c589 10Mbps Ethernet
--- /usr/src/sys/dev/pcmcia/files.pcmcia 2002-04-22 10:41:22.000000000 +0100
+++ sys/dev/pcmcia/files.pcmcia 2005-02-22 19:30:04.000000000 +0000
@@ -132,3 +132,7 @@
attach opl at esl with opl_esl
file dev/pcmcia/opl_esl.c opl_esl
+# 3Com Bluetooth card 3CRW6096
+device bt3c: btbus
+attach bt3c at pcmcia
+file dev/pcmcia/bt3c.c bt3c
--- /dev/null 2005-04-22 10:38:10.000000000 +0100
+++ sys/dev/pcmcia/bt3cfw.h 2005-03-11 10:41:55.000000000 +0000
@@ -0,0 +1,37 @@
+/* $NetBSD: bt3cfw.h$ */
+
+/*
+ * Copyright (c) 2005 Iain D. Hibbert,
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ */
+
+struct bt3c_firmware_block {
+ u_int16_t addr;
+ u_int8_t len;
+ u_int16_t data[15]; /* 15 words max in each block */
+};
+
+extern struct bt3c_firmware_block *bt3c_firmware;
--- /dev/null 2005-04-22 10:38:10.000000000 +0100
+++ sys/dev/pcmcia/bt3c.c 2005-03-14 17:47:32.000000000 +0000
@@ -0,0 +1,925 @@
+/* $NetBSD: bt3c.c$ */
+
+/*
+ * Copyright (c) 2005 Iain D. Hibbert,
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ */
+
+/*
+ * Driver for the 3Com Bluetooth PC Card 3CRWB6096, written with reference to
+ * FreeBSD and BlueZ drivers for same, with credit for those going to:
+ *
+ * Maksim Yevmenkin <m_evmenkin@yahoo.com> (FreeBSD)
+ * Marcel Holtmann <marcel@holtmann.org> (BlueZ)
+ * Jose Orlando Pereira <jop@di.uminho.pt> (BlueZ)
+ * David Hinds <dahinds@users.sourceforge.net> (Original Code)
+ */
+
+/*
+ * The CIS info from the card:
+ *
+ * pcmcia1: CIS tuple chain:
+ * CISTPL_DEVICE type=null speed=null
+ * 01 03 00 00 ff
+ * CISTPL_VERS_1
+ * 15 24 05 00 33 43 4f 4d 00 33 43 52 57 42 36 30
+ * 2d 41 00 42 6c 75 65 74 6f 6f 74 68 20 50 43 20
+ * 43 61 72 64 00 ff
+ * CISTPL_MANFID
+ * 20 04 01 01 40 00
+ * CISTPL_FUNCID
+ * 21 02 02 01
+ * CISTPL_CONFIG
+ * 1a 06 05 30 20 03 17 00
+ * CISTPL_CFTABLE_ENTRY
+ * 1b 09 f0 41 18 a0 40 07 30 ff ff
+ * unhandled CISTPL 80
+ * 80 0a 02 01 40 00 2d 00 00 00 00 ff
+ * CISTPL_NO_LINK
+ * 14 00
+ * CISTPL_END
+ * ff
+ * pcmcia1: CIS version PC Card Standard 5.0
+ * pcmcia1: CIS info: 3COM, 3CRWB60-A, Bluetooth PC Card
+ * pcmcia1: Manufacturer code 0x101, product 0x40
+ * pcmcia1: function 0: serial port, ccr addr 320 mask 17
+ * pcmcia1: function 0, config table entry 48: I/O card; irq mask ffff; iomask 0, iospace 0-7; rdybsy_active io8 irqlevel
+ */
+
+#include <sys/cdefs.h>
+__KERNEL_RCSID(0, "$NetBSD: bt3c.c$");
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/device.h>
+#include <sys/mbuf.h>
+
+#include <machine/cpu.h>
+#include <machine/bus.h>
+#include <machine/intr.h>
+
+#include <dev/pcmcia/pcmciareg.h>
+#include <dev/pcmcia/pcmciavar.h>
+#include <dev/pcmcia/pcmciadevs.h>
+
+#include <dev/bluetooth/bluetooth.h>
+
+/**************************************************************************
+ *
+ * The firmware is loaded in a kernel module, see sys/lkm/misc/bt3cpcc
+ */
+
+#include <dev/pcmcia/bt3cfw.h>
+
+struct bt3c_firmware_block *bt3c_firmware = NULL;
+
+/**************************************************************************
+ *
+ * bt3c autoconfig glue
+ */
+
+static int bt3c_match __P((struct device *, struct cfdata *, void *));
+static void bt3c_attach __P((struct device *, struct device *, void *));
+static int bt3c_detach __P((struct device *, int));
+static int bt3c_activate __P((struct device *, enum devact));
+static void bt3c_power __P((int, void *));
+
+struct bt3c_softc {
+ struct device sc_dev; /* required */
+
+ /* PCMCIA specific */
+ struct pcmcia_function *sc_pf; /* our PCMCIA function */
+ struct pcmcia_io_handle sc_pcioh; /* PCMCIA i/o space info */
+ int sc_iow; /* our i/o window */
+
+ /* the bthci layer */
+ struct device *sc_bthci;
+ struct btframe_callback_methods const *sc_cb;
+
+ void *sc_hard; /* hardware interrupt handler */
+ void *sc_soft; /* soft interrupt handler */
+ u_int16_t sc_sw_isr; /* soft interrupt status register */
+ u_int16_t sc_flags; /* bt3c device flags */
+
+ int sc_state; /* receive state */
+ int sc_want; /* how many bytes we are expecting */
+ struct mbuf *sc_m; /* current frame */
+ struct mbuf *sc_rxq; /* received packets queue */
+ struct mbuf *sc_txq; /* transmit queue */
+
+ void *sc_powerhook; /* power hook descriptor */
+};
+
+/* sc_state */
+#define BT3C_RECV_PKT_ID 0 /* waiting for packet id */
+#define BT3C_RECV_ACL_HDR 1 /* waiting for acl header */
+#define BT3C_RECV_SCO_HDR 2 /* waiting for sco header */
+#define BT3C_RECV_EVENT_HDR 3 /* waiting for event header */
+#define BT3C_RECV_PKT_DATA 4 /* waiting for packet data */
+
+/* sc_flags */
+#define BT3C_DYING (1<<0) /* we've been asked to die */
+#define BT3C_ANTENNA (1<<1) /* antenna position */
+#define BT3C_XMIT (1<<2) /* transmitting */
+#define BT3C_OPEN (1<<3) /* if we are enabled, for the powerhook */
+
+CFATTACH_DECL(bt3c, sizeof(struct bt3c_softc),
+ bt3c_match, bt3c_attach, bt3c_detach, bt3c_activate);
+
+
+/**************************************************************************
+ *
+ * bthci callback methods
+ */
+
+static int bt3c_open __P((void *, int, int, struct proc *));
+static int bt3c_close __P((void *, int, int, struct proc *));
+static u_int8_t *bt3c_alloc_control __P((void *, size_t, struct btframe_buffer **));
+static u_int8_t *bt3c_alloc_acldata __P((void *, size_t, struct btframe_buffer **));
+static u_int8_t *bt3c_alloc_scodata __P((void *, size_t, struct btframe_buffer **));
+static int bt3c_send __P((void *, struct btframe_buffer *, size_t));
+static int bt3c_splraise __P((void));
+
+static struct btframe_methods const bt3c_methods = {
+ bt3c_open, bt3c_close,
+ {bt3c_alloc_control, bt3c_send},
+ {bt3c_alloc_acldata, bt3c_send},
+ {bt3c_alloc_scodata, bt3c_send},
+ bt3c_splraise
+};
+
+
+/**************************************************************************
+ *
+ * Hardware Definitions & IO routines
+ * I made up the names for most of these defs since we dont have
+ * manufacturers recommendations, but I dont like raw numbers..
+ */
+#define BT3C_ISR 0x7001 /* Interrupt Status Register */
+#define BT3C_ISR_RXRDY (1<<0)
+#define BT3C_ISR_TXRDY (1<<1)
+#define BT3C_ISR_ANTENNA (1<<5)
+
+#define BT3C_CSR 0x7002 /* Card Status Register */
+#define BT3C_CSR_ANTENNA (1<<4)
+
+#define BT3C_TX_COUNT 0x7005 /* Tx fifo contents */
+#define BT3C_TX_FIFO 0x7080 /* Transmit Fifo */
+#define BT3C_RX_COUNT 0x7006 /* Rx fifo contents */
+#define BT3C_RX_FIFO 0x7480 /* Receive Fifo */
+#define BT3C_FIFO_SIZE 256
+
+/* IO Registers */
+#define BT3C_IOR_DATA_L 0x00 /* data low byte */
+#define BT3C_IOR_DATA_H 0x01 /* data high byte */
+#define BT3C_IOR_ADDR_L 0x02 /* address low byte */
+#define BT3C_IOR_ADDR_H 0x03 /* address high byte */
+#define BT3C_IOR_CNTL 0x04 /* control byte */
+#define BT3C_IOR_CNTL_BOOT (1<<6) /* Boot Card */
+#define BT3C_IOR_CNTL_INTR (1<<7) /* Interrupt Requested */
+#define BT3C_IOR_LEN 0x05
+
+static inline u_int16_t
+bt3c_get(struct bt3c_softc *sc)
+{
+u_int16_t data;
+
+ bus_space_barrier(sc->sc_pcioh.iot, sc->sc_pcioh.ioh, 0, BT3C_IOR_LEN, BUS_SPACE_BARRIER_READ);
+ data = bus_space_read_1(sc->sc_pcioh.iot, sc->sc_pcioh.ioh, BT3C_IOR_DATA_L);
+ data |= bus_space_read_1(sc->sc_pcioh.iot, sc->sc_pcioh.ioh, BT3C_IOR_DATA_H) << 8;
+
+ return data;
+}
+
+static inline void
+bt3c_put(struct bt3c_softc *sc, u_int16_t data)
+{
+ bus_space_barrier(sc->sc_pcioh.iot, sc->sc_pcioh.ioh, 0, BT3C_IOR_LEN, BUS_SPACE_BARRIER_WRITE);
+ bus_space_write_1(sc->sc_pcioh.iot, sc->sc_pcioh.ioh, BT3C_IOR_DATA_L, data & 0xff);
+ bus_space_write_1(sc->sc_pcioh.iot, sc->sc_pcioh.ioh, BT3C_IOR_DATA_H, (data >> 8) & 0xff);
+}
+
+static inline u_int8_t
+bt3c_read_control(struct bt3c_softc *sc)
+{
+ bus_space_barrier(sc->sc_pcioh.iot, sc->sc_pcioh.ioh, 0, BT3C_IOR_LEN, BUS_SPACE_BARRIER_READ);
+ return bus_space_read_1(sc->sc_pcioh.iot, sc->sc_pcioh.ioh, BT3C_IOR_CNTL);
+}
+
+static inline void
+bt3c_write_control(struct bt3c_softc *sc, u_int8_t data)
+{
+ bus_space_barrier(sc->sc_pcioh.iot, sc->sc_pcioh.ioh, 0, BT3C_IOR_LEN, BUS_SPACE_BARRIER_WRITE);
+ bus_space_write_1(sc->sc_pcioh.iot, sc->sc_pcioh.ioh, BT3C_IOR_CNTL, data);
+}
+
+static inline void
+bt3c_set_address(struct bt3c_softc *sc, u_int16_t addr)
+{
+ bus_space_barrier(sc->sc_pcioh.iot, sc->sc_pcioh.ioh, 0, BT3C_IOR_LEN, BUS_SPACE_BARRIER_WRITE);
+ bus_space_write_1(sc->sc_pcioh.iot, sc->sc_pcioh.ioh, BT3C_IOR_ADDR_L, addr & 0xff);
+ bus_space_write_1(sc->sc_pcioh.iot, sc->sc_pcioh.ioh, BT3C_IOR_ADDR_H, (addr >> 8) & 0xff);
+}
+
+static inline u_int16_t
+bt3c_read(struct bt3c_softc *sc, u_int16_t addr)
+{
+ bt3c_set_address(sc, addr);
+ return bt3c_get(sc);
+}
+
+static inline void
+bt3c_write(struct bt3c_softc *sc, u_int16_t addr, u_int16_t data)
+{
+ bt3c_set_address(sc, addr);
+ bt3c_put(sc, data);
+}
+
+/*
+ * receive incoming data from FIFO
+ * running at IPL_SERIAL
+ */
+static void
+bt3c_receive(struct bt3c_softc *sc)
+{
+ u_int16_t i, count;
+ u_int8_t data;
+
+ /* Receive data from the card */
+ count = bt3c_read(sc, BT3C_RX_COUNT);
+
+ bt3c_set_address(sc, BT3C_RX_FIFO);
+
+ for (i = 0 ; i < count ; i++) {
+ if (sc->sc_m == NULL) { /* Allocate new mbuf if needed */
+ MGETHDR(sc->sc_m, M_DONTWAIT, MT_DATA);
+ if (sc->sc_m == NULL) {
+ printf("%s: Could not get mbuf\n", sc->sc_dev.dv_xname);
+ break;
+ }
+
+ MCLGET(sc->sc_m, M_DONTWAIT);
+ if (!(sc->sc_m->m_flags & M_EXT)) {
+ m_free(sc->sc_m);
+ sc->sc_m = NULL;
+ printf("%s: Could not get mbuf cluster\n", sc->sc_dev.dv_xname);
+ break;
+ }
+
+ sc->sc_m->m_len = sc->sc_m->m_pkthdr.len = 0;
+ sc->sc_state = BT3C_RECV_PKT_ID;
+ sc->sc_want = 1; /* sizeof packet ID */
+ }
+
+ data = bt3c_get(sc);
+ if (sc->sc_m->m_len < MCLBYTES) {
+ /* store char in mbuf if we have room */
+ mtod(sc->sc_m, u_int8_t *)[sc->sc_m->m_len++] = data;
+ sc->sc_m->m_pkthdr.len ++;
+ }
+
+ sc->sc_want--;
+ if (sc->sc_want)
+ continue; /* wait for more */
+
+ switch (sc->sc_state) {
+ case BT3C_RECV_PKT_ID: /* Got packet ID */
+
+ switch (*mtod(sc->sc_m, u_int8_t *)) {
+ case BTHCI_PKTID_ACL_DATA:
+ sc->sc_state = BT3C_RECV_ACL_HDR;
+ sc->sc_want = BTHCI_ACL_DATA_MIN_LEN;
+ break;
+
+ case BTHCI_PKTID_SCO_DATA:
+ sc->sc_state = BT3C_RECV_SCO_HDR;
+ sc->sc_want = BTHCI_SCO_DATA_MIN_LEN;
+ break;
+
+ case BTHCI_PKTID_EVENT:
+ sc->sc_state = BT3C_RECV_EVENT_HDR;
+ sc->sc_want = BTHCI_EVENT_MIN_LEN;
+ break;
+
+ default:
+ printf("%s: Unknown packet type=%#x! (ignoring)\n", sc->sc_dev.dv_xname, *mtod(sc->sc_m, u_int8_t *));
+
+ m_free(sc->sc_m);
+ sc->sc_m = NULL;
+ break; /* XXX lost sync */
+ }
+
+ break;
+
+ case BT3C_RECV_ACL_HDR: /* Got ACL Header */
+ sc->sc_state = BT3C_RECV_PKT_DATA;
+ sc->sc_want = BTGETW(mtod(sc->sc_m, u_int8_t *) + 1 + BTHCI_ACL_DATA_LEN_OFFT);
+ break;
+
+ case BT3C_RECV_SCO_HDR: /* Got SCO Header */
+ sc->sc_state = BT3C_RECV_PKT_DATA;
+ sc->sc_want = mtod(sc->sc_m, u_int8_t *)[1 + BTHCI_SCO_DATA_LEN_OFFT];
+ break;
+
+ case BT3C_RECV_EVENT_HDR: /* Got Event Header */
+ sc->sc_state = BT3C_RECV_PKT_DATA;
+ sc->sc_want = mtod(sc->sc_m, u_int8_t *)[1 + BTHCI_EVENT_LEN_OFFT];
+ break;
+
+ case BT3C_RECV_PKT_DATA: /* Got packet data */
+ /* queue the packet */
+ /* XX should there be a limit to how many we queue? */
+ sc->sc_m->m_nextpkt = sc->sc_rxq;
+ sc->sc_rxq = sc->sc_m;
+ sc->sc_m = NULL;
+ break;
+
+ default:
+ panic("%s: invalid state %d!\n", sc->sc_dev.dv_xname, sc->sc_state);
+ }
+ }
+
+ bt3c_write(sc, BT3C_RX_COUNT, 0x0000);
+}
+
+/*
+ * transmit packet queue to device
+ * running at IPL_SERIAL
+ */
+static void
+bt3c_transmit(struct bt3c_softc *sc)
+{
+ struct mbuf *m = sc->sc_txq;
+ int count, rlen;
+ u_int8_t *rptr;
+
+ sc->sc_flags &= ~BT3C_XMIT;
+
+ if (m == NULL)
+ return;
+
+ bt3c_set_address(sc, BT3C_TX_FIFO);
+
+ count = rlen = 0;
+ rptr = mtod(m, u_int8_t *);
+ for(;;) {
+ if (rlen == m->m_len) {
+ sc->sc_txq = m->m_nextpkt;
+ m_free(m);
+ m = sc->sc_txq;
+ if (m) {
+ rlen = 0;
+ rptr = mtod(m, u_int8_t *);
+ continue;
+ } else {
+ break;
+ }
+ }
+ if (count == BT3C_FIFO_SIZE) {
+ m_adj(m, rlen);
+ break;
+ }
+ bt3c_put(sc, *rptr++);
+ rlen++;
+ count++;
+ }
+
+ if (count > 0) {
+ bt3c_write(sc, BT3C_TX_COUNT, count);
+ sc->sc_flags |= BT3C_XMIT;
+ }
+}
+
+/*
+ * hard interrupt routine
+ * running at IPL_SERIAL
+ */
+static int
+bt3c_hard_intr(void *arg)
+{
+ struct bt3c_softc *sc = arg;
+ u_int16_t control, isr;
+ int serviced = 0, needsoftint = 0;
+
+ control = bt3c_read_control(sc);
+ if (control & BT3C_IOR_CNTL_INTR) {
+ isr = bt3c_read(sc, BT3C_ISR); /* fetch Interrupt Status Register */
+ if ((isr & 0xff) == 0x7f) {
+ printf("%s: bt3c_hard_intr got strange ISR=%04x\n", sc->sc_dev.dv_xname, isr);
+ } else if ((isr & 0xff) != 0xff) {
+
+ if (isr & BT3C_ISR_RXRDY) { /* Receive complete */
+ bt3c_receive(sc);
+ if (sc->sc_rxq)
+ needsoftint = 1;
+ }
+
+ if (isr & BT3C_ISR_TXRDY) { /* Transmit complete */
+ bt3c_transmit(sc);
+ }
+
+ if (isr & BT3C_ISR_ANTENNA) { /* Antenna position changed */
+ if (bt3c_read(sc, BT3C_CSR) & BT3C_CSR_ANTENNA)
+ sc->sc_flags |= BT3C_ANTENNA;
+ else
+ sc->sc_flags &= ~BT3C_ANTENNA;
+ needsoftint = 1;
+ }
+
+ bt3c_write(sc, BT3C_ISR, 0x0000); /* clear Interrupt Status */
+ bt3c_write_control(sc, control); /* and let the card know */
+
+ /* record the reason and trigger the soft interrupt */
+ if (needsoftint) {
+ sc->sc_sw_isr |= isr;
+ softintr_schedule(sc->sc_soft);
+ }
+ serviced = 1;
+ }
+ }
+
+ return(serviced);
+}
+
+/*
+ * forward a (queue of) packet(s) to the HCI layer.
+ * running at IPL_SOFTSERIAL
+ */
+static void
+bt3c_forward(struct bt3c_softc *sc, struct mbuf *pkt)
+{
+ u_int8_t *buf;
+
+ /* list is backward, 'nextpkt' gets sent first */
+ if (pkt->m_nextpkt)
+ bt3c_forward(sc, pkt->m_nextpkt);
+
+ buf = mtod(pkt, u_int8_t *);
+ switch (buf[0]) {
+ case BTHCI_PKTID_ACL_DATA:
+ sc->sc_cb->bt_recvacldata(sc->sc_bthci, buf + 1, pkt->m_len - 1);
+ break;
+ case BTHCI_PKTID_SCO_DATA:
+ sc->sc_cb->bt_recvscodata(sc->sc_bthci, buf + 1, pkt->m_len - 1);
+ break;
+ case BTHCI_PKTID_EVENT:
+ sc->sc_cb->bt_recveventdata(sc->sc_bthci, buf + 1, pkt->m_len - 1);
+ break;
+ }
+ m_free(pkt);
+}
+
+/* soft interrupt routine
+ * running at IPL_SOFTSERIAL
+ */
+static void
+bt3c_soft_intr(void *arg)
+{
+ struct bt3c_softc *sc = arg;
+ u_int16_t isr;
+ int s;
+
+ s = splserial();
+ isr = sc->sc_sw_isr;
+ sc->sc_sw_isr = 0;
+ splx(s);
+
+ if (isr & BT3C_ISR_RXRDY) { /* Receive complete */
+ struct mbuf *rxq;
+
+ s = splserial();
+ rxq = sc->sc_rxq; /* empty the packet queue */
+ sc->sc_rxq = NULL;
+ splx(s);
+
+ if (rxq) bt3c_forward(sc, rxq);
+ }
+
+ if (isr & BT3C_ISR_TXRDY) { /* Transmit complete */
+ }
+
+ if (isr & BT3C_ISR_ANTENNA) { /* Antenna position changed */
+ printf("%s: Antenna %s\n", sc->sc_dev.dv_xname,
+ (sc->sc_flags & BT3C_ANTENNA) ? "Out" : "In");
+ }
+}
+
+/*
+ * write firmware blocks to the device
+ */
+static void
+bt3c_write_firmware(struct bt3c_softc *sc)
+{
+ struct bt3c_firmware_block *b = bt3c_firmware;
+ int i;
+
+ /* Reset */
+ bt3c_write(sc, 0x8040, 0x0404);
+ bt3c_write(sc, 0x8040, 0x0400);
+ DELAY(1);
+ bt3c_write(sc, 0x8040, 0x0404);
+ DELAY(17);
+
+ /* write the firmware */
+ while (b->len) {
+ bt3c_set_address(sc, b->addr);
+ for (i = 0 ; i < b->len ; i++)
+ bt3c_put(sc, b->data[i]);
+ b++;
+ }
+ DELAY(17);
+
+ /* Boot */
+ bt3c_set_address(sc, 0x3000);
+ bt3c_write_control(sc, (bt3c_read_control(sc) | BT3C_IOR_CNTL_BOOT));
+ DELAY(17);
+
+ /* Clear Registers */
+ bt3c_write(sc, BT3C_RX_COUNT, 0x0000);
+ bt3c_write(sc, BT3C_TX_COUNT, 0x0000);
+ bt3c_write(sc, BT3C_ISR, 0x0000);
+ DELAY(1000);
+}
+
+
+/**************************************************************************
+ *
+ * bthci callback routines
+ * see dev/bluetooth/bluetooth.h
+ */
+
+static int
+bt3c_open(h, flag, mode, p)
+ void *h;
+ int flag;
+ int mode;
+ struct proc *p;
+{
+ struct bt3c_softc *sc = h;
+ int err;
+
+ if (bt3c_firmware == NULL) {
+ printf("%s: no firmware\n", sc->sc_dev.dv_xname);
+ err = ENXIO;
+ goto bad;
+ }
+
+ if (pcmcia_function_enable(sc->sc_pf)) {
+ err = EIO;
+ goto bad;
+ }
+
+ bt3c_write_firmware(sc);
+
+ sc->sc_hard = pcmcia_intr_establish(sc->sc_pf, IPL_SERIAL, bt3c_hard_intr, sc);
+ if (sc->sc_hard == NULL) {
+ err = EIO;
+ goto bad1;
+ }
+
+ sc->sc_soft = softintr_establish(IPL_SOFTSERIAL, bt3c_soft_intr, sc);
+ if (sc->sc_soft == NULL) {
+ err = EIO;
+ goto bad2;
+ }
+
+ sc->sc_flags |= BT3C_OPEN;
+ return 0; /* ok */
+
+bad2:
+ pcmcia_intr_disestablish(sc->sc_pf, sc->sc_hard);
+ sc->sc_hard = NULL;
+bad1:
+ pcmcia_function_disable(sc->sc_pf);
+bad:
+ return err;
+}
+
+static int
+bt3c_close(h, flag, mode, p)
+ void *h;
+ int flag;
+ int mode;
+ struct proc *p;
+{
+ struct bt3c_softc *sc = h;
+
+ if (sc->sc_hard) {
+ pcmcia_intr_disestablish(sc->sc_pf, sc->sc_hard);
+ sc->sc_hard = NULL;
+ }
+
+ if (sc->sc_soft) {
+ softintr_disestablish(sc->sc_soft);
+ sc->sc_soft = NULL;
+ }
+
+ sc->sc_flags &= ~BT3C_OPEN;
+
+ pcmcia_function_disable(sc->sc_pf);
+
+ return 0;
+}
+
+static u_int8_t *
+bt3c_alloc_control(h, len, buf)
+ void *h;
+ size_t len;
+ struct btframe_buffer **buf;
+{
+// struct bt3c_softc *sc = h;
+ u_int8_t *p;
+
+ MALLOC(p, u_int8_t *, len + 1, M_TEMP, M_NOWAIT);
+ if (p) {
+ *p++ = BTHCI_PKTID_COMMAND;
+ *buf = (struct btframe_buffer *)p;
+ }
+
+ return p;
+}
+
+static u_int8_t *
+bt3c_alloc_acldata(h, len, buf)
+ void *h;
+ size_t len;
+ struct btframe_buffer **buf;
+{
+// struct bt3c_softc *sc = h;
+ u_int8_t *p;
+
+ MALLOC(p, u_int8_t *, len + 1, M_TEMP, M_NOWAIT);
+ if (p) {
+ *p++ = BTHCI_PKTID_ACL_DATA;
+ *buf = (struct btframe_buffer *)p;
+ }
+
+ return p;
+}
+
+static u_int8_t *
+bt3c_alloc_scodata(h, len, buf)
+ void *h;
+ size_t len;
+ struct btframe_buffer **buf;
+{
+// struct bt3c_softc *sc = h;
+ u_int8_t *p;
+
+ MALLOC(p, u_int8_t *, len + 1, M_TEMP, M_NOWAIT);
+ if (p) {
+ *p++ = BTHCI_PKTID_SCO_DATA;
+ *buf = (struct btframe_buffer *)p;
+ }
+
+ return p;
+}
+
+static int
+bt3c_send(h, buf, len)
+ void *h;
+ struct btframe_buffer *buf;
+ size_t len;
+{
+ struct bt3c_softc *sc = h;
+ u_int8_t *b = (u_int8_t *)buf;
+ struct mbuf **txq, *m;
+ int s;
+
+ if (sc->sc_flags & BT3C_DYING)
+ return(EIO);
+
+ /*
+ * allocate an mbuf, using our pre-allocated btframe_buffer for data area
+ */
+ MGETHDR(m, M_DONTWAIT, MT_DATA);
+ if (m == NULL)
+ return ENOMEM;
+
+ MEXTADD(m, b - 1, len + 1, M_TEMP, NULL, NULL);
+ m->m_len = m->m_pkthdr.len = len + 1;
+
+ /*
+ * tag it on the end of the queue, and trigger
+ * a transmit if necessary
+ */
+ s = splserial();
+ txq = &sc->sc_txq;
+ while (*txq)
+ txq = &(*txq)->m_nextpkt;
+
+ *txq = m;
+
+ if ((sc->sc_flags & BT3C_XMIT) == 0)
+ bt3c_transmit(sc);
+ splx(s);
+
+ return 0;
+}
+
+static int
+bt3c_splraise(void)
+{
+ /* to block callbacks on this device (in bt3c_forward) */
+ return splsoftserial();
+}
+
+
+/**************************************************************************
+ *
+ * bt3c PCMCIA autoconfig glue
+ */
+
+static int
+bt3c_match(parent, match, aux)
+ struct device *parent;
+ struct cfdata *match;
+ void *aux;
+{
+ struct pcmcia_attach_args *pa = aux;
+
+ if (pa->manufacturer == PCMCIA_VENDOR_3COM &&
+ pa->product == PCMCIA_PRODUCT_3COM_3CRWB6096)
+ return 10; /* 'com' also claims this, so trump them */
+
+ return 0;
+}
+
+static void
+bt3c_attach(parent, self, aux)
+ struct device *parent;
+ struct device *self;
+ void *aux;
+{
+ struct bt3c_softc *sc = (struct bt3c_softc *)self;
+ struct pcmcia_attach_args *pa = aux;
+ struct pcmcia_config_entry *cfe;
+ struct bt_attach_args bt;
+ char devinfo[256];
+
+ sc->sc_pf = pa->pf;
+
+ pcmcia_devinfo(&pa->pf->sc->card, 0, devinfo, sizeof(devinfo));
+ printf(": %s\n", devinfo);
+
+ /* Find a PCMCIA config entry we can use */
+ SIMPLEQ_FOREACH(cfe, &pa->pf->cfe_head, cfe_list) {
+ if (cfe->num_memspace != 0)
+ continue;
+
+ if (cfe->num_iospace != 1)
+ continue;
+
+ if (pcmcia_io_alloc(pa->pf, cfe->iospace[0].start, cfe->iospace[0].length, 0, &sc->sc_pcioh) == 0)
+ break;
+ }
+
+ if (cfe == 0) {
+ printf("bt3c_attach: cannot allocate io space\n");
+ goto no_config_entry;
+ }
+
+ /* Map in the io space */
+ if (pcmcia_io_map(pa->pf, PCMCIA_WIDTH_AUTO, 0, sc->sc_pcioh.size, &sc->sc_pcioh, &sc->sc_iow)) {
+ printf("bt3c_attach: cannot map io space\n");
+ goto iomap_failed;
+ }
+
+ /* Enable the card */
+ pcmcia_function_init(pa->pf, cfe);
+ if (pcmcia_function_enable(pa->pf)) {
+ printf("bt3c_attach: function enable failed\n");
+ goto enable_failed;
+ }
+
+ /* configure the bthci device */
+ bt.bt_methods = &bt3c_methods;
+ bt.bt_cb = &sc->sc_cb;
+ bt.bt_handle = sc;
+ sc->sc_bthci = config_found_sm(self, &bt, bt_print, NULL);
+
+ /* establish a power change hook */
+ sc->sc_powerhook = powerhook_establish(bt3c_power, sc);
+
+ /* and shut down the device for now */
+ pcmcia_function_disable(pa->pf);
+ return;
+
+enable_failed:
+ /* unmap io window */
+ pcmcia_io_unmap(pa->pf, sc->sc_iow);
+
+iomap_failed:
+ /* unmap io space */
+ pcmcia_io_free(pa->pf, &sc->sc_pcioh);
+
+no_config_entry:
+ sc->sc_iow = -1;
+}
+
+static int
+bt3c_detach(self, flags)
+ struct device *self;
+ int flags;
+{
+ struct bt3c_softc *sc = (struct bt3c_softc *)self;
+
+ sc->sc_flags |= BT3C_DYING;
+
+ if (sc->sc_hard)
+ pcmcia_intr_disestablish(sc->sc_pf, sc->sc_hard);
+
+ if (sc->sc_soft)
+ softintr_disestablish(sc->sc_soft);
+
+ if (sc->sc_powerhook)
+ powerhook_disestablish(sc->sc_powerhook);
+
+ if (sc->sc_bthci)
+ config_detach(sc->sc_bthci, flags);
+
+ if (sc->sc_iow != -1) {
+ pcmcia_io_unmap(sc->sc_pf, sc->sc_iow);
+ pcmcia_io_free(sc->sc_pf, &sc->sc_pcioh);
+ }
+
+ return (0);
+}
+
+static int
+bt3c_activate(self, act)
+ struct device *self;
+ enum devact act;
+{
+ struct bt3c_softc *sc = (struct bt3c_softc *)self;
+ int rv = 0;
+
+ switch(act) {
+ case DVACT_ACTIVATE:
+ rv = EOPNOTSUPP;
+ printf("bt3c_activate: activate\n");
+ break;
+
+ case DVACT_DEACTIVATE:
+ sc->sc_flags |= BT3C_DYING;
+ if (sc->sc_bthci)
+ rv = config_deactivate(sc->sc_bthci);
+ break;
+ }
+
+ return(rv);
+}
+
+static void
+bt3c_power(why, arg)
+ int why;
+ void *arg;
+{
+ struct bt3c_softc *sc = arg;
+
+ switch(why) {
+ case PWR_SUSPEND:
+ case PWR_STANDBY:
+ if (sc->sc_flags & BT3C_OPEN) {
+ printf("%s: sleeping\n", sc->sc_dev.dv_xname);
+ pcmcia_function_disable(sc->sc_pf);
+ }
+ break;
+
+ case PWR_RESUME:
+ if (sc->sc_flags & BT3C_OPEN) {
+ printf("%s: waking up\n", sc->sc_dev.dv_xname);
+ pcmcia_function_enable(sc->sc_pf);
+ bt3c_write_firmware(sc);
+ }
+ break;
+
+ case PWR_SOFTSUSPEND:
+ case PWR_SOFTSTANDBY:
+ case PWR_SOFTRESUME:
+ break;
+ }
+}
--- /dev/null 2005-04-22 10:38:10.000000000 +0100
+++ sys/lkm/misc/bt3cpcc/Makefile 2005-03-11 10:57:56.000000000 +0000
@@ -0,0 +1,37 @@
+# $NetBSD: Makefile$
+#
+# The firmware for the 3COM Bluetooth PC Card cannot be distributed with
+# the driver for copyright reasons. The user is assumed to use the one
+# delivered with the card on CD, or download the newest version from
+# http://www.3com.com
+#
+# Use the command
+#
+# make BT3CPCC=<path to firmware file>
+#
+# to generate a kernel module containing the firmware which you can then
+# load for the device driver to find.
+#
+# If the BT3CPCC.bin file does not exist, the Makefile will generate
+# a empty byte array and the module will load but the device wont
+# actually work
+
+BT3CPCC?= BT3CPCC.bin
+
+.NOPATH: ${BT3CPCC}
+
+.if !exists(${BT3CPCC})
+BT3CPCC= /dev/null
+.endif
+
+KMOD= bt3cpcc
+SRCS= lkminit_bt3cpcc.c
+CLEANFILES+= bt3cpcc.h
+
+bt3cpcc.h: ${BT3CPCC}
+ @rm -f bt3cpcc.h
+ ${AWK} -f ./bt3cpcc.awk ${BT3CPCC}
+
+lkminit_bt3cpcc.c: bt3cpcc.h
+
+.include <bsd.kmod.mk>
--- /dev/null 2005-04-22 10:38:10.000000000 +0100
+++ sys/lkm/misc/bt3cpcc/bt3cpcc.awk 2005-03-11 11:07:25.000000000 +0000
@@ -0,0 +1,73 @@
+#! /usr/bin/awk -f
+#
+# Copyright (c) 2005 Iain D. Hibbert,
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+# 3. The name of the author may not be used to endorse or promote products
+# derived from this software without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+# IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+# OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+# IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+# NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+# THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#
+# Convert the 3Com Bluetooth PC Card firmware file into C source to
+# compile into our module. The file is ASCII and lines are of the
+# form:
+#
+# S<type><num><addr><word1><word2> ... <wordN><cksum>
+#
+
+function hexdigit(c) {
+ return index("0123456789ABCDEF", toupper(c)) - 1
+}
+
+function hexbyte(s, i) {
+ return (hexdigit(substr(s, i, 1)) * 16) + hexdigit(substr(s, i + 1, 1))
+}
+
+BEGIN {
+ header = "bt3cpcc.h"
+ printf "static struct bt3c_firmware_block bt3cpcc[] = {\n" > header
+}
+
+substr($0, 0, 2) == "S3" {
+ sum = 0
+ for( i = 3 ; i < length($0) ; i = i + 2 )
+ sum = (sum + hexbyte($0, i)) % 256
+ if( sum != 255 ) {
+ printf "ERROR checksum 0x%x invalid\n", sum
+ exit 1
+ }
+
+ len = (hexbyte($0, 3) - 5) / 2
+ if( len > 15 ) {
+ printf "ERROR block length %d too large\n", len
+ exit 1
+ }
+ printf "\t{ 0x%s, %2d, ", substr($0, 9, 4), len > header
+ for( i = 0 ; i < len ; i++ ) {
+ printf "%s 0x%s", (i == 0 ? "{" : ","), substr($0, 13 + 4 * i, 4) > header
+ }
+ printf " } },\n" > header
+}
+
+END {
+ printf "\t{ 0x0000, 0 }\n" > header
+ printf "};\n" > header
+};
--- /dev/null 2005-04-22 10:38:10.000000000 +0100
+++ sys/lkm/misc/bt3cpcc/lkminit_bt3cpcc.c 2005-03-11 10:44:22.000000000 +0000
@@ -0,0 +1,104 @@
+/* $NetBSD: lkminit_bt3cpcc.c$ */
+
+/*
+ * Copyright (c) 2005 Iain D. Hibbert,
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ */
+
+/*
+ * The firmware for the 3COM Bluetooth PC Card cannot be distributed with
+ * NetBSD for copyright reasons. The user is assumed to use the one
+ * delivered with the card on CD, or download the newest version from
+ * http://www.3com.com
+ *
+ * use 'make BT3CPCC=<path to firmware file>' to generate a kernel module
+ * containing the firmware which you can then load for the device driver
+ * to find. If the firmware file does not exist, an empty byte array will
+ * be generated.
+ */
+
+#include <sys/cdefs.h>
+__KERNEL_RCSID(0, "$NetBSD: lkminit_bt3cpcc.c$");
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/lkm.h>
+#include <sys/errno.h>
+
+#include <dev/pcmcia/bt3cfw.h>
+#include "bt3cpcc.h"
+
+int bt3cpcc_lkmentry __P((struct lkm_table *lkmtp, int, int));
+static int bt3cpcc_handle __P((struct lkm_table *, int));
+
+MOD_MISC("bt3cpcc");
+
+static int
+bt3cpcc_handle(lkmtp, cmd)
+ struct lkm_table *lkmtp;
+ int cmd;
+{
+ int err = 0;
+
+ switch(cmd) {
+ case LKM_E_LOAD:
+
+ /*
+ * We only need to patch our firmware block list into
+ * the pointer defined with the device driver.
+ * Don't do it twice.
+ */
+ if (bt3c_firmware)
+ err = EEXIST;
+ else
+ bt3c_firmware = bt3cpcc;
+ break;
+
+ case LKM_E_UNLOAD:
+
+ bt3c_firmware = NULL;
+ break;
+
+ default: /* we only understand load/unload */
+
+ err = EINVAL;
+ break;
+ }
+
+ return(err);
+}
+
+
+/*
+ * External entry point
+ */
+int
+bt3cpcc_lkmentry(lkmtp, cmd, ver)
+ struct lkm_table *lkmtp;
+ int cmd, ver;
+{
+ DISPATCH(lkmtp, cmd, ver, bt3cpcc_handle, bt3cpcc_handle, lkm_nofunc)
+}
--- /dev/null 2005-04-22 10:38:10.000000000 +0100
+++ share/man/man4/bt3c.4 2005-03-16 12:58:32.000000000 +0000
@@ -0,0 +1,76 @@
+.\" $NetBSD: bt3c.4 $
+.\"
+.\" Copyright (c) 2005 The NetBSD Foundation, Inc.
+.\" All rights reserved.
+.\"
+.\" This code is derived from software contributed to The NetBSD Foundation
+.\" by Iain Hibbert.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. All advertising materials mentioning features or use of this software
+.\" must display the following acknowledgement:
+.\" This product includes software developed by the NetBSD
+.\" Foundation, Inc. and its contributors.
+.\" 4. Neither the name of The NetBSD Foundation nor the names of its
+.\" contributors may be used to endorse or promote products derived
+.\" from this software without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+.\" ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+.\" TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+.\" PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
+.\" BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+.\" CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+.\" SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+.\" INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+.\" CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+.\" ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+.\" POSSIBILITY OF SUCH DAMAGE.
+.\"
+.Dd Feb 28, 2005
+.Dt BT3C 4
+.Os
+.Sh NAME
+.Nm bt3c
+.Nd 3Com Bluetooth PC Card
+.Sh SYNOPSIS
+.Cd "bt3c* at pcmcia? function ?
+.Cd "bthci* at bt3c?
+.Sh DESCRIPTION
+The
+.Nm
+driver provides support for the 3Com Bluetooth PC Card, model 3CRWB6096, to
+the bthci device layer.
+.Sh FIRMWARE
+This card needs firmware loaded before it will work. Due to copyright restrictions
+we cannot distribute the firmware with NetBSD, but if you have the card then you
+should have received a CD with the drivers on, or you may download the latest
+version from the 3Com website. Extract the archive and find the firmware file
+called
+.Nm "BT3CPCC.bin" .
+Now execute the command:
+.Bd -literal -offset indent
+make BT3CPCC=<path to firmware file>
+.Ed
+.Pp
+in the
+.Nm "sys/lkm/misc/bt3cpcc"
+directory to generate a kernel module which will attach the firmware to the
+device driver.
+.Sh SEE ALSO
+.Xr bthci 4 ,
+.Xr pcmcia 4 ,
+.Xr lkm 4
+.Sh HISTORY
+This
+.Nm
+device driver was written by Iain D. Hibbert, with generous reference to the
+FreeBSD and BlueZ drivers for the same card.
+.Sh BUGS
--- /usr/src/share/man/man4/Makefile 2004-08-30 10:46:53.000000000 +0100
+++ share/man/man4/Makefile 2005-03-01 20:50:00.000000000 +0000
@@ -75,7 +75,7 @@
wds.4 we.4 wss.4 wt.4
# machine-independent PCMCIA devices
-MAN+= pcic.4 tcic.4 pcmcom.4 xi.4
+MAN+= bt3c.4 pcic.4 tcic.4 pcmcom.4 xi.4
# machine-independent obio (mac68k and macppc) devices
MAN+= adb.4 akbd.4 ams.4 mc.4