Path: blob/main/usr.sbin/bluetooth/rtlbtfw/rtlbt_hw.c
105774 views
/*-1* SPDX-License-Identifier: BSD-2-Clause2*3* Copyright (c) 2019 Vladimir Kondratyev <[email protected]>4* Copyright (c) 2023 Future Crew LLC.5*6* Redistribution and use in source and binary forms, with or without7* modification, are permitted provided that the following conditions8* are met:9* 1. Redistributions of source code must retain the above copyright10* notice, this list of conditions and the following disclaimer.11* 2. Redistributions in binary form must reproduce the above copyright12* notice, this list of conditions and the following disclaimer in the13* documentation and/or other materials provided with the distribution.14*15* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND16* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE17* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE18* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE19* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL20* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS21* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)22* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT23* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY24* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF25* SUCH DAMAGE.26*/2728#include <sys/param.h>29#include <sys/endian.h>30#include <sys/stat.h>3132#include <netgraph/bluetooth/include/ng_hci.h>3334#include <err.h>35#include <errno.h>36#include <stddef.h>37#include <stdio.h>38#include <stdlib.h>39#include <string.h>40#include <time.h>41#include <unistd.h>4243#include <libusb.h>4445#include "rtlbt_fw.h"46#include "rtlbt_hw.h"47#include "rtlbt_dbg.h"4849static int50rtlbt_hci_command(struct libusb_device_handle *hdl, struct rtlbt_hci_cmd *cmd,51void *event, int size, int *transferred, int timeout)52{53struct timespec to, now, remains;54int ret;5556ret = libusb_control_transfer(hdl,57LIBUSB_REQUEST_TYPE_CLASS | LIBUSB_RECIPIENT_DEVICE,580,590,600,61(uint8_t *)cmd,62RTLBT_HCI_CMD_SIZE(cmd),63timeout);6465if (ret < 0) {66rtlbt_err("libusb_control_transfer() failed: err=%s",67libusb_strerror(ret));68return (ret);69}7071clock_gettime(CLOCK_MONOTONIC, &now);72to = RTLBT_MSEC2TS(timeout);73timespecadd(&to, &now, &to);7475do {76timespecsub(&to, &now, &remains);77ret = libusb_interrupt_transfer(hdl,78RTLBT_INTERRUPT_ENDPOINT_ADDR,79event,80size,81transferred,82RTLBT_TS2MSEC(remains) + 1);8384if (ret < 0) {85rtlbt_err("libusb_interrupt_transfer() failed: err=%s",86libusb_strerror(ret));87return (ret);88}8990switch (((struct rtlbt_hci_event *)event)->header.event) {91case NG_HCI_EVENT_COMMAND_COMPL:92if (*transferred <93(int)offsetof(struct rtlbt_hci_event_cmd_compl, data))94break;95if (cmd->opcode !=96((struct rtlbt_hci_event_cmd_compl *)event)->opcode)97break;98return (0);99default:100break;101}102rtlbt_debug("Stray HCI event: %x",103((struct rtlbt_hci_event *)event)->header.event);104} while (timespeccmp(&to, &now, >));105106rtlbt_err("libusb_interrupt_transfer() failed: err=%s",107libusb_strerror(LIBUSB_ERROR_TIMEOUT));108109return (LIBUSB_ERROR_TIMEOUT);110}111112int113rtlbt_read_local_ver(struct libusb_device_handle *hdl,114ng_hci_read_local_ver_rp *ver)115{116int ret, transferred;117struct rtlbt_hci_event_cmd_compl *event;118struct rtlbt_hci_cmd cmd = {119.opcode = htole16(NG_HCI_OPCODE(NG_HCI_OGF_INFO,120NG_HCI_OCF_READ_LOCAL_VER)),121.length = 0,122};123uint8_t buf[RTLBT_HCI_EVT_COMPL_SIZE(ng_hci_read_local_ver_rp)];124125memset(buf, 0, sizeof(buf));126127ret = rtlbt_hci_command(hdl,128&cmd,129buf,130sizeof(buf),131&transferred,132RTLBT_HCI_CMD_TIMEOUT);133134if (ret < 0 || transferred != sizeof(buf)) {135rtlbt_debug("Can't read local version: code=%d, size=%d",136ret,137transferred);138return (-1);139}140141event = (struct rtlbt_hci_event_cmd_compl *)buf;142memcpy(ver, event->data, sizeof(ng_hci_read_local_ver_rp));143144return (0);145}146147int148rtlbt_read_rom_ver(struct libusb_device_handle *hdl, uint8_t *ver)149{150int ret, transferred;151struct rtlbt_hci_event_cmd_compl *event;152struct rtlbt_hci_cmd cmd = {153.opcode = htole16(0xfc6d),154.length = 0,155};156uint8_t buf[RTLBT_HCI_EVT_COMPL_SIZE(struct rtlbt_rom_ver_rp)];157158memset(buf, 0, sizeof(buf));159160ret = rtlbt_hci_command(hdl,161&cmd,162buf,163sizeof(buf),164&transferred,165RTLBT_HCI_CMD_TIMEOUT);166167if (ret < 0 || transferred != sizeof(buf)) {168rtlbt_debug("Can't read ROM version: code=%d, size=%d",169ret,170transferred);171return (-1);172}173174event = (struct rtlbt_hci_event_cmd_compl *)buf;175*ver = ((struct rtlbt_rom_ver_rp *)event->data)->version;176177return (0);178}179180int181rtlbt_read_reg16(struct libusb_device_handle *hdl,182struct rtlbt_vendor_cmd *vcmd, uint8_t *resp)183{184int ret, transferred;185struct rtlbt_hci_event_cmd_compl *event;186uint8_t cmd_buf[offsetof(struct rtlbt_hci_cmd, data) + sizeof(*vcmd)];187struct rtlbt_hci_cmd *cmd = (struct rtlbt_hci_cmd *)cmd_buf;188cmd->opcode = htole16(0xfc61);189cmd->length = sizeof(struct rtlbt_vendor_cmd);190memcpy(cmd->data, vcmd, sizeof(struct rtlbt_vendor_cmd));191uint8_t buf[RTLBT_HCI_EVT_COMPL_SIZE(struct rtlbt_vendor_rp)];192193memset(buf, 0, sizeof(buf));194195ret = rtlbt_hci_command(hdl,196cmd,197buf,198sizeof(buf),199&transferred,200RTLBT_HCI_CMD_TIMEOUT);201202if (ret < 0 || transferred != sizeof(buf)) {203rtlbt_debug("Can't read reg16: code=%d, size=%d",204ret,205transferred);206return (-1);207}208209event = (struct rtlbt_hci_event_cmd_compl *)buf;210memcpy(resp, &(((struct rtlbt_vendor_rp *)event->data)->data), 2);211212return (0);213}214215int216rtlbt_load_fwfile(struct libusb_device_handle *hdl,217const struct rtlbt_firmware *fw)218{219uint8_t cmd_buf[RTLBT_HCI_MAX_CMD_SIZE];220struct rtlbt_hci_cmd *cmd = (struct rtlbt_hci_cmd *)cmd_buf;221struct rtlbt_hci_dl_cmd *dl_cmd = (struct rtlbt_hci_dl_cmd *)cmd->data;222uint8_t evt_buf[RTLBT_HCI_EVT_COMPL_SIZE(struct rtlbt_hci_dl_rp)];223uint8_t *data = fw->buf;224int frag_num = fw->len / RTLBT_MAX_CMD_DATA_LEN + 1;225int frag_len = RTLBT_MAX_CMD_DATA_LEN;226int i, j;227int ret, transferred;228229for (i = 0, j = 0; i < frag_num; i++, j++) {230231rtlbt_debug("download fw (%d/%d)", i + 1, frag_num);232233memset(cmd_buf, 0, sizeof(cmd_buf));234cmd->opcode = htole16(0xfc20);235if (j > 0x7f)236j = 1;237dl_cmd->index = j;238239if (i == (frag_num - 1)) {240dl_cmd->index |= 0x80; /* data end */241frag_len = fw->len % RTLBT_MAX_CMD_DATA_LEN;242}243cmd->length = frag_len + 1;244memcpy(dl_cmd->data, data, frag_len);245246/* Send download command */247ret = rtlbt_hci_command(hdl,248cmd,249evt_buf,250sizeof(evt_buf),251&transferred,252RTLBT_HCI_CMD_TIMEOUT);253if (ret < 0) {254rtlbt_err("download fw command failed (%d)", errno);255goto out;256}257if (transferred != sizeof(evt_buf)) {258rtlbt_err("download fw event length mismatch");259errno = EIO;260ret = -1;261goto out;262}263264data += RTLBT_MAX_CMD_DATA_LEN;265}266267out:268return (ret);269}270271272