mirror of
https://github.com/opnsense/src.git
synced 2026-02-18 18:20:26 -05:00
Firmware files are available in the comms/rtlbt-firmware port. Sponsored by: Future Crew LLC MFC after: 1 month Differential Revision: https://reviews.freebsd.org/D46739 (cherry picked from commit 5036d9652a5701d00e9e40ea942c278e9f77d33d)
236 lines
6.1 KiB
C
236 lines
6.1 KiB
C
/*-
|
|
* SPDX-License-Identifier: BSD-2-Clause
|
|
*
|
|
* Copyright (c) 2019 Vladimir Kondratyev <wulf@FreeBSD.org>
|
|
* Copyright (c) 2023 Future Crew LLC.
|
|
*
|
|
* 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.
|
|
*/
|
|
|
|
#include <sys/param.h>
|
|
#include <sys/endian.h>
|
|
#include <sys/stat.h>
|
|
|
|
#include <netgraph/bluetooth/include/ng_hci.h>
|
|
|
|
#include <err.h>
|
|
#include <errno.h>
|
|
#include <stddef.h>
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <time.h>
|
|
#include <unistd.h>
|
|
|
|
#include <libusb.h>
|
|
|
|
#include "rtlbt_fw.h"
|
|
#include "rtlbt_hw.h"
|
|
#include "rtlbt_dbg.h"
|
|
|
|
static int
|
|
rtlbt_hci_command(struct libusb_device_handle *hdl, struct rtlbt_hci_cmd *cmd,
|
|
void *event, int size, int *transferred, int timeout)
|
|
{
|
|
struct timespec to, now, remains;
|
|
int ret;
|
|
|
|
ret = libusb_control_transfer(hdl,
|
|
LIBUSB_REQUEST_TYPE_CLASS | LIBUSB_RECIPIENT_DEVICE,
|
|
0,
|
|
0,
|
|
0,
|
|
(uint8_t *)cmd,
|
|
RTLBT_HCI_CMD_SIZE(cmd),
|
|
timeout);
|
|
|
|
if (ret < 0) {
|
|
rtlbt_err("libusb_control_transfer() failed: err=%s",
|
|
libusb_strerror(ret));
|
|
return (ret);
|
|
}
|
|
|
|
clock_gettime(CLOCK_MONOTONIC, &now);
|
|
to = RTLBT_MSEC2TS(timeout);
|
|
timespecadd(&to, &now, &to);
|
|
|
|
do {
|
|
timespecsub(&to, &now, &remains);
|
|
ret = libusb_interrupt_transfer(hdl,
|
|
RTLBT_INTERRUPT_ENDPOINT_ADDR,
|
|
event,
|
|
size,
|
|
transferred,
|
|
RTLBT_TS2MSEC(remains) + 1);
|
|
|
|
if (ret < 0) {
|
|
rtlbt_err("libusb_interrupt_transfer() failed: err=%s",
|
|
libusb_strerror(ret));
|
|
return (ret);
|
|
}
|
|
|
|
switch (((struct rtlbt_hci_event *)event)->header.event) {
|
|
case NG_HCI_EVENT_COMMAND_COMPL:
|
|
if (*transferred <
|
|
(int)offsetof(struct rtlbt_hci_event_cmd_compl, data))
|
|
break;
|
|
if (cmd->opcode !=
|
|
((struct rtlbt_hci_event_cmd_compl *)event)->opcode)
|
|
break;
|
|
return (0);
|
|
default:
|
|
break;
|
|
}
|
|
rtlbt_debug("Stray HCI event: %x",
|
|
((struct rtlbt_hci_event *)event)->header.event);
|
|
} while (timespeccmp(&to, &now, >));
|
|
|
|
rtlbt_err("libusb_interrupt_transfer() failed: err=%s",
|
|
libusb_strerror(LIBUSB_ERROR_TIMEOUT));
|
|
|
|
return (LIBUSB_ERROR_TIMEOUT);
|
|
}
|
|
|
|
int
|
|
rtlbt_read_local_ver(struct libusb_device_handle *hdl,
|
|
ng_hci_read_local_ver_rp *ver)
|
|
{
|
|
int ret, transferred;
|
|
struct rtlbt_hci_event_cmd_compl *event;
|
|
struct rtlbt_hci_cmd cmd = {
|
|
.opcode = htole16(NG_HCI_OPCODE(NG_HCI_OGF_INFO,
|
|
NG_HCI_OCF_READ_LOCAL_VER)),
|
|
.length = 0,
|
|
};
|
|
uint8_t buf[RTLBT_HCI_EVT_COMPL_SIZE(ng_hci_read_local_ver_rp)];
|
|
|
|
memset(buf, 0, sizeof(buf));
|
|
|
|
ret = rtlbt_hci_command(hdl,
|
|
&cmd,
|
|
buf,
|
|
sizeof(buf),
|
|
&transferred,
|
|
RTLBT_HCI_CMD_TIMEOUT);
|
|
|
|
if (ret < 0 || transferred != sizeof(buf)) {
|
|
rtlbt_debug("Can't read local version: code=%d, size=%d",
|
|
ret,
|
|
transferred);
|
|
return (-1);
|
|
}
|
|
|
|
event = (struct rtlbt_hci_event_cmd_compl *)buf;
|
|
memcpy(ver, event->data, sizeof(ng_hci_read_local_ver_rp));
|
|
|
|
return (0);
|
|
}
|
|
|
|
int
|
|
rtlbt_read_rom_ver(struct libusb_device_handle *hdl, uint8_t *ver)
|
|
{
|
|
int ret, transferred;
|
|
struct rtlbt_hci_event_cmd_compl *event;
|
|
struct rtlbt_hci_cmd cmd = {
|
|
.opcode = htole16(0xfc6d),
|
|
.length = 0,
|
|
};
|
|
uint8_t buf[RTLBT_HCI_EVT_COMPL_SIZE(struct rtlbt_rom_ver_rp)];
|
|
|
|
memset(buf, 0, sizeof(buf));
|
|
|
|
ret = rtlbt_hci_command(hdl,
|
|
&cmd,
|
|
buf,
|
|
sizeof(buf),
|
|
&transferred,
|
|
RTLBT_HCI_CMD_TIMEOUT);
|
|
|
|
if (ret < 0 || transferred != sizeof(buf)) {
|
|
rtlbt_debug("Can't read ROM version: code=%d, size=%d",
|
|
ret,
|
|
transferred);
|
|
return (-1);
|
|
}
|
|
|
|
event = (struct rtlbt_hci_event_cmd_compl *)buf;
|
|
*ver = ((struct rtlbt_rom_ver_rp *)event->data)->version;
|
|
|
|
return (0);
|
|
}
|
|
|
|
int
|
|
rtlbt_load_fwfile(struct libusb_device_handle *hdl,
|
|
const struct rtlbt_firmware *fw)
|
|
{
|
|
uint8_t cmd_buf[RTLBT_HCI_MAX_CMD_SIZE];
|
|
struct rtlbt_hci_cmd *cmd = (struct rtlbt_hci_cmd *)cmd_buf;
|
|
struct rtlbt_hci_dl_cmd *dl_cmd = (struct rtlbt_hci_dl_cmd *)cmd->data;
|
|
uint8_t evt_buf[RTLBT_HCI_EVT_COMPL_SIZE(struct rtlbt_hci_dl_rp)];
|
|
uint8_t *data = fw->buf;
|
|
int frag_num = fw->len / RTLBT_MAX_CMD_DATA_LEN + 1;
|
|
int frag_len = RTLBT_MAX_CMD_DATA_LEN;
|
|
int i;
|
|
int ret, transferred;
|
|
|
|
for (i = 0; i < frag_num; i++) {
|
|
|
|
rtlbt_debug("download fw (%d/%d)", i + 1, frag_num);
|
|
|
|
memset(cmd_buf, 0, sizeof(cmd_buf));
|
|
cmd->opcode = htole16(0xfc20);
|
|
if (i > 0x7f)
|
|
dl_cmd->index = (i & 0x7f) + 1;
|
|
else
|
|
dl_cmd->index = i;
|
|
|
|
if (i == (frag_num - 1)) {
|
|
dl_cmd->index |= 0x80; /* data end */
|
|
frag_len = fw->len % RTLBT_MAX_CMD_DATA_LEN;
|
|
}
|
|
cmd->length = frag_len + 1;
|
|
memcpy(dl_cmd->data, data, frag_len);
|
|
|
|
/* Send download command */
|
|
ret = rtlbt_hci_command(hdl,
|
|
cmd,
|
|
evt_buf,
|
|
sizeof(evt_buf),
|
|
&transferred,
|
|
RTLBT_HCI_CMD_TIMEOUT);
|
|
if (ret < 0) {
|
|
rtlbt_err("download fw command failed (%d)", errno);
|
|
goto out;
|
|
}
|
|
if (transferred != sizeof(evt_buf)) {
|
|
rtlbt_err("download fw event length mismatch");
|
|
errno = EIO;
|
|
ret = -1;
|
|
goto out;
|
|
}
|
|
|
|
data += RTLBT_MAX_CMD_DATA_LEN;
|
|
}
|
|
|
|
out:
|
|
return (ret);
|
|
}
|