1 /*-
2 * SPDX-License-Identifier: BSD-2-Clause
3 *
4 * Copyright (c) 2019 Vladimir Kondratyev <wulf@FreeBSD.org>
5 * Copyright (c) 2023 Future Crew LLC.
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
10 * 1. Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in the
14 * documentation and/or other materials provided with the distribution.
15 *
16 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
17 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
20 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
22 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
23 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
24 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26 * SUCH DAMAGE.
27 */
28
29 #include <sys/param.h>
30 #include <sys/endian.h>
31 #include <sys/stat.h>
32
33 #include <netgraph/bluetooth/include/ng_hci.h>
34
35 #include <err.h>
36 #include <errno.h>
37 #include <stddef.h>
38 #include <stdio.h>
39 #include <stdlib.h>
40 #include <string.h>
41 #include <time.h>
42 #include <unistd.h>
43
44 #include <libusb.h>
45
46 #include "rtlbt_fw.h"
47 #include "rtlbt_hw.h"
48 #include "rtlbt_dbg.h"
49
50 static int
rtlbt_hci_command(struct libusb_device_handle * hdl,struct rtlbt_hci_cmd * cmd,void * event,int size,int * transferred,int timeout)51 rtlbt_hci_command(struct libusb_device_handle *hdl, struct rtlbt_hci_cmd *cmd,
52 void *event, int size, int *transferred, int timeout)
53 {
54 struct timespec to, now, remains;
55 int ret;
56
57 ret = libusb_control_transfer(hdl,
58 LIBUSB_REQUEST_TYPE_CLASS | LIBUSB_RECIPIENT_DEVICE,
59 0,
60 0,
61 0,
62 (uint8_t *)cmd,
63 RTLBT_HCI_CMD_SIZE(cmd),
64 timeout);
65
66 if (ret < 0) {
67 rtlbt_err("libusb_control_transfer() failed: err=%s",
68 libusb_strerror(ret));
69 return (ret);
70 }
71
72 clock_gettime(CLOCK_MONOTONIC, &now);
73 to = RTLBT_MSEC2TS(timeout);
74 timespecadd(&to, &now, &to);
75
76 do {
77 timespecsub(&to, &now, &remains);
78 ret = libusb_interrupt_transfer(hdl,
79 RTLBT_INTERRUPT_ENDPOINT_ADDR,
80 event,
81 size,
82 transferred,
83 RTLBT_TS2MSEC(remains) + 1);
84
85 if (ret < 0) {
86 rtlbt_err("libusb_interrupt_transfer() failed: err=%s",
87 libusb_strerror(ret));
88 return (ret);
89 }
90
91 switch (((struct rtlbt_hci_event *)event)->header.event) {
92 case NG_HCI_EVENT_COMMAND_COMPL:
93 if (*transferred <
94 (int)offsetof(struct rtlbt_hci_event_cmd_compl, data))
95 break;
96 if (cmd->opcode !=
97 ((struct rtlbt_hci_event_cmd_compl *)event)->opcode)
98 break;
99 return (0);
100 default:
101 break;
102 }
103 rtlbt_debug("Stray HCI event: %x",
104 ((struct rtlbt_hci_event *)event)->header.event);
105 } while (timespeccmp(&to, &now, >));
106
107 rtlbt_err("libusb_interrupt_transfer() failed: err=%s",
108 libusb_strerror(LIBUSB_ERROR_TIMEOUT));
109
110 return (LIBUSB_ERROR_TIMEOUT);
111 }
112
113 int
rtlbt_read_local_ver(struct libusb_device_handle * hdl,ng_hci_read_local_ver_rp * ver)114 rtlbt_read_local_ver(struct libusb_device_handle *hdl,
115 ng_hci_read_local_ver_rp *ver)
116 {
117 int ret, transferred;
118 struct rtlbt_hci_event_cmd_compl *event;
119 struct rtlbt_hci_cmd cmd = {
120 .opcode = htole16(NG_HCI_OPCODE(NG_HCI_OGF_INFO,
121 NG_HCI_OCF_READ_LOCAL_VER)),
122 .length = 0,
123 };
124 uint8_t buf[RTLBT_HCI_EVT_COMPL_SIZE(ng_hci_read_local_ver_rp)];
125
126 memset(buf, 0, sizeof(buf));
127
128 ret = rtlbt_hci_command(hdl,
129 &cmd,
130 buf,
131 sizeof(buf),
132 &transferred,
133 RTLBT_HCI_CMD_TIMEOUT);
134
135 if (ret < 0 || transferred != sizeof(buf)) {
136 rtlbt_debug("Can't read local version: code=%d, size=%d",
137 ret,
138 transferred);
139 return (-1);
140 }
141
142 event = (struct rtlbt_hci_event_cmd_compl *)buf;
143 memcpy(ver, event->data, sizeof(ng_hci_read_local_ver_rp));
144
145 return (0);
146 }
147
148 int
rtlbt_read_rom_ver(struct libusb_device_handle * hdl,uint8_t * ver)149 rtlbt_read_rom_ver(struct libusb_device_handle *hdl, uint8_t *ver)
150 {
151 int ret, transferred;
152 struct rtlbt_hci_event_cmd_compl *event;
153 struct rtlbt_hci_cmd cmd = {
154 .opcode = htole16(0xfc6d),
155 .length = 0,
156 };
157 uint8_t buf[RTLBT_HCI_EVT_COMPL_SIZE(struct rtlbt_rom_ver_rp)];
158
159 memset(buf, 0, sizeof(buf));
160
161 ret = rtlbt_hci_command(hdl,
162 &cmd,
163 buf,
164 sizeof(buf),
165 &transferred,
166 RTLBT_HCI_CMD_TIMEOUT);
167
168 if (ret < 0 || transferred != sizeof(buf)) {
169 rtlbt_debug("Can't read ROM version: code=%d, size=%d",
170 ret,
171 transferred);
172 return (-1);
173 }
174
175 event = (struct rtlbt_hci_event_cmd_compl *)buf;
176 *ver = ((struct rtlbt_rom_ver_rp *)event->data)->version;
177
178 return (0);
179 }
180
181 int
rtlbt_load_fwfile(struct libusb_device_handle * hdl,const struct rtlbt_firmware * fw)182 rtlbt_load_fwfile(struct libusb_device_handle *hdl,
183 const struct rtlbt_firmware *fw)
184 {
185 uint8_t cmd_buf[RTLBT_HCI_MAX_CMD_SIZE];
186 struct rtlbt_hci_cmd *cmd = (struct rtlbt_hci_cmd *)cmd_buf;
187 struct rtlbt_hci_dl_cmd *dl_cmd = (struct rtlbt_hci_dl_cmd *)cmd->data;
188 uint8_t evt_buf[RTLBT_HCI_EVT_COMPL_SIZE(struct rtlbt_hci_dl_rp)];
189 uint8_t *data = fw->buf;
190 int frag_num = fw->len / RTLBT_MAX_CMD_DATA_LEN + 1;
191 int frag_len = RTLBT_MAX_CMD_DATA_LEN;
192 int i;
193 int ret, transferred;
194
195 for (i = 0; i < frag_num; i++) {
196
197 rtlbt_debug("download fw (%d/%d)", i + 1, frag_num);
198
199 memset(cmd_buf, 0, sizeof(cmd_buf));
200 cmd->opcode = htole16(0xfc20);
201 if (i > 0x7f)
202 dl_cmd->index = (i & 0x7f) + 1;
203 else
204 dl_cmd->index = i;
205
206 if (i == (frag_num - 1)) {
207 dl_cmd->index |= 0x80; /* data end */
208 frag_len = fw->len % RTLBT_MAX_CMD_DATA_LEN;
209 }
210 cmd->length = frag_len + 1;
211 memcpy(dl_cmd->data, data, frag_len);
212
213 /* Send download command */
214 ret = rtlbt_hci_command(hdl,
215 cmd,
216 evt_buf,
217 sizeof(evt_buf),
218 &transferred,
219 RTLBT_HCI_CMD_TIMEOUT);
220 if (ret < 0) {
221 rtlbt_err("download fw command failed (%d)", errno);
222 goto out;
223 }
224 if (transferred != sizeof(evt_buf)) {
225 rtlbt_err("download fw event length mismatch");
226 errno = EIO;
227 ret = -1;
228 goto out;
229 }
230
231 data += RTLBT_MAX_CMD_DATA_LEN;
232 }
233
234 out:
235 return (ret);
236 }
237