Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
freebsd
GitHub Repository: freebsd/freebsd-src
Path: blob/main/usr.sbin/bluetooth/rtlbtfw/rtlbt_hw.c
105774 views
1
/*-
2
* SPDX-License-Identifier: BSD-2-Clause
3
*
4
* Copyright (c) 2019 Vladimir Kondratyev <[email protected]>
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
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
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
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
182
rtlbt_read_reg16(struct libusb_device_handle *hdl,
183
struct rtlbt_vendor_cmd *vcmd, uint8_t *resp)
184
{
185
int ret, transferred;
186
struct rtlbt_hci_event_cmd_compl *event;
187
uint8_t cmd_buf[offsetof(struct rtlbt_hci_cmd, data) + sizeof(*vcmd)];
188
struct rtlbt_hci_cmd *cmd = (struct rtlbt_hci_cmd *)cmd_buf;
189
cmd->opcode = htole16(0xfc61);
190
cmd->length = sizeof(struct rtlbt_vendor_cmd);
191
memcpy(cmd->data, vcmd, sizeof(struct rtlbt_vendor_cmd));
192
uint8_t buf[RTLBT_HCI_EVT_COMPL_SIZE(struct rtlbt_vendor_rp)];
193
194
memset(buf, 0, sizeof(buf));
195
196
ret = rtlbt_hci_command(hdl,
197
cmd,
198
buf,
199
sizeof(buf),
200
&transferred,
201
RTLBT_HCI_CMD_TIMEOUT);
202
203
if (ret < 0 || transferred != sizeof(buf)) {
204
rtlbt_debug("Can't read reg16: code=%d, size=%d",
205
ret,
206
transferred);
207
return (-1);
208
}
209
210
event = (struct rtlbt_hci_event_cmd_compl *)buf;
211
memcpy(resp, &(((struct rtlbt_vendor_rp *)event->data)->data), 2);
212
213
return (0);
214
}
215
216
int
217
rtlbt_load_fwfile(struct libusb_device_handle *hdl,
218
const struct rtlbt_firmware *fw)
219
{
220
uint8_t cmd_buf[RTLBT_HCI_MAX_CMD_SIZE];
221
struct rtlbt_hci_cmd *cmd = (struct rtlbt_hci_cmd *)cmd_buf;
222
struct rtlbt_hci_dl_cmd *dl_cmd = (struct rtlbt_hci_dl_cmd *)cmd->data;
223
uint8_t evt_buf[RTLBT_HCI_EVT_COMPL_SIZE(struct rtlbt_hci_dl_rp)];
224
uint8_t *data = fw->buf;
225
int frag_num = fw->len / RTLBT_MAX_CMD_DATA_LEN + 1;
226
int frag_len = RTLBT_MAX_CMD_DATA_LEN;
227
int i, j;
228
int ret, transferred;
229
230
for (i = 0, j = 0; i < frag_num; i++, j++) {
231
232
rtlbt_debug("download fw (%d/%d)", i + 1, frag_num);
233
234
memset(cmd_buf, 0, sizeof(cmd_buf));
235
cmd->opcode = htole16(0xfc20);
236
if (j > 0x7f)
237
j = 1;
238
dl_cmd->index = j;
239
240
if (i == (frag_num - 1)) {
241
dl_cmd->index |= 0x80; /* data end */
242
frag_len = fw->len % RTLBT_MAX_CMD_DATA_LEN;
243
}
244
cmd->length = frag_len + 1;
245
memcpy(dl_cmd->data, data, frag_len);
246
247
/* Send download command */
248
ret = rtlbt_hci_command(hdl,
249
cmd,
250
evt_buf,
251
sizeof(evt_buf),
252
&transferred,
253
RTLBT_HCI_CMD_TIMEOUT);
254
if (ret < 0) {
255
rtlbt_err("download fw command failed (%d)", errno);
256
goto out;
257
}
258
if (transferred != sizeof(evt_buf)) {
259
rtlbt_err("download fw event length mismatch");
260
errno = EIO;
261
ret = -1;
262
goto out;
263
}
264
265
data += RTLBT_MAX_CMD_DATA_LEN;
266
}
267
268
out:
269
return (ret);
270
}
271
272