xref: /freebsd/usr.sbin/bluetooth/iwmbtfw/iwmbt_hw.c (revision d65c432aa693043a241e9061d040c4d2b3e86d96)
1 /*-
2  * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
3  *
4  * Copyright (c) 2019 Vladimir Kondratyev <wulf@FreeBSD.org>
5  *
6  * Redistribution and use in source and binary forms, with or without
7  * modification, are permitted provided that the following conditions
8  * are met:
9  * 1. Redistributions of source code must retain the above copyright
10  *    notice, this list of conditions and the following disclaimer.
11  * 2. Redistributions in binary form must reproduce the above copyright
12  *    notice, this list of conditions and the following disclaimer in the
13  *    documentation and/or other materials provided with the distribution.
14  *
15  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
16  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18  * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
19  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
21  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
23  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
24  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
25  * SUCH DAMAGE.
26  *
27  * $FreeBSD$
28  */
29 
30 #include <sys/types.h>
31 #include <sys/endian.h>
32 #include <sys/stat.h>
33 
34 #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 <unistd.h>
41 
42 #include <libusb.h>
43 
44 #include "iwmbt_fw.h"
45 #include "iwmbt_hw.h"
46 #include "iwmbt_dbg.h"
47 
48 #define	XMIN(x, y)	((x) < (y) ? (x) : (y))
49 
50 static int
51 iwmbt_send_fragment(struct libusb_device_handle *hdl,
52     uint8_t fragment_type, const void *data, uint8_t len, int timeout)
53 {
54 	int ret, transferred;
55 	uint8_t buf[IWMBT_HCI_MAX_CMD_SIZE];
56 	struct iwmbt_hci_cmd *cmd = (struct iwmbt_hci_cmd *) buf;
57 
58 	memset(buf, 0, sizeof(buf));
59 	cmd->opcode = htole16(0xfc09),
60 	cmd->length = len + 1,
61 	cmd->data[0] = fragment_type;
62 	memcpy(cmd->data + 1, data, len);
63 
64 	ret = libusb_bulk_transfer(hdl,
65 	    IWMBT_BULK_OUT_ENDPOINT_ADDR,
66 	    (uint8_t *)cmd,
67 	    IWMBT_HCI_CMD_SIZE(cmd),
68 	    &transferred,
69 	    timeout);
70 
71 	if (ret < 0 || transferred != (int)IWMBT_HCI_CMD_SIZE(cmd)) {
72 		iwmbt_err("libusb_bulk_transfer() failed: err=%s, size=%zu",
73 		    libusb_strerror(ret),
74 		    IWMBT_HCI_CMD_SIZE(cmd));
75 		return (-1);
76 	}
77 
78 	ret = libusb_bulk_transfer(hdl,
79 	    IWMBT_BULK_IN_ENDPOINT_ADDR,
80 	    buf,
81 	    sizeof(buf),
82 	    &transferred,
83 	    timeout);
84 
85 	if (ret < 0) {
86 		iwmbt_err("libusb_bulk_transfer() failed: err=%s",
87 		    libusb_strerror(ret));
88 		return (-1);
89 	}
90 
91 	return (0);
92 }
93 
94 static int
95 iwmbt_hci_command(struct libusb_device_handle *hdl, struct iwmbt_hci_cmd *cmd,
96     void *event, int size, int *transferred, int timeout)
97 {
98 	int ret;
99 
100 	ret = libusb_control_transfer(hdl,
101 	    LIBUSB_REQUEST_TYPE_CLASS | LIBUSB_RECIPIENT_DEVICE,
102 	    0,
103 	    0,
104 	    0,
105 	    (uint8_t *)cmd,
106 	    IWMBT_HCI_CMD_SIZE(cmd),
107 	    timeout);
108 
109 	if (ret < 0) {
110 		iwmbt_err("libusb_control_transfer() failed: err=%s",
111 		    libusb_strerror(ret));
112 		return (ret);
113 	}
114 
115 	ret = libusb_interrupt_transfer(hdl,
116 	    IWMBT_INTERRUPT_ENDPOINT_ADDR,
117 	    event,
118 	    size,
119 	    transferred,
120 	    timeout);
121 
122 	if (ret < 0)
123 		iwmbt_err("libusb_interrupt_transfer() failed: err=%s",
124 		    libusb_strerror(ret));
125 
126 	return (ret);
127 }
128 
129 int
130 iwmbt_load_fwfile(struct libusb_device_handle *hdl,
131     const struct iwmbt_firmware *fw, uint32_t *boot_param)
132 {
133 	int ready = 0, sent = 0;
134 	int ret, transferred;
135 	struct iwmbt_hci_cmd *cmd;
136 	struct iwmbt_hci_event *event;
137 	uint8_t buf[IWMBT_HCI_MAX_EVENT_SIZE];
138 
139 #define	IWMBT_SEND_FRAGMENT(fragment_type, size, msg)	do {		\
140 	iwmbt_debug("transferring %d bytes, offset %d", size, sent);	\
141 									\
142 	ret = iwmbt_send_fragment(hdl,					\
143 	    fragment_type,						\
144 	    fw->buf + sent,						\
145 	    XMIN(size, fw->len - sent),					\
146 	    IWMBT_HCI_CMD_TIMEOUT);					\
147 									\
148 	if (ret < 0) {							\
149 		iwmbt_debug("Failed to send "msg": code=%d", ret);	\
150 		return (-1);						\
151 	}								\
152 	sent += size;							\
153 } while (0)
154 
155 	if (fw->len < 644) {
156 		iwmbt_err("Invalid size of firmware file (%d)", fw->len);
157 		return (-1);
158 	}
159 
160 	iwmbt_debug("file=%s, size=%d", fw->fwname, fw->len);
161 
162 	IWMBT_SEND_FRAGMENT(0x00, 0x80, "CCS segment");
163 	IWMBT_SEND_FRAGMENT(0x03, 0x80, "public key / part 1");
164 	IWMBT_SEND_FRAGMENT(0x03, 0x80, "public key / part 2");
165 
166 	/* skip 4 bytes */
167 	sent += 4;
168 
169 	IWMBT_SEND_FRAGMENT(0x02, 0x80, "signature / part 1");
170 	IWMBT_SEND_FRAGMENT(0x02, 0x80, "signature / part 2");
171 
172 	/*
173 	 * Send firmware chunks. Chunk len must be 4 byte aligned.
174 	 * multiple commands can be combined
175 	 */
176 	while (fw->len - sent - ready >= (int) sizeof(struct iwmbt_hci_cmd)) {
177 		cmd = (struct iwmbt_hci_cmd *)(fw->buf + sent + ready);
178 		/* Parse firmware for Intel Reset HCI command parameter */
179 		if (cmd->opcode == htole16(0xfc0e)) {
180 			*boot_param = le32dec(cmd->data);
181 			iwmbt_debug("boot_param=0x%08x", *boot_param);
182 		}
183 		ready += IWMBT_HCI_CMD_SIZE(cmd);
184 		while (ready >= 0xFC) {
185 			IWMBT_SEND_FRAGMENT(0x01, 0xFC, "firmware chunk");
186 			ready -= 0xFC;
187 		}
188 		if (ready > 0 && ready % 4 == 0) {
189 			IWMBT_SEND_FRAGMENT(0x01, ready, "firmware chunk");
190 			ready = 0;
191 		}
192 	}
193 
194 	/* Wait for firmware download completion event */
195 	ret = libusb_interrupt_transfer(hdl,
196 	    IWMBT_INTERRUPT_ENDPOINT_ADDR,
197 	    buf,
198 	    sizeof(buf),
199 	    &transferred,
200 	    IWMBT_LOADCMPL_TIMEOUT);
201 
202 	if (ret < 0 || transferred < (int)sizeof(struct iwmbt_hci_event) + 1) {
203 		iwmbt_err("libusb_interrupt_transfer() failed: "
204 		    "err=%s, size=%d",
205 		    libusb_strerror(ret),
206 		    transferred);
207 		return (-1);
208 	}
209 
210 	/* Expect Vendor Specific Event 0x06 */
211 	event = (struct iwmbt_hci_event *)buf;
212 	if (event->header.event != 0xFF || event->data[0] != 0x06) {
213 		iwmbt_err("firmware download completion event missed");
214 		return (-1);
215 	}
216 
217 	return (0);
218 }
219 
220 int
221 iwmbt_get_version(struct libusb_device_handle *hdl,
222     struct iwmbt_version *version)
223 {
224 	int ret, transferred;
225 	struct iwmbt_hci_event_cmd_compl*event;
226 	struct iwmbt_hci_cmd cmd = {
227 		.opcode = htole16(0xfc05),
228 		.length = 0,
229 	};
230 	uint8_t buf[IWMBT_HCI_EVT_COMPL_SIZE(struct iwmbt_version)];
231 
232 	memset(buf, 0, sizeof(buf));
233 
234 	ret = iwmbt_hci_command(hdl,
235 	    &cmd,
236 	    buf,
237 	    sizeof(buf),
238 	    &transferred,
239 	    IWMBT_HCI_CMD_TIMEOUT);
240 
241 	if (ret < 0 || transferred != sizeof(buf)) {
242 		 iwmbt_debug("Can't get version: : code=%d, size=%d",
243 		     ret,
244 		     transferred);
245 		 return (-1);
246 	}
247 
248 	event = (struct iwmbt_hci_event_cmd_compl *)buf;
249 	memcpy(version, event->data, sizeof(struct iwmbt_version));
250 
251 	return (0);
252 }
253 
254 int
255 iwmbt_get_boot_params(struct libusb_device_handle *hdl,
256     struct iwmbt_boot_params *params)
257 {
258 	int ret, transferred = 0;
259 	struct iwmbt_hci_event_cmd_compl *event;
260 	struct iwmbt_hci_cmd cmd = {
261 		.opcode = htole16(0xfc0d),
262 		.length = 0,
263 	};
264 	uint8_t buf[IWMBT_HCI_EVT_COMPL_SIZE(struct iwmbt_boot_params)];
265 
266 	memset(buf, 0, sizeof(buf));
267 
268 	ret = iwmbt_hci_command(hdl,
269 	    &cmd,
270 	    buf,
271 	    sizeof(buf),
272 	    &transferred,
273 	    IWMBT_HCI_CMD_TIMEOUT);
274 
275 	if (ret < 0 || transferred != sizeof(buf)) {
276 		 iwmbt_debug("Can't get boot params: code=%d, size=%d",
277 		     ret,
278 		     transferred);
279 		 return (-1);
280 	}
281 
282 	event = (struct iwmbt_hci_event_cmd_compl *)buf;
283 	memcpy(params, event->data, sizeof(struct iwmbt_boot_params));
284 
285 	return (0);
286 }
287 
288 int
289 iwmbt_intel_reset(struct libusb_device_handle *hdl, uint32_t boot_param)
290 {
291 	int ret, transferred = 0;
292 	struct iwmbt_hci_event *event;
293 	static struct iwmbt_hci_cmd cmd = {
294 		.opcode = htole16(0xfc01),
295 		.length = 8,
296 		.data = { 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00 },
297 	};
298 	uint8_t buf[IWMBT_HCI_MAX_EVENT_SIZE];
299 
300 	le32enc(cmd.data + 4, boot_param);
301 	memset(buf, 0, sizeof(buf));
302 
303 	ret = iwmbt_hci_command(hdl,
304 	    &cmd,
305 	    buf,
306 	    sizeof(buf),
307 	    &transferred,
308 	    IWMBT_HCI_CMD_TIMEOUT);
309 
310 	if (ret < 0 || transferred < (int)sizeof(struct iwmbt_hci_event) + 1) {
311 		 iwmbt_debug("Intel Reset command failed: code=%d, size=%d",
312 		    ret,
313 		    transferred);
314 		 return (ret);
315 	}
316 
317 	/* expect Vendor Specific Event 0x02 */
318 	event = (struct iwmbt_hci_event *)buf;
319 	if (event->header.event != 0xFF || event->data[0] != 0x02) {
320 		iwmbt_err("Intel Reset completion event missed");
321 		return (-1);
322 	}
323 
324 	return (0);
325 }
326 
327 int
328 iwmbt_load_ddc(struct libusb_device_handle *hdl,
329     const struct iwmbt_firmware *ddc)
330 {
331 	int size, sent = 0;
332 	int ret, transferred;
333 	uint8_t buf[IWMBT_HCI_MAX_CMD_SIZE];
334 	struct iwmbt_hci_cmd *cmd = (struct iwmbt_hci_cmd *)buf;
335 
336 	size = ddc->len;
337 
338 	iwmbt_debug("file=%s, size=%d", ddc->fwname, size);
339 
340 	while (size > 0) {
341 
342 		memset(buf, 0, sizeof(buf));
343 		cmd->opcode = htole16(0xfc8b);
344 		cmd->length = ddc->buf[sent] + 1;
345 		memcpy(cmd->data, ddc->buf + sent, XMIN(ddc->buf[sent], size));
346 
347 		iwmbt_debug("transferring %d bytes, offset %d",
348 		    cmd->length,
349 		    sent);
350 
351 		size -= cmd->length;
352 		sent += cmd->length;
353 
354 		ret = iwmbt_hci_command(hdl,
355 		    cmd,
356 		    buf,
357 		    sizeof(buf),
358 		    &transferred,
359 		    IWMBT_HCI_CMD_TIMEOUT);
360 
361 		if (ret < 0) {
362 			 iwmbt_debug("Intel Write DDC failed: code=%d", ret);
363 			 return (-1);
364 		}
365 	}
366 
367 	return (0);
368 }
369 
370 int
371 iwmbt_set_event_mask(struct libusb_device_handle *hdl)
372 {
373 	int ret, transferred = 0;
374 	static struct iwmbt_hci_cmd cmd = {
375 		.opcode = htole16(0xfc52),
376 		.length = 8,
377 		.data = { 0x87, 0x0c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 },
378 	};
379 	uint8_t buf[IWMBT_HCI_MAX_EVENT_SIZE];
380 
381 	ret = iwmbt_hci_command(hdl,
382 	    &cmd,
383 	    buf,
384 	    sizeof(buf),
385 	    &transferred,
386 	    IWMBT_HCI_CMD_TIMEOUT);
387 
388 	if (ret < 0)
389 		 iwmbt_debug("Intel Set Event Mask failed: code=%d", ret);
390 
391 	return (ret);
392 }
393