Index: head/sys/conf/files =================================================================== --- head/sys/conf/files +++ head/sys/conf/files @@ -4147,6 +4147,7 @@ netgraph/bluetooth/drivers/bt3c/ng_bt3c_pccard.c optional netgraph_bluetooth_bt3c netgraph/bluetooth/drivers/h4/ng_h4.c optional netgraph_bluetooth_h4 netgraph/bluetooth/drivers/ubt/ng_ubt.c optional netgraph_bluetooth_ubt usb +netgraph/bluetooth/drivers/ubt/ng_ubt_intel.c optional netgraph_bluetooth_ubt usb netgraph/bluetooth/drivers/ubtbcmfw/ubtbcmfw.c optional netgraph_bluetooth_ubtbcmfw usb netgraph/bluetooth/hci/ng_hci_cmds.c optional netgraph_bluetooth_hci netgraph/bluetooth/hci/ng_hci_evnt.c optional netgraph_bluetooth_hci Index: head/sys/modules/netgraph/bluetooth/ubt/Makefile =================================================================== --- head/sys/modules/netgraph/bluetooth/ubt/Makefile +++ head/sys/modules/netgraph/bluetooth/ubt/Makefile @@ -7,7 +7,7 @@ -I${SRCTOP}/sys/netgraph/bluetooth/drivers/ubt KMOD= ng_ubt -SRCS= ng_ubt.c opt_bus.h opt_usb.h device_if.h bus_if.h \ - usb_if.h usbdevs.h +SRCS= ng_ubt.c ng_ubt_intel.c opt_bus.h opt_usb.h device_if.h \ + bus_if.h usb_if.h usbdevs.h .include Index: head/sys/netgraph/bluetooth/drivers/ubt/ng_ubt.c =================================================================== --- head/sys/netgraph/bluetooth/drivers/ubt/ng_ubt.c +++ head/sys/netgraph/bluetooth/drivers/ubt/ng_ubt.c @@ -252,6 +252,7 @@ ****************************************************************************/ /* USB methods */ +static usb_callback_t ubt_probe_intr_callback; static usb_callback_t ubt_ctrl_write_callback; static usb_callback_t ubt_intr_read_callback; static usb_callback_t ubt_bulk_read_callback; @@ -421,6 +422,13 @@ /* Atheros AR5BBU12 with sflash firmware */ { USB_VPI(0x0489, 0xe03c, 0), USB_DEV_BCD_LTEQ(1) }, { USB_VPI(0x0489, 0xe036, 0), USB_DEV_BCD_LTEQ(1) }, + + /* Intel Wireless 8260 and successors are handled in ng_ubt_intel.c */ + { USB_VPI(USB_VENDOR_INTEL2, 0x0a2b, 0) }, + { USB_VPI(USB_VENDOR_INTEL2, 0x0aaa, 0) }, + { USB_VPI(USB_VENDOR_INTEL2, 0x0025, 0) }, + { USB_VPI(USB_VENDOR_INTEL2, 0x0026, 0) }, + { USB_VPI(USB_VENDOR_INTEL2, 0x0029, 0) }, }; /* List of supported bluetooth devices */ @@ -503,6 +511,79 @@ }; /* + * Does a synchronous (waits for completion event) execution of HCI command. + * Size of both command and response buffers are passed in length field of + * corresponding structures in "Parameter Total Length" format i.e. + * not including HCI packet headers. + * + * Must not be used after USB transfers have been configured in attach routine. + */ + +usb_error_t +ubt_do_hci_request(struct usb_device *udev, struct ubt_hci_cmd *cmd, + void *evt, usb_timeout_t timeout) +{ + static const struct usb_config ubt_probe_config = { + .type = UE_INTERRUPT, + .endpoint = UE_ADDR_ANY, + .direction = UE_DIR_IN, + .flags = { .pipe_bof = 1, .short_xfer_ok = 1 }, + .bufsize = UBT_INTR_BUFFER_SIZE, + .callback = &ubt_probe_intr_callback, + }; + struct usb_device_request req; + struct usb_xfer *xfer[1]; + struct mtx mtx; + usb_error_t error = USB_ERR_NORMAL_COMPLETION; + uint8_t iface_index = 0; + + /* Initialize a USB control request and then do it */ + bzero(&req, sizeof(req)); + req.bmRequestType = UBT_HCI_REQUEST; + req.wIndex[0] = iface_index; + USETW(req.wLength, UBT_HCI_CMD_SIZE(cmd)); + + error = usbd_do_request(udev, NULL, &req, cmd); + if (error != USB_ERR_NORMAL_COMPLETION) { + printf("ng_ubt: usbd_do_request error=%s\n", + usbd_errstr(error)); + return (error); + } + + if (evt == NULL) + return (USB_ERR_NORMAL_COMPLETION); + + /* Initialize INTR endpoint xfer and wait for response */ + mtx_init(&mtx, "ubt pb", NULL, MTX_DEF); + + error = usbd_transfer_setup(udev, &iface_index, xfer, + &ubt_probe_config, 1, evt, &mtx); + if (error == USB_ERR_NORMAL_COMPLETION) { + + mtx_lock(&mtx); + usbd_transfer_start(*xfer); + + if (msleep_sbt(evt, &mtx, 0, "ubt pb", SBT_1MS * timeout, + 0, C_HARDCLOCK) == EWOULDBLOCK) { + printf("ng_ubt: HCI command 0x%04x timed out\n", + le16toh(cmd->opcode)); + error = USB_ERR_TIMEOUT; + } + + usbd_transfer_stop(*xfer); + mtx_unlock(&mtx); + + usbd_transfer_unsetup(xfer, 1); + } else + printf("ng_ubt: usbd_transfer_setup error=%s\n", + usbd_errstr(error)); + + mtx_destroy(&mtx); + + return (error); +} + +/* * Probe for a USB Bluetooth device. * USB context. */ @@ -717,6 +798,49 @@ return (0); } /* ubt_detach */ +/* + * Called when incoming interrupt transfer (HCI event) has completed, i.e. + * HCI event was received from the device during device probe stage. + * USB context. + */ + +static void +ubt_probe_intr_callback(struct usb_xfer *xfer, usb_error_t error) +{ + struct ubt_hci_event *evt = usbd_xfer_softc(xfer); + struct usb_page_cache *pc; + int actlen; + + usbd_xfer_status(xfer, &actlen, NULL, NULL, NULL); + + switch (USB_GET_STATE(xfer)) { + case USB_ST_TRANSFERRED: + if (actlen > UBT_HCI_EVENT_SIZE(evt)) + actlen = UBT_HCI_EVENT_SIZE(evt); + pc = usbd_xfer_get_frame(xfer, 0); + usbd_copy_out(pc, 0, evt, actlen); + /* OneShot mode */ + wakeup(evt); + break; + + case USB_ST_SETUP: +submit_next: + /* Try clear stall first */ + usbd_xfer_set_stall(xfer); + usbd_xfer_set_frame_len(xfer, 0, usbd_xfer_max_len(xfer)); + usbd_transfer_submit(xfer); + break; + + default: + if (error != USB_ERR_CANCELLED) { + printf("ng_ubt: interrupt transfer failed: %s\n", + usbd_errstr(error)); + goto submit_next; + } + break; + } +} /* ubt_probe_intr_callback */ + /* * Called when outgoing control request (HCI command) has completed, i.e. * HCI command was sent to the device. @@ -1852,7 +1976,7 @@ return (error); } /* ubt_modevent */ -static devclass_t ubt_devclass; +devclass_t ubt_devclass; static device_method_t ubt_methods[] = { @@ -1862,7 +1986,7 @@ DEVMETHOD_END }; -static driver_t ubt_driver = +driver_t ubt_driver = { .name = "ubt", .methods = ubt_methods, Index: head/sys/netgraph/bluetooth/drivers/ubt/ng_ubt_intel.c =================================================================== --- head/sys/netgraph/bluetooth/drivers/ubt/ng_ubt_intel.c +++ head/sys/netgraph/bluetooth/drivers/ubt/ng_ubt_intel.c @@ -0,0 +1,161 @@ +/* + * ng_ubt_intel.c + */ + +/*- + * SPDX-License-Identifier: BSD-2-Clause-FreeBSD + * + * Copyright (c) 2019 Vladimir Kondratyev + * + * 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. + * + * $FreeBSD$ + */ + +/* + * Attempt to initialize FreeBSD bluetooth stack while Intel Wireless 8260/8265 + * device is in bootloader mode locks the adapter hardly so it requires power + * on/off cycle to restore. This driver blocks ng_ubt attachment until + * operational firmware is loaded by iwmbtfw utility thus avoiding the lock up. + */ + +#include +#include +#include +#include +#include +#include +#include + +#include "usbdevs.h" +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +static device_probe_t ubt_intel_probe; + +/* + * List of supported bluetooth devices. If you add a new device PID here ensure + * that it is blacklisted in ng_ubt.c and is supported by iwmbtfw utility. + */ + +static const STRUCT_USB_HOST_ID ubt_intel_devs[] = +{ + /* Intel Wireless 8260/8265 and successors */ + { USB_VPI(USB_VENDOR_INTEL2, 0x0a2b, 0) }, + { USB_VPI(USB_VENDOR_INTEL2, 0x0aaa, 0) }, + { USB_VPI(USB_VENDOR_INTEL2, 0x0025, 0) }, + { USB_VPI(USB_VENDOR_INTEL2, 0x0026, 0) }, + { USB_VPI(USB_VENDOR_INTEL2, 0x0029, 0) }, +}; + +/* + * Find if the Intel Wireless 8260/8265 device is in bootloader mode or is + * running operational firmware with checking of 4-th byte "Intel version" + * HCI command response. The value 0x23 identifies the operational firmware. + */ + +static bool +ubt_intel_check_firmware_state(struct usb_device *udev) +{ +#define UBT_INTEL_VER_LEN 13 +#define UBT_INTEL_HCICMD_TIMEOUT 2000 /* ms */ + struct ubt_hci_event_command_compl *evt; + uint8_t buf[offsetof(struct ubt_hci_event, data) + UBT_INTEL_VER_LEN]; + static struct ubt_hci_cmd cmd = { + .opcode = htole16(NG_HCI_OPCODE(NG_HCI_OGF_VENDOR, 0x05)), + .length = 0, + }; + usb_error_t error; + + bzero(buf, sizeof(buf)); + evt = (struct ubt_hci_event_command_compl *)buf; + evt->header.length = UBT_INTEL_VER_LEN; + + error = ubt_do_hci_request(udev, &cmd, evt, UBT_INTEL_HCICMD_TIMEOUT); + if (error != USB_ERR_NORMAL_COMPLETION) + return false; + + return (evt->header.event == NG_HCI_EVENT_COMMAND_COMPL && + evt->header.length == UBT_INTEL_VER_LEN && + evt->data[4] == 0x23); +} + +/* + * Probe for a Intel Wireless Bluetooth device. + */ + +static int +ubt_intel_probe(device_t dev) +{ + struct usb_attach_arg *uaa = device_get_ivars(dev); + int error; + + if (uaa->usb_mode != USB_MODE_HOST) + return (ENXIO); + + if (uaa->info.bIfaceIndex != 0) + return (ENXIO); + + error = usbd_lookup_id_by_uaa(ubt_intel_devs, sizeof(ubt_intel_devs), + uaa); + if (error != 0) + return (error); + + if (!ubt_intel_check_firmware_state(uaa->device)) + return (ENXIO); + + return (BUS_PROBE_DEFAULT); +} + +/* + * Module interface. Attach and detach methods, netgraph node type + * registration and PNP string are inherited from ng_ubt.c driver. + */ + +static device_method_t ubt_intel_methods[] = +{ + DEVMETHOD(device_probe, ubt_intel_probe), + DEVMETHOD_END +}; + +static kobj_class_t ubt_baseclasses[] = { &ubt_driver, NULL }; +static driver_t ubt_intel_driver = +{ + .name = "ubt", + .methods = ubt_intel_methods, + .size = sizeof(struct ubt_softc), + .baseclasses = ubt_baseclasses, +}; + +DRIVER_MODULE(ng_ubt_intel, uhub, ubt_intel_driver, ubt_devclass, 0, 0); +MODULE_VERSION(ng_ubt_intel, NG_BLUETOOTH_VERSION); +MODULE_DEPEND(ng_ubt_intel, netgraph, NG_ABI_VERSION, NG_ABI_VERSION, NG_ABI_VERSION); +MODULE_DEPEND(ng_ubt_intel, ng_hci, NG_BLUETOOTH_VERSION, NG_BLUETOOTH_VERSION, NG_BLUETOOTH_VERSION); +MODULE_DEPEND(ng_ubt_intel, usb, 1, 1, 1); Index: head/sys/netgraph/bluetooth/drivers/ubt/ng_ubt_var.h =================================================================== --- head/sys/netgraph/bluetooth/drivers/ubt/ng_ubt_var.h +++ head/sys/netgraph/bluetooth/drivers/ubt/ng_ubt_var.h @@ -74,6 +74,35 @@ UBT_N_TRANSFER, /* total number of transfers */ }; +/* USB control request (HCI command) structure */ +struct ubt_hci_cmd { + uint16_t opcode; + uint8_t length; + uint8_t data[]; +} __attribute__ ((packed)); +#define UBT_HCI_CMD_SIZE(cmd) \ + ((cmd)->length + offsetof(struct ubt_hci_cmd, data)) + +/* USB interrupt transfer HCI event header structure */ +struct ubt_hci_evhdr { + uint8_t event; + uint8_t length; +} __attribute__ ((packed)); +/* USB interrupt transfer (generic HCI event) structure */ +struct ubt_hci_event { + struct ubt_hci_evhdr header; + uint8_t data[]; +} __attribute__ ((packed)); +/* USB interrupt transfer (HCI command completion event) structure */ +struct ubt_hci_event_command_compl { + struct ubt_hci_evhdr header; + uint8_t numpkt; + uint16_t opcode; + uint8_t data[]; +} __attribute__ ((packed)); +#define UBT_HCI_EVENT_SIZE(evt) \ + ((evt)->header.length + offsetof(struct ubt_hci_event, data)) + /* USB device softc structure */ struct ubt_softc { device_t sc_dev; /* for debug printf */ @@ -128,6 +157,12 @@ }; typedef struct ubt_softc ubt_softc_t; typedef struct ubt_softc * ubt_softc_p; + +usb_error_t ubt_do_hci_request(struct usb_device *, struct ubt_hci_cmd *, + void *, usb_timeout_t); + +extern devclass_t ubt_devclass; +extern driver_t ubt_driver; #endif /* ndef _NG_UBT_VAR_H_ */