xref: /freebsd/usr.sbin/bluetooth/rtlbtfw/rtlbt_hw.c (revision 5036d9652a5701d00e9e40ea942c278e9f77d33d)
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