summaryrefslogtreecommitdiffstats
path: root/sys/netgraph/bluetooth/drivers
diff options
context:
space:
mode:
authorjulian <julian@FreeBSD.org>2002-11-20 23:01:59 +0000
committerjulian <julian@FreeBSD.org>2002-11-20 23:01:59 +0000
commitd72cb748a8219735d80767b8847995b1ff103f9a (patch)
treec38e3c48c390af16003a1f451848e93558978422 /sys/netgraph/bluetooth/drivers
parent97980d0d57dd5c2944c5f3306726610a3a1b6e51 (diff)
downloadFreeBSD-src-d72cb748a8219735d80767b8847995b1ff103f9a.zip
FreeBSD-src-d72cb748a8219735d80767b8847995b1ff103f9a.tar.gz
The second try a committing the bluetooth code
Has been seen to work on several cards and communicating with several mobile phones to use them as modems etc. We are still talking with 3com to try get them to allow us to include the firmware for their pccard in the driver but the driver is here.. In the mean time it can be downloaded from the 3com website and loaded using the utility bt3cfw(8) (supplied) (instructions in the man page) Not yet linked to the build Submitted by: Maksim Yevmenkin <myevmenk@exodus.net> Approved by: re
Diffstat (limited to 'sys/netgraph/bluetooth/drivers')
-rw-r--r--sys/netgraph/bluetooth/drivers/bt3c/ng_bt3c_pccard.c1247
-rw-r--r--sys/netgraph/bluetooth/drivers/bt3c/ng_bt3c_var.h103
-rw-r--r--sys/netgraph/bluetooth/drivers/h4/TODO13
-rw-r--r--sys/netgraph/bluetooth/drivers/h4/ng_h4.c1059
-rw-r--r--sys/netgraph/bluetooth/drivers/h4/ng_h4_prse.h122
-rw-r--r--sys/netgraph/bluetooth/drivers/h4/ng_h4_var.h100
-rw-r--r--sys/netgraph/bluetooth/drivers/ubt/TODO30
-rw-r--r--sys/netgraph/bluetooth/drivers/ubt/ng_ubt.c2166
-rw-r--r--sys/netgraph/bluetooth/drivers/ubt/ng_ubt_var.h143
9 files changed, 4983 insertions, 0 deletions
diff --git a/sys/netgraph/bluetooth/drivers/bt3c/ng_bt3c_pccard.c b/sys/netgraph/bluetooth/drivers/bt3c/ng_bt3c_pccard.c
new file mode 100644
index 0000000..443c18a
--- /dev/null
+++ b/sys/netgraph/bluetooth/drivers/bt3c/ng_bt3c_pccard.c
@@ -0,0 +1,1247 @@
+/*
+ * ng_bt3c_pccard.c
+ *
+ * Copyright (c) 2001-2002 Maksim Yevmenkin <m_evmenkin@yahoo.com>
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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.
+ *
+ * $Id: ng_bt3c_pccard.c,v 1.2 2002/11/12 00:51:45 max Exp $
+ * $FreeBSD$
+ *
+ * XXX XXX XX XXX XXX XXX XXX XXX XXX XXX XXX XXX XXX XXX XXX XXX XXX XXX XXX
+ *
+ * Based on information obrained from: Jose Orlando Pereira <jop@di.uminho.pt>
+ * and disassembled w2k driver.
+ *
+ * XXX XXX XX XXX XXX XXX XXX XXX XXX XXX XXX XXX XXX XXX XXX XXX XXX XXX XXX
+ *
+ */
+
+#include <sys/param.h>
+#include <sys/systm.h>
+
+#include <sys/bus.h>
+#include <machine/bus.h>
+
+#include <sys/conf.h>
+#include <sys/endian.h>
+#include <sys/interrupt.h>
+#include <sys/kernel.h>
+#include <sys/mbuf.h>
+#include <sys/module.h>
+
+#include <machine/resource.h>
+#include <sys/rman.h>
+
+#include <sys/socket.h>
+#include <net/if.h>
+#include <net/if_var.h>
+
+#include <dev/pccard/pccardreg.h>
+#include <dev/pccard/pccardvar.h>
+#include <dev/pccard/pccarddevs.h>
+
+#include <netgraph/ng_message.h>
+#include <netgraph/netgraph.h>
+#include <netgraph/ng_parse.h>
+#include <ng_bluetooth.h>
+#include <ng_hci.h>
+#include "ng_bt3c.h"
+#include "ng_bt3c_var.h"
+
+/* Netgraph methods */
+static ng_constructor_t ng_bt3c_constructor;
+static ng_shutdown_t ng_bt3c_shutdown;
+static ng_newhook_t ng_bt3c_newhook;
+static ng_connect_t ng_bt3c_connect;
+static ng_disconnect_t ng_bt3c_disconnect;
+static ng_rcvmsg_t ng_bt3c_rcvmsg;
+static ng_rcvdata_t ng_bt3c_rcvdata;
+
+/* PCMCIA driver methods */
+static int bt3c_pccard_match (device_t);
+static int bt3c_pccard_probe (device_t);
+static int bt3c_pccard_attach (device_t);
+static int bt3c_pccard_detach (device_t);
+
+static void bt3c_intr (void *);
+static void bt3c_receive (bt3c_softc_p);
+static int bt3c_append (struct mbuf *, int);
+
+static void bt3c_swi_intr (void *);
+static void bt3c_forward (node_p, hook_p, void *, int);
+static void bt3c_send (node_p, hook_p, void *, int);
+
+static void bt3c_download_firmware (bt3c_softc_p, char const *, int);
+
+#define bt3c_set_address(sc, address) \
+do { \
+ outb(rman_get_start((sc)->iobase) + BT3C_ADDR_L, ((address) & 0xff)); \
+ outb(rman_get_start((sc)->iobase) + BT3C_ADDR_H, (((address) >> 8) & 0xff)); \
+} while (0)
+
+#define bt3c_read_data(sc, data) \
+do { \
+ (data) = inb(rman_get_start((sc)->iobase) + BT3C_DATA_L); \
+ (data) |= ((inb(rman_get_start((sc)->iobase) + BT3C_DATA_H) & 0xff) << 8); \
+} while (0)
+
+#define bt3c_write_data(sc, data) \
+do { \
+ outb(rman_get_start((sc)->iobase) + BT3C_DATA_L, ((data) & 0xff)); \
+ outb(rman_get_start((sc)->iobase) + BT3C_DATA_H, (((data) >> 8) & 0xff)); \
+} while (0)
+
+#define bt3c_read_control(sc, data) \
+do { \
+ (data) = inb(rman_get_start((sc)->iobase) + BT3C_CONTROL); \
+} while (0)
+
+#define bt3c_write_control(sc, data) \
+do { \
+ outb(rman_get_start((sc)->iobase) + BT3C_CONTROL, (data)); \
+} while (0)
+
+#define bt3c_read(sc, address, data) \
+do { \
+ bt3c_set_address((sc), (address)); \
+ bt3c_read_data((sc), (data)); \
+} while(0)
+
+#define bt3c_write(sc, address, data) \
+do { \
+ bt3c_set_address((sc), (address)); \
+ bt3c_write_data((sc), (data)); \
+} while(0)
+
+static MALLOC_DEFINE(M_BT3C, "bt3c", "bt3c data structures");
+
+/****************************************************************************
+ ****************************************************************************
+ ** Netgraph specific
+ ****************************************************************************
+ ****************************************************************************/
+
+/*
+ * Netgraph node type
+ */
+
+/* Queue length */
+static const struct ng_parse_struct_field ng_bt3c_node_qlen_type_fields[] =
+{
+ { "queue", &ng_parse_int32_type, },
+ { "qlen", &ng_parse_int32_type, },
+ { NULL, }
+};
+static const struct ng_parse_type ng_bt3c_node_qlen_type = {
+ &ng_parse_struct_type,
+ &ng_bt3c_node_qlen_type_fields
+};
+
+/* Stat info */
+static const struct ng_parse_struct_field ng_bt3c_node_stat_type_fields[] =
+{
+ { "pckts_recv", &ng_parse_uint32_type, },
+ { "bytes_recv", &ng_parse_uint32_type, },
+ { "pckts_sent", &ng_parse_uint32_type, },
+ { "bytes_sent", &ng_parse_uint32_type, },
+ { "oerrors", &ng_parse_uint32_type, },
+ { "ierrors", &ng_parse_uint32_type, },
+ { NULL, }
+};
+static const struct ng_parse_type ng_bt3c_node_stat_type = {
+ &ng_parse_struct_type,
+ &ng_bt3c_node_stat_type_fields
+};
+
+static const struct ng_cmdlist ng_bt3c_cmdlist[] = {
+{
+ NGM_BT3C_COOKIE,
+ NGM_BT3C_NODE_GET_STATE,
+ "get_state",
+ NULL,
+ &ng_parse_uint16_type
+},
+{
+ NGM_BT3C_COOKIE,
+ NGM_BT3C_NODE_SET_DEBUG,
+ "set_debug",
+ &ng_parse_uint16_type,
+ NULL
+},
+{
+ NGM_BT3C_COOKIE,
+ NGM_BT3C_NODE_GET_DEBUG,
+ "get_debug",
+ NULL,
+ &ng_parse_uint16_type
+},
+{
+ NGM_BT3C_COOKIE,
+ NGM_BT3C_NODE_GET_QLEN,
+ "get_qlen",
+ NULL,
+ &ng_bt3c_node_qlen_type
+},
+{
+ NGM_BT3C_COOKIE,
+ NGM_BT3C_NODE_SET_QLEN,
+ "set_qlen",
+ &ng_bt3c_node_qlen_type,
+ NULL
+},
+{
+ NGM_BT3C_COOKIE,
+ NGM_BT3C_NODE_GET_STAT,
+ "get_stat",
+ NULL,
+ &ng_bt3c_node_stat_type
+},
+{
+ NGM_BT3C_COOKIE,
+ NGM_BT3C_NODE_RESET_STAT,
+ "reset_stat",
+ NULL,
+ NULL
+},
+{ 0, }
+};
+
+static struct ng_type typestruct = {
+ NG_ABI_VERSION,
+ NG_BT3C_NODE_TYPE, /* typename */
+ NULL, /* modevent */
+ ng_bt3c_constructor, /* constructor */
+ ng_bt3c_rcvmsg, /* control message */
+ ng_bt3c_shutdown, /* destructor */
+ ng_bt3c_newhook, /* new hook */
+ NULL, /* find hook */
+ ng_bt3c_connect, /* connect hook */
+ ng_bt3c_rcvdata, /* data */
+ ng_bt3c_disconnect, /* disconnect hook */
+ ng_bt3c_cmdlist /* node command list */
+};
+NETGRAPH_INIT(bt3c, &typestruct);
+MODULE_VERSION(ng_bt3c, NG_BLUETOOTH_VERSION);
+
+/*
+ * Netgraph node constructor. Do not allow to create node of this type.
+ */
+
+static int
+ng_bt3c_constructor(node_p node)
+{
+ return (EINVAL);
+} /* ng_bt3c_constructor */
+
+/*
+ * Netgraph node destructor. Destroy node only when device has been detached
+ */
+
+static int
+ng_bt3c_shutdown(node_p node)
+{
+ bt3c_softc_p sc = (bt3c_softc_p) NG_NODE_PRIVATE(node);
+
+ /* Let old node go */
+ NG_NODE_SET_PRIVATE(node, NULL);
+ NG_NODE_UNREF(node);
+
+ /* Create new fresh one if we are not going down */
+ if (sc == NULL)
+ goto out;
+
+ /* Create new Netgraph node */
+ if (ng_make_node_common(&typestruct, &sc->node) != 0) {
+ device_printf(sc->dev, "Could not create Netgraph node\n");
+ sc->node = NULL;
+ goto out;
+ }
+
+ /* Name new Netgraph node */
+ if (ng_name_node(sc->node, device_get_nameunit(sc->dev)) != 0) {
+ device_printf(sc->dev, "Could not name Netgraph node\n");
+ NG_NODE_UNREF(sc->node);
+ sc->node = NULL;
+ goto out;
+ }
+
+ NG_NODE_SET_PRIVATE(sc->node, sc);
+out:
+ return (0);
+} /* ng_bt3c_shutdown */
+
+/*
+ * Create new hook. There can only be one.
+ */
+
+static int
+ng_bt3c_newhook(node_p node, hook_p hook, char const *name)
+{
+ bt3c_softc_p sc = (bt3c_softc_p) NG_NODE_PRIVATE(node);
+
+ if (strcmp(name, NG_BT3C_HOOK) != 0)
+ return (EINVAL);
+
+ if (sc->hook != NULL)
+ return (EISCONN);
+
+ sc->hook = hook;
+
+ return (0);
+} /* ng_bt3c_newhook */
+
+/*
+ * Connect hook. Say YEP, that's OK with me.
+ */
+
+static int
+ng_bt3c_connect(hook_p hook)
+{
+ bt3c_softc_p sc = (bt3c_softc_p) NG_NODE_PRIVATE(NG_HOOK_NODE(hook));
+
+ if (hook != sc->hook) {
+ sc->hook = NULL;
+ return (EINVAL);
+ }
+
+ /* set the hook into queueing mode (for incoming (from wire) packets) */
+ NG_HOOK_FORCE_QUEUE(NG_HOOK_PEER(hook));
+
+ return (0);
+} /* ng_bt3c_connect */
+
+/*
+ * Disconnect hook
+ */
+
+static int
+ng_bt3c_disconnect(hook_p hook)
+{
+ bt3c_softc_p sc = (bt3c_softc_p) NG_NODE_PRIVATE(NG_HOOK_NODE(hook));
+
+ /*
+ * We need to check for sc != NULL because we can be called from
+ * bt3c_pccard_detach() via ng_rmnode_self()
+ */
+
+ if (sc != NULL) {
+ if (hook != sc->hook)
+ return (EINVAL);
+
+ IF_DRAIN(&sc->inq);
+ IF_DRAIN(&sc->outq);
+
+ sc->hook = NULL;
+ }
+
+ return (0);
+} /* ng_bt3c_disconnect */
+
+/*
+ * Process control message
+ */
+
+static int
+ng_bt3c_rcvmsg(node_p node, item_p item, hook_p lasthook)
+{
+ bt3c_softc_p sc = (bt3c_softc_p) NG_NODE_PRIVATE(node);
+ struct ng_mesg *msg = NULL, *rsp = NULL;
+ int error = 0;
+
+ if (sc == NULL) {
+ NG_FREE_ITEM(item);
+ return (EHOSTDOWN);
+ }
+
+ NGI_GET_MSG(item, msg);
+
+ switch (msg->header.typecookie) {
+ case NGM_GENERIC_COOKIE:
+ switch (msg->header.cmd) {
+ case NGM_TEXT_STATUS:
+ NG_MKRESPONSE(rsp, msg, NG_TEXTRESPONSE, M_NOWAIT);
+ if (rsp == NULL)
+ error = ENOMEM;
+ else
+ snprintf(rsp->data, NG_TEXTRESPONSE,
+ "Hook: %s\n" \
+ "Flags: %#x\n" \
+ "Debug: %d\n" \
+ "State: %d\n" \
+ "IncmQ: [len:%d,max:%d]\n" \
+ "OutgQ: [len:%d,max:%d]\n",
+ (sc->hook != NULL)? NG_BT3C_HOOK : "",
+ sc->flags,
+ sc->debug,
+ sc->state,
+ _IF_QLEN(&sc->inq), /* XXX */
+ sc->inq.ifq_maxlen, /* XXX */
+ _IF_QLEN(&sc->outq), /* XXX */
+ sc->outq.ifq_maxlen /* XXX */
+ );
+ break;
+
+ default:
+ error = EINVAL;
+ break;
+ }
+ break;
+
+ case NGM_BT3C_COOKIE:
+ switch (msg->header.cmd) {
+ case NGM_BT3C_NODE_GET_STATE:
+ NG_MKRESPONSE(rsp, msg, sizeof(ng_bt3c_node_state_ep),
+ M_NOWAIT);
+ if (rsp == NULL)
+ error = ENOMEM;
+ else
+ *((ng_bt3c_node_state_ep *)(rsp->data)) =
+ sc->state;
+ break;
+
+ case NGM_BT3C_NODE_SET_DEBUG:
+ if (msg->header.arglen != sizeof(ng_bt3c_node_debug_ep))
+ error = EMSGSIZE;
+ else
+ sc->debug =
+ *((ng_bt3c_node_debug_ep *)(msg->data));
+ break;
+
+ case NGM_BT3C_NODE_GET_DEBUG:
+ NG_MKRESPONSE(rsp, msg, sizeof(ng_bt3c_node_debug_ep),
+ M_NOWAIT);
+ if (rsp == NULL)
+ error = ENOMEM;
+ else
+ *((ng_bt3c_node_debug_ep *)(rsp->data)) =
+ sc->debug;
+ break;
+
+ case NGM_BT3C_NODE_GET_QLEN:
+ NG_MKRESPONSE(rsp, msg, sizeof(ng_bt3c_node_qlen_ep),
+ M_NOWAIT);
+ if (rsp == NULL) {
+ error = ENOMEM;
+ break;
+ }
+
+ switch (((ng_bt3c_node_qlen_ep *)(msg->data))->queue) {
+ case NGM_BT3C_NODE_IN_QUEUE:
+ ((ng_bt3c_node_qlen_ep *)(rsp->data))->queue =
+ NGM_BT3C_NODE_IN_QUEUE;
+ ((ng_bt3c_node_qlen_ep *)(rsp->data))->qlen =
+ sc->inq.ifq_maxlen;
+ break;
+
+ case NGM_BT3C_NODE_OUT_QUEUE:
+ ((ng_bt3c_node_qlen_ep *)(rsp->data))->queue =
+ NGM_BT3C_NODE_OUT_QUEUE;
+ ((ng_bt3c_node_qlen_ep *)(rsp->data))->qlen =
+ sc->outq.ifq_maxlen;
+ break;
+
+ default:
+ NG_FREE_MSG(rsp);
+ error = EINVAL;
+ break;
+ }
+ break;
+
+ case NGM_BT3C_NODE_SET_QLEN:
+ if (msg->header.arglen != sizeof(ng_bt3c_node_qlen_ep)){
+ error = EMSGSIZE;
+ break;
+ }
+
+ if (((ng_bt3c_node_qlen_ep *)(msg->data))->qlen <= 0) {
+ error = EINVAL;
+ break;
+ }
+
+ switch (((ng_bt3c_node_qlen_ep *)(msg->data))->queue) {
+ case NGM_BT3C_NODE_IN_QUEUE:
+ sc->inq.ifq_maxlen = ((ng_bt3c_node_qlen_ep *)
+ (msg->data))->qlen; /* XXX */
+ break;
+
+ case NGM_BT3C_NODE_OUT_QUEUE:
+ sc->outq.ifq_maxlen = ((ng_bt3c_node_qlen_ep *)
+ (msg->data))->qlen; /* XXX */
+ break;
+
+ default:
+ error = EINVAL;
+ break;
+ }
+ break;
+
+ case NGM_BT3C_NODE_GET_STAT:
+ NG_MKRESPONSE(rsp, msg, sizeof(ng_bt3c_node_stat_ep),
+ M_NOWAIT);
+ if (rsp == NULL)
+ error = ENOMEM;
+ else
+ bcopy(&sc->stat, rsp->data,
+ sizeof(ng_bt3c_node_stat_ep));
+ break;
+
+ case NGM_BT3C_NODE_RESET_STAT:
+ NG_BT3C_STAT_RESET(sc->stat);
+ break;
+
+ case NGM_BT3C_NODE_DOWNLOAD_FIRMWARE:
+ if (msg->header.arglen <
+ sizeof(ng_bt3c_firmware_block_ep))
+ error = EMSGSIZE;
+ else
+ bt3c_download_firmware(sc, msg->data,
+ msg->header.arglen);
+ break;
+
+ default:
+ error = EINVAL;
+ break;
+ }
+ break;
+
+ default:
+ error = EINVAL;
+ break;
+ }
+
+ NG_RESPOND_MSG(error, node, item, rsp);
+ NG_FREE_MSG(msg);
+
+ return (error);
+} /* ng_bt3c_rcvmsg */
+
+/*
+ * Process data
+ */
+
+static int
+ng_bt3c_rcvdata(hook_p hook, item_p item)
+{
+ bt3c_softc_p sc = (bt3c_softc_p)NG_NODE_PRIVATE(NG_HOOK_NODE(hook));
+ struct mbuf *m = NULL;
+ int error = 0;
+
+ if (sc == NULL) {
+ error = EHOSTDOWN;
+ goto out;
+ }
+
+ if (hook != sc->hook) {
+ error = EINVAL;
+ goto out;
+ }
+
+ NGI_GET_M(item, m);
+
+ IF_LOCK(&sc->outq);
+ if (_IF_QFULL(&sc->outq)) {
+ NG_BT3C_ERR(sc->dev,
+"Outgoing queue is full. Dropping mbuf, len=%d\n", m->m_pkthdr.len);
+
+ _IF_DROP(&sc->outq);
+ NG_BT3C_STAT_OERROR(sc->stat);
+
+ NG_FREE_M(m);
+ } else
+ _IF_ENQUEUE(&sc->outq, m);
+ IF_UNLOCK(&sc->outq);
+
+ error = ng_send_fn(sc->node, NULL, bt3c_send, NULL, 0 /* new send */);
+out:
+ NG_FREE_ITEM(item);
+
+ return (error);
+} /* ng_bt3c_rcvdata */
+
+/****************************************************************************
+ ****************************************************************************
+ ** PCMCIA driver specific
+ ****************************************************************************
+ ****************************************************************************/
+
+/*
+ * PC-Card (PCMCIA) match routine
+ */
+
+static int
+bt3c_pccard_match(device_t dev)
+{
+ static struct pccard_product const bt3c_pccard_products[] = {
+ PCMCIA_CARD(3COM, 3CRWB609, 0),
+ { NULL, }
+ };
+
+ struct pccard_product const *pp = NULL;
+
+ pp = pccard_product_lookup(dev, bt3c_pccard_products,
+ sizeof(bt3c_pccard_products[0]), NULL);
+ if (pp == NULL)
+ return (EIO);
+
+ device_set_desc(dev, pp->pp_name);
+
+ return (0);
+} /* bt3c_pccacd_match */
+
+/*
+ * PC-Card (PCMCIA) probe routine
+ * XXX FIXME
+ */
+
+static int
+bt3c_pccard_probe(device_t dev)
+{
+ return (0);
+} /* bt3c_pccacd_probe */
+
+/*
+ * PC-Card (PCMCIA) attach routine
+ */
+
+static int
+bt3c_pccard_attach(device_t dev)
+{
+ bt3c_softc_p sc = NULL;
+
+ sc = (bt3c_softc_p) malloc(sizeof(*sc), M_BT3C, M_NOWAIT|M_ZERO);
+ if (sc == NULL)
+ return (ENOMEM);
+
+ /* Allocate I/O ports */
+ sc->iobase_rid = 0;
+ sc->iobase = bus_alloc_resource(dev, SYS_RES_IOPORT, &sc->iobase_rid,
+ 0, ~0, 8, RF_ACTIVE);
+ if (sc->iobase == NULL) {
+ device_printf(dev, "Could not allocate I/O ports\n");
+ goto bad;
+ }
+
+ /* Allocate IRQ */
+ sc->irq_rid = 0;
+ sc->irq = bus_alloc_resource(dev, SYS_RES_IRQ, &sc->irq_rid,
+ 0, ~0, 1, RF_ACTIVE);
+ if (sc->irq == NULL) {
+ device_printf(dev, "Could not allocate IRQ\n");
+ goto bad;
+ }
+
+ sc->irq_cookie = NULL;
+ if (bus_setup_intr(dev, sc->irq, INTR_TYPE_TTY, bt3c_intr, sc,
+ &sc->irq_cookie) != 0) {
+ device_printf(dev, "Could not setup ISR\n");
+ goto bad;
+ }
+
+ /* Attach handler to TTY SWI thread */
+ sc->ith = NULL;
+ if (swi_add(&tty_ithd, device_get_nameunit(dev),
+ bt3c_swi_intr, sc, SWI_TTY, 0, &sc->ith) < 0) {
+ device_printf(dev, "Could not setup SWI ISR\n");
+ goto bad;
+ }
+
+ /* Create Netgraph node */
+ if (ng_make_node_common(&typestruct, &sc->node) != 0) {
+ device_printf(dev, "Could not create Netgraph node\n");
+ sc->node = NULL;
+ goto bad;
+ }
+
+ /* Name Netgraph node */
+ if (ng_name_node(sc->node, device_get_nameunit(dev)) != 0) {
+ device_printf(dev, "Could not name Netgraph node\n");
+ NG_NODE_UNREF(sc->node);
+ sc->node = NULL;
+ goto bad;
+ }
+
+ sc->dev = dev;
+ sc->debug = NG_BT3C_WARN_LEVEL;
+
+ sc->inq.ifq_maxlen = sc->outq.ifq_maxlen = BT3C_DEFAULTQLEN;
+ mtx_init(&sc->inq.ifq_mtx, "BT3C inq", NULL, MTX_DEF);
+ mtx_init(&sc->outq.ifq_mtx, "BT3C outq", NULL, MTX_DEF);
+
+ sc->state = NG_BT3C_W4_PKT_IND;
+ sc->want = 1;
+
+ NG_NODE_SET_PRIVATE(sc->node, sc);
+ device_set_softc(dev, sc);
+
+ return (0);
+bad:
+ if (sc->ith != NULL) {
+ ithread_remove_handler(sc->ith);
+ sc->ith = NULL;
+ }
+
+ if (sc->irq != NULL) {
+ if (sc->irq_cookie != NULL)
+ bus_teardown_intr(dev, sc->irq, sc->irq_cookie);
+
+ bus_release_resource(dev, SYS_RES_IRQ,
+ sc->irq_rid, sc->irq);
+
+ sc->irq = NULL;
+ sc->irq_rid = 0;
+ }
+
+ if (sc->iobase != NULL) {
+ bus_release_resource(dev, SYS_RES_IOPORT,
+ sc->iobase_rid, sc->iobase);
+
+ sc->iobase = NULL;
+ sc->iobase_rid = 0;
+ }
+
+ free(sc, M_BT3C);
+
+ return (ENXIO);
+} /* bt3c_pccacd_attach */
+
+/*
+ * PC-Card (PCMCIA) detach routine
+ */
+
+static int
+bt3c_pccard_detach(device_t dev)
+{
+ bt3c_softc_p sc = (bt3c_softc_p) device_get_softc(dev);
+
+ if (sc == NULL)
+ return (0);
+
+ device_set_softc(dev, NULL);
+
+ ithread_remove_handler(sc->ith);
+ sc->ith = NULL;
+
+ bus_teardown_intr(dev, sc->irq, sc->irq_cookie);
+ bus_release_resource(dev, SYS_RES_IRQ, sc->irq_rid, sc->irq);
+ sc->irq_cookie = NULL;
+ sc->irq = NULL;
+ sc->irq_rid = 0;
+
+ bus_release_resource(dev, SYS_RES_IOPORT, sc->iobase_rid, sc->iobase);
+ sc->iobase = NULL;
+ sc->iobase_rid = 0;
+
+ if (sc->node != NULL) {
+ NG_NODE_SET_PRIVATE(sc->node, NULL);
+ ng_rmnode_self(sc->node);
+ sc->node = NULL;
+ }
+
+ NG_FREE_M(sc->m);
+ IF_DRAIN(&sc->inq);
+ IF_DRAIN(&sc->outq);
+
+ mtx_destroy(&sc->inq.ifq_mtx);
+ mtx_destroy(&sc->outq.ifq_mtx);
+
+ bzero(sc, sizeof(*sc));
+ free(sc, M_BT3C);
+
+ return (0);
+} /* bt3c_pccacd_detach */
+
+/*
+ * Interrupt service routine's
+ */
+
+static void
+bt3c_intr(void *context)
+{
+ bt3c_softc_p sc = (bt3c_softc_p) context;
+ u_int16_t control, status;
+
+ if (sc == NULL || sc->ith == NULL) {
+ printf("%s: bogus interrupt\n", NG_BT3C_NODE_TYPE);
+ return;
+ }
+
+ bt3c_read_control(sc, control);
+ if ((control & 0x80) == 0)
+ return;
+
+ bt3c_read(sc, 0x7001, status);
+ NG_BT3C_INFO(sc->dev, "control=%#x, status=%#x\n", control, status);
+
+ if ((status & 0xff) == 0x7f || (status & 0xff) == 0xff) {
+ NG_BT3C_WARN(sc->dev, "Strange status=%#x\n", status);
+ return;
+ }
+
+ /* Receive complete */
+ if (status & 0x0001)
+ bt3c_receive(sc);
+
+ /* Record status and schedule SWI */
+ sc->status |= status;
+ swi_sched(sc->ith, 0);
+
+ /* Complete interrupt */
+ bt3c_write(sc, 0x7001, 0x0000);
+ bt3c_write_control(sc, control);
+} /* bt3c_intr */
+
+/*
+ * Receive data
+ */
+
+static void
+bt3c_receive(bt3c_softc_p sc)
+{
+ u_int16_t i, count, c;
+
+ /* Receive data from the card */
+ bt3c_read(sc, 0x7006, count);
+ NG_BT3C_INFO(sc->dev, "The card has %d characters\n", count);
+
+ bt3c_set_address(sc, 0x7480);
+
+ for (i = 0; i < count; i++) {
+ /* Allocate new mbuf if needed */
+ if (sc->m == NULL) {
+ sc->state = NG_BT3C_W4_PKT_IND;
+ sc->want = 1;
+
+ MGETHDR(sc->m, M_DONTWAIT, MT_DATA);
+ if (sc->m == NULL) {
+ NG_BT3C_ERR(sc->dev, "Could not get mbuf\n");
+ NG_BT3C_STAT_IERROR(sc->stat);
+
+ break; /* XXX lost of sync */
+ }
+
+ sc->m->m_len = sc->m->m_pkthdr.len = 0;
+ }
+
+ /* Read and append character to mbuf */
+ bt3c_read_data(sc, c);
+ if (bt3c_append(sc->m, c) != 0) {
+ NG_FREE_M(sc->m);
+ sc->state = NG_BT3C_W4_PKT_IND;
+ sc->want = 1;
+
+ break; /* XXX lost of sync */
+ }
+
+ NG_BT3C_INFO(sc->dev,
+"Got char %#x, want=%d, got=%d\n", c, sc->want, sc->m->m_pkthdr.len);
+
+ if (sc->m->m_pkthdr.len < sc->want)
+ continue; /* wait for more */
+
+ switch (sc->state) {
+ /* Got packet indicator */
+ case NG_BT3C_W4_PKT_IND:
+ NG_BT3C_INFO(sc->dev,
+"Got packet indicator %#x\n", *mtod(sc->m, u_int8_t *));
+
+ sc->state = NG_BT3C_W4_PKT_HDR;
+
+ /*
+ * Since packet indicator included in the packet
+ * header just set sc->want to sizeof(packet header).
+ */
+
+ switch (*mtod(sc->m, u_int8_t *)) {
+ case NG_HCI_ACL_DATA_PKT:
+ sc->want = sizeof(ng_hci_acldata_pkt_t);
+ break;
+
+ case NG_HCI_SCO_DATA_PKT:
+ sc->want = sizeof(ng_hci_scodata_pkt_t);
+ break;
+
+ case NG_HCI_EVENT_PKT:
+ sc->want = sizeof(ng_hci_event_pkt_t);
+ break;
+
+ default:
+ NG_BT3C_ERR(sc->dev,
+"Ignoring unknown packet type=%#x\n", *mtod(sc->m, u_int8_t *));
+
+ NG_BT3C_STAT_IERROR(sc->stat);
+
+ NG_FREE_M(sc->m);
+ sc->state = NG_BT3C_W4_PKT_IND;
+ sc->want = 1;
+ break;
+ }
+ break;
+
+ /* Got packet header */
+ case NG_BT3C_W4_PKT_HDR:
+ sc->state = NG_BT3C_W4_PKT_DATA;
+
+ switch (*mtod(sc->m, u_int8_t *)) {
+ case NG_HCI_ACL_DATA_PKT:
+ c = le16toh(mtod(sc->m,
+ ng_hci_acldata_pkt_t *)->length);
+ break;
+
+ case NG_HCI_SCO_DATA_PKT:
+ c = mtod(sc->m, ng_hci_scodata_pkt_t*)->length;
+ break;
+
+ case NG_HCI_EVENT_PKT:
+ c = mtod(sc->m, ng_hci_event_pkt_t *)->length;
+ break;
+
+ default:
+ KASSERT(0,
+("Invalid packet type=%#x\n", *mtod(sc->m, u_int8_t *)));
+ break;
+ }
+
+ NG_BT3C_INFO(sc->dev,
+"Got packet header, packet type=%#x, got so far %d, payload size=%d\n",
+ *mtod(sc->m, u_int8_t *), sc->m->m_pkthdr.len,
+ c);
+
+ if (c > 0) {
+ sc->want += c;
+ break;
+ }
+
+ /* else FALLTHROUGH and deliver frame */
+ /* XXX is this true? should we deliver empty frame? */
+
+ /* Got packet data */
+ case NG_BT3C_W4_PKT_DATA:
+ NG_BT3C_INFO(sc->dev,
+"Got full packet, packet type=%#x, packet size=%d\n",
+ *mtod(sc->m, u_int8_t *), sc->m->m_pkthdr.len);
+
+ NG_BT3C_STAT_BYTES_RECV(sc->stat, sc->m->m_pkthdr.len);
+ NG_BT3C_STAT_PCKTS_RECV(sc->stat);
+
+ IF_LOCK(&sc->inq);
+ if (_IF_QFULL(&sc->inq)) {
+ NG_BT3C_ERR(sc->dev,
+"Incoming queue is full. Dropping mbuf, len=%d\n", sc->m->m_pkthdr.len);
+
+ _IF_DROP(&sc->inq);
+ NG_BT3C_STAT_IERROR(sc->stat);
+
+ NG_FREE_M(sc->m);
+ } else {
+ _IF_ENQUEUE(&sc->inq, sc->m);
+ sc->m = NULL;
+ }
+ IF_UNLOCK(&sc->inq);
+
+ sc->state = NG_BT3C_W4_PKT_IND;
+ sc->want = 1;
+ break;
+
+ default:
+ KASSERT(0,
+("Invalid node state=%d", sc->state));
+ break;
+ }
+ }
+
+ bt3c_write(sc, 0x7006, 0x0000);
+} /* bt3c_receive */
+
+/*
+ * Append character to the mbuf.
+ * XXX assumes mbuf has header
+ * XXX does not handle external mbuf's
+ * XXX always appends char to the end of chain
+ */
+
+static int
+bt3c_append(struct mbuf *m0, int c)
+{
+ struct mbuf *m = m0;
+ int len;
+
+ if (m0->m_next == NULL)
+ len = MHLEN;
+ else {
+ len = MLEN;
+
+ while (m->m_next != NULL)
+ m = m->m_next;
+ }
+
+ if (m->m_len >= len) {
+ MGET(m->m_next, M_DONTWAIT, m0->m_type);
+ if (m->m_next == NULL)
+ return (ENOBUFS);
+
+ m = m->m_next;
+ m->m_len = 0;
+ }
+
+ m->m_data[m->m_len ++] = (char) c;
+ m0->m_pkthdr.len ++;
+
+ return (0);
+} /* bt3c_append */
+
+/*
+ * SWI interrupt handler
+ * Netgraph part is handled via ng_send_fn() to avoid race with hook
+ * connection/disconnection
+ */
+
+static void
+bt3c_swi_intr(void *context)
+{
+ bt3c_softc_p sc = (bt3c_softc_p) context;
+ u_int16_t data;
+
+ /* Receive complete */
+ if (sc->status & 0x0001) {
+ sc->status &= ~0x0001; /* XXX is it safe? */
+
+ if (ng_send_fn(sc->node, NULL, &bt3c_forward, NULL, 0) != 0)
+ NG_BT3C_ALERT(sc->dev, "Could not forward frames!\n");
+ }
+
+ /* Send complete */
+ if (sc->status & 0x0002) {
+ sc->status &= ~0x0002; /* XXX is it safe */
+
+ if (ng_send_fn(sc->node, NULL, &bt3c_send, NULL, 1) != 0)
+ NG_BT3C_ALERT(sc->dev, "Could not send frames!\n");
+ }
+
+ /* Antenna position */
+ if (sc->status & 0x0020) {
+ sc->status &= ~0x0020; /* XXX is it safe */
+
+ bt3c_read(sc, 0x7002, data);
+ data &= 0x10;
+
+ if (data)
+ sc->flags |= BT3C_ANTENNA_OUT;
+ else
+ sc->flags &= ~BT3C_ANTENNA_OUT;
+
+ NG_BT3C_INFO(sc->dev, "Antenna %s\n", data? "OUT" : "IN");
+ }
+} /* bt3c_swi_intr */
+
+/*
+ * Send all incoming frames to the upper layer
+ */
+
+static void
+bt3c_forward(node_p node, hook_p hook, void *arg1, int arg2)
+{
+ bt3c_softc_p sc = (bt3c_softc_p) NG_NODE_PRIVATE(node);
+ struct mbuf *m = NULL;
+ int error;
+
+ if (sc == NULL)
+ return;
+
+ if (sc->hook != NULL && NG_HOOK_IS_VALID(sc->hook)) {
+ for (;;) {
+ IF_DEQUEUE(&sc->inq, m);
+ if (m == NULL)
+ break;
+
+ NG_SEND_DATA_ONLY(error, sc->hook, m);
+ if (error != 0)
+ NG_BT3C_STAT_IERROR(sc->stat);
+ }
+ } else {
+ IF_LOCK(&sc->inq);
+ for (;;) {
+ _IF_DEQUEUE(&sc->inq, m);
+ if (m == NULL)
+ break;
+
+ NG_BT3C_STAT_IERROR(sc->stat);
+ NG_FREE_M(m);
+ }
+ IF_UNLOCK(&sc->inq);
+ }
+} /* bt3c_forward */
+
+/*
+ * Send more data to the device. Must be called when node is locked
+ */
+
+static void
+bt3c_send(node_p node, hook_p hook, void *arg, int completed)
+{
+ bt3c_softc_p sc = (bt3c_softc_p) NG_NODE_PRIVATE(node);
+ struct mbuf *m = NULL;
+ int i, wrote, len;
+
+ if (sc == NULL)
+ return;
+
+ if (completed)
+ sc->flags &= ~BT3C_XMIT;
+
+ if (sc->flags & BT3C_XMIT)
+ return;
+
+ bt3c_set_address(sc, 0x7080);
+
+ for (wrote = 0; wrote < BT3C_FIFO_SIZE; ) {
+ IF_DEQUEUE(&sc->outq, m);
+ if (m == NULL)
+ break;
+
+ while (m != NULL) {
+ len = min((BT3C_FIFO_SIZE - wrote), m->m_len);
+
+ for (i = 0; i < m->m_len; i++)
+ bt3c_write_data(sc, m->m_data[i]);
+
+ wrote += len;
+ m->m_data += len;
+ m->m_len -= len;
+
+ if (m->m_len > 0)
+ break;
+
+ m = m_free(m);
+ }
+
+ if (m != NULL) {
+ IF_PREPEND(&sc->outq, m);
+ break;
+ }
+
+ NG_BT3C_STAT_PCKTS_SENT(sc->stat);
+ }
+
+ if (wrote > 0) {
+ NG_BT3C_INFO(sc->dev, "Wrote %d bytes\n", wrote);
+ NG_BT3C_STAT_BYTES_SENT(sc->stat, wrote);
+
+ bt3c_write(sc, 0x7005, wrote);
+ sc->flags |= BT3C_XMIT;
+ }
+} /* bt3c_send */
+
+/*
+ * Download chip firmware
+ */
+
+static void
+bt3c_download_firmware(bt3c_softc_p sc, char const *firmware, int firmware_size)
+{
+ ng_bt3c_firmware_block_ep const *block = NULL;
+ u_int16_t const *data = NULL;
+ int i, size;
+ u_int8_t c;
+
+ /* Reset */
+ device_printf(sc->dev, "Reseting the card...\n");
+ bt3c_write(sc, 0x8040, 0x0404);
+ bt3c_write(sc, 0x8040, 0x0400);
+ DELAY(1);
+
+ bt3c_write(sc, 0x8040, 0x0404);
+ DELAY(17);
+
+ /* Download firmware */
+ device_printf(sc->dev, "Starting firmware download process...\n");
+
+ for (size = 0; size < firmware_size; ) {
+ block = (ng_bt3c_firmware_block_ep const *)(firmware + size);
+ data = (u_int16_t const *)(block + 1);
+
+ if (bootverbose)
+ device_printf(sc->dev, "Download firmware block, " \
+ "address=%#08x, size=%d words, aligment=%d\n",
+ block->block_address, block->block_size,
+ block->block_alignment);
+
+ bt3c_set_address(sc, block->block_address);
+ for (i = 0; i < block->block_size; i++)
+ bt3c_write_data(sc, data[i]);
+
+ size += (sizeof(*block) + (block->block_size * 2) +
+ block->block_alignment);
+ }
+
+ DELAY(17);
+ device_printf(sc->dev, "Firmware download process complete\n");
+
+ /* Boot */
+ device_printf(sc->dev, "Starting the card...\n");
+ bt3c_set_address(sc, 0x3000);
+ bt3c_read_control(sc, c);
+ bt3c_write_control(sc, (c | 0x40));
+ DELAY(17);
+
+ /* Clear registers */
+ device_printf(sc->dev, "Clearing card registers...\n");
+ bt3c_write(sc, 0x7006, 0x0000);
+ bt3c_write(sc, 0x7005, 0x0000);
+ bt3c_write(sc, 0x7001, 0x0000);
+ DELAY(1000);
+} /* bt3c_download_firmware */
+
+/****************************************************************************
+ ****************************************************************************
+ ** Driver module
+ ****************************************************************************
+ ****************************************************************************/
+
+/*
+ * PC-Card (PCMCIA) driver
+ */
+
+static device_method_t bt3c_pccard_methods[] = {
+ /* Device interface */
+ DEVMETHOD(device_probe, pccard_compat_probe),
+ DEVMETHOD(device_attach, pccard_compat_attach),
+ DEVMETHOD(device_detach, bt3c_pccard_detach),
+
+ /* Card interface */
+ DEVMETHOD(card_compat_match, bt3c_pccard_match),
+ DEVMETHOD(card_compat_probe, bt3c_pccard_probe),
+ DEVMETHOD(card_compat_attach, bt3c_pccard_attach),
+ { 0, 0 }
+};
+
+static driver_t bt3c_pccard_driver = {
+ NG_BT3C_NODE_TYPE,
+ bt3c_pccard_methods,
+ 0
+};
+
+static devclass_t bt3c_devclass;
+
+DRIVER_MODULE(bt3c, pccard, bt3c_pccard_driver, bt3c_devclass, 0, 0);
+
diff --git a/sys/netgraph/bluetooth/drivers/bt3c/ng_bt3c_var.h b/sys/netgraph/bluetooth/drivers/bt3c/ng_bt3c_var.h
new file mode 100644
index 0000000..89ac57c
--- /dev/null
+++ b/sys/netgraph/bluetooth/drivers/bt3c/ng_bt3c_var.h
@@ -0,0 +1,103 @@
+/*
+ * ng_bt3c_var.h
+ *
+ * Copyright (c) 2001-2002 Maksim Yevmenkin <m_evmenkin@yahoo.com>
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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.
+ *
+ * $Id: ng_bt3c_var.h,v 1.1 2002/11/09 19:07:56 max Exp $
+ * $FreeBSD$
+ *
+ * XXX XXX XX XXX XXX XXX XXX XXX XXX XXX XXX XXX XXX XXX XXX XXX XXX XXX XXX
+ *
+ * Based on information obrained from: Jose Orlando Pereira <jop@di.uminho.pt>
+ * and disassembled w2k driver.
+ *
+ * XXX XXX XX XXX XXX XXX XXX XXX XXX XXX XXX XXX XXX XXX XXX XXX XXX XXX XXX
+ *
+ */
+
+#ifndef _NG_BT3C_VAR_H_
+#define _NG_BT3C_VAR_H_
+
+/* Debug printf's */
+#define NG_BT3C_ALERT if (sc->debug >= NG_BT3C_ALERT_LEVEL) device_printf
+#define NG_BT3C_ERR if (sc->debug >= NG_BT3C_ERR_LEVEL) device_printf
+#define NG_BT3C_WARN if (sc->debug >= NG_BT3C_WARN_LEVEL) device_printf
+#define NG_BT3C_INFO if (sc->debug >= NG_BT3C_INFO_LEVEL) device_printf
+
+/* Device registers */
+#define BT3C_DATA_L 0x00 /* data low byte */
+#define BT3C_DATA_H 0x01 /* high byte */
+#define BT3C_ADDR_L 0x02 /* address low byte */
+#define BT3C_ADDR_H 0x03 /* high byte */
+#define BT3C_CONTROL 0x04 /* control */
+
+#define BT3C_FIFO_SIZE 256
+
+/* Device softc structure */
+struct bt3c_softc {
+ /* Device specific */
+ device_t dev; /* pointer back to device */
+ int iobase_rid; /* iobase RID */
+ struct resource *iobase; /* iobase */
+ int irq_rid; /* irq RID */
+ struct resource *irq; /* irq */
+ void *irq_cookie; /* irq cookie */
+
+ /* Netgraph specific */
+ node_p node; /* pointer back to node */
+ hook_p hook; /* hook */
+
+ ng_bt3c_node_debug_ep debug; /* debug level */
+ u_int16_t flags; /* device flags */
+#define BT3C_ANTENNA_OUT (1 << 0) /* antena is out */
+#define BT3C_XMIT (1 << 1) /* xmit in progress */
+
+ ng_bt3c_node_state_ep state; /* receiving state */
+
+ ng_bt3c_node_stat_ep stat; /* statistic */
+#define NG_BT3C_STAT_PCKTS_SENT(s) (s).pckts_sent ++
+#define NG_BT3C_STAT_BYTES_SENT(s, n) (s).bytes_sent += (n)
+#define NG_BT3C_STAT_PCKTS_RECV(s) (s).pckts_recv ++
+#define NG_BT3C_STAT_BYTES_RECV(s, n) (s).bytes_recv += (n)
+#define NG_BT3C_STAT_OERROR(s) (s).oerrors ++
+#define NG_BT3C_STAT_IERROR(s) (s).ierrors ++
+#define NG_BT3C_STAT_RESET(s) bzero(&(s), sizeof((s)))
+
+ u_int32_t status; /* from ISR */
+ void *ith; /* ithread handler */
+
+ struct mbuf *m; /* current frame */
+ u_int32_t want; /* # of chars we want */
+
+ struct ifqueue inq; /* queue of incoming mbuf's */
+ struct ifqueue outq; /* queue of outgoing mbuf's */
+#define BT3C_DEFAULTQLEN 12 /* XXX max. size of out queue */
+};
+
+typedef struct bt3c_softc bt3c_softc_t;
+typedef struct bt3c_softc * bt3c_softc_p;
+
+#endif /* ndef _NG_BT3C_VAR_H_ */
+
diff --git a/sys/netgraph/bluetooth/drivers/h4/TODO b/sys/netgraph/bluetooth/drivers/h4/TODO
new file mode 100644
index 0000000..67cfbba
--- /dev/null
+++ b/sys/netgraph/bluetooth/drivers/h4/TODO
@@ -0,0 +1,13 @@
+# $FreeBSD$
+$Id: TODO,v 1.6 2002/06/27 09:50:17 max Exp $
+
+FIXME/TODO list
+
+This is a list of open issues for H4 node
+
+1) Locking/SMP
+
+ External code now uses ng_send_fn to inject data into Netgraph, but
+ i still use splXXX to lock tty level. this is wrong and should be
+ fixed!
+
diff --git a/sys/netgraph/bluetooth/drivers/h4/ng_h4.c b/sys/netgraph/bluetooth/drivers/h4/ng_h4.c
new file mode 100644
index 0000000..650c6f4
--- /dev/null
+++ b/sys/netgraph/bluetooth/drivers/h4/ng_h4.c
@@ -0,0 +1,1059 @@
+/*
+ * ng_h4.c
+ *
+ * Copyright (c) 2001-2002 Maksim Yevmenkin <m_evmenkin@yahoo.com>
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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.
+ *
+ * $Id: ng_h4.c,v 1.25 2002/11/03 02:17:31 max Exp $
+ * $FreeBSD$
+ *
+ * Based on:
+ * ---------
+ *
+ * FreeBSD: src/sys/netgraph/ng_tty.c
+ * Author: Archie Cobbs <archie@freebsd.org>
+ *
+ */
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/kernel.h>
+#include <sys/conf.h>
+#include <sys/endian.h>
+#include <sys/errno.h>
+#include <sys/fcntl.h>
+#include <sys/ioccom.h>
+#include <sys/malloc.h>
+#include <sys/mbuf.h>
+#include <sys/tty.h>
+#include <sys/ttycom.h>
+#include <netgraph/ng_message.h>
+#include <netgraph/netgraph.h>
+#include <netgraph/ng_parse.h>
+#include <ng_bluetooth.h>
+#include <ng_hci.h>
+#include "ng_h4.h"
+#include "ng_h4_var.h"
+#include "ng_h4_prse.h"
+
+/*****************************************************************************
+ *****************************************************************************
+ ** This node implements a Bluetooth HCI UART transport layer as per chapter
+ ** H4 of the Bluetooth Specification Book v1.1. It is a terminal line
+ ** discipline that is also a netgraph node. Installing this line discipline
+ ** on a terminal device instantiates a new netgraph node of this type, which
+ ** allows access to the device via the "hook" hook of the node.
+ **
+ ** Once the line discipline is installed, you can find out the name of the
+ ** corresponding netgraph node via a NGIOCGINFO ioctl().
+ *****************************************************************************
+ *****************************************************************************/
+
+/* MALLOC define */
+#ifndef NG_SEPARATE_MALLOC
+MALLOC_DEFINE(M_NETGRAPH_H4, "netgraph_h4", "Netgraph Bluetooth H4 node");
+#else
+#define M_NETGRAPH_H4 M_NETGRAPH
+#endif /* NG_SEPARATE_MALLOC */
+
+/* Line discipline methods */
+static int ng_h4_open (dev_t, struct tty *);
+static int ng_h4_close (struct tty *, int);
+static int ng_h4_read (struct tty *, struct uio *, int);
+static int ng_h4_write (struct tty *, struct uio *, int);
+static int ng_h4_input (int, struct tty *);
+static int ng_h4_start (struct tty *);
+static void ng_h4_start2 (node_p, hook_p, void *, int);
+static int ng_h4_ioctl (struct tty *, u_long, caddr_t,
+ int, struct thread *);
+
+/* Line discipline descriptor */
+static struct linesw ng_h4_disc = {
+ ng_h4_open, /* open */
+ ng_h4_close, /* close */
+ ng_h4_read, /* read */
+ ng_h4_write, /* write */
+ ng_h4_ioctl, /* ioctl */
+ ng_h4_input, /* input */
+ ng_h4_start, /* start */
+ ttymodem, /* modem */
+ 0 /* hotchar (don't really care which one) */
+};
+
+/* Netgraph methods */
+static ng_constructor_t ng_h4_constructor;
+static ng_rcvmsg_t ng_h4_rcvmsg;
+static ng_shutdown_t ng_h4_shutdown;
+static ng_newhook_t ng_h4_newhook;
+static ng_connect_t ng_h4_connect;
+static ng_rcvdata_t ng_h4_rcvdata;
+static ng_disconnect_t ng_h4_disconnect;
+
+/* Other stuff */
+static void ng_h4_timeout (node_p);
+static void ng_h4_untimeout (node_p);
+static void ng_h4_queue_timeout (void *);
+static void ng_h4_process_timeout (node_p, hook_p, void *, int);
+static int ng_h4_mod_event (module_t, int, void *);
+
+/* Netgraph node type descriptor */
+static struct ng_type typestruct = {
+ NG_ABI_VERSION,
+ NG_H4_NODE_TYPE, /* typename */
+ ng_h4_mod_event, /* modevent */
+ ng_h4_constructor, /* constructor */
+ ng_h4_rcvmsg, /* control message */
+ ng_h4_shutdown, /* destructor */
+ ng_h4_newhook, /* new hook */
+ NULL, /* find hook */
+ ng_h4_connect, /* connect hook */
+ ng_h4_rcvdata, /* data */
+ ng_h4_disconnect, /* disconnect hook */
+ ng_h4_cmdlist /* node command list */
+};
+NETGRAPH_INIT(h4, &typestruct);
+MODULE_VERSION(ng_h4, NG_BLUETOOTH_VERSION);
+
+static int ng_h4_node = 0;
+
+/*****************************************************************************
+ *****************************************************************************
+ ** Line discipline methods
+ *****************************************************************************
+ *****************************************************************************/
+
+/*
+ * Set our line discipline on the tty.
+ */
+
+static int
+ng_h4_open(dev_t dev, struct tty *tp)
+{
+ char name[NG_NODELEN + 1];
+ ng_h4_info_p sc = NULL;
+ int s, error;
+
+ /* Super-user only */
+ error = suser(curthread); /* XXX */
+ if (error != 0)
+ return (error);
+
+ s = splnet(); /* XXX */
+ spltty(); /* XXX */
+
+ /* Already installed? */
+ if (tp->t_line == H4DISC) {
+ sc = (ng_h4_info_p) tp->t_sc;
+ if (sc != NULL && sc->tp == tp)
+ goto out;
+ }
+
+ /* Initialize private struct */
+ MALLOC(sc, ng_h4_info_p, sizeof(*sc), M_NETGRAPH_H4, M_WAITOK | M_ZERO);
+ if (sc == NULL) {
+ error = ENOMEM;
+ goto out;
+ }
+
+ sc->tp = tp;
+ sc->debug = NG_H4_WARN_LEVEL;
+
+ sc->state = NG_H4_W4_PKT_IND;
+ sc->want = 1;
+ sc->got = 0;
+
+ NG_BT_MBUFQ_INIT(&sc->outq, NG_H4_DEFAULTQLEN);
+ callout_handle_init(&sc->timo);
+
+ /* Setup netgraph node */
+ error = ng_make_node_common(&typestruct, &sc->node);
+ if (error != 0) {
+ bzero(sc, sizeof(*sc));
+ FREE(sc, M_NETGRAPH_H4);
+ goto out;
+ }
+
+ /* Assign node its name */
+ snprintf(name, sizeof(name), "%s%d", typestruct.name, ng_h4_node ++);
+
+ error = ng_name_node(sc->node, name);
+ if (error != 0) {
+ NG_H4_ALERT("%s: %s - node name exists?\n", __func__, name);
+ NG_NODE_UNREF(sc->node);
+ bzero(sc, sizeof(*sc));
+ FREE(sc, M_NETGRAPH_H4);
+ goto out;
+ }
+
+ /* Set back pointers */
+ NG_NODE_SET_PRIVATE(sc->node, sc);
+ tp->t_sc = (caddr_t) sc;
+
+ /* The node has to be a WRITER because data can change node status */
+ NG_NODE_FORCE_WRITER(sc->node);
+
+ /*
+ * Pre-allocate cblocks to the an appropriate amount.
+ * I'm not sure what is appropriate.
+ */
+
+ ttyflush(tp, FREAD | FWRITE);
+ clist_alloc_cblocks(&tp->t_canq, 0, 0);
+ clist_alloc_cblocks(&tp->t_rawq, 0, 0);
+ clist_alloc_cblocks(&tp->t_outq,
+ MLEN + NG_H4_HIWATER, MLEN + NG_H4_HIWATER);
+out:
+ splx(s); /* XXX */
+
+ return (error);
+} /* ng_h4_open */
+
+/*
+ * Line specific close routine, called from device close routine
+ * and from ttioctl. This causes the node to be destroyed as well.
+ */
+
+static int
+ng_h4_close(struct tty *tp, int flag)
+{
+ ng_h4_info_p sc = (ng_h4_info_p) tp->t_sc;
+ int s;
+
+ s = spltty(); /* XXX */
+
+ ttyflush(tp, FREAD | FWRITE);
+ clist_free_cblocks(&tp->t_outq);
+ tp->t_line = 0;
+ if (sc != NULL) {
+ tp->t_sc = NULL;
+
+ if (sc->node != NULL) {
+ if (sc->flags & NG_H4_TIMEOUT)
+ ng_h4_untimeout(sc->node);
+
+ NG_NODE_SET_PRIVATE(sc->node, NULL);
+ ng_rmnode_self(sc->node);
+ sc->node = NULL;
+ }
+
+ NG_BT_MBUFQ_DESTROY(&sc->outq);
+ bzero(sc, sizeof(*sc));
+ FREE(sc, M_NETGRAPH_H4);
+ }
+
+ splx(s); /* XXX */
+
+ return (0);
+} /* ng_h4_close */
+
+/*
+ * Once the device has been turned into a node, we don't allow reading.
+ */
+
+static int
+ng_h4_read(struct tty *tp, struct uio *uio, int flag)
+{
+ return (EIO);
+} /* ng_h4_read */
+
+/*
+ * Once the device has been turned into a node, we don't allow writing.
+ */
+
+static int
+ng_h4_write(struct tty *tp, struct uio *uio, int flag)
+{
+ return (EIO);
+} /* ng_h4_write */
+
+/*
+ * We implement the NGIOCGINFO ioctl() defined in ng_message.h.
+ */
+
+static int
+ng_h4_ioctl(struct tty *tp, u_long cmd, caddr_t data, int flag,
+ struct thread *td)
+{
+ ng_h4_info_p sc = (ng_h4_info_p) tp->t_sc;
+ int s, error = 0;
+
+ s = spltty(); /* XXX */
+
+ switch (cmd) {
+ case NGIOCGINFO:
+#undef NI
+#define NI(x) ((struct nodeinfo *)(x))
+
+ bzero(data, sizeof(*NI(data)));
+
+ if (NG_NODE_HAS_NAME(sc->node))
+ strncpy(NI(data)->name, NG_NODE_NAME(sc->node),
+ sizeof(NI(data)->name) - 1);
+
+ strncpy(NI(data)->type, sc->node->nd_type->name,
+ sizeof(NI(data)->type) - 1);
+
+ NI(data)->id = (u_int32_t) ng_node2ID(sc->node);
+ NI(data)->hooks = NG_NODE_NUMHOOKS(sc->node);
+ break;
+
+ default:
+ error = ENOIOCTL;
+ break;
+ }
+
+ splx(s); /* XXX */
+
+ return (error);
+} /* ng_h4_ioctl */
+
+/*
+ * Receive data coming from the device. We get one character at a time, which
+ * is kindof silly.
+ */
+
+static int
+ng_h4_input(int c, struct tty *tp)
+{
+ ng_h4_info_p sc = (ng_h4_info_p) tp->t_sc;
+
+ if (sc == NULL || tp != sc->tp ||
+ sc->node == NULL || NG_NODE_NOT_VALID(sc->node))
+ return (0);
+
+ /* Check for error conditions */
+ if ((sc->tp->t_state & TS_CONNECTED) == 0) {
+ NG_H4_INFO("%s: %s - no carrier\n", __func__,
+ NG_NODE_NAME(sc->node));
+
+ sc->state = NG_H4_W4_PKT_IND;
+ sc->want = 1;
+ sc->got = 0;
+
+ return (0); /* XXX Loss of synchronization here! */
+ }
+
+ /* Check for framing error or overrun on this char */
+ if (c & TTY_ERRORMASK) {
+ NG_H4_ERR("%s: %s - line error %#x, c=%#x\n", __func__,
+ NG_NODE_NAME(sc->node), c & TTY_ERRORMASK,
+ c & TTY_CHARMASK);
+
+ NG_H4_STAT_IERROR(sc->stat);
+
+ sc->state = NG_H4_W4_PKT_IND;
+ sc->want = 1;
+ sc->got = 0;
+
+ return (0); /* XXX Loss of synchronization here! */
+ }
+
+ NG_H4_STAT_BYTES_RECV(sc->stat, 1);
+
+ /* Append char to mbuf */
+ if (sc->got >= sizeof(sc->ibuf)) {
+ NG_H4_ALERT("%s: %s - input buffer overflow, c=%#x, got=%d\n",
+ __func__, NG_NODE_NAME(sc->node), c & TTY_CHARMASK,
+ sc->got);
+
+ NG_H4_STAT_IERROR(sc->stat);
+
+ sc->state = NG_H4_W4_PKT_IND;
+ sc->want = 1;
+ sc->got = 0;
+
+ return (0); /* XXX Loss of synchronization here! */
+ }
+
+ sc->ibuf[sc->got ++] = (c & TTY_CHARMASK);
+
+ NG_H4_INFO("%s: %s - got char %#x, want=%d, got=%d\n", __func__,
+ NG_NODE_NAME(sc->node), c, sc->want, sc->got);
+
+ if (sc->got < sc->want)
+ return (0); /* Wait for more */
+
+ switch (sc->state) {
+ /* Got packet indicator */
+ case NG_H4_W4_PKT_IND:
+ NG_H4_INFO("%s: %s - got packet indicator %#x\n", __func__,
+ NG_NODE_NAME(sc->node), sc->ibuf[0]);
+
+ sc->state = NG_H4_W4_PKT_HDR;
+
+ /*
+ * Since packet indicator included in the packet header
+ * just set sc->want to sizeof(packet header).
+ */
+
+ switch (sc->ibuf[0]) {
+ case NG_HCI_ACL_DATA_PKT:
+ sc->want = sizeof(ng_hci_acldata_pkt_t);
+ break;
+
+ case NG_HCI_SCO_DATA_PKT:
+ sc->want = sizeof(ng_hci_scodata_pkt_t);
+ break;
+
+ case NG_HCI_EVENT_PKT:
+ sc->want = sizeof(ng_hci_event_pkt_t);
+ break;
+
+ default:
+ NG_H4_WARN("%s: %s - ignoring unknown packet " \
+ "type=%#x\n", __func__, NG_NODE_NAME(sc->node),
+ sc->ibuf[0]);
+
+ NG_H4_STAT_IERROR(sc->stat);
+
+ sc->state = NG_H4_W4_PKT_IND;
+ sc->want = 1;
+ sc->got = 0;
+ break;
+ }
+ break;
+
+ /* Got packet header */
+ case NG_H4_W4_PKT_HDR:
+ sc->state = NG_H4_W4_PKT_DATA;
+
+ switch (sc->ibuf[0]) {
+ case NG_HCI_ACL_DATA_PKT:
+ c = le16toh(((ng_hci_acldata_pkt_t *)
+ (sc->ibuf))->length);
+ break;
+
+ case NG_HCI_SCO_DATA_PKT:
+ c = ((ng_hci_scodata_pkt_t *)(sc->ibuf))->length;
+ break;
+
+ case NG_HCI_EVENT_PKT:
+ c = ((ng_hci_event_pkt_t *)(sc->ibuf))->length;
+ break;
+
+ default:
+ KASSERT((0), ("Invalid packet type=%#x\n",
+ sc->ibuf[0]));
+ break;
+ }
+
+ NG_H4_INFO("%s: %s - got packet header, packet type=%#x, " \
+ "packet size=%d, payload size=%d\n", __func__,
+ NG_NODE_NAME(sc->node), sc->ibuf[0], sc->got, c);
+
+ if (c > 0) {
+ sc->want += c;
+
+ /*
+ * Try to prevent possible buffer overrun
+ *
+ * XXX I'm *really* confused here. It turns out
+ * that Xircom card sends us packets with length
+ * greater then 512 bytes! This is greater then
+ * our old receive buffer (ibuf) size. In the same
+ * time the card demands from us *not* to send
+ * packets greater then 192 bytes. Weird! How the
+ * hell i should know how big *receive* buffer
+ * should be? For now increase receiving buffer
+ * size to 1K and add the following check.
+ */
+
+ if (sc->want >= sizeof(sc->ibuf)) {
+ int b;
+
+ NG_H4_ALERT("%s: %s - packet too big for " \
+ "buffer, type=%#x, got=%d, want=%d, " \
+ "length=%d\n", __func__,
+ NG_NODE_NAME(sc->node), sc->ibuf[0],
+ sc->got, sc->want, c);
+
+ NG_H4_ALERT("Packet header:\n");
+ for (b = 0; b < sc->got; b++)
+ NG_H4_ALERT("%#x ", sc->ibuf[b]);
+ NG_H4_ALERT("\n");
+
+ /* Reset state */
+ NG_H4_STAT_IERROR(sc->stat);
+
+ sc->state = NG_H4_W4_PKT_IND;
+ sc->want = 1;
+ sc->got = 0;
+ }
+
+ break;
+ }
+
+ /* else FALLTHROUGH and deliver frame */
+ /* XXX Is this true? Should we deliver empty frame? */
+
+ /* Got packet data */
+ case NG_H4_W4_PKT_DATA:
+ NG_H4_INFO("%s: %s - got full packet, packet type=%#x, " \
+ "packet size=%d\n", __func__,
+ NG_NODE_NAME(sc->node), sc->ibuf[0], sc->got);
+
+ if (sc->hook != NULL && NG_HOOK_IS_VALID(sc->hook)) {
+ struct mbuf *m = NULL;
+
+ MGETHDR(m, M_DONTWAIT, MT_DATA);
+ if (m != NULL) {
+ m->m_pkthdr.len = 0;
+
+ /* XXX m_copyback() is stupid */
+ m->m_len = min(MHLEN, sc->got);
+
+ m_copyback(m, 0, sc->got, sc->ibuf);
+ NG_SEND_DATA_ONLY(c, sc->hook, m);
+ } else {
+ NG_H4_ERR("%s: %s - could not get mbuf\n",
+ __func__, NG_NODE_NAME(sc->node));
+
+ NG_H4_STAT_IERROR(sc->stat);
+ }
+ }
+
+ sc->state = NG_H4_W4_PKT_IND;
+ sc->want = 1;
+ sc->got = 0;
+
+ NG_H4_STAT_PCKTS_RECV(sc->stat);
+ break;
+
+ default:
+ KASSERT((0), ("Invalid H4 node state=%d", sc->state));
+ break;
+ }
+
+ return (0);
+} /* ng_h4_input */
+
+/*
+ * This is called when the device driver is ready for more output. Called from
+ * tty system.
+ */
+
+static int
+ng_h4_start(struct tty *tp)
+{
+ ng_h4_info_p sc = (ng_h4_info_p) tp->t_sc;
+
+ if (sc == NULL || tp != sc->tp ||
+ sc->node == NULL || NG_NODE_NOT_VALID(sc->node))
+ return (0);
+
+ return (ng_send_fn(sc->node, NULL, ng_h4_start2, NULL, 0));
+} /* ng_h4_start */
+
+/*
+ * Device driver is ready for more output. Part 2. Called (via ng_send_fn)
+ * ng_h4_start() and from ng_h4_rcvdata() when a new mbuf is available for
+ * output.
+ */
+
+static void
+ng_h4_start2(node_p node, hook_p hook, void *arg1, int arg2)
+{
+ ng_h4_info_p sc = (ng_h4_info_p) NG_NODE_PRIVATE(node);
+ struct mbuf *m = NULL;
+ int s, size;
+
+ s = spltty(); /* XXX */
+
+#if 0
+ while (sc->tp->t_outq.c_cc < NG_H4_HIWATER) { /* XXX 2.2 specific ? */
+#else
+ while (1) {
+#endif
+ /* Remove first mbuf from queue */
+ NG_BT_MBUFQ_DEQUEUE(&sc->outq, m);
+ if (m == NULL)
+ break;
+
+ /* Send as much of it as possible */
+ while (m != NULL) {
+ size = m->m_len - b_to_q(mtod(m, u_char *),
+ m->m_len, &sc->tp->t_outq);
+
+ NG_H4_STAT_BYTES_SENT(sc->stat, size);
+
+ m->m_data += size;
+ m->m_len -= size;
+ if (m->m_len > 0)
+ break; /* device can't take no more */
+
+ m = m_free(m);
+ }
+
+ /* Put remainder of mbuf chain (if any) back on queue */
+ if (m != NULL) {
+ NG_BT_MBUFQ_PREPEND(&sc->outq, m);
+ break;
+ }
+
+ /* Full packet has been sent */
+ NG_H4_STAT_PCKTS_SENT(sc->stat);
+ }
+
+ /*
+ * Call output process whether or not there is any output. We are
+ * being called in lieu of ttstart and must do what it would.
+ */
+
+ if (sc->tp->t_oproc != NULL)
+ (*sc->tp->t_oproc)(sc->tp);
+
+ /*
+ * This timeout is needed for operation on a pseudo-tty, because the
+ * pty code doesn't call pppstart after it has drained the t_outq.
+ */
+
+ if (NG_BT_MBUFQ_LEN(&sc->outq) > 0 && (sc->flags & NG_H4_TIMEOUT) == 0)
+ ng_h4_timeout(node);
+
+ splx(s); /* XXX */
+} /* ng_h4_start2 */
+
+/*****************************************************************************
+ *****************************************************************************
+ ** Netgraph node methods
+ *****************************************************************************
+ *****************************************************************************/
+
+/*
+ * Initialize a new node of this type. We only allow nodes to be created as
+ * a result of setting the line discipline on a tty, so always return an error
+ * if not.
+ */
+
+static int
+ng_h4_constructor(node_p node)
+{
+ return (EOPNOTSUPP);
+} /* ng_h4_constructor */
+
+/*
+ * Add a new hook. There can only be one.
+ */
+
+static int
+ng_h4_newhook(node_p node, hook_p hook, const char *name)
+{
+ ng_h4_info_p sc = (ng_h4_info_p) NG_NODE_PRIVATE(node);
+
+ if (strcmp(name, NG_H4_HOOK) != 0)
+ return (EINVAL);
+
+ if (sc->hook != NULL)
+ return (EISCONN);
+
+ sc->hook = hook;
+
+ return (0);
+} /* ng_h4_newhook */
+
+/*
+ * Connect hook. Just say yes.
+ */
+
+static int
+ng_h4_connect(hook_p hook)
+{
+ ng_h4_info_p sc = (ng_h4_info_p) NG_NODE_PRIVATE(NG_HOOK_NODE(hook));
+
+ if (hook != sc->hook) {
+ sc->hook = NULL;
+ return (EINVAL);
+ }
+
+ NG_HOOK_FORCE_QUEUE(NG_HOOK_PEER(hook));
+
+ return (0);
+} /* ng_h4_connect */
+
+/*
+ * Disconnect the hook
+ */
+
+static int
+ng_h4_disconnect(hook_p hook)
+{
+ ng_h4_info_p sc = (ng_h4_info_p) NG_NODE_PRIVATE(NG_HOOK_NODE(hook));
+
+ /*
+ * We need to check for sc != NULL because we can be called from
+ * ng_h4_clsoe() via ng_rmnode_self()
+ */
+
+ if (sc != NULL) {
+ if (hook != sc->hook)
+ return (EINVAL);
+
+ /* XXX do we have to untimeout and drain out queue? */
+ if (sc->flags & NG_H4_TIMEOUT)
+ ng_h4_untimeout(NG_HOOK_NODE(hook));
+
+ NG_BT_MBUFQ_DRAIN(&sc->outq);
+ sc->state = NG_H4_W4_PKT_IND;
+ sc->want = 1;
+ sc->got = 0;
+
+ sc->hook = NULL;
+ }
+
+ return (0);
+} /* ng_h4_disconnect */
+
+/*
+ * Remove this node. The does the netgraph portion of the shutdown.
+ * This should only be called indirectly from ng_h4_close().
+ */
+
+static int
+ng_h4_shutdown(node_p node)
+{
+ ng_h4_info_p sc = (ng_h4_info_p) NG_NODE_PRIVATE(node);
+ char name[NG_NODELEN + 1];
+
+ /* Let old node go */
+ NG_NODE_SET_PRIVATE(node, NULL);
+ NG_NODE_UNREF(node);
+
+ /* Check if device was closed */
+ if (sc == NULL)
+ goto out;
+
+ /* Setup new netgraph node */
+ if (ng_make_node_common(&typestruct, &sc->node) != 0) {
+ printf("%s: Unable to create new node!\n", __func__);
+ sc->node = NULL;
+ goto out;
+ }
+
+ /* Assign node its name */
+ snprintf(name, sizeof(name), "%s%d", typestruct.name, ng_h4_node ++);
+
+ if (ng_name_node(sc->node, name) != 0) {
+ printf("%s: %s - node name exists?\n", __func__, name);
+ NG_NODE_UNREF(sc->node);
+ sc->node = NULL;
+ goto out;
+ }
+
+ /* The node has to be a WRITER because data can change node status */
+ NG_NODE_FORCE_WRITER(sc->node);
+ NG_NODE_SET_PRIVATE(sc->node, sc);
+out:
+ return (0);
+} /* ng_h4_shutdown */
+
+/*
+ * Receive incoming data from Netgraph system. Put it on our
+ * output queue and start output if necessary.
+ */
+
+static int
+ng_h4_rcvdata(hook_p hook, item_p item)
+{
+ ng_h4_info_p sc = (ng_h4_info_p)NG_NODE_PRIVATE(NG_HOOK_NODE(hook));
+ int error = 0;
+ struct mbuf *m = NULL;
+
+ if (sc == NULL) {
+ error = EHOSTDOWN;
+ goto out;
+ }
+
+ if (hook != sc->hook) {
+ error = EINVAL;
+ goto out;
+ }
+
+ NGI_GET_M(item, m);
+
+ if (NG_BT_MBUFQ_FULL(&sc->outq)) {
+ NG_H4_ERR("%s: %s - dropping mbuf, len=%d\n", __func__,
+ NG_NODE_NAME(sc->node), m->m_pkthdr.len);
+
+ NG_BT_MBUFQ_DROP(&sc->outq);
+ NG_H4_STAT_OERROR(sc->stat);
+
+ NG_FREE_M(m);
+ error = ENOBUFS;
+ } else {
+ NG_H4_INFO("%s: %s - queue mbuf, len=%d\n", __func__,
+ NG_NODE_NAME(sc->node), m->m_pkthdr.len);
+
+ NG_BT_MBUFQ_ENQUEUE(&sc->outq, m);
+
+ /*
+ * We have lock on the node, so we can call ng_h4_start2()
+ * directly
+ */
+
+ ng_h4_start2(sc->node, NULL, NULL, 0);
+ }
+out:
+ NG_FREE_ITEM(item);
+
+ return (error);
+} /* ng_h4_rcvdata */
+
+/*
+ * Receive control message
+ */
+
+static int
+ng_h4_rcvmsg(node_p node, item_p item, hook_p lasthook)
+{
+ ng_h4_info_p sc = (ng_h4_info_p) NG_NODE_PRIVATE(node);
+ struct ng_mesg *msg = NULL, *resp = NULL;
+ int error = 0;
+
+ if (sc == NULL) {
+ error = EHOSTDOWN;
+ goto out;
+ }
+
+ NGI_GET_MSG(item, msg);
+
+ switch (msg->header.typecookie) {
+ case NGM_GENERIC_COOKIE:
+ switch (msg->header.cmd) {
+ case NGM_TEXT_STATUS:
+ NG_MKRESPONSE(resp, msg, NG_TEXTRESPONSE, M_NOWAIT);
+ if (resp == NULL)
+ error = ENOMEM;
+ else
+ snprintf(resp->data, NG_TEXTRESPONSE,
+ "Hook: %s\n" \
+ "Flags: %#x\n" \
+ "Debug: %d\n" \
+ "State: %d\n" \
+ "Queue: [have:%d,max:%d]\n" \
+ "Input: [got:%d,want:%d]",
+ (sc->hook != NULL)? NG_H4_HOOK : "",
+ sc->flags,
+ sc->debug,
+ sc->state,
+ NG_BT_MBUFQ_LEN(&sc->outq),
+ sc->outq.maxlen,
+ sc->got,
+ sc->want);
+ break;
+
+ default:
+ error = EINVAL;
+ break;
+ }
+ break;
+
+ case NGM_H4_COOKIE:
+ switch (msg->header.cmd) {
+ case NGM_H4_NODE_RESET:
+ NG_BT_MBUFQ_DRAIN(&sc->outq);
+ sc->state = NG_H4_W4_PKT_IND;
+ sc->want = 1;
+ sc->got = 0;
+ break;
+
+ case NGM_H4_NODE_GET_STATE:
+ NG_MKRESPONSE(resp, msg, sizeof(ng_h4_node_state_ep),
+ M_NOWAIT);
+ if (resp == NULL)
+ error = ENOMEM;
+ else
+ *((ng_h4_node_state_ep *)(resp->data)) =
+ sc->state;
+ break;
+
+ case NGM_H4_NODE_GET_DEBUG:
+ NG_MKRESPONSE(resp, msg, sizeof(ng_h4_node_debug_ep),
+ M_NOWAIT);
+ if (resp == NULL)
+ error = ENOMEM;
+ else
+ *((ng_h4_node_debug_ep *)(resp->data)) =
+ sc->debug;
+ break;
+
+ case NGM_H4_NODE_SET_DEBUG:
+ if (msg->header.arglen != sizeof(ng_h4_node_debug_ep))
+ error = EMSGSIZE;
+ else
+ sc->debug =
+ *((ng_h4_node_debug_ep *)(msg->data));
+ break;
+
+ case NGM_H4_NODE_GET_QLEN:
+ NG_MKRESPONSE(resp, msg, sizeof(ng_h4_node_qlen_ep),
+ M_NOWAIT);
+ if (resp == NULL)
+ error = ENOMEM;
+ else
+ *((ng_h4_node_qlen_ep *)(resp->data)) =
+ sc->outq.maxlen;
+ break;
+
+ case NGM_H4_NODE_SET_QLEN:
+ if (msg->header.arglen != sizeof(ng_h4_node_qlen_ep))
+ error = EMSGSIZE;
+ else if (*((ng_h4_node_qlen_ep *)(msg->data)) <= 0)
+ error = EINVAL;
+ else
+ sc->outq.maxlen =
+ *((ng_h4_node_qlen_ep *)(msg->data));
+ break;
+
+ case NGM_H4_NODE_GET_STAT:
+ NG_MKRESPONSE(resp, msg, sizeof(ng_h4_node_stat_ep),
+ M_NOWAIT);
+ if (resp == NULL)
+ error = ENOMEM;
+ else
+ bcopy(&sc->stat, resp->data,
+ sizeof(ng_h4_node_stat_ep));
+ break;
+
+ case NGM_H4_NODE_RESET_STAT:
+ NG_H4_STAT_RESET(sc->stat);
+ break;
+
+ default:
+ error = EINVAL;
+ break;
+ }
+ break;
+
+ default:
+ error = EINVAL;
+ break;
+ }
+out:
+ NG_RESPOND_MSG(error, node, item, resp);
+ NG_FREE_MSG(msg);
+
+ return (error);
+} /* ng_h4_rcvmsg */
+
+/*
+ * Set timeout
+ */
+
+static void
+ng_h4_timeout(node_p node)
+{
+ ng_h4_info_p sc = (ng_h4_info_p) NG_NODE_PRIVATE(node);
+
+ NG_NODE_REF(node);
+ sc->timo = timeout(ng_h4_queue_timeout, node, 1);
+ sc->flags |= NG_H4_TIMEOUT;
+} /* ng_h4_timeout */
+
+/*
+ * Unset timeout
+ */
+
+static void
+ng_h4_untimeout(node_p node)
+{
+ ng_h4_info_p sc = (ng_h4_info_p) NG_NODE_PRIVATE(node);
+
+ sc->flags &= ~NG_H4_TIMEOUT;
+ untimeout(ng_h4_queue_timeout, node, sc->timo);
+ NG_NODE_UNREF(node);
+} /* ng_h4_untimeout */
+
+/*
+ * OK, timeout has happend, so queue function to process it
+ */
+
+static void
+ng_h4_queue_timeout(void *context)
+{
+ node_p node = (node_p) context;
+
+ if (NG_NODE_IS_VALID(node))
+ ng_send_fn(node, NULL, &ng_h4_process_timeout, NULL, 0);
+
+ NG_NODE_UNREF(node);
+} /* ng_h4_queue_timeout */
+
+/*
+ * Timeout processing function.
+ * We still have data to output to the device, so try sending more.
+ */
+
+static void
+ng_h4_process_timeout(node_p node, hook_p hook, void *arg1, int arg2)
+{
+ ng_h4_info_p sc = (ng_h4_info_p) NG_NODE_PRIVATE(node);
+
+ sc->flags &= ~NG_H4_TIMEOUT;
+
+ /*
+ * We can call ng_h4_start2() directly here because we have lock
+ * on the node.
+ */
+
+ ng_h4_start2(node, NULL, NULL, 0);
+} /* ng_h4_process_timeout */
+
+/*
+ * Handle loading and unloading for this node type
+ */
+
+static int
+ng_h4_mod_event(module_t mod, int event, void *data)
+{
+ static int ng_h4_ldisc;
+ int s, error = 0;
+
+ s = spltty(); /* XXX */
+
+ switch (event) {
+ case MOD_LOAD:
+ /* Register line discipline */
+ ng_h4_ldisc = ldisc_register(H4DISC, &ng_h4_disc);
+ if (ng_h4_ldisc < 0) {
+ printf("%s: can't register H4 line discipline\n",
+ __func__);
+ error = EIO;
+ }
+ break;
+
+ case MOD_UNLOAD:
+ /* Unregister line discipline */
+ ldisc_deregister(ng_h4_ldisc);
+ break;
+
+ default:
+ error = EOPNOTSUPP;
+ break;
+ }
+
+ splx(s); /* XXX */
+
+ return (error);
+} /* ng_h4_mod_event */
+
diff --git a/sys/netgraph/bluetooth/drivers/h4/ng_h4_prse.h b/sys/netgraph/bluetooth/drivers/h4/ng_h4_prse.h
new file mode 100644
index 0000000..a4ba75d
--- /dev/null
+++ b/sys/netgraph/bluetooth/drivers/h4/ng_h4_prse.h
@@ -0,0 +1,122 @@
+/*
+ * ng_h4_prse.h
+ *
+ * Copyright (c) 2001 Maksim Yevmenkin <m_evmenkin@yahoo.com>
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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.
+ *
+ * $Id: ng_h4_prse.h,v 1.6 2002/09/04 21:35:01 max Exp $
+ * $FreeBSD$
+ */
+
+/***************************************************************************
+ ***************************************************************************
+ ** ng_parse definitions for the H4 node
+ ***************************************************************************
+ ***************************************************************************/
+
+#ifndef _NETGRAPH_H4_PRSE_H_
+#define _NETGRAPH_H4_PRSE_H_ 1
+
+/*
+ * H4 node command list
+ */
+
+/* Stat info */
+static const struct ng_parse_struct_field ng_h4_stat_type_fields[] =
+{
+ { "pckts_recv", &ng_parse_uint32_type, },
+ { "bytes_recv", &ng_parse_uint32_type, },
+ { "pckts_sent", &ng_parse_uint32_type, },
+ { "bytes_sent", &ng_parse_uint32_type, },
+ { "oerrors", &ng_parse_uint32_type, },
+ { "ierrors", &ng_parse_uint32_type, },
+ { NULL, }
+};
+static const struct ng_parse_type ng_h4_stat_type = {
+ &ng_parse_struct_type,
+ &ng_h4_stat_type_fields
+};
+
+static const struct ng_cmdlist ng_h4_cmdlist[] = {
+ {
+ NGM_H4_COOKIE,
+ NGM_H4_NODE_RESET,
+ "reset",
+ NULL,
+ NULL
+ },
+ {
+ NGM_H4_COOKIE,
+ NGM_H4_NODE_GET_STATE,
+ "get_state",
+ NULL,
+ &ng_parse_uint16_type
+ },
+ {
+ NGM_H4_COOKIE,
+ NGM_H4_NODE_GET_DEBUG,
+ "get_debug",
+ NULL,
+ &ng_parse_uint16_type
+ },
+ {
+ NGM_H4_COOKIE,
+ NGM_H4_NODE_SET_DEBUG,
+ "set_debug",
+ &ng_parse_uint16_type,
+ NULL
+ },
+ {
+ NGM_H4_COOKIE,
+ NGM_H4_NODE_GET_QLEN,
+ "get_qlen",
+ NULL,
+ &ng_parse_int32_type
+ },
+ {
+ NGM_H4_COOKIE,
+ NGM_H4_NODE_SET_QLEN,
+ "set_qlen",
+ &ng_parse_int32_type,
+ NULL
+ },
+ {
+ NGM_H4_COOKIE,
+ NGM_H4_NODE_GET_STAT,
+ "get_stat",
+ NULL,
+ &ng_h4_stat_type
+ },
+ {
+ NGM_H4_COOKIE,
+ NGM_H4_NODE_RESET_STAT,
+ "reset_stat",
+ NULL,
+ NULL
+ },
+ { 0, }
+};
+
+#endif /* ndef _NETGRAPH_H4_PRSE_H_ */
+
diff --git a/sys/netgraph/bluetooth/drivers/h4/ng_h4_var.h b/sys/netgraph/bluetooth/drivers/h4/ng_h4_var.h
new file mode 100644
index 0000000..1b0d5b8
--- /dev/null
+++ b/sys/netgraph/bluetooth/drivers/h4/ng_h4_var.h
@@ -0,0 +1,100 @@
+/*
+ * ng_h4_var.h
+ *
+ * Copyright (c) 2001-2002 Maksim Yevmenkin <m_evmenkin@yahoo.com>
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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.
+ *
+ * $Id: ng_h4_var.h,v 1.14 2002/11/03 02:16:31 max Exp $
+ * $FreeBSD$
+ *
+ * Based on:
+ * ---------
+ *
+ * FreeBSD: src/sys/netgraph/ng_tty.h
+ * Author: Archie Cobbs <archie@freebsd.org>
+ */
+
+#ifndef _NETGRAPH_H4_VAR_H_
+#define _NETGRAPH_H4_VAR_H_ 1
+
+/*
+ * Malloc declaration
+ */
+
+#ifndef NG_SEPARATE_MALLOC
+MALLOC_DECLARE(M_NETGRAPH_H4);
+#else
+#define M_NETGRAPH_H4 M_NETGRAPH
+#endif /* NG_SEPARATE_MALLOC */
+
+/*
+ * Debug
+ */
+
+#define NG_H4_ALERT if (sc->debug >= NG_H4_ALERT_LEVEL) printf
+#define NG_H4_ERR if (sc->debug >= NG_H4_ERR_LEVEL) printf
+#define NG_H4_WARN if (sc->debug >= NG_H4_WARN_LEVEL) printf
+#define NG_H4_INFO if (sc->debug >= NG_H4_INFO_LEVEL) printf
+
+#define NG_H4_HIWATER 256 /* High water mark on output */
+
+/*
+ * Per-node private info
+ */
+
+typedef struct ng_h4_info {
+ struct tty *tp; /* Terminal device */
+ node_p node; /* Netgraph node */
+
+ u_int32_t flags; /* Flags */
+#define NG_H4_TIMEOUT (1 << 0) /* A timeout is pending */
+
+ ng_h4_node_debug_ep debug; /* Debug level */
+ ng_h4_node_state_ep state; /* State */
+
+ ng_h4_node_stat_ep stat;
+#define NG_H4_STAT_PCKTS_SENT(s) (s).pckts_sent ++
+#define NG_H4_STAT_BYTES_SENT(s, n) (s).bytes_sent += (n)
+#define NG_H4_STAT_PCKTS_RECV(s) (s).pckts_recv ++
+#define NG_H4_STAT_BYTES_RECV(s, n) (s).bytes_recv += (n)
+#define NG_H4_STAT_OERROR(s) (s).oerrors ++
+#define NG_H4_STAT_IERROR(s) (s).ierrors ++
+#define NG_H4_STAT_RESET(s) bzero(&(s), sizeof((s)))
+
+ ng_bt_mbufq_t outq; /* Queue of outgoing mbuf's */
+#define NG_H4_DEFAULTQLEN 12 /* XXX max number of mbuf's in outq */
+
+#define NG_H4_IBUF_SIZE 1024 /* XXX must be big enough to hold full
+ frame */
+ u_int8_t ibuf[NG_H4_IBUF_SIZE]; /* Incoming data */
+ u_int32_t got; /* Number of bytes we have received */
+ u_int32_t want; /* Number of bytes we want to receive */
+
+ hook_p hook; /* Upstream hook */
+ struct callout_handle timo; /* See man timeout(9) */
+} ng_h4_info_t;
+typedef ng_h4_info_t * ng_h4_info_p;
+
+#endif /* _NETGRAPH_H4_VAR_H_ */
+
diff --git a/sys/netgraph/bluetooth/drivers/ubt/TODO b/sys/netgraph/bluetooth/drivers/ubt/TODO
new file mode 100644
index 0000000..aba45ed
--- /dev/null
+++ b/sys/netgraph/bluetooth/drivers/ubt/TODO
@@ -0,0 +1,30 @@
+# $FreeBSD$
+$Id: TODO,v 1.1.1.1 2002/06/09 20:21:47 max Exp $
+
+1) SMP/Locking
+
+ The code makes use of ng_send_fn() whenever possible. Just
+ need to verify and make sure i did it right
+
+2) Review USB ATTACH function
+
+ It is a bit ugly now. Probably need a better way to discover
+ USB device configuration.
+
+2) Firmware upgrade
+
+ According to Bluetooth spec device may present third interface
+ to perform firmware upgrade. 3Com USB Bluetooth dongle has
+ such interface. Need to implement set of Netgraph messages.
+
+3) Understand and fix isoc. USB transfers (SCO data)
+
+ Currenty device reports that is got zero bytes and calls
+ isoc_in_complete callback over and over again. Why?
+ Also might need to setup at least two isoc. transfers in
+ both directions and switch them on the fly. Just to ensure
+ there at least one transfer at any time ready to run.
+
+4) Currently interrupt transfers are done as bulk-in transfers
+
+ Need to check if that is allowed.
diff --git a/sys/netgraph/bluetooth/drivers/ubt/ng_ubt.c b/sys/netgraph/bluetooth/drivers/ubt/ng_ubt.c
new file mode 100644
index 0000000..982b939
--- /dev/null
+++ b/sys/netgraph/bluetooth/drivers/ubt/ng_ubt.c
@@ -0,0 +1,2166 @@
+/*
+ * ng_ubt.c
+ *
+ * Copyright (c) 2001-2002 Maksim Yevmenkin <m_evmenkin@yahoo.com>
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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.
+ *
+ * $Id: ng_ubt.c,v 1.1 2002/11/09 19:09:02 max Exp $
+ * $FreeBSD$
+ */
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/bus.h>
+#include <sys/endian.h>
+#include <sys/interrupt.h>
+#include <sys/mbuf.h>
+#include <sys/malloc.h>
+#include <sys/kernel.h>
+
+#include <sys/socket.h>
+#include <net/if.h>
+#include <net/if_var.h>
+
+#include <dev/usb/usb.h>
+#include <dev/usb/usbdi.h>
+#include <dev/usb/usbdi_util.h>
+#include <dev/usb/usbdivar.h>
+#include <dev/usb/usbdevs.h>
+
+#include <netgraph/ng_message.h>
+#include <netgraph/netgraph.h>
+#include <netgraph/ng_parse.h>
+#include <ng_bluetooth.h>
+#include <ng_hci.h>
+#include "ng_ubt.h"
+#include "ng_ubt_var.h"
+
+/*
+ * USB methods
+ */
+
+USB_DECLARE_DRIVER(ubt);
+
+Static usbd_status ubt_request_start (ubt_softc_p, struct mbuf *);
+Static void ubt_request_complete (usbd_xfer_handle,
+ usbd_private_handle, usbd_status);
+
+Static usbd_status ubt_intr_start (ubt_softc_p);
+Static void ubt_intr_complete (usbd_xfer_handle,
+ usbd_private_handle, usbd_status);
+
+Static usbd_status ubt_bulk_in_start (ubt_softc_p);
+Static void ubt_bulk_in_complete (usbd_xfer_handle,
+ usbd_private_handle, usbd_status);
+
+Static usbd_status ubt_bulk_out_start (ubt_softc_p, struct mbuf *);
+Static void ubt_bulk_out_complete (usbd_xfer_handle,
+ usbd_private_handle, usbd_status);
+
+Static usbd_status ubt_isoc_in_start (ubt_softc_p);
+Static void ubt_isoc_in_complete (usbd_xfer_handle,
+ usbd_private_handle, usbd_status);
+
+Static usbd_status ubt_isoc_out_start (ubt_softc_p, struct mbuf *);
+Static void ubt_isoc_out_complete (usbd_xfer_handle,
+ usbd_private_handle, usbd_status);
+
+Static void ubt_swi_intr (void *);
+
+/*
+ * Netgraph methods
+ */
+
+Static ng_constructor_t ng_ubt_constructor;
+Static ng_shutdown_t ng_ubt_shutdown;
+Static ng_newhook_t ng_ubt_newhook;
+Static ng_connect_t ng_ubt_connect;
+Static ng_disconnect_t ng_ubt_disconnect;
+Static ng_rcvmsg_t ng_ubt_rcvmsg;
+Static ng_rcvdata_t ng_ubt_rcvdata;
+Static void ng_ubt_reset (ubt_softc_p);
+
+/* Queue length */
+Static const struct ng_parse_struct_field ng_ubt_node_qlen_type_fields[] =
+{
+ { "queue", &ng_parse_int32_type, },
+ { "qlen", &ng_parse_int32_type, },
+ { NULL, }
+};
+Static const struct ng_parse_type ng_ubt_node_qlen_type = {
+ &ng_parse_struct_type,
+ &ng_ubt_node_qlen_type_fields
+};
+
+/* Stat info */
+Static const struct ng_parse_struct_field ng_ubt_node_stat_type_fields[] =
+{
+ { "pckts_recv", &ng_parse_uint32_type, },
+ { "bytes_recv", &ng_parse_uint32_type, },
+ { "pckts_sent", &ng_parse_uint32_type, },
+ { "bytes_sent", &ng_parse_uint32_type, },
+ { "oerrors", &ng_parse_uint32_type, },
+ { "ierrors", &ng_parse_uint32_type, },
+ { NULL, }
+};
+Static const struct ng_parse_type ng_ubt_node_stat_type = {
+ &ng_parse_struct_type,
+ &ng_ubt_node_stat_type_fields
+};
+
+/* Netgraph node command list */
+Static const struct ng_cmdlist ng_ubt_cmdlist[] = {
+{
+ NGM_UBT_COOKIE,
+ NGM_UBT_NODE_SET_DEBUG,
+ "set_debug",
+ &ng_parse_uint16_type,
+ NULL
+},
+{
+ NGM_UBT_COOKIE,
+ NGM_UBT_NODE_GET_DEBUG,
+ "get_debug",
+ NULL,
+ &ng_parse_uint16_type
+},
+{
+ NGM_UBT_COOKIE,
+ NGM_UBT_NODE_SET_QLEN,
+ "set_qlen",
+ &ng_ubt_node_qlen_type,
+ NULL
+},
+{
+ NGM_UBT_COOKIE,
+ NGM_UBT_NODE_GET_QLEN,
+ "get_qlen",
+ &ng_ubt_node_qlen_type,
+ &ng_ubt_node_qlen_type
+},
+{
+ NGM_UBT_COOKIE,
+ NGM_UBT_NODE_GET_STAT,
+ "get_stat",
+ NULL,
+ &ng_ubt_node_stat_type
+},
+{
+ NGM_UBT_COOKIE,
+ NGM_UBT_NODE_RESET_STAT,
+ "reset_stat",
+ NULL,
+ NULL
+},
+{ 0, }
+};
+
+/* Netgraph node type */
+Static struct ng_type typestruct = {
+ NG_ABI_VERSION,
+ NG_UBT_NODE_TYPE, /* typename */
+ NULL, /* modevent */
+ ng_ubt_constructor, /* constructor */
+ ng_ubt_rcvmsg, /* control message */
+ ng_ubt_shutdown, /* destructor */
+ ng_ubt_newhook, /* new hook */
+ NULL, /* find hook */
+ ng_ubt_connect, /* connect hook */
+ ng_ubt_rcvdata, /* data */
+ ng_ubt_disconnect, /* disconnect hook */
+ ng_ubt_cmdlist /* node command list */
+};
+NETGRAPH_INIT(ubt, &typestruct);
+
+/*
+ * Module
+ */
+
+DRIVER_MODULE(ubt, uhub, ubt_driver, ubt_devclass, usbd_driver_load, 0);
+MODULE_VERSION(ng_ubt, NG_BLUETOOTH_VERSION);
+
+/****************************************************************************
+ ****************************************************************************
+ ** USB specific
+ ****************************************************************************
+ ****************************************************************************/
+
+/*
+ * Probe for a USB Bluetooth device
+ */
+
+USB_MATCH(ubt)
+{
+ Static struct usb_devno const ubt_devices[] = {
+ { USB_VENDOR_3COM, USB_PRODUCT_3COM_3CREB96 },
+ { USB_VENDOR_MITSUMI, USB_PRODUCT_MITSUMI_BT_DONGLE },
+ { USB_VENDOR_TDK, USB_PRODUCT_TDK_BT_DONGLE },
+ { USB_VENDOR_MSI, USB_PRODUCT_MSI_BT_DONGLE },
+ { USB_VENDOR_BROADCOM, USB_PRODUCT_DBW_120M_BT_DONGLE },
+ { USB_VENDOR_EPOX, USB_PRODUCT_BT_DG02_DONGLE },
+ { 0, 0 }
+ };
+
+ USB_MATCH_START(ubt, uaa);
+
+ if (uaa->iface == NULL ||
+ usb_lookup(ubt_devices, uaa->vendor, uaa->product) == NULL)
+ return (UMATCH_NONE);
+
+ return (UMATCH_VENDOR_PRODUCT);
+} /* USB_MATCH(ubt) */
+
+/*
+ * Attach the device
+ */
+
+USB_ATTACH(ubt)
+{
+ USB_ATTACH_START(ubt, sc, uaa);
+ usb_config_descriptor_t *cd = NULL;
+ usb_interface_descriptor_t *id = NULL;
+ usb_endpoint_descriptor_t *ed = NULL;
+ char devinfo[1024];
+ usbd_status error;
+ int i, ai, alt_no, isoc_in, isoc_out,
+ isoc_isize, isoc_osize;
+
+ /* Get USB device info */
+ sc->sc_udev = uaa->device;
+ usbd_devinfo(sc->sc_udev, 0, devinfo);
+ USB_ATTACH_SETUP;
+ printf("%s: %s\n", USBDEVNAME(sc->sc_dev), devinfo);
+
+ /*
+ * Initialize device softc structure
+ */
+
+ /* State */
+ sc->sc_debug = NG_UBT_WARN_LEVEL;
+ sc->sc_flags = 0;
+ NG_UBT_STAT_RESET(sc->sc_stat);
+
+ /* Interfaces */
+ sc->sc_iface0 = sc->sc_iface1 = NULL;
+
+ /* Input queue */
+ bzero(&sc->sc_inq, sizeof(sc->sc_inq));
+ sc->sc_inq.ifq_maxlen = UBT_DEFAULT_QLEN;
+ mtx_init(&sc->sc_inq.ifq_mtx, "UBT inq", NULL, MTX_DEF);
+
+ /* Interrupt pipe */
+ sc->sc_intr_ep = -1;
+ sc->sc_intr_pipe = NULL;
+ sc->sc_intr_xfer = NULL;
+ sc->sc_intr_buffer = NULL;
+
+ /* Control pipe */
+ sc->sc_ctrl_xfer = NULL;
+ sc->sc_ctrl_buffer = NULL;
+ bzero(&sc->sc_cmdq, sizeof(sc->sc_cmdq));
+ sc->sc_cmdq.ifq_maxlen = UBT_DEFAULT_QLEN;
+ mtx_init(&sc->sc_cmdq.ifq_mtx, "UBT cmdq", NULL, MTX_DEF);
+
+ /* Bulk-in pipe */
+ sc->sc_bulk_in_ep = -1;
+ sc->sc_bulk_in_pipe = NULL;
+ sc->sc_bulk_in_xfer = NULL;
+ sc->sc_bulk_in_buffer = NULL;
+
+ /* Bulk-out pipe */
+ sc->sc_bulk_out_ep = -1;
+ sc->sc_bulk_out_pipe = NULL;
+ sc->sc_bulk_out_xfer = NULL;
+ sc->sc_bulk_out_buffer = NULL;
+ bzero(&sc->sc_aclq, sizeof(sc->sc_aclq));
+ sc->sc_aclq.ifq_maxlen = UBT_DEFAULT_QLEN;
+ mtx_init(&sc->sc_aclq.ifq_mtx, "UBT aclq", NULL, MTX_DEF);
+
+ /* Isoc-in pipe */
+ sc->sc_isoc_in_ep = -1;
+ sc->sc_isoc_in_pipe = NULL;
+ sc->sc_isoc_in_xfer = NULL;
+
+ /* Isoc-out pipe */
+ sc->sc_isoc_out_ep = -1;
+ sc->sc_isoc_out_pipe = NULL;
+ sc->sc_isoc_out_xfer = NULL;
+ sc->sc_isoc_size = -1;
+ bzero(&sc->sc_scoq, sizeof(sc->sc_scoq));
+ sc->sc_scoq.ifq_maxlen = UBT_DEFAULT_QLEN;
+ mtx_init(&sc->sc_scoq.ifq_mtx, "UBT scoq", NULL, MTX_DEF);
+
+ /* Netgraph part */
+ sc->sc_node = NULL;
+ sc->sc_hook = NULL;
+
+ /* Attach SWI handler to TTY SWI thread */
+ sc->sc_ith = NULL;
+ if (swi_add(&tty_ithd, USBDEVNAME(sc->sc_dev),
+ ubt_swi_intr, sc, SWI_TTY, 0, &sc->sc_ith) < 0) {
+ printf("%s: Could not setup SWI ISR\n", USBDEVNAME(sc->sc_dev));
+ goto bad;
+ }
+
+ /*
+ * XXX set configuration?
+ *
+ * Configure Bluetooth USB device. Discover all required USB interfaces
+ * and endpoints.
+ *
+ * USB device must present two interfaces:
+ * 1) Interface 0 that has 3 endpoints
+ * 1) Interrupt endpoint to receive HCI events
+ * 2) Bulk IN endpoint to receive ACL data
+ * 3) Bulk OUT endpoint to send ACL data
+ *
+ * 2) Interface 1 then has 2 endpoints
+ * 1) Isochronous IN endpoint to receive SCO data
+ * 2) Isochronous OUT endpoint to send SCO data
+ *
+ * Interface 1 (with isochronous endpoints) has several alternate
+ * configurations with different packet size.
+ */
+
+ /*
+ * Interface 0
+ */
+
+ error = usbd_device2interface_handle(sc->sc_udev, 0, &sc->sc_iface0);
+ if (error || sc->sc_iface0 == NULL) {
+ printf("%s: Could not get interface 0 handle. %s (%d), " \
+ "handle=%p\n", USBDEVNAME(sc->sc_dev),
+ usbd_errstr(error), error, sc->sc_iface0);
+ goto bad;
+ }
+
+ id = usbd_get_interface_descriptor(sc->sc_iface0);
+ if (id == NULL) {
+ printf("%s: Could not get interface 0 descriptor\n",
+ USBDEVNAME(sc->sc_dev));
+ goto bad;
+ }
+ if (id->bInterfaceClass != UICLASS_WIRELESS_CONTROLLER ||
+ id->bInterfaceSubClass != UISUBCLASS_RF_CONTROLLER ||
+ id->bInterfaceProtocol != UIPROTO_BLUETOOTH) {
+ printf("%s: Interface 0 is not supported, " \
+ "bInterfaceClass=%#x, bInterfaceSubClass=%#x, "\
+ "bInterfaceProtocol=%#x\n", USBDEVNAME(sc->sc_dev),
+ id->bInterfaceClass, id->bInterfaceSubClass,
+ id->bInterfaceProtocol);
+ goto bad;
+ }
+
+ for (i = 0; i < id->bNumEndpoints; i ++) {
+ ed = usbd_interface2endpoint_descriptor(sc->sc_iface0, i);
+ if (ed == NULL) {
+ printf("%s: Could not read endpoint descriptor for " \
+ "interface 0, i=%d\n", USBDEVNAME(sc->sc_dev),
+ i);
+ goto bad;
+ }
+
+ switch (UE_GET_XFERTYPE(ed->bmAttributes)) {
+ case UE_BULK:
+ if (UE_GET_DIR(ed->bEndpointAddress) == UE_DIR_IN)
+ sc->sc_bulk_in_ep = ed->bEndpointAddress;
+ else
+ sc->sc_bulk_out_ep = ed->bEndpointAddress;
+ break;
+
+ case UE_INTERRUPT:
+ sc->sc_intr_ep = ed->bEndpointAddress;
+ break;
+ }
+ }
+
+ /* Check if we got everything we wanted on Interface 0 */
+ if (sc->sc_intr_ep == -1) {
+ printf("%s: Could not detect interrupt endpoint\n",
+ USBDEVNAME(sc->sc_dev));
+ goto bad;
+ }
+ if (sc->sc_bulk_in_ep == -1) {
+ printf("%s: Could not detect bulk-in endpoint\n",
+ USBDEVNAME(sc->sc_dev));
+ goto bad;
+ }
+ if (sc->sc_bulk_out_ep == -1) {
+ printf("%s: Could not detect bulk-out endpoint\n",
+ USBDEVNAME(sc->sc_dev));
+ goto bad;
+ }
+
+ printf("%s: Interface 0 endpoints: interrupt=%#x, bulk-in=%#x, " \
+ "bulk-out=%#x\n", USBDEVNAME(sc->sc_dev),
+ sc->sc_intr_ep, sc->sc_bulk_in_ep, sc->sc_bulk_out_ep);
+
+ /*
+ * Interface 1
+ */
+
+ cd = usbd_get_config_descriptor(sc->sc_udev);
+ if (cd == NULL) {
+ printf("%s: Could not get device configuration descriptor\n",
+ USBDEVNAME(sc->sc_dev));
+ goto bad;
+ }
+
+ error = usbd_device2interface_handle(sc->sc_udev, 1, &sc->sc_iface1);
+ if (error || sc->sc_iface1 == NULL) {
+ printf("%s: Could not get interface 1 handle. %s (%d), " \
+ "handle=%p\n", USBDEVNAME(sc->sc_dev),
+ usbd_errstr(error), error, sc->sc_iface1);
+ goto bad;
+ }
+
+ id = usbd_get_interface_descriptor(sc->sc_iface1);
+ if (id == NULL) {
+ printf("%s: Could not get interface 1 descriptor\n",
+ USBDEVNAME(sc->sc_dev));
+ goto bad;
+ }
+ if (id->bInterfaceClass != UICLASS_WIRELESS_CONTROLLER ||
+ id->bInterfaceSubClass != UISUBCLASS_RF_CONTROLLER ||
+ id->bInterfaceProtocol != UIPROTO_BLUETOOTH) {
+ printf("%s: Interface 1 is not supported, " \
+ "bInterfaceClass=%#x, bInterfaceSubClass=%#x, "\
+ "bInterfaceProtocol=%#x\n", USBDEVNAME(sc->sc_dev),
+ id->bInterfaceClass, id->bInterfaceSubClass,
+ id->bInterfaceProtocol);
+ goto bad;
+ }
+
+ /*
+ * Scan all alternate configurations for interface 1
+ */
+
+ alt_no = -1;
+
+ for (ai = 0; ai < usbd_get_no_alts(cd, 1); ai++) {
+ error = usbd_set_interface(sc->sc_iface1, ai);
+ if (error) {
+ printf("%s: [SCAN] Could not set alternate " \
+ "configuration %d for interface 1. %s (%d)\n",
+ USBDEVNAME(sc->sc_dev), ai, usbd_errstr(error),
+ error);
+ goto bad;
+ }
+ id = usbd_get_interface_descriptor(sc->sc_iface1);
+ if (id == NULL) {
+ printf("%s: Could not get interface 1 descriptor for " \
+ "alternate configuration %d\n",
+ USBDEVNAME(sc->sc_dev), ai);
+ goto bad;
+ }
+
+ isoc_in = isoc_out = -1;
+ isoc_isize = isoc_osize = 0;
+
+ for (i = 0; i < id->bNumEndpoints; i ++) {
+ ed = usbd_interface2endpoint_descriptor(sc->sc_iface1, i);
+ if (ed == NULL) {
+ printf("%s: Could not read endpoint " \
+ "descriptor for interface 1, " \
+ "alternate configuration %d, i=%d\n",
+ USBDEVNAME(sc->sc_dev), ai, i);
+ goto bad;
+ }
+
+ if (UE_GET_XFERTYPE(ed->bmAttributes) != UE_ISOCHRONOUS)
+ continue;
+
+ if (UE_GET_DIR(ed->bEndpointAddress) == UE_DIR_IN) {
+ isoc_in = ed->bEndpointAddress;
+ isoc_isize = UGETW(ed->wMaxPacketSize);
+ } else {
+ isoc_out = ed->bEndpointAddress;
+ isoc_osize = UGETW(ed->wMaxPacketSize);
+ }
+ }
+
+ /*
+ * Make sure that configuration looks sane and if so
+ * update current settings
+ */
+
+ if (isoc_in != -1 && isoc_out != -1 &&
+ isoc_isize > 0 && isoc_osize > 0 &&
+ isoc_isize == isoc_osize && isoc_isize > sc->sc_isoc_size) {
+ sc->sc_isoc_in_ep = isoc_in;
+ sc->sc_isoc_out_ep = isoc_out;
+ sc->sc_isoc_size = isoc_isize;
+ alt_no = ai;
+ }
+ }
+
+ /* Check if we got everything we wanted on Interface 0 */
+ if (sc->sc_isoc_in_ep == -1) {
+ printf("%s: Could not detect isoc-in endpoint\n",
+ USBDEVNAME(sc->sc_dev));
+ goto bad;
+ }
+ if (sc->sc_isoc_out_ep == -1) {
+ printf("%s: Could not detect isoc-out endpoint\n",
+ USBDEVNAME(sc->sc_dev));
+ goto bad;
+ }
+ if (sc->sc_isoc_size <= 0) {
+ printf("%s: Invalid isoc. packet size=%d\n",
+ USBDEVNAME(sc->sc_dev), sc->sc_isoc_size);
+ goto bad;
+ }
+
+ error = usbd_set_interface(sc->sc_iface1, alt_no);
+ if (error) {
+ printf("%s: Could not set alternate configuration " \
+ "%d for interface 1. %s (%d)\n", USBDEVNAME(sc->sc_dev),
+ alt_no, usbd_errstr(error), error);
+ goto bad;
+ }
+
+ /* Allocate USB transfer handles and buffers */
+ sc->sc_ctrl_xfer = usbd_alloc_xfer(sc->sc_udev);
+ if (sc->sc_ctrl_xfer == NULL) {
+ printf("%s: Could not allocate control xfer handle\n",
+ USBDEVNAME(sc->sc_dev));
+ goto bad;
+ }
+ sc->sc_ctrl_buffer = usbd_alloc_buffer(sc->sc_ctrl_xfer,
+ UBT_CTRL_BUFFER_SIZE);
+ if (sc->sc_ctrl_buffer == NULL) {
+ printf("%s: Could not allocate control buffer\n",
+ USBDEVNAME(sc->sc_dev));
+ goto bad;
+ }
+
+ sc->sc_intr_xfer = usbd_alloc_xfer(sc->sc_udev);
+ if (sc->sc_intr_xfer == NULL) {
+ printf("%s: Could not allocate interrupt xfer handle\n",
+ USBDEVNAME(sc->sc_dev));
+ goto bad;
+ }
+ sc->sc_intr_buffer = usbd_alloc_buffer(sc->sc_intr_xfer,
+ UBT_INTR_BUFFER_SIZE);
+ if (sc->sc_intr_buffer == NULL) {
+ printf("%s: Could not allocate interrupt buffer\n",
+ USBDEVNAME(sc->sc_dev));
+ goto bad;
+ }
+
+ sc->sc_bulk_in_xfer = usbd_alloc_xfer(sc->sc_udev);
+ if (sc->sc_bulk_in_xfer == NULL) {
+ printf("%s: Could not allocate bulk-in xfer handle\n",
+ USBDEVNAME(sc->sc_dev));
+ goto bad;
+ }
+ sc->sc_bulk_in_buffer = usbd_alloc_buffer(sc->sc_bulk_in_xfer,
+ UBT_BULK_BUFFER_SIZE);
+ if (sc->sc_bulk_in_buffer == NULL) {
+ printf("%s: Could not allocate bulk-in buffer\n",
+ USBDEVNAME(sc->sc_dev));
+ goto bad;
+ }
+
+ sc->sc_bulk_out_xfer = usbd_alloc_xfer(sc->sc_udev);
+ if (sc->sc_bulk_out_xfer == NULL) {
+ printf("%s: Could not allocate bulk-out xfer handle\n",
+ USBDEVNAME(sc->sc_dev));
+ goto bad;
+ }
+ sc->sc_bulk_out_buffer = usbd_alloc_buffer(sc->sc_bulk_out_xfer,
+ UBT_BULK_BUFFER_SIZE);
+ if (sc->sc_bulk_out_buffer == NULL) {
+ printf("%s: Could not allocate bulk-out buffer\n",
+ USBDEVNAME(sc->sc_dev));
+ goto bad;
+ }
+
+ /*
+ * Allocate buffers for isoc. transfers
+ */
+
+ sc->sc_isoc_nframes = (UBT_ISOC_BUFFER_SIZE / sc->sc_isoc_size) + 1;
+
+ sc->sc_isoc_in_xfer = usbd_alloc_xfer(sc->sc_udev);
+ if (sc->sc_isoc_in_xfer == NULL) {
+ printf("%s: Could not allocate isoc-in xfer handle\n",
+ USBDEVNAME(sc->sc_dev));
+ goto bad;
+ }
+ sc->sc_isoc_in_buffer = usbd_alloc_buffer(sc->sc_isoc_in_xfer,
+ sc->sc_isoc_nframes * sc->sc_isoc_size);
+ if (sc->sc_isoc_in_buffer == NULL) {
+ printf("%s: Could not allocate isoc-in buffer\n",
+ USBDEVNAME(sc->sc_dev));
+ goto bad;
+ }
+ sc->sc_isoc_in_frlen = malloc(sizeof(u_int16_t) * sc->sc_isoc_nframes,
+ M_USBDEV, M_NOWAIT);
+ if (sc->sc_isoc_in_frlen == NULL) {
+ printf("%s: Could not allocate isoc-in frame sizes buffer\n",
+ USBDEVNAME(sc->sc_dev));
+ goto bad;
+ }
+
+ sc->sc_isoc_out_xfer = usbd_alloc_xfer(sc->sc_udev);
+ if (sc->sc_isoc_out_xfer == NULL) {
+ printf("%s: Could not allocate isoc-out xfer handle\n",
+ USBDEVNAME(sc->sc_dev));
+ goto bad;
+ }
+ sc->sc_isoc_out_buffer = usbd_alloc_buffer(sc->sc_isoc_out_xfer,
+ sc->sc_isoc_nframes * sc->sc_isoc_size);
+ if (sc->sc_isoc_out_buffer == NULL) {
+ printf("%s: Could not allocate isoc-out buffer\n",
+ USBDEVNAME(sc->sc_dev));
+ goto bad;
+ }
+ sc->sc_isoc_out_frlen = malloc(sizeof(u_int16_t) * sc->sc_isoc_nframes,
+ M_USBDEV, M_NOWAIT);
+ if (sc->sc_isoc_out_frlen == NULL) {
+ printf("%s: Could not allocate isoc-out frame sizes buffer\n",
+ USBDEVNAME(sc->sc_dev));
+ goto bad;
+ }
+
+ printf("%s: Interface 1 (alt.config %d) endpoints: isoc-in=%#x, " \
+ "isoc-out=%#x; wMaxPacketSize=%d; nframes=%d, buffer size=%d\n",
+ USBDEVNAME(sc->sc_dev), alt_no, sc->sc_isoc_in_ep,
+ sc->sc_isoc_out_ep, sc->sc_isoc_size, sc->sc_isoc_nframes,
+ (sc->sc_isoc_nframes * sc->sc_isoc_size));
+
+ /* Create Netgraph node */
+ if (ng_make_node_common(&typestruct, &sc->sc_node) != 0) {
+ printf("%s: Could not create Netgraph node\n",
+ USBDEVNAME(sc->sc_dev));
+ sc->sc_node = NULL;
+ goto bad;
+ }
+
+ /* Name node */
+ if (ng_name_node(sc->sc_node, USBDEVNAME(sc->sc_dev)) != 0) {
+ printf("%s: Could not name Netgraph node\n",
+ USBDEVNAME(sc->sc_dev));
+ NG_NODE_UNREF(sc->sc_node);
+ sc->sc_node = NULL;
+ goto bad;
+ }
+
+ NG_NODE_SET_PRIVATE(sc->sc_node, sc);
+
+ /*
+ * XXX Is that correct?
+ * Claim all interfaces on the device
+ */
+
+ for (i = 0; i < uaa->nifaces; i++)
+ uaa->ifaces[i] = NULL;
+
+ USB_ATTACH_SUCCESS_RETURN;
+bad:
+ ubt_detach(self);
+
+ USB_ATTACH_ERROR_RETURN;
+} /* USB_ATTACH(ubt) */
+
+/*
+ * Detach the device
+ */
+
+USB_DETACH(ubt)
+{
+ USB_DETACH_START(ubt, sc);
+
+ ng_ubt_reset(sc);
+
+ /* Destroy Netgraph node */
+ if (sc->sc_node != NULL) {
+ NG_NODE_SET_PRIVATE(sc->sc_node, NULL);
+ ng_rmnode_self(sc->sc_node);
+ sc->sc_node = NULL;
+ }
+
+ /* Destroy USB transfer handles */
+ if (sc->sc_ctrl_xfer != NULL) {
+ usbd_free_xfer(sc->sc_ctrl_xfer);
+ sc->sc_ctrl_xfer = NULL;
+ }
+
+ if (sc->sc_intr_xfer != NULL) {
+ usbd_free_xfer(sc->sc_intr_xfer);
+ sc->sc_intr_xfer = NULL;
+ }
+
+ if (sc->sc_bulk_in_xfer != NULL) {
+ usbd_free_xfer(sc->sc_bulk_in_xfer);
+ sc->sc_bulk_in_xfer = NULL;
+ }
+ if (sc->sc_bulk_out_xfer != NULL) {
+ usbd_free_xfer(sc->sc_bulk_out_xfer);
+ sc->sc_bulk_out_xfer = NULL;
+ }
+
+ if (sc->sc_isoc_in_xfer != NULL) {
+ usbd_free_xfer(sc->sc_isoc_in_xfer);
+ sc->sc_isoc_in_xfer = NULL;
+ }
+ if (sc->sc_isoc_out_xfer != NULL) {
+ usbd_free_xfer(sc->sc_isoc_out_xfer);
+ sc->sc_isoc_out_xfer = NULL;
+ }
+
+ /* Destroy isoc. frame size buffers */
+ if (sc->sc_isoc_in_frlen != NULL) {
+ free(sc->sc_isoc_in_frlen, M_USBDEV);
+ sc->sc_isoc_in_frlen = NULL;
+ }
+ if (sc->sc_isoc_out_frlen != NULL) {
+ free(sc->sc_isoc_out_frlen, M_USBDEV);
+ sc->sc_isoc_out_frlen = NULL;
+ }
+
+ if (sc->sc_ith != NULL) {
+ ithread_remove_handler(sc->sc_ith);
+ sc->sc_ith = NULL;
+ }
+
+ /* Destroy queues */
+ IF_DRAIN(&sc->sc_cmdq);
+ IF_DRAIN(&sc->sc_aclq);
+ IF_DRAIN(&sc->sc_scoq);
+ IF_DRAIN(&sc->sc_inq);
+
+ mtx_destroy(&sc->sc_cmdq.ifq_mtx);
+ mtx_destroy(&sc->sc_aclq.ifq_mtx);
+ mtx_destroy(&sc->sc_scoq.ifq_mtx);
+ mtx_destroy(&sc->sc_inq.ifq_mtx);
+
+ return (0);
+} /* USB_DETACH(ubt) */
+
+/*
+ * Start USB control request (HCI command)
+ */
+
+Static usbd_status
+ubt_request_start(ubt_softc_p sc, struct mbuf *m)
+{
+ usb_device_request_t req;
+ usbd_status status = USBD_NORMAL_COMPLETION;
+
+ IF_LOCK(&sc->sc_cmdq);
+
+ if (m != NULL) {
+ if (_IF_QFULL(&sc->sc_cmdq)) {
+ NG_UBT_ERR(
+"%s: %s - Dropping HCI command frame, len=%d. Queue full\n",
+ __func__, USBDEVNAME(sc->sc_dev),
+ m->m_pkthdr.len);
+
+ _IF_DROP(&sc->sc_cmdq);
+ NG_UBT_STAT_OERROR(sc->sc_stat);
+
+ NG_FREE_M(m);
+ } else
+ _IF_ENQUEUE(&sc->sc_cmdq, m);
+ } else
+ sc->sc_flags &= ~UBT_CMD_XMIT;
+
+ if (sc->sc_flags & UBT_CMD_XMIT) {
+ NG_UBT_INFO(
+"%s: %s - Another control request is pending\n",
+ __func__, USBDEVNAME(sc->sc_dev));
+ goto done;
+ }
+
+ _IF_DEQUEUE(&sc->sc_cmdq, m);
+ if (m == NULL) {
+ NG_UBT_INFO(
+"%s: %s - HCI command queue is empty\n", __func__, USBDEVNAME(sc->sc_dev));
+ goto done;
+ }
+
+ /*
+ * Check HCI command frame size and copy it back
+ * to linear USB transfer buffer.
+ */
+
+ if (m->m_pkthdr.len > UBT_CTRL_BUFFER_SIZE)
+ panic(
+"%s: %s - HCI command frame too big, size=%d, len=%d\n",
+ __func__, USBDEVNAME(sc->sc_dev), UBT_CTRL_BUFFER_SIZE,
+ m->m_pkthdr.len);
+
+ m_copydata(m, 0, m->m_pkthdr.len, sc->sc_ctrl_buffer);
+
+ /* Initialize a USB control request and then schedule it */
+
+ bzero(&req, sizeof(req));
+ req.bmRequestType = UBT_HCI_REQUEST;
+ USETW(req.wLength, m->m_pkthdr.len);
+
+ NG_UBT_INFO(
+"%s: %s - Sending control request, bmRequestType=%#x, wLength=%d\n",
+ __func__, USBDEVNAME(sc->sc_dev), req.bmRequestType,
+ UGETW(req.wLength));
+
+ usbd_setup_default_xfer(
+ sc->sc_ctrl_xfer,
+ sc->sc_udev,
+ (usbd_private_handle) sc,
+ USBD_DEFAULT_TIMEOUT, /* XXX */
+ &req,
+ sc->sc_ctrl_buffer,
+ m->m_pkthdr.len,
+ USBD_NO_COPY,
+ ubt_request_complete);
+
+ status = usbd_transfer(sc->sc_ctrl_xfer);
+ if (status && status != USBD_IN_PROGRESS) {
+ NG_UBT_ERR(
+"%s: %s - Could not start control request. %s (%d)\n",
+ __func__, USBDEVNAME(sc->sc_dev),
+ usbd_errstr(status), status);
+
+ _IF_DROP(&sc->sc_cmdq); /* XXX */
+ NG_UBT_STAT_OERROR(sc->sc_stat);
+
+ /* XXX FIXME: should we try to resubmit another request? */
+ } else {
+ NG_UBT_INFO(
+"%s: %s - Control request has been started\n",
+ __func__, USBDEVNAME(sc->sc_dev));
+
+ sc->sc_flags |= UBT_CMD_XMIT;
+
+ status = USBD_NORMAL_COMPLETION;
+ }
+
+ NG_FREE_M(m);
+done:
+ IF_UNLOCK(&sc->sc_cmdq);
+
+ return (status);
+} /* ubt_request_start */
+
+/*
+ * USB control request callback
+ */
+
+Static void
+ubt_request_complete(usbd_xfer_handle h, usbd_private_handle p, usbd_status s)
+{
+ ubt_softc_p sc = (ubt_softc_p) p;
+
+ if (s == USBD_CANCELLED) {
+ NG_UBT_INFO(
+"%s: %s - Control request cancelled\n", __func__, USBDEVNAME(sc->sc_dev));
+
+ return;
+ }
+
+ if (s != USBD_NORMAL_COMPLETION) {
+ NG_UBT_ERR(
+"%s: %s - Control request failed. %s (%d)\n",
+ __func__, USBDEVNAME(sc->sc_dev), usbd_errstr(s), s);
+
+ if (s == USBD_STALLED)
+ usbd_clear_endpoint_stall_async(h->pipe);
+
+ NG_UBT_STAT_OERROR(sc->sc_stat);
+ } else {
+ NG_UBT_INFO(
+"%s: %s - Sent %d bytes to control pipe\n",
+ __func__, USBDEVNAME(sc->sc_dev), h->actlen);
+
+ NG_UBT_STAT_BYTES_SENT(sc->sc_stat, h->actlen);
+ NG_UBT_STAT_PCKTS_SENT(sc->sc_stat);
+ }
+
+ ubt_request_start(sc, NULL /* completed request */);
+} /* ubt_request_complete */
+
+/*
+ * Start interrupt transfer
+ */
+
+Static usbd_status
+ubt_intr_start(ubt_softc_p sc)
+{
+ usbd_status status;
+
+ /* Initialize a USB transfer and then schedule it */
+ usbd_setup_xfer(
+ sc->sc_intr_xfer,
+ sc->sc_intr_pipe,
+ (usbd_private_handle) sc,
+ sc->sc_intr_buffer,
+ UBT_INTR_BUFFER_SIZE,
+ USBD_SHORT_XFER_OK | USBD_NO_COPY,
+ USBD_NO_TIMEOUT,
+ ubt_intr_complete);
+
+ status = usbd_transfer(sc->sc_intr_xfer);
+ if (status && status != USBD_IN_PROGRESS) {
+ NG_UBT_ERR(
+"%s: %s - Failed to start intrerrupt transfer. %s (%d)\n",
+ __func__, USBDEVNAME(sc->sc_dev), usbd_errstr(status),
+ status);
+
+ return (status);
+ }
+
+ return (USBD_NORMAL_COMPLETION);
+} /* ubt_intr_start */
+
+/*
+ * Process interrupt from USB device (We got data from interrupt pipe)
+ */
+
+Static void
+ubt_intr_complete(usbd_xfer_handle h, usbd_private_handle p, usbd_status s)
+{
+ ubt_softc_p sc = (ubt_softc_p) p;
+ struct mbuf *m = NULL;
+ ng_hci_event_pkt_t *hdr = NULL;
+ int off;
+
+ if (s == USBD_CANCELLED) {
+ NG_UBT_INFO(
+"%s: %s - Interrupt xfer cancelled\n", __func__, USBDEVNAME(sc->sc_dev));
+
+ return;
+ }
+
+ if (s != USBD_NORMAL_COMPLETION) {
+ NG_UBT_WARN(
+"%s: %s - Interrupt xfer failed. %s (%d)\n",
+ __func__, USBDEVNAME(sc->sc_dev), usbd_errstr(s), s);
+
+ if (s == USBD_STALLED)
+ usbd_clear_endpoint_stall_async(sc->sc_intr_pipe);
+
+ NG_UBT_STAT_IERROR(sc->sc_stat);
+ goto done;
+ }
+
+ NG_UBT_STAT_BYTES_RECV(sc->sc_stat, h->actlen);
+
+ NG_UBT_INFO(
+"%s: %s - Got %d bytes from interrupt pipe\n",
+ __func__, USBDEVNAME(sc->sc_dev), h->actlen);
+
+ if (h->actlen < sizeof(*hdr))
+ goto done;
+
+ /* Copy HCI event frame to mbuf */
+
+ MGETHDR(m, M_DONTWAIT, MT_DATA);
+ if (m == NULL) {
+ NG_UBT_ALERT(
+"%s: %s - Could not allocate mbuf\n", __func__, USBDEVNAME(sc->sc_dev));
+ NG_UBT_STAT_IERROR(sc->sc_stat);
+ goto done;
+ }
+
+ /*
+ * Copy data from USB buffer into mbuf and check if we got
+ * full HCI event frame. Fix HCI event frame if required.
+ */
+
+ if (!(sc->sc_flags & UBT_HAVE_FRAME_TYPE)) {
+ off = 1;
+ *mtod(m, u_int8_t *) = NG_HCI_EVENT_PKT;
+ } else
+ off = 0;
+
+ m->m_pkthdr.len = 0;
+ m->m_len = min(MHLEN, h->actlen + off); /* XXX m_copyback is stupid */
+ m_copyback(m, off, h->actlen, sc->sc_intr_buffer);
+
+ hdr = mtod(m, ng_hci_event_pkt_t *);
+ if (hdr->length == m->m_pkthdr.len - sizeof(*hdr)) {
+ NG_UBT_INFO(
+"%s: %s - Got complete HCI event frame, pktlen=%d, length=%d\n",
+ __func__, USBDEVNAME(sc->sc_dev), m->m_pkthdr.len,
+ hdr->length);
+
+ NG_UBT_STAT_PCKTS_RECV(sc->sc_stat);
+
+ IF_LOCK(&sc->sc_inq);
+ if (_IF_QFULL(&sc->sc_inq)) {
+ NG_UBT_ERR(
+"%s: %s -Incoming queue is full. Dropping mbuf, len=%d\n",
+ __func__, USBDEVNAME(sc->sc_dev),
+ m->m_pkthdr.len);
+
+ _IF_DROP(&sc->sc_inq);
+ NG_UBT_STAT_IERROR(sc->sc_stat);
+
+ NG_FREE_M(m);
+ } else
+ _IF_ENQUEUE(&sc->sc_inq, m);
+ IF_UNLOCK(&sc->sc_inq);
+
+ /* Schedule SWI */
+ swi_sched(sc->sc_ith, 0);
+ } else {
+ NG_UBT_ERR(
+"%s: %s - Invalid HCI event frame size, length=%d, pktlen=%d\n",
+ __func__, USBDEVNAME(sc->sc_dev), hdr->length,
+ m->m_pkthdr.len);
+
+ NG_UBT_STAT_IERROR(sc->sc_stat);
+ NG_FREE_M(m);
+ }
+done:
+ ubt_intr_start(sc);
+} /* ubt_intr_complete */
+
+/*
+ * Start bulk-in USB transfer (ACL data)
+ */
+
+Static usbd_status
+ubt_bulk_in_start(ubt_softc_p sc)
+{
+ usbd_status status;
+
+ /* Initialize a bulk-in USB transfer and then schedule it */
+ usbd_setup_xfer(
+ sc->sc_bulk_in_xfer,
+ sc->sc_bulk_in_pipe,
+ (usbd_private_handle) sc,
+ sc->sc_bulk_in_buffer,
+ UBT_BULK_BUFFER_SIZE,
+ USBD_SHORT_XFER_OK | USBD_NO_COPY,
+ USBD_NO_TIMEOUT,
+ ubt_bulk_in_complete);
+
+ status = usbd_transfer(sc->sc_bulk_in_xfer);
+ if (status && status != USBD_IN_PROGRESS) {
+ NG_UBT_ERR(
+"%s: %s - Failed to start bulk-in transfer. %s (%d)\n",
+ __func__, USBDEVNAME(sc->sc_dev), usbd_errstr(status),
+ status);
+
+ return (status);
+ }
+
+ return (USBD_NORMAL_COMPLETION);
+} /* ubt_bulk_in_start */
+
+/*
+ * USB bulk-in transfer callback
+ */
+
+Static void
+ubt_bulk_in_complete(usbd_xfer_handle h, usbd_private_handle p, usbd_status s)
+{
+ ubt_softc_p sc = (ubt_softc_p) p;
+ struct mbuf *m = NULL;
+ ng_hci_acldata_pkt_t *hdr = NULL;
+ int len;
+
+ if (s == USBD_CANCELLED) {
+ NG_UBT_INFO(
+"%s: %s - Bulk-in xfer cancelled, pipe=%p\n",
+ __func__, USBDEVNAME(sc->sc_dev), sc->sc_bulk_in_pipe);
+
+ return;
+ }
+
+ if (s != USBD_NORMAL_COMPLETION) {
+ NG_UBT_WARN(
+"%s: %s - Bulk-in xfer failed. %s (%d)\n",
+ __func__, USBDEVNAME(sc->sc_dev), usbd_errstr(s), s);
+
+ if (s == USBD_STALLED)
+ usbd_clear_endpoint_stall_async(sc->sc_bulk_in_pipe);
+
+ NG_UBT_STAT_IERROR(sc->sc_stat);
+ goto done;
+ }
+
+ NG_UBT_STAT_BYTES_RECV(sc->sc_stat, h->actlen);
+
+ NG_UBT_INFO(
+"%s: %s - Got %d bytes from bulk-in pipe\n",
+ __func__, USBDEVNAME(sc->sc_dev), h->actlen);
+
+ if (h->actlen < sizeof(*hdr))
+ goto done;
+
+ MGETHDR(m, M_DONTWAIT, MT_DATA);
+ if (m == NULL) {
+ NG_UBT_ALERT(
+"%s: %s - Could not allocate mbuf\n", __func__, USBDEVNAME(sc->sc_dev));
+
+ NG_UBT_STAT_IERROR(sc->sc_stat);
+ goto done;
+ }
+
+ /*
+ * Copy data from linear USB buffer into mbuf and check if we got
+ * full ACL data frame. Fix ACL data frame header if required.
+ */
+
+ if (!(sc->sc_flags & UBT_HAVE_FRAME_TYPE)) {
+ len = 1;
+ *mtod(m, u_int8_t *) = NG_HCI_ACL_DATA_PKT;
+ } else
+ len = 0;
+
+ m->m_pkthdr.len = 0;
+ m->m_len = min(MHLEN, h->actlen + len); /* XXX m_copyback is stupid */
+ m_copyback(m, len, h->actlen, sc->sc_bulk_in_buffer);
+
+ hdr = mtod(m, ng_hci_acldata_pkt_t *);
+ len = le16toh(hdr->length);
+
+ if (len == m->m_pkthdr.len - sizeof(*hdr)) {
+ NG_UBT_INFO(
+"%s: %s - Got complete ACL data frame, pktlen=%d, length=%d\n",
+ __func__, USBDEVNAME(sc->sc_dev), m->m_pkthdr.len,
+ len);
+
+ NG_UBT_STAT_PCKTS_RECV(sc->sc_stat);
+
+ IF_LOCK(&sc->sc_inq);
+ if (_IF_QFULL(&sc->sc_inq)) {
+ NG_UBT_ERR(
+"%s: %s -Incoming queue is full. Dropping mbuf, len=%d\n",
+ __func__, USBDEVNAME(sc->sc_dev),
+ m->m_pkthdr.len);
+
+ _IF_DROP(&sc->sc_inq);
+ NG_UBT_STAT_IERROR(sc->sc_stat);
+
+ NG_FREE_M(m);
+ } else
+ _IF_ENQUEUE(&sc->sc_inq, m);
+ IF_UNLOCK(&sc->sc_inq);
+
+ /* Schedule SWI */
+ swi_sched(sc->sc_ith, 0);
+ } else {
+ NG_UBT_ERR(
+"%s: %s - Invalid ACL frame size, length=%d, pktlen=%d\n",
+ __func__, USBDEVNAME(sc->sc_dev), len,
+ m->m_pkthdr.len);
+
+ NG_UBT_STAT_IERROR(sc->sc_stat);
+ NG_FREE_M(m);
+ }
+done:
+ ubt_bulk_in_start(sc);
+} /* ubt_bulk_in_complete */
+
+/*
+ * Start bulk-out USB transfer
+ */
+
+Static usbd_status
+ubt_bulk_out_start(ubt_softc_p sc, struct mbuf *m)
+{
+ usbd_status status = USBD_NORMAL_COMPLETION;
+
+ IF_LOCK(&sc->sc_aclq);
+
+ if (m != NULL) {
+ if (_IF_QFULL(&sc->sc_aclq)) {
+ NG_UBT_ERR(
+"%s: %s - Dropping HCI ACL frame, len=%d. Queue full\n",
+ __func__, USBDEVNAME(sc->sc_dev),
+ m->m_pkthdr.len);
+
+ _IF_DROP(&sc->sc_aclq);
+ NG_UBT_STAT_OERROR(sc->sc_stat);
+
+ NG_FREE_M(m);
+ } else
+ _IF_ENQUEUE(&sc->sc_aclq, m);
+ } else
+ sc->sc_flags &= ~UBT_ACL_XMIT;
+
+ if (sc->sc_flags & UBT_ACL_XMIT) {
+ NG_UBT_INFO(
+"%s: %s - Another bulk-out transfer is pending\n",
+ __func__, USBDEVNAME(sc->sc_dev));
+ goto done;
+ }
+
+ _IF_DEQUEUE(&sc->sc_aclq, m);
+ if (m == NULL) {
+ NG_UBT_INFO(
+"%s: %s - ACL data queue is empty\n", __func__, USBDEVNAME(sc->sc_dev));
+ goto done;
+ }
+
+ /*
+ * Check ACL data frame size and copy it back to linear USB
+ * transfer buffer.
+ */
+
+ if (m->m_pkthdr.len > UBT_BULK_BUFFER_SIZE)
+ panic(
+"%s: %s - ACL data frame too big, size=%d, len=%d\n",
+ __func__, USBDEVNAME(sc->sc_dev), UBT_BULK_BUFFER_SIZE,
+ m->m_pkthdr.len);
+
+ m_copydata(m, 0, m->m_pkthdr.len, sc->sc_bulk_out_buffer);
+
+ /* Initialize a bulk-out USB transfer and then schedule it */
+ usbd_setup_xfer(
+ sc->sc_bulk_out_xfer,
+ sc->sc_bulk_out_pipe,
+ (usbd_private_handle) sc,
+ sc->sc_bulk_out_buffer,
+ m->m_pkthdr.len,
+ USBD_NO_COPY,
+ USBD_DEFAULT_TIMEOUT, /* XXX */
+ ubt_bulk_out_complete);
+
+ status = usbd_transfer(sc->sc_bulk_out_xfer);
+ if (status && status != USBD_IN_PROGRESS) {
+ NG_UBT_ERR(
+"%s: %s - Could not start bulk-out transfer. %s (%d)\n",
+ __func__, USBDEVNAME(sc->sc_dev), usbd_errstr(status),
+ status);
+
+ _IF_DROP(&sc->sc_aclq); /* XXX */
+ NG_UBT_STAT_OERROR(sc->sc_stat);
+
+ /* XXX FIXME: should we try to start another transfer? */
+ } else {
+ NG_UBT_INFO(
+"%s: %s - Bulk-out transfer has been started, len=%d\n",
+ __func__, USBDEVNAME(sc->sc_dev), m->m_pkthdr.len);
+
+ sc->sc_flags |= UBT_ACL_XMIT;
+
+ status = USBD_NORMAL_COMPLETION;
+ }
+
+ NG_FREE_M(m);
+done:
+ IF_UNLOCK(&sc->sc_aclq);
+
+ return (status);
+} /* ubt_bulk_out_start */
+
+/*
+ * USB bulk-out transfer callback
+ */
+
+Static void
+ubt_bulk_out_complete(usbd_xfer_handle h, usbd_private_handle p, usbd_status s)
+{
+ ubt_softc_p sc = (ubt_softc_p) p;
+
+ if (s == USBD_CANCELLED) {
+ NG_UBT_INFO(
+"%s: %s - Bulk-out xfer cancelled, pipe=%p\n",
+ __func__, USBDEVNAME(sc->sc_dev),
+ sc->sc_bulk_out_pipe);
+
+ return;
+ }
+
+ if (s != USBD_NORMAL_COMPLETION) {
+ NG_UBT_WARN(
+"%s: %s - Bulk-out xfer failed. %s (%d)\n",
+ __func__, USBDEVNAME(sc->sc_dev), usbd_errstr(s), s);
+
+ if (s == USBD_STALLED)
+ usbd_clear_endpoint_stall_async(sc->sc_bulk_out_pipe);
+
+ NG_UBT_STAT_OERROR(sc->sc_stat);
+ } else {
+ NG_UBT_INFO(
+"%s: %s - Sent %d bytes to bulk-out pipe\n",
+ __func__, USBDEVNAME(sc->sc_dev), h->actlen);
+
+ NG_UBT_STAT_BYTES_SENT(sc->sc_stat, h->actlen);
+ NG_UBT_STAT_PCKTS_SENT(sc->sc_stat);
+ }
+
+ ubt_bulk_out_start(sc, NULL /* completed request */);
+} /* ubt_bulk_out_complete */
+
+/*
+ * Start Isochronous-in USB transfer
+ */
+
+Static usbd_status
+ubt_isoc_in_start(ubt_softc_p sc)
+{
+ usbd_status status;
+ int i;
+
+ /* Initialize a isoc-in USB transfer and then schedule it */
+
+ for (i = 0; i < sc->sc_isoc_nframes; i++)
+ sc->sc_isoc_in_frlen[i] = sc->sc_isoc_size;
+
+ usbd_setup_isoc_xfer(
+ sc->sc_isoc_in_xfer,
+ sc->sc_isoc_in_pipe,
+ (usbd_private_handle) sc,
+ sc->sc_isoc_in_frlen,
+ sc->sc_isoc_nframes,
+ USBD_NO_COPY, /* XXX flags */
+ ubt_isoc_in_complete);
+
+ status = usbd_transfer(sc->sc_isoc_in_xfer);
+ if (status && status != USBD_IN_PROGRESS) {
+ NG_UBT_ERR(
+"%s: %s - Failed to start isoc-in transfer. %s (%d)\n",
+ __func__, USBDEVNAME(sc->sc_dev),
+ usbd_errstr(status), status);
+
+ return (status);
+ }
+
+ return (USBD_NORMAL_COMPLETION);
+} /* ubt_isoc_in_start */
+
+/*
+ * USB isochronous transfer callback
+ */
+
+Static void
+ubt_isoc_in_complete(usbd_xfer_handle h, usbd_private_handle p, usbd_status s)
+{
+ ubt_softc_p sc = (ubt_softc_p) p;
+ struct mbuf *m = NULL;
+ ng_hci_scodata_pkt_t *hdr = NULL;
+ u_int8_t *b = NULL;
+ int i;
+
+ if (s == USBD_CANCELLED) {
+ NG_UBT_INFO(
+"%s: %s - Isoc-in xfer cancelled, pipe=%p\n",
+ __func__, USBDEVNAME(sc->sc_dev), sc->sc_isoc_in_pipe);
+
+ return;
+ }
+
+ if (s != USBD_NORMAL_COMPLETION) {
+ NG_UBT_WARN(
+"%s: %s - Isoc-in xfer failed. %s (%d)\n",
+ __func__, USBDEVNAME(sc->sc_dev), usbd_errstr(s), s);
+
+ if (s == USBD_STALLED)
+ usbd_clear_endpoint_stall_async(sc->sc_isoc_in_pipe);
+
+ NG_UBT_STAT_IERROR(sc->sc_stat);
+ goto done;
+ }
+
+ NG_UBT_STAT_BYTES_RECV(sc->sc_stat, h->actlen);
+
+ NG_UBT_INFO(
+"%s: %s - Got %d bytes from isoc-in pipe\n",
+ __func__, USBDEVNAME(sc->sc_dev), h->actlen);
+
+ if (h->actlen < sizeof(*hdr))
+ goto done;
+
+ /* Copy SCO data frame to mbuf */
+
+ MGETHDR(m, M_DONTWAIT, MT_DATA);
+ if (m == NULL) {
+ NG_UBT_ALERT(
+"%s: %s - Could not allocate mbuf\n",
+ __func__, USBDEVNAME(sc->sc_dev));
+
+ NG_UBT_STAT_IERROR(sc->sc_stat);
+ goto done;
+ }
+
+ /* Fix SCO data frame header if required */
+
+ if (sc->sc_flags & UBT_HAVE_FRAME_TYPE) {
+ m->m_pkthdr.len = m->m_len = 0;
+ } else {
+ *mtod(m, u_int8_t *) = NG_HCI_SCO_DATA_PKT;
+ m->m_pkthdr.len = m->m_len = 1;
+ }
+
+ /*
+ * XXX FIXME: how do we know how many frames we have received?
+ * XXX use frlen for now.
+ * XXX is that correct?
+ */
+
+ b = (u_int8_t *) sc->sc_isoc_in_buffer;
+
+ for (i = 0; i < sc->sc_isoc_nframes; i++) {
+ b += (i * sc->sc_isoc_size);
+
+ if (sc->sc_isoc_in_frlen[i] > 0)
+ m_copyback(m, m->m_pkthdr.len,
+ sc->sc_isoc_in_frlen[i], b);
+ }
+
+ NG_UBT_M_PULLUP(m, sizeof(*hdr));
+ if (m == NULL) {
+ NG_UBT_STAT_IERROR(sc->sc_stat);
+ goto done;
+ }
+
+ hdr = mtod(m, ng_hci_scodata_pkt_t *);
+
+ if (hdr->length == m->m_pkthdr.len - sizeof(*hdr)) {
+ NG_UBT_INFO(
+"%s: %s - Got complete SCO data frame, pktlen=%d, length=%d\n",
+ __func__, USBDEVNAME(sc->sc_dev), m->m_pkthdr.len,
+ hdr->length);
+
+ NG_UBT_STAT_PCKTS_RECV(sc->sc_stat);
+
+ IF_LOCK(&sc->sc_inq);
+ if (_IF_QFULL(&sc->sc_inq)) {
+ NG_UBT_ERR(
+"%s: %s -Incoming queue is full. Dropping mbuf, len=%d\n",
+ __func__, USBDEVNAME(sc->sc_dev),
+ m->m_pkthdr.len);
+
+ _IF_DROP(&sc->sc_inq);
+ NG_UBT_STAT_IERROR(sc->sc_stat);
+
+ NG_FREE_M(m);
+ } else
+ _IF_ENQUEUE(&sc->sc_inq, m);
+ IF_UNLOCK(&sc->sc_inq);
+
+ /* Schedule SWI */
+ swi_sched(sc->sc_ith, 0);
+ } else {
+ NG_UBT_ERR(
+"%s: %s - Invalid SCO frame size, length=%d, pktlen=%d\n",
+ __func__, USBDEVNAME(sc->sc_dev), hdr->length,
+ m->m_pkthdr.len);
+
+ NG_UBT_STAT_IERROR(sc->sc_stat);
+ NG_FREE_M(m);
+ }
+done:
+ ubt_isoc_in_start(sc);
+} /* ubt_bulk_in_complete */
+
+/*
+ * Start isochronous-out USB transfer
+ */
+
+Static usbd_status
+ubt_isoc_out_start(ubt_softc_p sc, struct mbuf *m)
+{
+ u_int8_t *b = NULL;
+ int i, len, nframes;
+ usbd_status status = USBD_NORMAL_COMPLETION;
+
+ IF_LOCK(&sc->sc_scoq);
+
+ if (m != NULL) {
+ if (_IF_QFULL(&sc->sc_scoq)) {
+ NG_UBT_ERR(
+"%s: %s - Dropping HCI SCO frame, len=%d. Queue full\n",
+ __func__, USBDEVNAME(sc->sc_dev),
+ m->m_pkthdr.len);
+
+ _IF_DROP(&sc->sc_scoq);
+ NG_UBT_STAT_OERROR(sc->sc_stat);
+
+ NG_FREE_M(m);
+ } else
+ _IF_ENQUEUE(&sc->sc_scoq, m);
+ } else
+ sc->sc_flags &= ~UBT_SCO_XMIT;
+
+ if (sc->sc_flags & UBT_SCO_XMIT) {
+ NG_UBT_INFO(
+"%s: %s - Another isoc-out transfer is pending\n",
+ __func__, USBDEVNAME(sc->sc_dev));
+ goto done;
+ }
+
+ _IF_DEQUEUE(&sc->sc_scoq, m);
+ if (m == NULL) {
+ NG_UBT_INFO(
+"%s: %s - SCO data queue is empty\n", __func__, USBDEVNAME(sc->sc_dev));
+ goto done;
+ }
+
+ /* Copy entire SCO frame into USB transfer buffer and start transfer */
+
+ b = (u_int8_t *) sc->sc_isoc_out_buffer;
+ nframes = 0;
+
+ for (i = 0; i < sc->sc_isoc_nframes; i++) {
+ b += (i * sc->sc_isoc_size);
+
+ len = min(m->m_pkthdr.len, sc->sc_isoc_size);
+ if (len > 0) {
+ m_copydata(m, 0, len, b);
+ m_adj(m, len);
+ nframes ++;
+ }
+
+ sc->sc_isoc_out_frlen[i] = len;
+ }
+
+ if (m->m_pkthdr.len > 0)
+ panic(
+"%s: %s - SCO data frame is too big, nframes=%d, size=%d, len=%d\n",
+ __func__, USBDEVNAME(sc->sc_dev), sc->sc_isoc_nframes,
+ sc->sc_isoc_size, m->m_pkthdr.len);
+
+ NG_FREE_M(m);
+
+ /* Initialize a isoc-out USB transfer and then schedule it */
+
+ usbd_setup_isoc_xfer(
+ sc->sc_isoc_out_xfer,
+ sc->sc_isoc_out_pipe,
+ (usbd_private_handle) sc,
+ sc->sc_isoc_out_frlen,
+ nframes,
+ USBD_NO_COPY,
+ ubt_isoc_out_complete);
+
+ status = usbd_transfer(sc->sc_isoc_out_xfer);
+ if (status && status != USBD_IN_PROGRESS) {
+ NG_UBT_ERR(
+"%s: %s - Could not start isoc-out transfer. %s (%d)\n",
+ __func__, USBDEVNAME(sc->sc_dev), usbd_errstr(status),
+ status);
+
+ _IF_DROP(&sc->sc_scoq); /* XXX */
+ NG_UBT_STAT_OERROR(sc->sc_stat);
+ } else {
+ NG_UBT_INFO(
+"%s: %s - Isoc-out transfer has been started, nframes=%d, size=%d\n",
+ __func__, USBDEVNAME(sc->sc_dev), nframes,
+ sc->sc_isoc_size);
+
+ sc->sc_flags |= UBT_SCO_XMIT;
+
+ status = USBD_NORMAL_COMPLETION;
+ }
+done:
+ IF_UNLOCK(&sc->sc_scoq);
+
+ return (status);
+} /* ubt_isoc_out_start */
+
+/*
+ * USB isoc-out. transfer callback
+ */
+
+Static void
+ubt_isoc_out_complete(usbd_xfer_handle h, usbd_private_handle p, usbd_status s)
+{
+ ubt_softc_p sc = (ubt_softc_p) p;
+
+ if (s == USBD_CANCELLED) {
+ NG_UBT_INFO(
+"%s: %s - Isoc-out xfer cancelled, pipe=%p\n",
+ __func__, USBDEVNAME(sc->sc_dev),
+ sc->sc_isoc_out_pipe);
+
+ return;
+ }
+
+ if (s != USBD_NORMAL_COMPLETION) {
+ NG_UBT_WARN(
+"%s: %s - Isoc-out xfer failed. %s (%d)\n",
+ __func__, USBDEVNAME(sc->sc_dev), usbd_errstr(s), s);
+
+ if (s == USBD_STALLED)
+ usbd_clear_endpoint_stall_async(sc->sc_isoc_out_pipe);
+
+ NG_UBT_STAT_OERROR(sc->sc_stat);
+ } else {
+ NG_UBT_INFO(
+"%s: %s - Sent %d bytes to isoc-out pipe\n",
+ __func__, USBDEVNAME(sc->sc_dev), h->actlen);
+
+ NG_UBT_STAT_BYTES_SENT(sc->sc_stat, h->actlen);
+ NG_UBT_STAT_PCKTS_SENT(sc->sc_stat);
+ }
+
+ ubt_isoc_out_start(sc, NULL /* completed request */);
+} /* ubt_isoc_out_complete */
+
+/*
+ * SWI interrupt handler
+ */
+
+Static void
+ubt_swi_intr(void *context)
+{
+ ubt_softc_p sc = (ubt_softc_p) context;
+ struct mbuf *m = NULL;
+ int error;
+
+ if (sc->sc_hook != NULL && NG_HOOK_IS_VALID(sc->sc_hook)) {
+ for (;;) {
+ IF_DEQUEUE(&sc->sc_inq, m);
+ if (m == NULL)
+ break;
+
+ NG_SEND_DATA_ONLY(error, sc->sc_hook, m);
+ if (error != 0)
+ NG_UBT_STAT_IERROR(sc->sc_stat);
+ }
+ } else {
+ IF_LOCK(&sc->sc_inq);
+ for (;;) {
+ _IF_DEQUEUE(&sc->sc_inq, m);
+ if (m == NULL)
+ break;
+
+ NG_UBT_STAT_IERROR(sc->sc_stat);
+ NG_FREE_M(m);
+ }
+ IF_UNLOCK(&sc->sc_inq);
+ }
+} /* ubt_swi_intr */
+
+/****************************************************************************
+ ****************************************************************************
+ ** Netgraph specific
+ ****************************************************************************
+ ****************************************************************************/
+
+/*
+ * Netgraph node constructor. Do not allow to create node of this type.
+ */
+
+Static int
+ng_ubt_constructor(node_p node)
+{
+ return (EINVAL);
+} /* ng_ubt_constructor */
+
+/*
+ * Netgraph node destructor. Destroy node only when device has been detached
+ */
+
+Static int
+ng_ubt_shutdown(node_p node)
+{
+ ubt_softc_p sc = (ubt_softc_p) NG_NODE_PRIVATE(node);
+
+ /* Let old node go */
+ NG_NODE_SET_PRIVATE(node, NULL);
+ NG_NODE_UNREF(node);
+
+ if (sc == NULL)
+ goto done;
+
+ /* Create Netgraph node */
+ if (ng_make_node_common(&typestruct, &sc->sc_node) != 0) {
+ printf("%s: Could not create Netgraph node\n",
+ USBDEVNAME(sc->sc_dev));
+ sc->sc_node = NULL;
+ goto done;
+ }
+
+ /* Name node */
+ if (ng_name_node(sc->sc_node, USBDEVNAME(sc->sc_dev)) != 0) {
+ printf("%s: Could not name Netgraph node\n",
+ USBDEVNAME(sc->sc_dev));
+ NG_NODE_UNREF(sc->sc_node);
+ sc->sc_node = NULL;
+ goto done;
+ }
+
+ NG_NODE_SET_PRIVATE(sc->sc_node, sc);
+done:
+ return (0);
+} /* ng_ubt_shutdown */
+
+/*
+ * Create new hook. There can only be one.
+ */
+
+Static int
+ng_ubt_newhook(node_p node, hook_p hook, char const *name)
+{
+ ubt_softc_p sc = (ubt_softc_p) NG_NODE_PRIVATE(node);
+ usbd_status status;
+
+ if (strcmp(name, NG_UBT_HOOK) != 0)
+ return (EINVAL);
+
+ if (sc->sc_hook != NULL)
+ return (EISCONN);
+
+ sc->sc_hook = hook;
+
+ /* Interrupt */
+ status = usbd_open_pipe(sc->sc_iface0, sc->sc_intr_ep,
+ USBD_EXCLUSIVE_USE, &sc->sc_intr_pipe);
+ if (status) {
+ NG_UBT_ALERT(
+"%s: %s - Could not open interrupt pipe. %s (%d)\n",
+ __func__, USBDEVNAME(sc->sc_dev), usbd_errstr(status),
+ status);
+ goto fail;
+ }
+
+ /* Bulk-in */
+ status = usbd_open_pipe(sc->sc_iface0, sc->sc_bulk_in_ep,
+ USBD_EXCLUSIVE_USE, &sc->sc_bulk_in_pipe);
+ if (status) {
+ NG_UBT_ALERT(
+"%s: %s - Could not open bulk-in pipe. %s (%d)\n",
+ __func__, USBDEVNAME(sc->sc_dev), usbd_errstr(status),
+ status);
+ goto fail;
+ }
+
+ /* Bulk-out */
+ status = usbd_open_pipe(sc->sc_iface0, sc->sc_bulk_out_ep,
+ USBD_EXCLUSIVE_USE, &sc->sc_bulk_out_pipe);
+ if (status) {
+ NG_UBT_ALERT(
+"%s: %s - Could not open bulk-out pipe. %s (%d)\n",
+ __func__, USBDEVNAME(sc->sc_dev), usbd_errstr(status),
+ status);
+ goto fail;
+ }
+
+#if __broken__ /* XXX FIXME */
+ /* Isoc-in */
+ status = usbd_open_pipe(sc->sc_iface1, sc->sc_isoc_in_ep,
+ USBD_EXCLUSIVE_USE, &sc->sc_isoc_in_pipe);
+ if (status) {
+ NG_UBT_ALERT(
+"%s: %s - Could not open isoc-in pipe. %s (%d)\n",
+ __func__, USBDEVNAME(sc->sc_dev), usbd_errstr(status),
+ status);
+ goto fail;
+ }
+
+ /* Isoc-out */
+ status = usbd_open_pipe(sc->sc_iface1, sc->sc_isoc_out_ep,
+ USBD_EXCLUSIVE_USE, &sc->sc_isoc_out_pipe);
+ if (status) {
+ NG_UBT_ALERT(
+"%s: %s - Could not open isoc-out pipe. %s (%d)\n",
+ __func__, USBDEVNAME(sc->sc_dev), usbd_errstr(status),
+ status);
+ goto fail;
+ }
+#endif /* __broken__ */
+
+ return (0);
+fail:
+ ng_ubt_reset(sc);
+ sc->sc_hook = NULL;
+
+ return (ENXIO);
+} /* ng_ubt_newhook */
+
+/*
+ * Connect hook. Start incoming USB transfers
+ */
+
+Static int
+ng_ubt_connect(hook_p hook)
+{
+ ubt_softc_p sc = (ubt_softc_p) NG_NODE_PRIVATE(NG_HOOK_NODE(hook));
+ usbd_status status;
+
+ /* Start intr transfer */
+ status = ubt_intr_start(sc);
+ if (status != USBD_NORMAL_COMPLETION) {
+ NG_UBT_ALERT(
+"%s: %s - Could not start interrupt transfer. %s (%d)\n",
+ __func__, USBDEVNAME(sc->sc_dev), usbd_errstr(status),
+ status);
+ goto fail;
+ }
+
+ /* Start bulk-in transfer */
+ status = ubt_bulk_in_start(sc);
+ if (status != USBD_NORMAL_COMPLETION) {
+ NG_UBT_ALERT(
+"%s: %s - Could not start bulk-in transfer. %s (%d)\n",
+ __func__, USBDEVNAME(sc->sc_dev), usbd_errstr(status),
+ status);
+ goto fail;
+ }
+
+#if __broken__ /* XXX FIXME */
+ /* Start isoc-in transfer */
+ status = ubt_isoc_in_start(sc);
+ if (status != USBD_NORMAL_COMPLETION) {
+ NG_UBT_ALERT(
+"%s: %s - Could not start isoc-in transfer. %s (%d)\n",
+ __func__, USBDEVNAME(sc->sc_dev), usbd_errstr(status),
+ status);
+ goto fail;
+ }
+#endif /* __broken__ */
+
+ NG_HOOK_FORCE_QUEUE(NG_HOOK_PEER(hook));
+
+ return (0);
+fail:
+ ng_ubt_reset(sc);
+ sc->sc_hook = NULL;
+
+ return (ENXIO);
+} /* ng_ubt_connect */
+
+/*
+ * Disconnect hook
+ */
+
+Static int
+ng_ubt_disconnect(hook_p hook)
+{
+ ubt_softc_p sc = (ubt_softc_p) NG_NODE_PRIVATE(NG_HOOK_NODE(hook));
+
+ if (sc != NULL) {
+ if (hook != sc->sc_hook)
+ return (EINVAL);
+
+ ng_ubt_reset(sc);
+ sc->sc_hook = NULL;
+ }
+
+ return (0);
+} /* ng_ubt_disconnect */
+
+/*
+ * Process control message
+ */
+
+Static int
+ng_ubt_rcvmsg(node_p node, item_p item, hook_p lasthook)
+{
+ ubt_softc_p sc = (ubt_softc_p) NG_NODE_PRIVATE(node);
+ struct ng_mesg *msg = NULL, *rsp = NULL;
+ int error = 0, queue, qlen;
+ struct ifqueue *q = NULL;
+
+ if (sc == NULL) {
+ NG_FREE_ITEM(item);
+ return (EHOSTDOWN);
+ }
+
+ NGI_GET_MSG(item, msg);
+
+ switch (msg->header.typecookie) {
+ case NGM_GENERIC_COOKIE:
+ switch (msg->header.cmd) {
+ case NGM_TEXT_STATUS:
+ NG_MKRESPONSE(rsp, msg, NG_TEXTRESPONSE, M_NOWAIT);
+ if (rsp == NULL)
+ error = ENOMEM;
+ else
+ snprintf(rsp->data, NG_TEXTRESPONSE,
+ "Hook: %s\n" \
+ "Flags: %#x\n" \
+ "Debug: %d\n" \
+ "CMD queue: [have:%d,max:%d]\n" \
+ "ACL queue: [have:%d,max:%d]\n" \
+ "SCO queue: [have:%d,max:%d]\n" \
+ "Inp queue: [have:%d,max:%d]",
+ (sc->sc_hook != NULL)? NG_UBT_HOOK : "",
+ sc->sc_flags,
+ sc->sc_debug,
+ _IF_QLEN(&sc->sc_cmdq), /* XXX */
+ sc->sc_cmdq.ifq_maxlen, /* XXX */
+ _IF_QLEN(&sc->sc_aclq), /* XXX */
+ sc->sc_aclq.ifq_maxlen, /* XXX */
+ _IF_QLEN(&sc->sc_scoq), /* XXX */
+ sc->sc_scoq.ifq_maxlen, /* XXX */
+ _IF_QLEN(&sc->sc_inq), /* XXX */
+ sc->sc_inq.ifq_maxlen /* XXX */ );
+ break;
+
+ default:
+ error = EINVAL;
+ break;
+ }
+ break;
+
+ case NGM_UBT_COOKIE:
+ switch (msg->header.cmd) {
+ case NGM_UBT_NODE_SET_DEBUG:
+ if (msg->header.arglen != sizeof(ng_ubt_node_debug_ep))
+ error = EMSGSIZE;
+ else
+ sc->sc_debug =
+ *((ng_ubt_node_debug_ep *)(msg->data));
+ break;
+
+ case NGM_UBT_NODE_GET_DEBUG:
+ NG_MKRESPONSE(rsp, msg, sizeof(ng_ubt_node_debug_ep),
+ M_NOWAIT);
+ if (rsp == NULL)
+ error = ENOMEM;
+ else
+ *((ng_ubt_node_debug_ep *)(rsp->data)) =
+ sc->sc_debug;
+ break;
+
+ case NGM_UBT_NODE_SET_QLEN:
+ if (msg->header.arglen != sizeof(ng_ubt_node_qlen_ep))
+ error = EMSGSIZE;
+ else {
+ queue = ((ng_ubt_node_qlen_ep *)
+ (msg->data))->queue;
+ qlen = ((ng_ubt_node_qlen_ep *)
+ (msg->data))->qlen;
+
+ if (qlen <= 0) {
+ error = EINVAL;
+ break;
+ }
+
+ switch (queue) {
+ case NGM_UBT_NODE_QUEUE_IN:
+ q = &sc->sc_inq;
+ break;
+
+ case NGM_UBT_NODE_QUEUE_CMD:
+ q = &sc->sc_cmdq;
+ break;
+
+ case NGM_UBT_NODE_QUEUE_ACL:
+ q = &sc->sc_aclq;
+ break;
+
+ case NGM_UBT_NODE_QUEUE_SCO:
+ q = &sc->sc_scoq;
+ break;
+
+ default:
+ q = NULL;
+ error = EINVAL;
+ break;
+ }
+
+ if (q != NULL)
+ q->ifq_maxlen = qlen; /* XXX */
+ }
+ break;
+
+ case NGM_UBT_NODE_GET_QLEN:
+ if (msg->header.arglen != sizeof(ng_ubt_node_qlen_ep)) {
+ error = EMSGSIZE;
+ break;
+ }
+
+ queue = ((ng_ubt_node_qlen_ep *)(msg->data))->queue;
+ switch (queue) {
+ case NGM_UBT_NODE_QUEUE_IN:
+ q = &sc->sc_inq;
+ break;
+
+ case NGM_UBT_NODE_QUEUE_CMD:
+ q = &sc->sc_cmdq;
+ break;
+
+ case NGM_UBT_NODE_QUEUE_ACL:
+ q = &sc->sc_aclq;
+ break;
+
+ case NGM_UBT_NODE_QUEUE_SCO:
+ q = &sc->sc_scoq;
+ break;
+
+ default:
+ q = NULL;
+ error = EINVAL;
+ break;
+ }
+
+ if (q != NULL) {
+ NG_MKRESPONSE(rsp, msg,
+ sizeof(ng_ubt_node_qlen_ep), M_NOWAIT);
+ if (rsp == NULL) {
+ error = ENOMEM;
+ break;
+ }
+
+ ((ng_ubt_node_qlen_ep *)(rsp->data))->queue =
+ queue;
+ ((ng_ubt_node_qlen_ep *)(rsp->data))->qlen =
+ q->ifq_maxlen; /* XXX */
+ }
+ break;
+
+ case NGM_UBT_NODE_GET_STAT:
+ NG_MKRESPONSE(rsp, msg, sizeof(ng_ubt_node_stat_ep),
+ M_NOWAIT);
+ if (rsp == NULL)
+ error = ENOMEM;
+ else
+ bcopy(&sc->sc_stat, rsp->data,
+ sizeof(ng_ubt_node_stat_ep));
+ break;
+
+ case NGM_UBT_NODE_RESET_STAT:
+ NG_UBT_STAT_RESET(sc->sc_stat);
+ break;
+
+ default:
+ error = EINVAL;
+ break;
+ }
+
+ default:
+ error = EINVAL;
+ break;
+ }
+
+ NG_RESPOND_MSG(error, node, item, rsp);
+ NG_FREE_MSG(msg);
+
+ return (error);
+} /* ng_ubt_rcvmsg */
+
+/*
+ * Process data
+ */
+
+Static int
+ng_ubt_rcvdata(hook_p hook, item_p item)
+{
+ ubt_softc_p sc = (ubt_softc_p) NG_NODE_PRIVATE(NG_HOOK_NODE(hook));
+ struct mbuf *m = NULL;
+ usbd_status (*f)(ubt_softc_p, struct mbuf *) = NULL;
+ int error = 0;
+
+ if (sc == NULL) {
+ error = EHOSTDOWN;
+ goto done;
+ }
+
+ if (hook != sc->sc_hook) {
+ error = EINVAL;
+ goto done;
+ }
+
+ /* Deatch mbuf and get HCI frame type */
+ NGI_GET_M(item, m);
+
+ /* Process HCI frame */
+ switch (*mtod(m, u_int8_t *)) { /* XXX call m_pullup ? */
+ case NG_HCI_CMD_PKT:
+ f = ubt_request_start;
+ break;
+
+ case NG_HCI_ACL_DATA_PKT:
+ f = ubt_bulk_out_start;
+ break;
+
+#if __broken__ /* XXX FIXME */
+ case NG_HCI_SCO_DATA_PKT:
+ f = ubt_isoc_out_start;
+ break;
+#endif /* __broken__ */
+
+ default:
+ NG_UBT_ERR(
+"%s: %s - Dropping unknown/unsupported HCI frame, type=%d, pktlen=%d\n",
+ __func__, USBDEVNAME(sc->sc_dev), *mtod(m, u_int8_t *),
+ m->m_pkthdr.len);
+
+ NG_FREE_M(m);
+ error = EINVAL;
+
+ goto done;
+ /* NOT REACHED */
+ }
+
+ /* Loose frame type, if required */
+ if (!(sc->sc_flags & UBT_NEED_FRAME_TYPE))
+ m_adj(m, sizeof(u_int8_t));
+
+ if ((*f)(sc, m) != USBD_NORMAL_COMPLETION)
+ error = EIO;
+done:
+ NG_FREE_ITEM(item);
+
+ return (error);
+} /* ng_ubt_rcvdata */
+
+/*
+ * Abort transfers and close all USB pipes.
+ */
+
+Static void
+ng_ubt_reset(ubt_softc_p sc)
+{
+ /* Abort transfers and close all USB pipes */
+
+ /* Interrupt */
+ if (sc->sc_intr_pipe != NULL) {
+ usbd_abort_pipe(sc->sc_intr_pipe);
+ usbd_close_pipe(sc->sc_intr_pipe);
+ sc->sc_intr_pipe = NULL;
+ }
+
+ /* Bulk-in/out */
+ if (sc->sc_bulk_in_pipe != NULL) {
+ usbd_abort_pipe(sc->sc_bulk_in_pipe);
+ usbd_close_pipe(sc->sc_bulk_in_pipe);
+ sc->sc_bulk_in_pipe = NULL;
+ }
+ if (sc->sc_bulk_out_pipe != NULL) {
+ usbd_abort_pipe(sc->sc_bulk_out_pipe);
+ usbd_close_pipe(sc->sc_bulk_out_pipe);
+ sc->sc_bulk_out_pipe = NULL;
+ }
+
+ /* Isoc-in/out */
+ if (sc->sc_isoc_in_pipe != NULL) {
+ usbd_abort_pipe(sc->sc_isoc_in_pipe);
+ usbd_close_pipe(sc->sc_isoc_in_pipe);
+ sc->sc_isoc_in_pipe = NULL;
+ }
+ if (sc->sc_isoc_out_pipe != NULL) {
+ usbd_abort_pipe(sc->sc_isoc_out_pipe);
+ usbd_close_pipe(sc->sc_isoc_out_pipe);
+ sc->sc_isoc_out_pipe = NULL;
+ }
+
+ /* Cleanup queues */
+ IF_DRAIN(&sc->sc_cmdq);
+ IF_DRAIN(&sc->sc_aclq);
+ IF_DRAIN(&sc->sc_scoq);
+ IF_DRAIN(&sc->sc_inq);
+} /* ng_ubt_reset */
+
diff --git a/sys/netgraph/bluetooth/drivers/ubt/ng_ubt_var.h b/sys/netgraph/bluetooth/drivers/ubt/ng_ubt_var.h
new file mode 100644
index 0000000..10b8911
--- /dev/null
+++ b/sys/netgraph/bluetooth/drivers/ubt/ng_ubt_var.h
@@ -0,0 +1,143 @@
+/*
+ * ng_ubt_var.h
+ *
+ * Copyright (c) 2001-2002 Maksim Yevmenkin <m_evmenkin@yahoo.com>
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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.
+ *
+ * $Id: ng_ubt_var.h,v 1.1 2002/11/09 19:09:02 max Exp $
+ * $FreeBSD$
+ */
+
+#ifndef _NG_UBT_VAR_H_
+#define _NG_UBT_VAR_H_
+
+/* pullup wrapper */
+#define NG_UBT_M_PULLUP(m, s) \
+ do { \
+ if ((m)->m_len < (s)) \
+ (m) = m_pullup((m), (s)); \
+ if ((m) == NULL) \
+ NG_UBT_ALERT("%s: %s - m_pullup(%d) failed\n", \
+ __func__, USBDEVNAME(sc->sc_dev), (s)); \
+ } while (0)
+
+/* Debug printf's */
+#define NG_UBT_ALERT if (sc->sc_debug >= NG_UBT_ALERT_LEVEL) printf
+#define NG_UBT_ERR if (sc->sc_debug >= NG_UBT_ERR_LEVEL) printf
+#define NG_UBT_WARN if (sc->sc_debug >= NG_UBT_WARN_LEVEL) printf
+#define NG_UBT_INFO if (sc->sc_debug >= NG_UBT_INFO_LEVEL) printf
+
+/* Bluetooth USB control request type */
+#define UBT_HCI_REQUEST 0x20
+#define UBT_DEFAULT_QLEN 12
+
+/* USB device softc structure */
+struct ubt_softc {
+ /* State */
+ ng_ubt_node_debug_ep sc_debug; /* debug level */
+ u_int32_t sc_flags; /* device flags */
+#define UBT_NEED_FRAME_TYPE (1 << 0) /* device required frame type */
+#define UBT_HAVE_FRAME_TYPE UBT_NEED_FRAME_TYPE
+#define UBT_CMD_XMIT (1 << 1) /* CMD xmit in progress */
+#define UBT_ACL_XMIT (1 << 2) /* ACL xmit in progress */
+#define UBT_SCO_XMIT (1 << 3) /* SCO xmit in progress */
+
+ ng_ubt_node_stat_ep sc_stat; /* statistic */
+#define NG_UBT_STAT_PCKTS_SENT(s) (s).pckts_sent ++
+#define NG_UBT_STAT_BYTES_SENT(s, n) (s).bytes_sent += (n)
+#define NG_UBT_STAT_PCKTS_RECV(s) (s).pckts_recv ++
+#define NG_UBT_STAT_BYTES_RECV(s, n) (s).bytes_recv += (n)
+#define NG_UBT_STAT_OERROR(s) (s).oerrors ++
+#define NG_UBT_STAT_IERROR(s) (s).ierrors ++
+#define NG_UBT_STAT_RESET(s) bzero(&(s), sizeof((s)))
+
+ /* USB device specific */
+ USBBASEDEVICE sc_dev; /* pointer back to USB device */
+ usbd_device_handle sc_udev; /* USB device handle */
+
+ usbd_interface_handle sc_iface0; /* USB interface 0 */
+ usbd_interface_handle sc_iface1; /* USB interface 1 */
+
+ struct ifqueue sc_inq; /* incoming queue */
+ void *sc_ith; /* SWI interrupt handler */
+
+ /* Interrupt pipe (HCI events) */
+ int sc_intr_ep; /* interrupt endpoint */
+ usbd_pipe_handle sc_intr_pipe; /* interrupt pipe handle */
+ usbd_xfer_handle sc_intr_xfer; /* intr xfer */
+ u_int8_t *sc_intr_buffer; /* interrupt buffer */
+#define UBT_INTR_BUFFER_SIZE \
+ (sizeof(ng_hci_event_pkt_t) + NG_HCI_EVENT_PKT_SIZE)
+
+ /* Control pipe (HCI commands) */
+ usbd_xfer_handle sc_ctrl_xfer; /* control xfer handle */
+ void *sc_ctrl_buffer; /* control buffer */
+ struct ifqueue sc_cmdq; /* HCI command queue */
+#define UBT_CTRL_BUFFER_SIZE \
+ (sizeof(ng_hci_cmd_pkt_t) + NG_HCI_CMD_PKT_SIZE)
+
+ /* Bulk in pipe (ACL data) */
+ int sc_bulk_in_ep; /* bulk-in enpoint */
+ usbd_pipe_handle sc_bulk_in_pipe; /* bulk-in pipe */
+ usbd_xfer_handle sc_bulk_in_xfer; /* bulk-in xfer */
+ void *sc_bulk_in_buffer; /* bulk-in buffer */
+
+ /* Bulk out pipe (ACL data) */
+ int sc_bulk_out_ep; /* bulk-out endpoint */
+ usbd_pipe_handle sc_bulk_out_pipe; /* bulk-out pipe */
+ usbd_xfer_handle sc_bulk_out_xfer; /* bulk-out xfer */
+ void *sc_bulk_out_buffer; /* bulk-out buffer */
+ struct ifqueue sc_aclq; /* ACL data queue */
+#define UBT_BULK_BUFFER_SIZE \
+ 512 /* XXX should be big enough to hold one frame */
+
+ /* Isoc. in pipe (SCO data) */
+ int sc_isoc_in_ep; /* isoc-in endpoint */
+ usbd_pipe_handle sc_isoc_in_pipe; /* isoc-in pipe */
+ usbd_xfer_handle sc_isoc_in_xfer; /* isoc-in xfer */
+ void *sc_isoc_in_buffer; /* isoc-in buffer */
+ u_int16_t *sc_isoc_in_frlen; /* isoc-in. frame length */
+
+ /* Isoc. out pipe (ACL data) */
+ int sc_isoc_out_ep; /* isoc-out endpoint */
+ usbd_pipe_handle sc_isoc_out_pipe; /* isoc-out pipe */
+ usbd_xfer_handle sc_isoc_out_xfer; /* isoc-out xfer */
+ void *sc_isoc_out_buffer; /* isoc-in buffer */
+ u_int16_t *sc_isoc_out_frlen; /* isoc-out. frame length */
+ struct ifqueue sc_scoq; /* SCO data queue */
+
+ int sc_isoc_size; /* max. size of isoc. packet */
+ u_int32_t sc_isoc_nframes; /* num. isoc. frames */
+#define UBT_ISOC_BUFFER_SIZE \
+ (sizeof(ng_hci_scodata_pkt_t) + NG_HCI_SCO_PKT_SIZE)
+
+ /* Netgraph specific */
+ node_p sc_node; /* pointer back to node */
+ hook_p sc_hook; /* upstream hook */
+};
+typedef struct ubt_softc ubt_softc_t;
+typedef struct ubt_softc * ubt_softc_p;
+
+#endif /* ndef _NG_UBT_VAR_H_ */
+
OpenPOWER on IntegriCloud