2007-10-02 09:38:06 +04:00
|
|
|
/* $NetBSD: ubt.c,v 1.25 2007/10/02 05:38:06 plunky Exp $ */
|
2002-08-22 14:15:57 +04:00
|
|
|
|
2006-06-19 19:44:33 +04:00
|
|
|
/*-
|
|
|
|
* Copyright (c) 2006 Itronix Inc.
|
|
|
|
* All rights reserved.
|
|
|
|
*
|
|
|
|
* Written by Iain Hibbert for Itronix Inc.
|
|
|
|
*
|
|
|
|
* Redistribution and use in source and binary forms, with or without
|
|
|
|
* modification, are permitted provided that the following conditions
|
|
|
|
* are met:
|
|
|
|
* 1. Redistributions of source code must retain the above copyright
|
|
|
|
* notice, this list of conditions and the following disclaimer.
|
|
|
|
* 2. Redistributions in binary form must reproduce the above copyright
|
|
|
|
* notice, this list of conditions and the following disclaimer in the
|
|
|
|
* documentation and/or other materials provided with the distribution.
|
|
|
|
* 3. The name of Itronix Inc. may not be used to endorse
|
|
|
|
* or promote products derived from this software without specific
|
|
|
|
* prior written permission.
|
|
|
|
*
|
|
|
|
* THIS SOFTWARE IS PROVIDED BY ITRONIX INC. ``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 ITRONIX INC. 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.
|
|
|
|
*/
|
2002-08-22 14:15:57 +04:00
|
|
|
/*
|
2003-01-05 08:20:20 +03:00
|
|
|
* Copyright (c) 2002, 2003 The NetBSD Foundation, Inc.
|
2002-08-22 14:15:57 +04:00
|
|
|
* All rights reserved.
|
|
|
|
*
|
|
|
|
* This code is derived from software contributed to The NetBSD Foundation
|
2003-01-05 08:20:20 +03:00
|
|
|
* by Lennart Augustsson (lennart@augustsson.net) and
|
|
|
|
* David Sainty (David.Sainty@dtsp.co.nz).
|
2002-08-22 14:15:57 +04:00
|
|
|
*
|
|
|
|
* Redistribution and use in source and binary forms, with or without
|
|
|
|
* modification, are permitted provided that the following conditions
|
|
|
|
* are met:
|
|
|
|
* 1. Redistributions of source code must retain the above copyright
|
|
|
|
* notice, this list of conditions and the following disclaimer.
|
|
|
|
* 2. Redistributions in binary form must reproduce the above copyright
|
|
|
|
* notice, this list of conditions and the following disclaimer in the
|
|
|
|
* documentation and/or other materials provided with the distribution.
|
|
|
|
* 3. All advertising materials mentioning features or use of this software
|
|
|
|
* must display the following acknowledgement:
|
|
|
|
* This product includes software developed by the NetBSD
|
|
|
|
* Foundation, Inc. and its contributors.
|
|
|
|
* 4. Neither the name of The NetBSD Foundation nor the names of its
|
|
|
|
* contributors may be used to endorse or promote products derived
|
|
|
|
* from this software without specific prior written permission.
|
|
|
|
*
|
|
|
|
* THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
|
|
|
|
* ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
|
|
|
|
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
|
|
|
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
|
|
|
|
* BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
|
|
|
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
|
|
|
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
|
|
|
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
|
|
|
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
|
|
|
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
|
|
|
* POSSIBILITY OF SUCH DAMAGE.
|
|
|
|
*/
|
2006-06-19 19:44:33 +04:00
|
|
|
/*
|
|
|
|
* This driver originally written by Lennart Augustsson and David Sainty,
|
|
|
|
* but was mostly rewritten for the NetBSD Bluetooth protocol stack by
|
|
|
|
* Iain Hibbert for Itronix, Inc using the FreeBSD ng_ubt.c driver as a
|
|
|
|
* reference.
|
|
|
|
*/
|
2002-08-22 14:15:57 +04:00
|
|
|
|
|
|
|
#include <sys/cdefs.h>
|
2007-10-02 09:38:06 +04:00
|
|
|
__KERNEL_RCSID(0, "$NetBSD: ubt.c,v 1.25 2007/10/02 05:38:06 plunky Exp $");
|
2002-08-22 14:15:57 +04:00
|
|
|
|
|
|
|
#include <sys/param.h>
|
|
|
|
#include <sys/device.h>
|
|
|
|
#include <sys/ioctl.h>
|
2006-06-19 19:44:33 +04:00
|
|
|
#include <sys/kernel.h>
|
|
|
|
#include <sys/malloc.h>
|
|
|
|
#include <sys/mbuf.h>
|
2002-08-22 14:15:57 +04:00
|
|
|
#include <sys/proc.h>
|
2006-06-19 19:44:33 +04:00
|
|
|
#include <sys/sysctl.h>
|
|
|
|
#include <sys/systm.h>
|
2002-08-22 14:15:57 +04:00
|
|
|
|
|
|
|
#include <dev/usb/usb.h>
|
|
|
|
#include <dev/usb/usbdi.h>
|
|
|
|
#include <dev/usb/usbdi_util.h>
|
|
|
|
#include <dev/usb/usbdevs.h>
|
|
|
|
|
2006-06-19 19:44:33 +04:00
|
|
|
#include <netbt/bluetooth.h>
|
|
|
|
#include <netbt/hci.h>
|
|
|
|
|
|
|
|
/*******************************************************************************
|
|
|
|
*
|
|
|
|
* debugging stuff
|
|
|
|
*/
|
|
|
|
#undef DPRINTF
|
|
|
|
#undef DPRINTFN
|
2002-08-24 21:31:19 +04:00
|
|
|
|
2002-08-22 14:15:57 +04:00
|
|
|
#ifdef UBT_DEBUG
|
2006-06-19 19:44:33 +04:00
|
|
|
int ubt_debug = UBT_DEBUG;
|
|
|
|
|
|
|
|
#define DPRINTF(fmt, args...) do { \
|
|
|
|
if (ubt_debug) \
|
|
|
|
printf("%s: "fmt, __func__ , ##args); \
|
|
|
|
} while (/* CONSTCOND */0)
|
|
|
|
|
|
|
|
#define DPRINTFN(n, fmt, args...) do { \
|
|
|
|
if (ubt_debug > (n)) \
|
|
|
|
printf("%s: "fmt, __func__ , ##args); \
|
|
|
|
} while (/* CONSTCOND */0)
|
|
|
|
|
|
|
|
SYSCTL_SETUP(sysctl_hw_ubt_debug_setup, "sysctl hw.ubt_debug setup")
|
|
|
|
{
|
|
|
|
|
|
|
|
sysctl_createv(NULL, 0, NULL, NULL,
|
|
|
|
CTLFLAG_PERMANENT,
|
|
|
|
CTLTYPE_NODE, "hw",
|
|
|
|
NULL,
|
|
|
|
NULL, 0,
|
|
|
|
NULL, 0,
|
|
|
|
CTL_HW, CTL_EOL);
|
|
|
|
|
|
|
|
sysctl_createv(NULL, 0, NULL, NULL,
|
|
|
|
CTLFLAG_PERMANENT | CTLFLAG_READWRITE,
|
|
|
|
CTLTYPE_INT, "ubt_debug",
|
|
|
|
SYSCTL_DESCR("ubt debug level"),
|
|
|
|
NULL, 0,
|
|
|
|
&ubt_debug, sizeof(ubt_debug),
|
|
|
|
CTL_HW, CTL_CREATE, CTL_EOL);
|
|
|
|
}
|
2002-08-22 14:15:57 +04:00
|
|
|
#else
|
2006-06-19 19:44:33 +04:00
|
|
|
#define DPRINTF(...)
|
|
|
|
#define DPRINTFN(...)
|
2002-08-22 14:15:57 +04:00
|
|
|
#endif
|
|
|
|
|
2006-06-19 19:44:33 +04:00
|
|
|
/*******************************************************************************
|
|
|
|
*
|
|
|
|
* ubt softc structure
|
|
|
|
*
|
|
|
|
*/
|
|
|
|
|
|
|
|
/* buffer sizes */
|
|
|
|
/*
|
|
|
|
* NB: although ACL packets can extend to 65535 bytes, most devices
|
|
|
|
* have max_acl_size at much less (largest I have seen is 384)
|
|
|
|
*/
|
|
|
|
#define UBT_BUFSIZ_CMD (HCI_CMD_PKT_SIZE - 1)
|
|
|
|
#define UBT_BUFSIZ_ACL (2048 - 1)
|
|
|
|
#define UBT_BUFSIZ_EVENT (HCI_EVENT_PKT_SIZE - 1)
|
|
|
|
|
|
|
|
/* Transmit timeouts */
|
|
|
|
#define UBT_CMD_TIMEOUT USBD_DEFAULT_TIMEOUT
|
|
|
|
#define UBT_ACL_TIMEOUT USBD_DEFAULT_TIMEOUT
|
|
|
|
|
2002-08-22 14:15:57 +04:00
|
|
|
/*
|
2006-06-19 19:44:33 +04:00
|
|
|
* ISOC transfers
|
|
|
|
*
|
|
|
|
* xfer buffer size depends on the frame size, and the number
|
|
|
|
* of frames per transfer is fixed, as each frame should be
|
|
|
|
* 1ms worth of data. This keeps the rate that xfers complete
|
|
|
|
* fairly constant. We use multiple xfers to keep the hardware
|
|
|
|
* busy
|
2002-08-22 14:15:57 +04:00
|
|
|
*/
|
2006-06-19 19:44:33 +04:00
|
|
|
#define UBT_NXFERS 3 /* max xfers to queue */
|
|
|
|
#define UBT_NFRAMES 10 /* frames per xfer */
|
|
|
|
|
|
|
|
struct ubt_isoc_xfer {
|
|
|
|
struct ubt_softc *softc;
|
|
|
|
usbd_xfer_handle xfer;
|
|
|
|
uint8_t *buf;
|
|
|
|
uint16_t size[UBT_NFRAMES];
|
|
|
|
int busy;
|
|
|
|
};
|
2002-08-22 14:15:57 +04:00
|
|
|
|
|
|
|
struct ubt_softc {
|
2006-06-19 19:44:33 +04:00
|
|
|
USBBASEDEVICE sc_dev;
|
|
|
|
usbd_device_handle sc_udev;
|
|
|
|
int sc_refcnt;
|
|
|
|
int sc_dying;
|
2002-08-22 14:15:57 +04:00
|
|
|
|
2006-06-19 19:44:33 +04:00
|
|
|
/* Control Interface */
|
|
|
|
usbd_interface_handle sc_iface0;
|
2003-01-05 08:20:20 +03:00
|
|
|
|
2006-06-19 19:44:33 +04:00
|
|
|
/* Commands (control) */
|
|
|
|
usbd_xfer_handle sc_cmd_xfer;
|
|
|
|
uint8_t *sc_cmd_buf;
|
|
|
|
|
|
|
|
/* Events (interrupt) */
|
|
|
|
int sc_evt_addr; /* endpoint address */
|
|
|
|
usbd_pipe_handle sc_evt_pipe;
|
|
|
|
uint8_t *sc_evt_buf;
|
2003-01-05 08:20:20 +03:00
|
|
|
|
|
|
|
/* ACL data (in) */
|
2006-06-19 19:44:33 +04:00
|
|
|
int sc_aclrd_addr; /* endpoint address */
|
|
|
|
usbd_pipe_handle sc_aclrd_pipe; /* read pipe */
|
|
|
|
usbd_xfer_handle sc_aclrd_xfer; /* read xfer */
|
|
|
|
uint8_t *sc_aclrd_buf; /* read buffer */
|
|
|
|
int sc_aclrd_busy; /* reading */
|
2003-01-05 08:20:20 +03:00
|
|
|
|
|
|
|
/* ACL data (out) */
|
2006-06-19 19:44:33 +04:00
|
|
|
int sc_aclwr_addr; /* endpoint address */
|
|
|
|
usbd_pipe_handle sc_aclwr_pipe; /* write pipe */
|
|
|
|
usbd_xfer_handle sc_aclwr_xfer; /* write xfer */
|
|
|
|
uint8_t *sc_aclwr_buf; /* write buffer */
|
|
|
|
|
|
|
|
/* ISOC interface */
|
|
|
|
usbd_interface_handle sc_iface1; /* ISOC interface */
|
|
|
|
struct sysctllog *sc_log; /* sysctl log */
|
|
|
|
int sc_config; /* current config no */
|
|
|
|
int sc_alt_config; /* no of alternates */
|
|
|
|
|
|
|
|
/* SCO data (in) */
|
|
|
|
int sc_scord_addr; /* endpoint address */
|
|
|
|
usbd_pipe_handle sc_scord_pipe; /* read pipe */
|
|
|
|
int sc_scord_size; /* frame length */
|
|
|
|
struct ubt_isoc_xfer sc_scord[UBT_NXFERS];
|
|
|
|
struct mbuf *sc_scord_mbuf; /* current packet */
|
|
|
|
|
|
|
|
/* SCO data (out) */
|
|
|
|
int sc_scowr_addr; /* endpoint address */
|
|
|
|
usbd_pipe_handle sc_scowr_pipe; /* write pipe */
|
|
|
|
int sc_scowr_size; /* frame length */
|
|
|
|
struct ubt_isoc_xfer sc_scowr[UBT_NXFERS];
|
|
|
|
struct mbuf *sc_scowr_mbuf; /* current packet */
|
|
|
|
|
|
|
|
/* Protocol structure */
|
|
|
|
struct hci_unit sc_unit;
|
2007-03-12 23:32:00 +03:00
|
|
|
|
|
|
|
/* Successfully attached */
|
|
|
|
int sc_ok;
|
2006-06-19 19:44:33 +04:00
|
|
|
};
|
2002-08-22 14:15:57 +04:00
|
|
|
|
2006-06-19 19:44:33 +04:00
|
|
|
/*******************************************************************************
|
|
|
|
*
|
|
|
|
* Bluetooth unit/USB callback routines
|
|
|
|
*
|
|
|
|
*/
|
|
|
|
static int ubt_enable(struct hci_unit *);
|
|
|
|
static void ubt_disable(struct hci_unit *);
|
2003-01-05 08:20:20 +03:00
|
|
|
|
2006-06-19 19:44:33 +04:00
|
|
|
static void ubt_xmit_cmd_start(struct hci_unit *);
|
|
|
|
static void ubt_xmit_cmd_complete(usbd_xfer_handle,
|
|
|
|
usbd_private_handle, usbd_status);
|
2002-08-22 14:15:57 +04:00
|
|
|
|
2006-06-19 19:44:33 +04:00
|
|
|
static void ubt_xmit_acl_start(struct hci_unit *);
|
|
|
|
static void ubt_xmit_acl_complete(usbd_xfer_handle,
|
|
|
|
usbd_private_handle, usbd_status);
|
2003-01-05 08:20:20 +03:00
|
|
|
|
2006-06-19 19:44:33 +04:00
|
|
|
static void ubt_xmit_sco_start(struct hci_unit *);
|
|
|
|
static void ubt_xmit_sco_start1(struct ubt_softc *, struct ubt_isoc_xfer *);
|
|
|
|
static void ubt_xmit_sco_complete(usbd_xfer_handle,
|
|
|
|
usbd_private_handle, usbd_status);
|
2003-01-11 09:12:09 +03:00
|
|
|
|
2006-06-19 19:44:33 +04:00
|
|
|
static void ubt_recv_event(usbd_xfer_handle,
|
|
|
|
usbd_private_handle, usbd_status);
|
2003-01-11 09:12:09 +03:00
|
|
|
|
2006-06-19 19:44:33 +04:00
|
|
|
static void ubt_recv_acl_start(struct ubt_softc *);
|
|
|
|
static void ubt_recv_acl_complete(usbd_xfer_handle,
|
|
|
|
usbd_private_handle, usbd_status);
|
2003-01-05 08:20:20 +03:00
|
|
|
|
2006-06-19 19:44:33 +04:00
|
|
|
static void ubt_recv_sco_start1(struct ubt_softc *, struct ubt_isoc_xfer *);
|
|
|
|
static void ubt_recv_sco_complete(usbd_xfer_handle,
|
|
|
|
usbd_private_handle, usbd_status);
|
2003-01-05 08:20:20 +03:00
|
|
|
|
|
|
|
|
2006-06-19 19:44:33 +04:00
|
|
|
/*******************************************************************************
|
|
|
|
*
|
|
|
|
* USB Autoconfig stuff
|
|
|
|
*
|
|
|
|
*/
|
2003-01-05 08:20:20 +03:00
|
|
|
|
2002-08-22 14:15:57 +04:00
|
|
|
USB_DECLARE_DRIVER(ubt);
|
|
|
|
|
2006-06-19 19:44:33 +04:00
|
|
|
static int ubt_set_isoc_config(struct ubt_softc *);
|
|
|
|
static int ubt_sysctl_config(SYSCTLFN_PROTO);
|
|
|
|
static void ubt_abortdealloc(struct ubt_softc *);
|
|
|
|
|
2006-08-19 20:35:02 +04:00
|
|
|
/*
|
2007-03-12 23:32:00 +03:00
|
|
|
* Match against the whole device, since we want to take
|
|
|
|
* both interfaces. If a device should be ignored then add
|
2006-08-19 20:35:02 +04:00
|
|
|
*
|
|
|
|
* { VendorID, ProductID }
|
|
|
|
*
|
2007-03-12 23:32:00 +03:00
|
|
|
* to the ubt_ignore list.
|
2006-08-19 20:35:02 +04:00
|
|
|
*/
|
|
|
|
static const struct usb_devno ubt_ignore[] = {
|
|
|
|
{ USB_VENDOR_BROADCOM, USB_PRODUCT_BROADCOM_BCM2033NF },
|
|
|
|
{ 0, 0 } /* end of list */
|
|
|
|
};
|
|
|
|
|
2002-08-22 14:15:57 +04:00
|
|
|
USB_MATCH(ubt)
|
|
|
|
{
|
|
|
|
USB_MATCH_START(ubt, uaa);
|
|
|
|
|
2006-06-19 19:44:33 +04:00
|
|
|
DPRINTFN(50, "ubt_match\n");
|
2002-08-22 14:15:57 +04:00
|
|
|
|
2006-08-19 20:35:02 +04:00
|
|
|
if (usb_lookup(ubt_ignore, uaa->vendor, uaa->product))
|
|
|
|
return UMATCH_NONE;
|
|
|
|
|
Introduce different autoconf interface attributes for USB drivers
matching (and handling) a whole device and those which match an
interface only. This will allow to enforce some rules, eg that
the former don't use interface information for matching or that the
latter don't modify global device state.
The previous way left too much freedom do the drivers which led to
inconsistencies and abuse.
For now, I've not changed locators and submatch rules, this will
happen later.
There should not be any change in behaviour, except in the case of
some drivers which did behave inconsistently:
if_atu, if_axe, uep: matched the configured device in the interface
stage, but did configuration again. I've converted them to match
in the device stage.
ustir, utoppy: matched in the interface stage, but only against
vendor/device information, and used any configuration/interface
without checking. Changed to match in device stage, and added
some simple code to configure and use the first interface.
If you have one of those devices, please test!
2007-03-13 16:51:53 +03:00
|
|
|
if (uaa->class == UDCLASS_WIRELESS
|
|
|
|
&& uaa->subclass == UDSUBCLASS_RF
|
|
|
|
&& uaa->proto == UDPROTO_BLUETOOTH)
|
2007-03-12 23:32:00 +03:00
|
|
|
return UMATCH_DEVCLASS_DEVSUBCLASS_DEVPROTO;
|
2006-06-19 19:44:33 +04:00
|
|
|
|
|
|
|
return UMATCH_NONE;
|
2002-08-22 14:15:57 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
USB_ATTACH(ubt)
|
|
|
|
{
|
|
|
|
USB_ATTACH_START(ubt, sc, uaa);
|
2006-06-19 19:44:33 +04:00
|
|
|
usb_config_descriptor_t *cd;
|
|
|
|
usb_endpoint_descriptor_t *ed;
|
|
|
|
const struct sysctlnode *node;
|
|
|
|
char *devinfop;
|
|
|
|
int err;
|
|
|
|
uint8_t count, i;
|
|
|
|
|
|
|
|
DPRINTFN(50, "ubt_attach: sc=%p\n", sc);
|
|
|
|
|
|
|
|
sc->sc_udev = uaa->device;
|
|
|
|
|
|
|
|
devinfop = usbd_devinfo_alloc(sc->sc_udev, 0);
|
2002-08-22 14:15:57 +04:00
|
|
|
USB_ATTACH_SETUP;
|
2006-06-19 19:44:33 +04:00
|
|
|
aprint_normal("%s: %s\n", USBDEVNAME(sc->sc_dev), devinfop);
|
2005-05-11 14:02:28 +04:00
|
|
|
usbd_devinfo_free(devinfop);
|
2002-08-22 14:15:57 +04:00
|
|
|
|
2006-06-19 19:44:33 +04:00
|
|
|
/*
|
2007-03-12 23:32:00 +03:00
|
|
|
* Move the device into the configured state
|
2006-06-19 19:44:33 +04:00
|
|
|
*/
|
2007-03-12 23:32:00 +03:00
|
|
|
err = usbd_set_config_index(sc->sc_udev, 0, 1);
|
|
|
|
if (err) {
|
|
|
|
aprint_error("%s: failed to set configuration idx 0: %s\n",
|
|
|
|
USBDEVNAME(sc->sc_dev), usbd_errstr(err));
|
2006-06-19 19:44:33 +04:00
|
|
|
|
|
|
|
USB_ATTACH_ERROR_RETURN;
|
|
|
|
}
|
2002-08-22 14:15:57 +04:00
|
|
|
|
2003-01-05 08:20:20 +03:00
|
|
|
/*
|
2006-06-19 19:44:33 +04:00
|
|
|
* Interface 0 must have 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
|
2002-08-24 21:31:19 +04:00
|
|
|
*/
|
2007-03-12 23:32:00 +03:00
|
|
|
err = usbd_device2interface_handle(sc->sc_udev, 0, &sc->sc_iface0);
|
2006-06-19 19:44:33 +04:00
|
|
|
if (err) {
|
|
|
|
aprint_error("%s: Could not get interface 0 handle %s (%d)\n",
|
|
|
|
USBDEVNAME(sc->sc_dev), usbd_errstr(err), err);
|
|
|
|
|
|
|
|
USB_ATTACH_ERROR_RETURN;
|
|
|
|
}
|
2003-01-05 08:20:20 +03:00
|
|
|
|
|
|
|
sc->sc_evt_addr = -1;
|
|
|
|
sc->sc_aclrd_addr = -1;
|
|
|
|
sc->sc_aclwr_addr = -1;
|
|
|
|
|
2006-06-19 19:44:33 +04:00
|
|
|
count = 0;
|
2007-03-12 23:32:00 +03:00
|
|
|
(void)usbd_endpoint_count(sc->sc_iface0, &count);
|
2006-06-19 19:44:33 +04:00
|
|
|
|
|
|
|
for (i = 0 ; i < count ; i++) {
|
|
|
|
int dir, type;
|
|
|
|
|
2007-03-12 23:32:00 +03:00
|
|
|
ed = usbd_interface2endpoint_descriptor(sc->sc_iface0, i);
|
2002-08-22 14:15:57 +04:00
|
|
|
if (ed == NULL) {
|
2006-06-19 19:44:33 +04:00
|
|
|
aprint_error("%s: could not read endpoint descriptor %d\n",
|
2002-08-22 14:15:57 +04:00
|
|
|
USBDEVNAME(sc->sc_dev), i);
|
2006-06-19 19:44:33 +04:00
|
|
|
|
2002-08-22 14:15:57 +04:00
|
|
|
USB_ATTACH_ERROR_RETURN;
|
|
|
|
}
|
2003-01-05 08:20:20 +03:00
|
|
|
|
2006-06-19 19:44:33 +04:00
|
|
|
dir = UE_GET_DIR(ed->bEndpointAddress);
|
|
|
|
type = UE_GET_XFERTYPE(ed->bmAttributes);
|
2003-01-05 08:20:20 +03:00
|
|
|
|
2006-06-19 19:44:33 +04:00
|
|
|
if (dir == UE_DIR_IN && type == UE_INTERRUPT)
|
2003-01-05 08:20:20 +03:00
|
|
|
sc->sc_evt_addr = ed->bEndpointAddress;
|
2006-06-19 19:44:33 +04:00
|
|
|
else if (dir == UE_DIR_IN && type == UE_BULK)
|
2003-01-05 08:20:20 +03:00
|
|
|
sc->sc_aclrd_addr = ed->bEndpointAddress;
|
2006-06-19 19:44:33 +04:00
|
|
|
else if (dir == UE_DIR_OUT && type == UE_BULK)
|
2003-01-05 08:20:20 +03:00
|
|
|
sc->sc_aclwr_addr = ed->bEndpointAddress;
|
2002-08-22 14:15:57 +04:00
|
|
|
}
|
2003-01-05 08:20:20 +03:00
|
|
|
|
2006-06-19 19:44:33 +04:00
|
|
|
if (sc->sc_evt_addr == -1) {
|
|
|
|
aprint_error("%s: missing INTERRUPT endpoint on interface 0\n",
|
|
|
|
USBDEVNAME(sc->sc_dev));
|
|
|
|
|
2002-08-22 14:15:57 +04:00
|
|
|
USB_ATTACH_ERROR_RETURN;
|
|
|
|
}
|
2006-06-19 19:44:33 +04:00
|
|
|
if (sc->sc_aclrd_addr == -1) {
|
|
|
|
aprint_error("%s: missing BULK IN endpoint on interface 0\n",
|
|
|
|
USBDEVNAME(sc->sc_dev));
|
2002-08-22 14:15:57 +04:00
|
|
|
|
2006-06-19 19:44:33 +04:00
|
|
|
USB_ATTACH_ERROR_RETURN;
|
2002-08-24 21:31:19 +04:00
|
|
|
}
|
2006-06-19 19:44:33 +04:00
|
|
|
if (sc->sc_aclwr_addr == -1) {
|
|
|
|
aprint_error("%s: missing BULK OUT endpoint on interface 0\n",
|
|
|
|
USBDEVNAME(sc->sc_dev));
|
2002-08-24 21:31:19 +04:00
|
|
|
|
2006-06-19 19:44:33 +04:00
|
|
|
USB_ATTACH_ERROR_RETURN;
|
|
|
|
}
|
2002-08-22 14:15:57 +04:00
|
|
|
|
2006-06-19 19:44:33 +04:00
|
|
|
/*
|
|
|
|
* Interface 1 must have 2 endpoints
|
|
|
|
* 1) Isochronous IN endpoint to receive SCO data
|
|
|
|
* 2) Isochronous OUT endpoint to send SCO data
|
|
|
|
*
|
|
|
|
* and will have several configurations, which can be selected
|
|
|
|
* via a sysctl variable. We select config 0 to start, which
|
|
|
|
* means that no SCO data will be available.
|
|
|
|
*/
|
2007-03-12 23:32:00 +03:00
|
|
|
err = usbd_device2interface_handle(sc->sc_udev, 1, &sc->sc_iface1);
|
2006-06-19 19:44:33 +04:00
|
|
|
if (err) {
|
|
|
|
aprint_error("%s: Could not get interface 1 handle %s (%d)\n",
|
|
|
|
USBDEVNAME(sc->sc_dev), usbd_errstr(err), err);
|
2002-08-22 14:15:57 +04:00
|
|
|
|
2006-06-19 19:44:33 +04:00
|
|
|
USB_ATTACH_ERROR_RETURN;
|
|
|
|
}
|
2002-08-22 14:15:57 +04:00
|
|
|
|
2006-06-19 19:44:33 +04:00
|
|
|
cd = usbd_get_config_descriptor(sc->sc_udev);
|
|
|
|
if (cd == NULL) {
|
|
|
|
aprint_error("%s: could not get config descriptor\n",
|
|
|
|
USBDEVNAME(sc->sc_dev));
|
2003-01-05 08:20:20 +03:00
|
|
|
|
2006-06-19 19:44:33 +04:00
|
|
|
USB_ATTACH_ERROR_RETURN;
|
2003-01-05 08:20:20 +03:00
|
|
|
}
|
2006-06-19 19:44:33 +04:00
|
|
|
|
|
|
|
sc->sc_alt_config = usbd_get_no_alts(cd, 1);
|
|
|
|
|
|
|
|
/* set initial config */
|
|
|
|
err = ubt_set_isoc_config(sc);
|
|
|
|
if (err) {
|
|
|
|
aprint_error("%s: ISOC config failed\n",
|
|
|
|
USBDEVNAME(sc->sc_dev));
|
|
|
|
|
|
|
|
USB_ATTACH_ERROR_RETURN;
|
2003-01-05 08:20:20 +03:00
|
|
|
}
|
2006-06-19 19:44:33 +04:00
|
|
|
|
|
|
|
/* Attach HCI */
|
|
|
|
sc->sc_unit.hci_softc = sc;
|
|
|
|
sc->sc_unit.hci_devname = USBDEVNAME(sc->sc_dev);
|
|
|
|
sc->sc_unit.hci_enable = ubt_enable;
|
|
|
|
sc->sc_unit.hci_disable = ubt_disable;
|
|
|
|
sc->sc_unit.hci_start_cmd = ubt_xmit_cmd_start;
|
|
|
|
sc->sc_unit.hci_start_acl = ubt_xmit_acl_start;
|
|
|
|
sc->sc_unit.hci_start_sco = ubt_xmit_sco_start;
|
2006-12-21 18:55:21 +03:00
|
|
|
sc->sc_unit.hci_ipl = makeiplcookie(IPL_USB); /* XXX: IPL_SOFTUSB ?? */
|
2006-06-19 19:44:33 +04:00
|
|
|
hci_attach(&sc->sc_unit);
|
|
|
|
|
|
|
|
usbd_add_drv_event(USB_EVENT_DRIVER_ATTACH, sc->sc_udev,
|
|
|
|
USBDEV(sc->sc_dev));
|
|
|
|
|
|
|
|
/* sysctl set-up for alternate configs */
|
|
|
|
sysctl_createv(&sc->sc_log, 0, NULL, NULL,
|
|
|
|
CTLFLAG_PERMANENT,
|
|
|
|
CTLTYPE_NODE, "hw",
|
|
|
|
NULL,
|
|
|
|
NULL, 0,
|
|
|
|
NULL, 0,
|
|
|
|
CTL_HW, CTL_EOL);
|
|
|
|
|
|
|
|
sysctl_createv(&sc->sc_log, 0, NULL, &node,
|
|
|
|
0,
|
|
|
|
CTLTYPE_NODE, USBDEVNAME(sc->sc_dev),
|
|
|
|
SYSCTL_DESCR("ubt driver information"),
|
|
|
|
NULL, 0,
|
|
|
|
NULL, 0,
|
|
|
|
CTL_HW,
|
|
|
|
CTL_CREATE, CTL_EOL);
|
|
|
|
|
|
|
|
if (node != NULL) {
|
|
|
|
sysctl_createv(&sc->sc_log, 0, NULL, NULL,
|
|
|
|
CTLFLAG_READWRITE,
|
|
|
|
CTLTYPE_INT, "config",
|
|
|
|
SYSCTL_DESCR("configuration number"),
|
|
|
|
ubt_sysctl_config, 0,
|
|
|
|
sc, 0,
|
|
|
|
CTL_HW, node->sysctl_num,
|
|
|
|
CTL_CREATE, CTL_EOL);
|
|
|
|
|
|
|
|
sysctl_createv(&sc->sc_log, 0, NULL, NULL,
|
|
|
|
CTLFLAG_READONLY,
|
|
|
|
CTLTYPE_INT, "alt_config",
|
|
|
|
SYSCTL_DESCR("number of alternate configurations"),
|
|
|
|
NULL, 0,
|
|
|
|
&sc->sc_alt_config, sizeof(sc->sc_alt_config),
|
|
|
|
CTL_HW, node->sysctl_num,
|
|
|
|
CTL_CREATE, CTL_EOL);
|
|
|
|
|
|
|
|
sysctl_createv(&sc->sc_log, 0, NULL, NULL,
|
|
|
|
CTLFLAG_READONLY,
|
|
|
|
CTLTYPE_INT, "sco_rxsize",
|
|
|
|
SYSCTL_DESCR("max SCO receive size"),
|
|
|
|
NULL, 0,
|
|
|
|
&sc->sc_scord_size, sizeof(sc->sc_scord_size),
|
|
|
|
CTL_HW, node->sysctl_num,
|
|
|
|
CTL_CREATE, CTL_EOL);
|
|
|
|
|
|
|
|
sysctl_createv(&sc->sc_log, 0, NULL, NULL,
|
|
|
|
CTLFLAG_READONLY,
|
|
|
|
CTLTYPE_INT, "sco_txsize",
|
|
|
|
SYSCTL_DESCR("max SCO transmit size"),
|
|
|
|
NULL, 0,
|
|
|
|
&sc->sc_scowr_size, sizeof(sc->sc_scowr_size),
|
|
|
|
CTL_HW, node->sysctl_num,
|
|
|
|
CTL_CREATE, CTL_EOL);
|
2003-01-05 08:20:20 +03:00
|
|
|
}
|
2006-06-19 19:44:33 +04:00
|
|
|
|
2007-03-12 23:32:00 +03:00
|
|
|
sc->sc_ok = 1;
|
2006-06-19 19:44:33 +04:00
|
|
|
USB_ATTACH_SUCCESS_RETURN;
|
2003-01-05 08:20:20 +03:00
|
|
|
}
|
|
|
|
|
2002-08-22 14:15:57 +04:00
|
|
|
USB_DETACH(ubt)
|
|
|
|
{
|
|
|
|
USB_DETACH_START(ubt, sc);
|
|
|
|
int s;
|
|
|
|
|
2006-06-19 19:44:33 +04:00
|
|
|
DPRINTF("sc=%p flags=%d\n", sc, flags);
|
2002-08-22 14:15:57 +04:00
|
|
|
|
|
|
|
sc->sc_dying = 1;
|
2003-01-05 08:20:20 +03:00
|
|
|
|
2007-03-12 23:32:00 +03:00
|
|
|
if (!sc->sc_ok)
|
|
|
|
return 0;
|
|
|
|
|
2006-06-19 19:44:33 +04:00
|
|
|
/* delete sysctl nodes */
|
|
|
|
sysctl_teardown(&sc->sc_log);
|
|
|
|
|
|
|
|
/* Detach HCI interface */
|
|
|
|
hci_detach(&sc->sc_unit);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Abort all pipes. Causes processes waiting for transfer to wake.
|
|
|
|
*
|
|
|
|
* Actually, hci_detach() above will call ubt_disable() which may
|
|
|
|
* call ubt_abortdealloc(), but lets be sure since doing it twice
|
|
|
|
* wont cause an error.
|
|
|
|
*/
|
2003-01-05 08:20:20 +03:00
|
|
|
ubt_abortdealloc(sc);
|
2002-08-22 14:15:57 +04:00
|
|
|
|
2006-06-19 19:44:33 +04:00
|
|
|
/* wait for all processes to finish */
|
2002-08-22 14:15:57 +04:00
|
|
|
s = splusb();
|
2006-06-19 19:44:33 +04:00
|
|
|
if (sc->sc_refcnt-- > 0)
|
2002-08-22 14:15:57 +04:00
|
|
|
usb_detach_wait(USBDEV(sc->sc_dev));
|
|
|
|
|
2006-06-19 19:44:33 +04:00
|
|
|
splx(s);
|
2002-08-22 14:15:57 +04:00
|
|
|
|
|
|
|
usbd_add_drv_event(USB_EVENT_DRIVER_DETACH, sc->sc_udev,
|
|
|
|
USBDEV(sc->sc_dev));
|
|
|
|
|
2006-06-19 19:44:33 +04:00
|
|
|
DPRINTFN(1, "driver detached\n");
|
2003-01-05 08:20:20 +03:00
|
|
|
|
2006-06-19 19:44:33 +04:00
|
|
|
return 0;
|
2002-08-22 14:15:57 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
int
|
|
|
|
ubt_activate(device_ptr_t self, enum devact act)
|
|
|
|
{
|
|
|
|
struct ubt_softc *sc = (struct ubt_softc *)self;
|
|
|
|
int error = 0;
|
|
|
|
|
2007-10-02 09:38:06 +04:00
|
|
|
DPRINTFN(1, "sc=%p, act=%d\n", sc, act);
|
2006-06-19 19:44:33 +04:00
|
|
|
|
2002-08-22 14:15:57 +04:00
|
|
|
switch (act) {
|
|
|
|
case DVACT_ACTIVATE:
|
|
|
|
break;
|
|
|
|
|
|
|
|
case DVACT_DEACTIVATE:
|
|
|
|
sc->sc_dying = 1;
|
|
|
|
break;
|
2007-10-02 09:38:06 +04:00
|
|
|
|
|
|
|
default:
|
|
|
|
error = EOPNOTSUPP:
|
|
|
|
break;
|
2002-08-22 14:15:57 +04:00
|
|
|
}
|
2007-10-02 09:38:06 +04:00
|
|
|
|
2006-06-19 19:44:33 +04:00
|
|
|
return error;
|
2002-08-22 14:15:57 +04:00
|
|
|
}
|
2003-01-05 08:20:20 +03:00
|
|
|
|
2006-06-19 19:44:33 +04:00
|
|
|
/* set ISOC configuration */
|
2003-01-05 08:20:20 +03:00
|
|
|
static int
|
2006-06-19 19:44:33 +04:00
|
|
|
ubt_set_isoc_config(struct ubt_softc *sc)
|
2003-01-05 08:20:20 +03:00
|
|
|
{
|
2006-06-19 19:44:33 +04:00
|
|
|
usb_endpoint_descriptor_t *ed;
|
|
|
|
int rd_addr, wr_addr, rd_size, wr_size;
|
|
|
|
uint8_t count, i;
|
|
|
|
int err;
|
|
|
|
|
|
|
|
err = usbd_set_interface(sc->sc_iface1, sc->sc_config);
|
2006-09-19 23:45:48 +04:00
|
|
|
if (err != USBD_NORMAL_COMPLETION) {
|
2006-06-19 19:44:33 +04:00
|
|
|
aprint_error(
|
|
|
|
"%s: Could not set config %d on ISOC interface. %s (%d)\n",
|
|
|
|
USBDEVNAME(sc->sc_dev), sc->sc_config, usbd_errstr(err), err);
|
|
|
|
|
2006-09-19 23:45:48 +04:00
|
|
|
return err == USBD_IN_USE ? EBUSY : EIO;
|
2006-06-19 19:44:33 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* We wont get past the above if there are any pipes open, so no
|
|
|
|
* need to worry about buf/xfer/pipe deallocation. If we get an
|
|
|
|
* error after this, the frame quantities will be 0 and no SCO
|
|
|
|
* data will be possible.
|
|
|
|
*/
|
|
|
|
|
|
|
|
sc->sc_scord_size = rd_size = 0;
|
|
|
|
sc->sc_scord_addr = rd_addr = -1;
|
|
|
|
|
|
|
|
sc->sc_scowr_size = wr_size = 0;
|
|
|
|
sc->sc_scowr_addr = wr_addr = -1;
|
|
|
|
|
|
|
|
count = 0;
|
|
|
|
(void)usbd_endpoint_count(sc->sc_iface1, &count);
|
|
|
|
|
|
|
|
for (i = 0 ; i < count ; i++) {
|
|
|
|
ed = usbd_interface2endpoint_descriptor(sc->sc_iface1, i);
|
|
|
|
if (ed == NULL) {
|
|
|
|
printf("%s: could not read endpoint descriptor %d\n",
|
|
|
|
USBDEVNAME(sc->sc_dev), i);
|
|
|
|
|
|
|
|
return EIO;
|
|
|
|
}
|
|
|
|
|
|
|
|
DPRINTFN(5, "%s: endpoint type %02x (%02x) addr %02x (%s)\n",
|
|
|
|
USBDEVNAME(sc->sc_dev),
|
|
|
|
UE_GET_XFERTYPE(ed->bmAttributes),
|
|
|
|
UE_GET_ISO_TYPE(ed->bmAttributes),
|
|
|
|
ed->bEndpointAddress,
|
|
|
|
UE_GET_DIR(ed->bEndpointAddress) ? "in" : "out");
|
|
|
|
|
|
|
|
if (UE_GET_XFERTYPE(ed->bmAttributes) != UE_ISOCHRONOUS)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
if (UE_GET_DIR(ed->bEndpointAddress) == UE_DIR_IN) {
|
|
|
|
rd_addr = ed->bEndpointAddress;
|
|
|
|
rd_size = UGETW(ed->wMaxPacketSize);
|
|
|
|
} else {
|
|
|
|
wr_addr = ed->bEndpointAddress;
|
|
|
|
wr_size = UGETW(ed->wMaxPacketSize);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (rd_addr == -1) {
|
|
|
|
aprint_error(
|
|
|
|
"%s: missing ISOC IN endpoint on interface config %d\n",
|
|
|
|
USBDEVNAME(sc->sc_dev), sc->sc_config);
|
|
|
|
|
|
|
|
return ENOENT;
|
|
|
|
}
|
|
|
|
if (wr_addr == -1) {
|
|
|
|
aprint_error(
|
|
|
|
"%s: missing ISOC OUT endpoint on interface config %d\n",
|
|
|
|
USBDEVNAME(sc->sc_dev), sc->sc_config);
|
|
|
|
|
|
|
|
return ENOENT;
|
|
|
|
}
|
|
|
|
|
|
|
|
#ifdef DIAGNOSTIC
|
|
|
|
if (rd_size > MLEN) {
|
|
|
|
printf("%s: rd_size=%d exceeds MLEN\n",
|
|
|
|
USBDEVNAME(sc->sc_dev), rd_size);
|
|
|
|
|
|
|
|
return EOVERFLOW;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (wr_size > MLEN) {
|
|
|
|
printf("%s: wr_size=%d exceeds MLEN\n",
|
|
|
|
USBDEVNAME(sc->sc_dev), wr_size);
|
|
|
|
|
|
|
|
return EOVERFLOW;
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
|
|
sc->sc_scord_size = rd_size;
|
|
|
|
sc->sc_scord_addr = rd_addr;
|
|
|
|
|
|
|
|
sc->sc_scowr_size = wr_size;
|
|
|
|
sc->sc_scowr_addr = wr_addr;
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* sysctl helper to set alternate configurations */
|
|
|
|
static int
|
|
|
|
ubt_sysctl_config(SYSCTLFN_ARGS)
|
|
|
|
{
|
|
|
|
struct sysctlnode node;
|
|
|
|
struct ubt_softc *sc;
|
|
|
|
int t, error;
|
|
|
|
|
|
|
|
node = *rnode;
|
|
|
|
sc = node.sysctl_data;
|
|
|
|
|
|
|
|
t = sc->sc_config;
|
|
|
|
node.sysctl_data = &t;
|
|
|
|
error = sysctl_lookup(SYSCTLFN_CALL(&node));
|
|
|
|
if (error || newp == NULL)
|
|
|
|
return error;
|
|
|
|
|
|
|
|
if (t < 0 || t >= sc->sc_alt_config)
|
|
|
|
return EINVAL;
|
|
|
|
|
2006-09-20 00:34:33 +04:00
|
|
|
/* This may not change when the unit is enabled */
|
|
|
|
if (sc->sc_unit.hci_flags & BTF_RUNNING)
|
|
|
|
return EBUSY;
|
|
|
|
|
2006-06-19 19:44:33 +04:00
|
|
|
sc->sc_config = t;
|
|
|
|
return ubt_set_isoc_config(sc);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
ubt_abortdealloc(struct ubt_softc *sc)
|
|
|
|
{
|
|
|
|
int i;
|
|
|
|
|
|
|
|
DPRINTFN(1, "sc=%p\n", sc);
|
|
|
|
|
|
|
|
/* Abort all pipes */
|
|
|
|
if (sc->sc_evt_pipe != NULL) {
|
|
|
|
usbd_abort_pipe(sc->sc_evt_pipe);
|
|
|
|
usbd_close_pipe(sc->sc_evt_pipe);
|
|
|
|
sc->sc_evt_pipe = NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (sc->sc_aclrd_pipe != NULL) {
|
|
|
|
usbd_abort_pipe(sc->sc_aclrd_pipe);
|
|
|
|
usbd_close_pipe(sc->sc_aclrd_pipe);
|
|
|
|
sc->sc_aclrd_pipe = NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (sc->sc_aclwr_pipe != NULL) {
|
|
|
|
usbd_abort_pipe(sc->sc_aclwr_pipe);
|
|
|
|
usbd_close_pipe(sc->sc_aclwr_pipe);
|
|
|
|
sc->sc_aclwr_pipe = NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (sc->sc_scord_pipe != NULL) {
|
|
|
|
usbd_abort_pipe(sc->sc_scord_pipe);
|
|
|
|
usbd_close_pipe(sc->sc_scord_pipe);
|
|
|
|
sc->sc_scord_pipe = NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (sc->sc_scowr_pipe != NULL) {
|
|
|
|
usbd_abort_pipe(sc->sc_scowr_pipe);
|
|
|
|
usbd_close_pipe(sc->sc_scowr_pipe);
|
|
|
|
sc->sc_scowr_pipe = NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Free event buffer */
|
|
|
|
if (sc->sc_evt_buf != NULL) {
|
|
|
|
free(sc->sc_evt_buf, M_USBDEV);
|
|
|
|
sc->sc_evt_buf = NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Free all xfers and xfer buffers (implicit) */
|
|
|
|
if (sc->sc_cmd_xfer != NULL) {
|
|
|
|
usbd_free_xfer(sc->sc_cmd_xfer);
|
|
|
|
sc->sc_cmd_xfer = NULL;
|
|
|
|
sc->sc_cmd_buf = NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (sc->sc_aclrd_xfer != NULL) {
|
|
|
|
usbd_free_xfer(sc->sc_aclrd_xfer);
|
|
|
|
sc->sc_aclrd_xfer = NULL;
|
|
|
|
sc->sc_aclrd_buf = NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (sc->sc_aclwr_xfer != NULL) {
|
|
|
|
usbd_free_xfer(sc->sc_aclwr_xfer);
|
|
|
|
sc->sc_aclwr_xfer = NULL;
|
|
|
|
sc->sc_aclwr_buf = NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
for (i = 0 ; i < UBT_NXFERS ; i++) {
|
|
|
|
if (sc->sc_scord[i].xfer != NULL) {
|
|
|
|
usbd_free_xfer(sc->sc_scord[i].xfer);
|
|
|
|
sc->sc_scord[i].xfer = NULL;
|
|
|
|
sc->sc_scord[i].buf = NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (sc->sc_scowr[i].xfer != NULL) {
|
|
|
|
usbd_free_xfer(sc->sc_scowr[i].xfer);
|
|
|
|
sc->sc_scowr[i].xfer = NULL;
|
|
|
|
sc->sc_scowr[i].buf = NULL;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Free partial SCO packets */
|
|
|
|
if (sc->sc_scord_mbuf != NULL) {
|
|
|
|
m_freem(sc->sc_scord_mbuf);
|
|
|
|
sc->sc_scord_mbuf = NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (sc->sc_scowr_mbuf != NULL) {
|
|
|
|
m_freem(sc->sc_scowr_mbuf);
|
|
|
|
sc->sc_scowr_mbuf = NULL;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/*******************************************************************************
|
|
|
|
*
|
|
|
|
* Bluetooth Unit/USB callbacks
|
|
|
|
*
|
|
|
|
* All of this will be called at the IPL_ we specified above
|
|
|
|
*/
|
|
|
|
static int
|
|
|
|
ubt_enable(struct hci_unit *unit)
|
|
|
|
{
|
|
|
|
struct ubt_softc *sc = unit->hci_softc;
|
2003-01-05 08:20:20 +03:00
|
|
|
usbd_status err;
|
2006-06-19 19:44:33 +04:00
|
|
|
int i, error;
|
|
|
|
|
|
|
|
DPRINTFN(1, "sc=%p\n", sc);
|
2003-01-05 08:20:20 +03:00
|
|
|
|
2006-06-19 19:44:33 +04:00
|
|
|
if (unit->hci_flags & BTF_RUNNING)
|
|
|
|
return 0;
|
2003-01-05 08:20:20 +03:00
|
|
|
|
2006-06-19 19:44:33 +04:00
|
|
|
/* Events */
|
|
|
|
sc->sc_evt_buf = malloc(UBT_BUFSIZ_EVENT, M_USBDEV, M_NOWAIT);
|
2003-01-05 08:20:20 +03:00
|
|
|
if (sc->sc_evt_buf == NULL) {
|
|
|
|
error = ENOMEM;
|
2006-06-19 19:44:33 +04:00
|
|
|
goto bad;
|
2003-01-05 08:20:20 +03:00
|
|
|
}
|
2006-06-19 19:44:33 +04:00
|
|
|
err = usbd_open_pipe_intr(sc->sc_iface0,
|
|
|
|
sc->sc_evt_addr,
|
|
|
|
USBD_SHORT_XFER_OK,
|
|
|
|
&sc->sc_evt_pipe,
|
|
|
|
sc,
|
|
|
|
sc->sc_evt_buf,
|
|
|
|
UBT_BUFSIZ_EVENT,
|
|
|
|
ubt_recv_event,
|
2006-12-01 23:41:23 +03:00
|
|
|
USBD_DEFAULT_INTERVAL);
|
2003-01-05 08:20:20 +03:00
|
|
|
if (err != USBD_NORMAL_COMPLETION) {
|
|
|
|
error = EIO;
|
2006-06-19 19:44:33 +04:00
|
|
|
goto bad;
|
2003-01-05 08:20:20 +03:00
|
|
|
}
|
|
|
|
|
2006-06-19 19:44:33 +04:00
|
|
|
/* Commands */
|
|
|
|
sc->sc_cmd_xfer = usbd_alloc_xfer(sc->sc_udev);
|
|
|
|
if (sc->sc_cmd_xfer == NULL) {
|
|
|
|
error = ENOMEM;
|
|
|
|
goto bad;
|
|
|
|
}
|
|
|
|
sc->sc_cmd_buf = usbd_alloc_buffer(sc->sc_cmd_xfer, UBT_BUFSIZ_CMD);
|
|
|
|
if (sc->sc_cmd_buf == NULL) {
|
|
|
|
error = ENOMEM;
|
|
|
|
goto bad;
|
2003-01-05 08:20:20 +03:00
|
|
|
}
|
|
|
|
|
2006-06-19 19:44:33 +04:00
|
|
|
/* ACL read */
|
|
|
|
err = usbd_open_pipe(sc->sc_iface0, sc->sc_aclrd_addr,
|
|
|
|
USBD_EXCLUSIVE_USE, &sc->sc_aclrd_pipe);
|
2003-01-05 08:20:20 +03:00
|
|
|
if (err != USBD_NORMAL_COMPLETION) {
|
|
|
|
error = EIO;
|
2006-06-19 19:44:33 +04:00
|
|
|
goto bad;
|
2003-01-05 08:20:20 +03:00
|
|
|
}
|
|
|
|
sc->sc_aclrd_xfer = usbd_alloc_xfer(sc->sc_udev);
|
|
|
|
if (sc->sc_aclrd_xfer == NULL) {
|
|
|
|
error = ENOMEM;
|
2006-06-19 19:44:33 +04:00
|
|
|
goto bad;
|
2003-01-05 08:20:20 +03:00
|
|
|
}
|
2006-06-19 19:44:33 +04:00
|
|
|
sc->sc_aclrd_buf = usbd_alloc_buffer(sc->sc_aclrd_xfer, UBT_BUFSIZ_ACL);
|
|
|
|
if (sc->sc_aclrd_buf == NULL) {
|
2003-01-05 08:20:20 +03:00
|
|
|
error = ENOMEM;
|
2006-06-19 19:44:33 +04:00
|
|
|
goto bad;
|
2003-01-05 08:20:20 +03:00
|
|
|
}
|
2006-06-19 19:44:33 +04:00
|
|
|
sc->sc_aclrd_busy = 0;
|
|
|
|
ubt_recv_acl_start(sc);
|
2003-01-05 08:20:20 +03:00
|
|
|
|
2006-06-19 19:44:33 +04:00
|
|
|
/* ACL write */
|
|
|
|
err = usbd_open_pipe(sc->sc_iface0, sc->sc_aclwr_addr,
|
|
|
|
USBD_EXCLUSIVE_USE, &sc->sc_aclwr_pipe);
|
|
|
|
if (err != USBD_NORMAL_COMPLETION) {
|
|
|
|
error = EIO;
|
|
|
|
goto bad;
|
2003-01-05 08:20:20 +03:00
|
|
|
}
|
2006-06-19 19:44:33 +04:00
|
|
|
sc->sc_aclwr_xfer = usbd_alloc_xfer(sc->sc_udev);
|
|
|
|
if (sc->sc_aclwr_xfer == NULL) {
|
2003-01-05 08:20:20 +03:00
|
|
|
error = ENOMEM;
|
2006-06-19 19:44:33 +04:00
|
|
|
goto bad;
|
2003-01-05 08:20:20 +03:00
|
|
|
}
|
2006-06-19 19:44:33 +04:00
|
|
|
sc->sc_aclwr_buf = usbd_alloc_buffer(sc->sc_aclwr_xfer, UBT_BUFSIZ_ACL);
|
2003-01-05 08:20:20 +03:00
|
|
|
if (sc->sc_aclwr_buf == NULL) {
|
|
|
|
error = ENOMEM;
|
2006-06-19 19:44:33 +04:00
|
|
|
goto bad;
|
2003-01-05 08:20:20 +03:00
|
|
|
}
|
|
|
|
|
2006-06-19 19:44:33 +04:00
|
|
|
/* SCO read */
|
|
|
|
if (sc->sc_scord_size > 0) {
|
|
|
|
err = usbd_open_pipe(sc->sc_iface1, sc->sc_scord_addr,
|
|
|
|
USBD_EXCLUSIVE_USE, &sc->sc_scord_pipe);
|
|
|
|
if (err != USBD_NORMAL_COMPLETION) {
|
|
|
|
error = EIO;
|
|
|
|
goto bad;
|
|
|
|
}
|
2003-01-05 08:20:20 +03:00
|
|
|
|
2006-06-19 19:44:33 +04:00
|
|
|
for (i = 0 ; i < UBT_NXFERS ; i++) {
|
|
|
|
sc->sc_scord[i].xfer = usbd_alloc_xfer(sc->sc_udev);
|
|
|
|
if (sc->sc_scord[i].xfer == NULL) {
|
|
|
|
error = ENOMEM;
|
|
|
|
goto bad;
|
|
|
|
}
|
|
|
|
sc->sc_scord[i].buf = usbd_alloc_buffer(sc->sc_scord[i].xfer,
|
|
|
|
sc->sc_scord_size * UBT_NFRAMES);
|
|
|
|
if (sc->sc_scord[i].buf == NULL) {
|
|
|
|
error = ENOMEM;
|
|
|
|
goto bad;
|
|
|
|
}
|
|
|
|
sc->sc_scord[i].softc = sc;
|
|
|
|
sc->sc_scord[i].busy = 0;
|
|
|
|
ubt_recv_sco_start1(sc, &sc->sc_scord[i]);
|
|
|
|
}
|
|
|
|
}
|
2003-01-05 08:20:20 +03:00
|
|
|
|
2006-06-19 19:44:33 +04:00
|
|
|
/* SCO write */
|
|
|
|
if (sc->sc_scowr_size > 0) {
|
|
|
|
err = usbd_open_pipe(sc->sc_iface1, sc->sc_scowr_addr,
|
|
|
|
USBD_EXCLUSIVE_USE, &sc->sc_scowr_pipe);
|
|
|
|
if (err != USBD_NORMAL_COMPLETION) {
|
|
|
|
error = EIO;
|
|
|
|
goto bad;
|
|
|
|
}
|
2003-01-05 08:20:20 +03:00
|
|
|
|
2006-06-19 19:44:33 +04:00
|
|
|
for (i = 0 ; i < UBT_NXFERS ; i++) {
|
|
|
|
sc->sc_scowr[i].xfer = usbd_alloc_xfer(sc->sc_udev);
|
|
|
|
if (sc->sc_scowr[i].xfer == NULL) {
|
|
|
|
error = ENOMEM;
|
|
|
|
goto bad;
|
|
|
|
}
|
|
|
|
sc->sc_scowr[i].buf = usbd_alloc_buffer(sc->sc_scowr[i].xfer,
|
|
|
|
sc->sc_scowr_size * UBT_NFRAMES);
|
|
|
|
if (sc->sc_scowr[i].buf == NULL) {
|
|
|
|
error = ENOMEM;
|
|
|
|
goto bad;
|
|
|
|
}
|
|
|
|
sc->sc_scowr[i].softc = sc;
|
|
|
|
sc->sc_scowr[i].busy = 0;
|
|
|
|
}
|
|
|
|
}
|
2003-01-05 08:20:20 +03:00
|
|
|
|
2006-06-19 19:44:33 +04:00
|
|
|
unit->hci_flags &= ~BTF_XMIT;
|
|
|
|
unit->hci_flags |= BTF_RUNNING;
|
|
|
|
return 0;
|
2003-01-05 08:20:20 +03:00
|
|
|
|
2006-06-19 19:44:33 +04:00
|
|
|
bad:
|
2003-01-05 08:20:20 +03:00
|
|
|
ubt_abortdealloc(sc);
|
2006-06-19 19:44:33 +04:00
|
|
|
return error;
|
2003-01-05 08:20:20 +03:00
|
|
|
}
|
|
|
|
|
2006-06-19 19:44:33 +04:00
|
|
|
static void
|
|
|
|
ubt_disable(struct hci_unit *unit)
|
2003-01-11 09:12:09 +03:00
|
|
|
{
|
2006-06-19 19:44:33 +04:00
|
|
|
struct ubt_softc *sc = unit->hci_softc;
|
2003-01-11 09:12:09 +03:00
|
|
|
|
2006-06-19 19:44:33 +04:00
|
|
|
DPRINTFN(1, "sc=%p\n", sc);
|
|
|
|
|
|
|
|
if ((unit->hci_flags & BTF_RUNNING) == 0)
|
|
|
|
return;
|
|
|
|
|
|
|
|
ubt_abortdealloc(sc);
|
2003-01-11 09:12:09 +03:00
|
|
|
|
2006-06-19 19:44:33 +04:00
|
|
|
unit->hci_flags &= ~BTF_RUNNING;
|
2003-01-11 09:12:09 +03:00
|
|
|
}
|
|
|
|
|
2006-06-19 19:44:33 +04:00
|
|
|
static void
|
|
|
|
ubt_xmit_cmd_start(struct hci_unit *unit)
|
2003-01-05 08:20:20 +03:00
|
|
|
{
|
2006-06-19 19:44:33 +04:00
|
|
|
struct ubt_softc *sc = unit->hci_softc;
|
2003-01-05 08:20:20 +03:00
|
|
|
usb_device_request_t req;
|
|
|
|
usbd_status status;
|
2006-06-19 19:44:33 +04:00
|
|
|
struct mbuf *m;
|
|
|
|
int len;
|
2003-01-05 08:20:20 +03:00
|
|
|
|
2006-06-19 19:44:33 +04:00
|
|
|
if (sc->sc_dying)
|
|
|
|
return;
|
2003-01-05 08:20:20 +03:00
|
|
|
|
2006-06-19 19:44:33 +04:00
|
|
|
if (MBUFQ_FIRST(&unit->hci_cmdq) == NULL)
|
|
|
|
return;
|
2003-01-11 09:12:09 +03:00
|
|
|
|
2006-06-19 19:44:33 +04:00
|
|
|
MBUFQ_DEQUEUE(&unit->hci_cmdq, m);
|
|
|
|
KASSERT(m != NULL);
|
2003-01-05 08:20:20 +03:00
|
|
|
|
2006-06-19 19:44:33 +04:00
|
|
|
DPRINTFN(15, "%s: xmit CMD packet (%d bytes)\n",
|
|
|
|
unit->hci_devname, m->m_pkthdr.len);
|
2003-01-05 08:20:20 +03:00
|
|
|
|
|
|
|
sc->sc_refcnt++;
|
2006-06-19 19:44:33 +04:00
|
|
|
unit->hci_flags |= BTF_XMIT_CMD;
|
|
|
|
|
|
|
|
len = m->m_pkthdr.len - 1;
|
|
|
|
m_copydata(m, 1, len, sc->sc_cmd_buf);
|
|
|
|
m_freem(m);
|
2003-01-05 08:20:20 +03:00
|
|
|
|
|
|
|
memset(&req, 0, sizeof(req));
|
|
|
|
req.bmRequestType = UT_WRITE_CLASS_DEVICE;
|
|
|
|
USETW(req.wLength, len);
|
|
|
|
|
2006-06-19 19:44:33 +04:00
|
|
|
usbd_setup_default_xfer(sc->sc_cmd_xfer,
|
2003-01-05 08:20:20 +03:00
|
|
|
sc->sc_udev,
|
2006-06-19 19:44:33 +04:00
|
|
|
unit,
|
|
|
|
UBT_CMD_TIMEOUT,
|
|
|
|
&req,
|
|
|
|
sc->sc_cmd_buf,
|
|
|
|
len,
|
|
|
|
USBD_NO_COPY | USBD_FORCE_SHORT_XFER,
|
|
|
|
ubt_xmit_cmd_complete);
|
2003-01-05 08:20:20 +03:00
|
|
|
|
2006-06-19 19:44:33 +04:00
|
|
|
status = usbd_transfer(sc->sc_cmd_xfer);
|
2003-01-05 08:20:20 +03:00
|
|
|
|
2006-06-19 19:44:33 +04:00
|
|
|
KASSERT(status != USBD_NORMAL_COMPLETION);
|
2003-01-05 08:20:20 +03:00
|
|
|
|
2006-06-19 19:44:33 +04:00
|
|
|
if (status != USBD_IN_PROGRESS) {
|
|
|
|
DPRINTF("usbd_transfer status=%s (%d)\n",
|
|
|
|
usbd_errstr(status), status);
|
2003-01-05 08:20:20 +03:00
|
|
|
|
2006-06-19 19:44:33 +04:00
|
|
|
sc->sc_refcnt--;
|
|
|
|
unit->hci_flags &= ~BTF_XMIT_CMD;
|
|
|
|
}
|
2003-01-05 08:20:20 +03:00
|
|
|
}
|
|
|
|
|
2006-06-19 19:44:33 +04:00
|
|
|
static void
|
|
|
|
ubt_xmit_cmd_complete(usbd_xfer_handle xfer,
|
|
|
|
usbd_private_handle h, usbd_status status)
|
2003-01-11 09:12:09 +03:00
|
|
|
{
|
2006-06-19 19:44:33 +04:00
|
|
|
struct hci_unit *unit = h;
|
|
|
|
struct ubt_softc *sc = unit->hci_softc;
|
|
|
|
uint32_t count;
|
2003-01-11 09:12:09 +03:00
|
|
|
|
2006-06-19 19:44:33 +04:00
|
|
|
DPRINTFN(15, "%s: CMD complete status=%s (%d)\n",
|
|
|
|
unit->hci_devname, usbd_errstr(status), status);
|
|
|
|
|
|
|
|
unit->hci_flags &= ~BTF_XMIT_CMD;
|
|
|
|
|
|
|
|
if (--sc->sc_refcnt < 0) {
|
|
|
|
DPRINTF("sc_refcnt=%d\n", sc->sc_refcnt);
|
|
|
|
usb_detach_wakeup(USBDEV(sc->sc_dev));
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (sc->sc_dying) {
|
|
|
|
DPRINTF("sc_dying\n");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (status != USBD_NORMAL_COMPLETION) {
|
|
|
|
DPRINTF("status=%s (%d)\n",
|
|
|
|
usbd_errstr(status), status);
|
|
|
|
|
|
|
|
unit->hci_stats.err_tx++;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
usbd_get_xfer_status(xfer, NULL, NULL, &count, NULL);
|
|
|
|
unit->hci_stats.cmd_tx++;
|
|
|
|
unit->hci_stats.byte_tx += count;
|
2003-01-11 09:12:09 +03:00
|
|
|
|
2006-06-19 19:44:33 +04:00
|
|
|
ubt_xmit_cmd_start(unit);
|
2003-01-11 09:12:09 +03:00
|
|
|
}
|
|
|
|
|
2006-06-19 19:44:33 +04:00
|
|
|
static void
|
|
|
|
ubt_xmit_acl_start(struct hci_unit *unit)
|
2003-01-05 08:20:20 +03:00
|
|
|
{
|
2006-06-19 19:44:33 +04:00
|
|
|
struct ubt_softc *sc = unit->hci_softc;
|
|
|
|
struct mbuf *m;
|
2003-01-05 08:20:20 +03:00
|
|
|
usbd_status status;
|
2006-06-19 19:44:33 +04:00
|
|
|
int len;
|
2003-01-11 09:12:09 +03:00
|
|
|
|
2003-01-05 08:20:20 +03:00
|
|
|
if (sc->sc_dying)
|
2006-06-19 19:44:33 +04:00
|
|
|
return;
|
2003-01-05 08:20:20 +03:00
|
|
|
|
2006-06-19 19:44:33 +04:00
|
|
|
if (MBUFQ_FIRST(&unit->hci_acltxq) == NULL)
|
|
|
|
return;
|
2003-01-05 08:20:20 +03:00
|
|
|
|
|
|
|
sc->sc_refcnt++;
|
2006-06-19 19:44:33 +04:00
|
|
|
unit->hci_flags |= BTF_XMIT_ACL;
|
|
|
|
|
|
|
|
MBUFQ_DEQUEUE(&unit->hci_acltxq, m);
|
|
|
|
KASSERT(m != NULL);
|
|
|
|
|
|
|
|
DPRINTFN(15, "%s: xmit ACL packet (%d bytes)\n",
|
|
|
|
unit->hci_devname, m->m_pkthdr.len);
|
|
|
|
|
|
|
|
len = m->m_pkthdr.len - 1;
|
|
|
|
if (len > UBT_BUFSIZ_ACL) {
|
|
|
|
DPRINTF("%s: truncating ACL packet (%d => %d)!\n",
|
|
|
|
unit->hci_devname, len, UBT_BUFSIZ_ACL);
|
|
|
|
|
|
|
|
len = UBT_BUFSIZ_ACL;
|
|
|
|
}
|
|
|
|
|
|
|
|
m_copydata(m, 1, len, sc->sc_aclwr_buf);
|
|
|
|
m_freem(m);
|
|
|
|
|
|
|
|
unit->hci_stats.acl_tx++;
|
|
|
|
unit->hci_stats.byte_tx += len;
|
2003-01-05 08:20:20 +03:00
|
|
|
|
|
|
|
usbd_setup_xfer(sc->sc_aclwr_xfer,
|
|
|
|
sc->sc_aclwr_pipe,
|
2006-06-19 19:44:33 +04:00
|
|
|
unit,
|
|
|
|
sc->sc_aclwr_buf,
|
|
|
|
len,
|
|
|
|
USBD_NO_COPY | USBD_FORCE_SHORT_XFER,
|
|
|
|
UBT_ACL_TIMEOUT,
|
|
|
|
ubt_xmit_acl_complete);
|
2003-01-05 08:20:20 +03:00
|
|
|
|
|
|
|
status = usbd_transfer(sc->sc_aclwr_xfer);
|
|
|
|
|
2006-06-19 19:44:33 +04:00
|
|
|
KASSERT(status != USBD_NORMAL_COMPLETION);
|
|
|
|
|
|
|
|
if (status != USBD_IN_PROGRESS) {
|
|
|
|
DPRINTF("usbd_transfer status=%s (%d)\n",
|
|
|
|
usbd_errstr(status), status);
|
|
|
|
|
|
|
|
sc->sc_refcnt--;
|
|
|
|
unit->hci_flags &= ~BTF_XMIT_ACL;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
2006-11-16 04:32:37 +03:00
|
|
|
ubt_xmit_acl_complete(usbd_xfer_handle xfer,
|
2006-06-19 19:44:33 +04:00
|
|
|
usbd_private_handle h, usbd_status status)
|
|
|
|
{
|
|
|
|
struct hci_unit *unit = h;
|
|
|
|
struct ubt_softc *sc = unit->hci_softc;
|
|
|
|
|
|
|
|
DPRINTFN(15, "%s: ACL complete status=%s (%d)\n",
|
|
|
|
unit->hci_devname, usbd_errstr(status), status);
|
|
|
|
|
|
|
|
unit->hci_flags &= ~BTF_XMIT_ACL;
|
|
|
|
|
|
|
|
if (--sc->sc_refcnt < 0) {
|
2003-01-05 08:20:20 +03:00
|
|
|
usb_detach_wakeup(USBDEV(sc->sc_dev));
|
2006-06-19 19:44:33 +04:00
|
|
|
return;
|
|
|
|
}
|
2003-01-05 08:20:20 +03:00
|
|
|
|
2006-06-19 19:44:33 +04:00
|
|
|
if (sc->sc_dying)
|
|
|
|
return;
|
2003-01-05 08:20:20 +03:00
|
|
|
|
2006-06-19 19:44:33 +04:00
|
|
|
if (status != USBD_NORMAL_COMPLETION) {
|
|
|
|
DPRINTF("status=%s (%d)\n",
|
|
|
|
usbd_errstr(status), status);
|
|
|
|
|
|
|
|
unit->hci_stats.err_tx++;
|
|
|
|
|
|
|
|
if (status == USBD_STALLED)
|
|
|
|
usbd_clear_endpoint_stall_async(sc->sc_aclwr_pipe);
|
|
|
|
else
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
ubt_xmit_acl_start(unit);
|
2003-01-05 08:20:20 +03:00
|
|
|
}
|
|
|
|
|
2006-06-19 19:44:33 +04:00
|
|
|
static void
|
|
|
|
ubt_xmit_sco_start(struct hci_unit *unit)
|
2003-01-11 09:12:09 +03:00
|
|
|
{
|
2006-06-19 19:44:33 +04:00
|
|
|
struct ubt_softc *sc = unit->hci_softc;
|
|
|
|
int i;
|
|
|
|
|
|
|
|
if (sc->sc_dying || sc->sc_scowr_size == 0)
|
|
|
|
return;
|
|
|
|
|
|
|
|
for (i = 0 ; i < UBT_NXFERS ; i++) {
|
|
|
|
if (sc->sc_scowr[i].busy)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
ubt_xmit_sco_start1(sc, &sc->sc_scowr[i]);
|
|
|
|
}
|
2003-01-11 09:12:09 +03:00
|
|
|
}
|
|
|
|
|
2006-06-19 19:44:33 +04:00
|
|
|
static void
|
|
|
|
ubt_xmit_sco_start1(struct ubt_softc *sc, struct ubt_isoc_xfer *isoc)
|
2003-01-05 08:20:20 +03:00
|
|
|
{
|
2006-06-19 19:44:33 +04:00
|
|
|
struct mbuf *m;
|
|
|
|
uint8_t *buf;
|
|
|
|
int num, len, size, space;
|
2003-01-05 08:20:20 +03:00
|
|
|
|
2006-06-19 19:44:33 +04:00
|
|
|
space = sc->sc_scowr_size * UBT_NFRAMES;
|
|
|
|
buf = isoc->buf;
|
|
|
|
len = 0;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Fill the request buffer with data from the queue,
|
|
|
|
* keeping any leftover packet on our private hook.
|
|
|
|
*
|
|
|
|
* Complete packets are passed back up to the stack
|
|
|
|
* for disposal, since we can't rely on the controller
|
|
|
|
* to tell us when it has finished with them.
|
|
|
|
*/
|
|
|
|
|
|
|
|
m = sc->sc_scowr_mbuf;
|
|
|
|
while (space > 0) {
|
|
|
|
if (m == NULL) {
|
|
|
|
MBUFQ_DEQUEUE(&sc->sc_unit.hci_scotxq, m);
|
|
|
|
if (m == NULL)
|
|
|
|
break;
|
|
|
|
|
|
|
|
m_adj(m, 1); /* packet type */
|
|
|
|
}
|
|
|
|
|
|
|
|
if (m->m_pkthdr.len > 0) {
|
|
|
|
size = MIN(m->m_pkthdr.len, space);
|
|
|
|
|
|
|
|
m_copydata(m, 0, size, buf);
|
|
|
|
m_adj(m, size);
|
|
|
|
|
|
|
|
buf += size;
|
|
|
|
len += size;
|
|
|
|
space -= size;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (m->m_pkthdr.len == 0) {
|
|
|
|
sc->sc_unit.hci_stats.sco_tx++;
|
|
|
|
hci_complete_sco(&sc->sc_unit, m);
|
|
|
|
m = NULL;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
sc->sc_scowr_mbuf = m;
|
|
|
|
|
|
|
|
DPRINTFN(15, "isoc=%p, len=%d, space=%d\n", isoc, len, space);
|
|
|
|
|
|
|
|
if (len == 0) /* nothing to send */
|
|
|
|
return;
|
|
|
|
|
|
|
|
sc->sc_refcnt++;
|
|
|
|
sc->sc_unit.hci_flags |= BTF_XMIT_SCO;
|
|
|
|
sc->sc_unit.hci_stats.byte_tx += len;
|
|
|
|
isoc->busy = 1;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* calculate number of isoc frames and sizes
|
|
|
|
*/
|
|
|
|
|
|
|
|
for (num = 0 ; len > 0 ; num++) {
|
|
|
|
size = MIN(sc->sc_scowr_size, len);
|
|
|
|
|
|
|
|
isoc->size[num] = size;
|
|
|
|
len -= size;
|
|
|
|
}
|
|
|
|
|
|
|
|
usbd_setup_isoc_xfer(isoc->xfer,
|
|
|
|
sc->sc_scowr_pipe,
|
|
|
|
isoc,
|
|
|
|
isoc->size,
|
|
|
|
num,
|
|
|
|
USBD_NO_COPY | USBD_FORCE_SHORT_XFER,
|
|
|
|
ubt_xmit_sco_complete);
|
|
|
|
|
|
|
|
usbd_transfer(isoc->xfer);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
2006-11-16 04:32:37 +03:00
|
|
|
ubt_xmit_sco_complete(usbd_xfer_handle xfer,
|
2006-06-19 19:44:33 +04:00
|
|
|
usbd_private_handle h, usbd_status status)
|
|
|
|
{
|
|
|
|
struct ubt_isoc_xfer *isoc = h;
|
|
|
|
struct ubt_softc *sc;
|
|
|
|
int i;
|
|
|
|
|
|
|
|
KASSERT(xfer == isoc->xfer);
|
|
|
|
sc = isoc->softc;
|
|
|
|
|
|
|
|
DPRINTFN(15, "isoc=%p, status=%s (%d)\n",
|
|
|
|
isoc, usbd_errstr(status), status);
|
|
|
|
|
|
|
|
isoc->busy = 0;
|
|
|
|
|
|
|
|
for (i = 0 ; ; i++) {
|
|
|
|
if (i == UBT_NXFERS) {
|
|
|
|
sc->sc_unit.hci_flags &= ~BTF_XMIT_SCO;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (sc->sc_scowr[i].busy)
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (--sc->sc_refcnt < 0) {
|
|
|
|
usb_detach_wakeup(USBDEV(sc->sc_dev));
|
|
|
|
return;
|
|
|
|
}
|
2003-01-05 08:20:20 +03:00
|
|
|
|
|
|
|
if (sc->sc_dying)
|
2006-06-19 19:44:33 +04:00
|
|
|
return;
|
|
|
|
|
|
|
|
if (status != USBD_NORMAL_COMPLETION) {
|
|
|
|
DPRINTF("status=%s (%d)\n",
|
|
|
|
usbd_errstr(status), status);
|
2003-01-05 08:20:20 +03:00
|
|
|
|
2006-06-19 19:44:33 +04:00
|
|
|
sc->sc_unit.hci_stats.err_tx++;
|
|
|
|
|
|
|
|
if (status == USBD_STALLED)
|
|
|
|
usbd_clear_endpoint_stall_async(sc->sc_scowr_pipe);
|
|
|
|
else
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
ubt_xmit_sco_start(&sc->sc_unit);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* load incoming data into an mbuf with
|
|
|
|
* leading type byte
|
|
|
|
*/
|
|
|
|
static struct mbuf *
|
|
|
|
ubt_mbufload(uint8_t *buf, int count, uint8_t type)
|
|
|
|
{
|
|
|
|
struct mbuf *m;
|
|
|
|
|
|
|
|
MGETHDR(m, M_DONTWAIT, MT_DATA);
|
|
|
|
if (m == NULL)
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
*mtod(m, uint8_t *) = type;
|
|
|
|
m->m_pkthdr.len = m->m_len = MHLEN;
|
|
|
|
m_copyback(m, 1, count, buf); // (extends if necessary)
|
|
|
|
if (m->m_pkthdr.len != MAX(MHLEN, count + 1)) {
|
|
|
|
m_free(m);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
m->m_pkthdr.len = count + 1;
|
|
|
|
m->m_len = MIN(MHLEN, m->m_pkthdr.len);
|
|
|
|
|
|
|
|
return m;
|
2003-01-05 08:20:20 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
2006-06-19 19:44:33 +04:00
|
|
|
ubt_recv_event(usbd_xfer_handle xfer, usbd_private_handle h, usbd_status status)
|
2003-01-05 08:20:20 +03:00
|
|
|
{
|
|
|
|
struct ubt_softc *sc = h;
|
2006-06-19 19:44:33 +04:00
|
|
|
struct mbuf *m;
|
|
|
|
uint32_t count;
|
2003-01-05 08:20:20 +03:00
|
|
|
void *buf;
|
|
|
|
|
2006-06-19 19:44:33 +04:00
|
|
|
DPRINTFN(15, "sc=%p status=%s (%d)\n",
|
|
|
|
sc, usbd_errstr(status), status);
|
2003-01-05 08:20:20 +03:00
|
|
|
|
2006-06-19 19:44:33 +04:00
|
|
|
if (status != USBD_NORMAL_COMPLETION || sc->sc_dying)
|
2003-01-05 08:20:20 +03:00
|
|
|
return;
|
|
|
|
|
2006-06-19 19:44:33 +04:00
|
|
|
usbd_get_xfer_status(xfer, NULL, &buf, &count, NULL);
|
|
|
|
|
2006-08-30 23:42:37 +04:00
|
|
|
if (count < sizeof(hci_event_hdr_t) - 1) {
|
|
|
|
DPRINTF("dumped undersized event (count = %d)\n", count);
|
|
|
|
sc->sc_unit.hci_stats.err_rx++;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2006-06-19 19:44:33 +04:00
|
|
|
sc->sc_unit.hci_stats.evt_rx++;
|
|
|
|
sc->sc_unit.hci_stats.byte_rx += count;
|
2003-01-05 08:20:20 +03:00
|
|
|
|
2006-06-19 19:44:33 +04:00
|
|
|
m = ubt_mbufload(buf, count, HCI_EVENT_PKT);
|
|
|
|
if (m != NULL)
|
|
|
|
hci_input_event(&sc->sc_unit, m);
|
|
|
|
else
|
|
|
|
sc->sc_unit.hci_stats.err_rx++;
|
2003-01-05 08:20:20 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
2006-06-19 19:44:33 +04:00
|
|
|
ubt_recv_acl_start(struct ubt_softc *sc)
|
2003-01-05 08:20:20 +03:00
|
|
|
{
|
|
|
|
usbd_status status;
|
|
|
|
|
2006-06-19 19:44:33 +04:00
|
|
|
DPRINTFN(15, "sc=%p\n", sc);
|
2003-01-05 08:20:20 +03:00
|
|
|
|
2006-06-19 19:44:33 +04:00
|
|
|
if (sc->sc_aclrd_busy || sc->sc_dying) {
|
|
|
|
DPRINTF("sc_aclrd_busy=%d, sc_dying=%d\n",
|
|
|
|
sc->sc_aclrd_busy,
|
|
|
|
sc->sc_dying);
|
2003-01-05 08:20:20 +03:00
|
|
|
|
|
|
|
return;
|
|
|
|
}
|
2003-01-11 09:12:09 +03:00
|
|
|
|
2006-06-19 19:44:33 +04:00
|
|
|
sc->sc_refcnt++;
|
|
|
|
sc->sc_aclrd_busy = 1;
|
|
|
|
|
|
|
|
usbd_setup_xfer(sc->sc_aclrd_xfer,
|
|
|
|
sc->sc_aclrd_pipe,
|
|
|
|
sc,
|
|
|
|
sc->sc_aclrd_buf,
|
|
|
|
UBT_BUFSIZ_ACL,
|
|
|
|
USBD_NO_COPY | USBD_SHORT_XFER_OK,
|
|
|
|
USBD_NO_TIMEOUT,
|
|
|
|
ubt_recv_acl_complete);
|
2003-01-05 08:20:20 +03:00
|
|
|
|
|
|
|
status = usbd_transfer(sc->sc_aclrd_xfer);
|
2004-01-02 05:36:25 +03:00
|
|
|
|
2006-06-19 19:44:33 +04:00
|
|
|
KASSERT(status != USBD_NORMAL_COMPLETION);
|
2003-01-05 08:20:20 +03:00
|
|
|
|
2006-06-19 19:44:33 +04:00
|
|
|
if (status != USBD_IN_PROGRESS) {
|
|
|
|
DPRINTF("usbd_transfer status=%s (%d)\n",
|
|
|
|
usbd_errstr(status), status);
|
2003-01-05 08:20:20 +03:00
|
|
|
|
2006-06-19 19:44:33 +04:00
|
|
|
sc->sc_refcnt--;
|
|
|
|
sc->sc_aclrd_busy = 0;
|
|
|
|
}
|
2003-01-05 08:20:20 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
2006-06-19 19:44:33 +04:00
|
|
|
ubt_recv_acl_complete(usbd_xfer_handle xfer,
|
|
|
|
usbd_private_handle h, usbd_status status)
|
2003-01-05 08:20:20 +03:00
|
|
|
{
|
|
|
|
struct ubt_softc *sc = h;
|
2006-06-19 19:44:33 +04:00
|
|
|
struct mbuf *m;
|
|
|
|
uint32_t count;
|
2003-01-05 08:20:20 +03:00
|
|
|
void *buf;
|
|
|
|
|
2006-06-19 19:44:33 +04:00
|
|
|
DPRINTFN(15, "sc=%p status=%s (%d)\n",
|
|
|
|
sc, usbd_errstr(status), status);
|
2003-01-05 08:20:20 +03:00
|
|
|
|
2006-06-19 19:44:33 +04:00
|
|
|
sc->sc_aclrd_busy = 0;
|
|
|
|
|
|
|
|
if (--sc->sc_refcnt < 0) {
|
|
|
|
DPRINTF("refcnt = %d\n", sc->sc_refcnt);
|
|
|
|
usb_detach_wakeup(USBDEV(sc->sc_dev));
|
|
|
|
return;
|
|
|
|
}
|
2003-01-05 08:20:20 +03:00
|
|
|
|
2006-06-19 19:44:33 +04:00
|
|
|
if (sc->sc_dying) {
|
|
|
|
DPRINTF("sc_dying\n");
|
2003-01-05 08:20:20 +03:00
|
|
|
return;
|
2006-06-19 19:44:33 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
if (status != USBD_NORMAL_COMPLETION) {
|
|
|
|
DPRINTF("status=%s (%d)\n",
|
|
|
|
usbd_errstr(status), status);
|
2003-01-05 08:20:20 +03:00
|
|
|
|
2006-06-19 19:44:33 +04:00
|
|
|
sc->sc_unit.hci_stats.err_rx++;
|
2003-01-05 08:20:20 +03:00
|
|
|
|
2006-06-19 19:44:33 +04:00
|
|
|
if (status == USBD_STALLED)
|
|
|
|
usbd_clear_endpoint_stall_async(sc->sc_aclrd_pipe);
|
|
|
|
else
|
|
|
|
return;
|
|
|
|
} else {
|
|
|
|
usbd_get_xfer_status(xfer, NULL, &buf, &count, NULL);
|
2003-01-05 08:20:20 +03:00
|
|
|
|
2006-08-30 23:42:37 +04:00
|
|
|
if (count < sizeof(hci_acldata_hdr_t) - 1) {
|
|
|
|
DPRINTF("dumped undersized packet (%d)\n", count);
|
2006-06-19 19:44:33 +04:00
|
|
|
sc->sc_unit.hci_stats.err_rx++;
|
2006-08-30 23:42:37 +04:00
|
|
|
} else {
|
|
|
|
sc->sc_unit.hci_stats.acl_rx++;
|
|
|
|
sc->sc_unit.hci_stats.byte_rx += count;
|
|
|
|
|
|
|
|
m = ubt_mbufload(buf, count, HCI_ACL_DATA_PKT);
|
|
|
|
if (m != NULL)
|
|
|
|
hci_input_acl(&sc->sc_unit, m);
|
|
|
|
else
|
|
|
|
sc->sc_unit.hci_stats.err_rx++;
|
|
|
|
}
|
2006-06-19 19:44:33 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
/* and restart */
|
|
|
|
ubt_recv_acl_start(sc);
|
2003-01-05 08:20:20 +03:00
|
|
|
}
|
|
|
|
|
2006-06-19 19:44:33 +04:00
|
|
|
static void
|
|
|
|
ubt_recv_sco_start1(struct ubt_softc *sc, struct ubt_isoc_xfer *isoc)
|
|
|
|
{
|
|
|
|
int i;
|
|
|
|
|
|
|
|
DPRINTFN(15, "sc=%p, isoc=%p\n", sc, isoc);
|
|
|
|
|
|
|
|
if (isoc->busy || sc->sc_dying || sc->sc_scord_size == 0) {
|
|
|
|
DPRINTF("%s%s%s\n",
|
|
|
|
isoc->busy ? " busy" : "",
|
|
|
|
sc->sc_dying ? " dying" : "",
|
|
|
|
sc->sc_scord_size == 0 ? " size=0" : "");
|
|
|
|
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
sc->sc_refcnt++;
|
|
|
|
isoc->busy = 1;
|
|
|
|
|
|
|
|
for (i = 0 ; i < UBT_NFRAMES ; i++)
|
|
|
|
isoc->size[i] = sc->sc_scord_size;
|
|
|
|
|
|
|
|
usbd_setup_isoc_xfer(isoc->xfer,
|
|
|
|
sc->sc_scord_pipe,
|
|
|
|
isoc,
|
|
|
|
isoc->size,
|
|
|
|
UBT_NFRAMES,
|
|
|
|
USBD_NO_COPY | USBD_SHORT_XFER_OK,
|
|
|
|
ubt_recv_sco_complete);
|
|
|
|
|
|
|
|
usbd_transfer(isoc->xfer);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
ubt_recv_sco_complete(usbd_xfer_handle xfer,
|
|
|
|
usbd_private_handle h, usbd_status status)
|
2003-01-05 08:20:20 +03:00
|
|
|
{
|
2006-06-19 19:44:33 +04:00
|
|
|
struct ubt_isoc_xfer *isoc = h;
|
|
|
|
struct ubt_softc *sc;
|
|
|
|
struct mbuf *m;
|
|
|
|
uint32_t count;
|
|
|
|
uint8_t *ptr, *frame;
|
|
|
|
int i, size, got, want;
|
|
|
|
|
|
|
|
KASSERT(isoc != NULL);
|
|
|
|
KASSERT(isoc->xfer == xfer);
|
|
|
|
|
|
|
|
sc = isoc->softc;
|
|
|
|
isoc->busy = 0;
|
|
|
|
|
|
|
|
if (--sc->sc_refcnt < 0) {
|
|
|
|
DPRINTF("refcnt=%d\n", sc->sc_refcnt);
|
|
|
|
usb_detach_wakeup(USBDEV(sc->sc_dev));
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (sc->sc_dying) {
|
|
|
|
DPRINTF("sc_dying\n");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (status != USBD_NORMAL_COMPLETION) {
|
|
|
|
DPRINTF("status=%s (%d)\n",
|
|
|
|
usbd_errstr(status), status);
|
|
|
|
|
|
|
|
sc->sc_unit.hci_stats.err_rx++;
|
|
|
|
|
|
|
|
if (status == USBD_STALLED) {
|
|
|
|
usbd_clear_endpoint_stall_async(sc->sc_scord_pipe);
|
|
|
|
goto restart;
|
|
|
|
}
|
|
|
|
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
usbd_get_xfer_status(xfer, NULL, NULL, &count, NULL);
|
|
|
|
if (count == 0)
|
|
|
|
goto restart;
|
|
|
|
|
|
|
|
DPRINTFN(15, "sc=%p, isoc=%p, count=%u\n",
|
|
|
|
sc, isoc, count);
|
|
|
|
|
|
|
|
sc->sc_unit.hci_stats.byte_rx += count;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Extract SCO packets from ISOC frames. The way we have it,
|
|
|
|
* no SCO packet can be bigger than MHLEN. This is unlikely
|
|
|
|
* to actually happen, but if we ran out of mbufs and lost
|
|
|
|
* sync then we may get spurious data that makes it seem that
|
|
|
|
* way, so we discard data that wont fit. This doesnt really
|
|
|
|
* help with the lost sync situation alas.
|
|
|
|
*/
|
|
|
|
|
|
|
|
m = sc->sc_scord_mbuf;
|
|
|
|
if (m != NULL) {
|
|
|
|
sc->sc_scord_mbuf = NULL;
|
|
|
|
ptr = mtod(m, uint8_t *) + m->m_pkthdr.len;
|
|
|
|
got = m->m_pkthdr.len;
|
|
|
|
want = sizeof(hci_scodata_hdr_t);
|
|
|
|
if (got >= want)
|
|
|
|
want += mtod(m, hci_scodata_hdr_t *)->length ;
|
|
|
|
} else {
|
|
|
|
ptr = NULL;
|
|
|
|
got = 0;
|
|
|
|
want = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
for (i = 0 ; i < UBT_NFRAMES ; i++) {
|
|
|
|
frame = isoc->buf + (i * sc->sc_scord_size);
|
|
|
|
|
|
|
|
while (isoc->size[i] > 0) {
|
|
|
|
size = isoc->size[i];
|
|
|
|
|
|
|
|
if (m == NULL) {
|
|
|
|
MGETHDR(m, M_DONTWAIT, MT_DATA);
|
|
|
|
if (m == NULL) {
|
|
|
|
printf("%s: out of memory (xfer halted)\n",
|
|
|
|
USBDEVNAME(sc->sc_dev));
|
|
|
|
|
|
|
|
sc->sc_unit.hci_stats.err_rx++;
|
|
|
|
return; /* lost sync */
|
|
|
|
}
|
|
|
|
|
|
|
|
ptr = mtod(m, uint8_t *);
|
|
|
|
*ptr++ = HCI_SCO_DATA_PKT;
|
|
|
|
got = 1;
|
|
|
|
want = sizeof(hci_scodata_hdr_t);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (got + size > want)
|
|
|
|
size = want - got;
|
|
|
|
|
|
|
|
if (got + size > MHLEN)
|
|
|
|
memcpy(ptr, frame, MHLEN - got);
|
|
|
|
else
|
|
|
|
memcpy(ptr, frame, size);
|
|
|
|
|
|
|
|
ptr += size;
|
|
|
|
got += size;
|
|
|
|
frame += size;
|
|
|
|
|
|
|
|
if (got == want) {
|
|
|
|
/*
|
|
|
|
* If we only got a header, add the packet
|
|
|
|
* length to our want count. Send complete
|
|
|
|
* packets up to protocol stack.
|
|
|
|
*/
|
|
|
|
if (want == sizeof(hci_scodata_hdr_t))
|
|
|
|
want += mtod(m, hci_scodata_hdr_t *)->length;
|
|
|
|
|
|
|
|
if (got == want) {
|
|
|
|
m->m_pkthdr.len = m->m_len = got;
|
|
|
|
sc->sc_unit.hci_stats.sco_rx++;
|
|
|
|
hci_input_sco(&sc->sc_unit, m);
|
|
|
|
m = NULL;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
isoc->size[i] -= size;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (m != NULL) {
|
|
|
|
m->m_pkthdr.len = m->m_len = got;
|
|
|
|
sc->sc_scord_mbuf = m;
|
|
|
|
}
|
|
|
|
|
|
|
|
restart: /* and restart */
|
|
|
|
ubt_recv_sco_start1(sc, isoc);
|
2003-01-05 08:20:20 +03:00
|
|
|
}
|