xref: /freebsd/usr.sbin/bluetooth/iwmbtfw/iwmbt_hw.c (revision 59c8e88e72633afbc47a4ace0d2170d00d51f7dc)
1 /*-
2  * SPDX-License-Identifier: BSD-2-Clause
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 
28 #include <sys/param.h>
29 #include <sys/endian.h>
30 #include <sys/stat.h>
31 
32 #include <err.h>
33 #include <errno.h>
34 #include <stddef.h>
35 #include <stdio.h>
36 #include <stdlib.h>
37 #include <string.h>
38 #include <unistd.h>
39 
40 #include <libusb.h>
41 
42 #include "iwmbt_fw.h"
43 #include "iwmbt_hw.h"
44 #include "iwmbt_dbg.h"
45 
46 #define	XMIN(x, y)	((x) < (y) ? (x) : (y))
47 
48 static int
49 iwmbt_send_fragment(struct libusb_device_handle *hdl,
50     uint8_t fragment_type, const void *data, uint8_t len, int timeout)
51 {
52 	int ret, transferred;
53 	uint8_t buf[IWMBT_HCI_MAX_CMD_SIZE];
54 	struct iwmbt_hci_cmd *cmd = (struct iwmbt_hci_cmd *) buf;
55 
56 	memset(buf, 0, sizeof(buf));
57 	cmd->opcode = htole16(0xfc09),
58 	cmd->length = len + 1,
59 	cmd->data[0] = fragment_type;
60 	memcpy(cmd->data + 1, data, len);
61 
62 	ret = libusb_bulk_transfer(hdl,
63 	    IWMBT_BULK_OUT_ENDPOINT_ADDR,
64 	    (uint8_t *)cmd,
65 	    IWMBT_HCI_CMD_SIZE(cmd),
66 	    &transferred,
67 	    timeout);
68 
69 	if (ret < 0 || transferred != (int)IWMBT_HCI_CMD_SIZE(cmd)) {
70 		iwmbt_err("libusb_bulk_transfer() failed: err=%s, size=%zu",
71 		    libusb_strerror(ret),
72 		    IWMBT_HCI_CMD_SIZE(cmd));
73 		return (-1);
74 	}
75 
76 	ret = libusb_bulk_transfer(hdl,
77 	    IWMBT_BULK_IN_ENDPOINT_ADDR,
78 	    buf,
79 	    sizeof(buf),
80 	    &transferred,
81 	    timeout);
82 
83 	if (ret < 0) {
84 		iwmbt_err("libusb_bulk_transfer() failed: err=%s",
85 		    libusb_strerror(ret));
86 		return (-1);
87 	}
88 
89 	return (0);
90 }
91 
92 static int
93 iwmbt_hci_command(struct libusb_device_handle *hdl, struct iwmbt_hci_cmd *cmd,
94     void *event, int size, int *transferred, int timeout)
95 {
96 	int ret;
97 
98 	ret = libusb_control_transfer(hdl,
99 	    LIBUSB_REQUEST_TYPE_CLASS | LIBUSB_RECIPIENT_DEVICE,
100 	    0,
101 	    0,
102 	    0,
103 	    (uint8_t *)cmd,
104 	    IWMBT_HCI_CMD_SIZE(cmd),
105 	    timeout);
106 
107 	if (ret < 0) {
108 		iwmbt_err("libusb_control_transfer() failed: err=%s",
109 		    libusb_strerror(ret));
110 		return (ret);
111 	}
112 
113 	ret = libusb_interrupt_transfer(hdl,
114 	    IWMBT_INTERRUPT_ENDPOINT_ADDR,
115 	    event,
116 	    size,
117 	    transferred,
118 	    timeout);
119 
120 	if (ret < 0)
121 		iwmbt_err("libusb_interrupt_transfer() failed: err=%s",
122 		    libusb_strerror(ret));
123 
124 	return (ret);
125 }
126 
127 int
128 iwmbt_patch_fwfile(struct libusb_device_handle *hdl,
129     const struct iwmbt_firmware *fw)
130 {
131 	int ret, transferred;
132 	struct iwmbt_firmware fw_job = *fw;
133 	uint16_t cmd_opcode;
134 	uint8_t cmd_length;
135 	struct iwmbt_hci_cmd *cmd_buf;
136 	uint8_t evt_code;
137 	uint8_t evt_length;
138 	uint8_t evt_buf[IWMBT_HCI_MAX_EVENT_SIZE];
139 	int activate_patch = 0;
140 
141 	while (fw_job.len > 0) {
142 		if (fw_job.len < 4) {
143 			iwmbt_err("Invalid firmware, unexpected EOF in HCI "
144 			    "command header. Remains=%d", fw_job.len);
145 			return (-1);
146 		}
147 
148 		if (fw_job.buf[0] != 0x01) {
149 			iwmbt_err("Invalid firmware, expected HCI command (%d)",
150 					fw_job.buf[0]);
151 			return (-1);
152 		}
153 
154 		/* Advance by one. */
155 		fw_job.buf++;
156 		fw_job.len--;
157 
158 		/* Load in the HCI command to perform. */
159 		cmd_opcode = le16dec(fw_job.buf);
160 		cmd_length = fw_job.buf[2];
161 		cmd_buf = (struct iwmbt_hci_cmd *)fw_job.buf;
162 
163 		iwmbt_debug("opcode=%04x, len=%02x", cmd_opcode, cmd_length);
164 
165 		/*
166 		 * If there is a command that loads a patch in the
167 		 * firmware file, then activate the patch upon success,
168 		 * otherwise just disable the manufacturer mode.
169 		 */
170 		if (cmd_opcode == 0xfc8e)
171 			activate_patch = 1;
172 
173 		/* Advance by three. */
174 		fw_job.buf += 3;
175 		fw_job.len -= 3;
176 
177 		if (fw_job.len < cmd_length) {
178 			iwmbt_err("Invalid firmware, unexpected EOF in HCI "
179 			    "command data. len=%d, remains=%d",
180 			    cmd_length, fw_job.len);
181 			return (-1);
182 		}
183 
184 		/* Advance by data length. */
185 		fw_job.buf += cmd_length;
186 		fw_job.len -= cmd_length;
187 
188 		ret = libusb_control_transfer(hdl,
189 		    LIBUSB_REQUEST_TYPE_CLASS | LIBUSB_RECIPIENT_DEVICE,
190 		    0,
191 		    0,
192 		    0,
193 		    (uint8_t *)cmd_buf,
194 		    IWMBT_HCI_CMD_SIZE(cmd_buf),
195 		    IWMBT_HCI_CMD_TIMEOUT);
196 
197 		if (ret < 0) {
198 			iwmbt_err("libusb_control_transfer() failed: err=%s",
199 			    libusb_strerror(ret));
200 			return (-1);
201 		}
202 
203 		/*
204 		 * Every command has its associated event: data must match
205 		 * what is recorded in the firmware file. Perform that check
206 		 * now.
207 		 */
208 
209 		while (fw_job.len > 0 && fw_job.buf[0] == 0x02) {
210 			/* Is this the end of the file? */
211 			if (fw_job.len < 3) {
212 				iwmbt_err("Invalid firmware, unexpected EOF in"
213 				    "event header. remains=%d", fw_job.len);
214 				return (-1);
215 			}
216 
217 			/* Advance by one. */
218 			fw_job.buf++;
219 			fw_job.len--;
220 
221 			/* Load in the HCI event. */
222 			evt_code = fw_job.buf[0];
223 			evt_length = fw_job.buf[1];
224 
225 			/* Advance by two. */
226 			fw_job.buf += 2;
227 			fw_job.len -= 2;
228 
229 			/* Prepare HCI event buffer. */
230 			memset(evt_buf, 0, IWMBT_HCI_MAX_EVENT_SIZE);
231 
232 			iwmbt_debug("event=%04x, len=%02x",
233 					evt_code, evt_length);
234 
235 			if (fw_job.len < evt_length) {
236 				iwmbt_err("Invalid firmware, unexpected EOF in"
237 				    " event data. len=%d, remains=%d",
238 				    evt_length, fw_job.len);
239 				return (-1);
240 			}
241 
242 			ret = libusb_interrupt_transfer(hdl,
243 			    IWMBT_INTERRUPT_ENDPOINT_ADDR,
244 			    evt_buf,
245 			    IWMBT_HCI_MAX_EVENT_SIZE,
246 			    &transferred,
247 			    IWMBT_HCI_CMD_TIMEOUT);
248 
249 			if (ret < 0) {
250 				iwmbt_err("libusb_interrupt_transfer() failed:"
251 				    " err=%s", libusb_strerror(ret));
252 				return (-1);
253 			}
254 
255 			if ((int)evt_length + 2 != transferred ||
256 			    memcmp(evt_buf + 2, fw_job.buf, evt_length) != 0) {
257 				iwmbt_err("event does not match firmware");
258 				return (-1);
259 			}
260 
261 			/* Advance by data length. */
262 			fw_job.buf += evt_length;
263 			fw_job.len -= evt_length;
264 		}
265 	}
266 
267 	return (activate_patch);
268 }
269 
270 int
271 iwmbt_load_fwfile(struct libusb_device_handle *hdl,
272     const struct iwmbt_firmware *fw, uint32_t *boot_param)
273 {
274 	int ready = 0, sent = 0;
275 	int ret, transferred;
276 	struct iwmbt_hci_cmd *cmd;
277 	struct iwmbt_hci_event *event;
278 	uint8_t buf[IWMBT_HCI_MAX_EVENT_SIZE];
279 
280 #define	IWMBT_SEND_FRAGMENT(fragment_type, size, msg)	do {		\
281 	iwmbt_debug("transferring %d bytes, offset %d", size, sent);	\
282 									\
283 	ret = iwmbt_send_fragment(hdl,					\
284 	    fragment_type,						\
285 	    fw->buf + sent,						\
286 	    XMIN(size, fw->len - sent),					\
287 	    IWMBT_HCI_CMD_TIMEOUT);					\
288 									\
289 	if (ret < 0) {							\
290 		iwmbt_debug("Failed to send "msg": code=%d", ret);	\
291 		return (-1);						\
292 	}								\
293 	sent += size;							\
294 } while (0)
295 
296 	if (fw->len < 644) {
297 		iwmbt_err("Invalid size of firmware file (%d)", fw->len);
298 		return (-1);
299 	}
300 
301 	iwmbt_debug("file=%s, size=%d", fw->fwname, fw->len);
302 
303 	IWMBT_SEND_FRAGMENT(0x00, 0x80, "CCS segment");
304 	IWMBT_SEND_FRAGMENT(0x03, 0x80, "public key / part 1");
305 	IWMBT_SEND_FRAGMENT(0x03, 0x80, "public key / part 2");
306 
307 	/* skip 4 bytes */
308 	sent += 4;
309 
310 	IWMBT_SEND_FRAGMENT(0x02, 0x80, "signature / part 1");
311 	IWMBT_SEND_FRAGMENT(0x02, 0x80, "signature / part 2");
312 
313 	/*
314 	 * Send firmware chunks. Chunk len must be 4 byte aligned.
315 	 * multiple commands can be combined
316 	 */
317 	while (fw->len - sent - ready >= (int) sizeof(struct iwmbt_hci_cmd)) {
318 		cmd = (struct iwmbt_hci_cmd *)(fw->buf + sent + ready);
319 		/* Parse firmware for Intel Reset HCI command parameter */
320 		if (cmd->opcode == htole16(0xfc0e)) {
321 			*boot_param = le32dec(cmd->data);
322 			iwmbt_debug("boot_param=0x%08x", *boot_param);
323 		}
324 		ready += IWMBT_HCI_CMD_SIZE(cmd);
325 		while (ready >= 0xFC) {
326 			IWMBT_SEND_FRAGMENT(0x01, 0xFC, "firmware chunk");
327 			ready -= 0xFC;
328 		}
329 		if (ready > 0 && ready % 4 == 0) {
330 			IWMBT_SEND_FRAGMENT(0x01, ready, "firmware chunk");
331 			ready = 0;
332 		}
333 	}
334 
335 	/* Wait for firmware download completion event */
336 	ret = libusb_interrupt_transfer(hdl,
337 	    IWMBT_INTERRUPT_ENDPOINT_ADDR,
338 	    buf,
339 	    sizeof(buf),
340 	    &transferred,
341 	    IWMBT_LOADCMPL_TIMEOUT);
342 
343 	if (ret < 0 || transferred < (int)sizeof(struct iwmbt_hci_event) + 1) {
344 		iwmbt_err("libusb_interrupt_transfer() failed: "
345 		    "err=%s, size=%d",
346 		    libusb_strerror(ret),
347 		    transferred);
348 		return (-1);
349 	}
350 
351 	/* Expect Vendor Specific Event 0x06 */
352 	event = (struct iwmbt_hci_event *)buf;
353 	if (event->header.event != 0xFF || event->data[0] != 0x06) {
354 		iwmbt_err("firmware download completion event missed");
355 		return (-1);
356 	}
357 
358 	return (0);
359 }
360 
361 int
362 iwmbt_enter_manufacturer(struct libusb_device_handle *hdl)
363 {
364 	int ret, transferred;
365 	static struct iwmbt_hci_cmd cmd = {
366 		.opcode = htole16(0xfc11),
367 		.length = 2,
368 		.data = { 0x01, 0x00 },
369 	};
370 	uint8_t buf[IWMBT_HCI_MAX_EVENT_SIZE];
371 
372 	ret = iwmbt_hci_command(hdl,
373 	    &cmd,
374 	    buf,
375 	    sizeof(buf),
376 	    &transferred,
377 	    IWMBT_HCI_CMD_TIMEOUT);
378 
379 	if (ret < 0) {
380 		 iwmbt_debug("Can't enter manufacturer mode: code=%d, size=%d",
381 		     ret,
382 		     transferred);
383 		 return (-1);
384 	}
385 
386 	return (0);
387 }
388 
389 int
390 iwmbt_exit_manufacturer(struct libusb_device_handle *hdl, int mode)
391 {
392 	int ret, transferred;
393 	static struct iwmbt_hci_cmd cmd = {
394 		.opcode = htole16(0xfc11),
395 		.length = 2,
396 		.data = { 0x00, 0x00 },
397 	};
398 	uint8_t buf[IWMBT_HCI_MAX_EVENT_SIZE];
399 
400 	/*
401 	 * The mode sets the type of reset we want to perform:
402 	 * 0x00: simply exit manufacturer mode without a reset.
403 	 * 0x01: exit manufacturer mode with a reset and patches disabled
404 	 * 0x02: exit manufacturer mode with a reset and patches enabled
405 	 */
406 	if (mode > 2) {
407 		iwmbt_debug("iwmbt_exit_manufacturer(): unknown mode (%d)",
408 				mode);
409 	}
410 	cmd.data[1] = mode;
411 
412 	ret = iwmbt_hci_command(hdl,
413 	    &cmd,
414 	    buf,
415 	    sizeof(buf),
416 	    &transferred,
417 	    IWMBT_HCI_CMD_TIMEOUT);
418 
419 	if (ret < 0) {
420 		 iwmbt_debug("Can't exit manufacturer mode: code=%d, size=%d",
421 		     ret,
422 		     transferred);
423 		 return (-1);
424 	}
425 
426 	return (0);
427 }
428 
429 int
430 iwmbt_get_version(struct libusb_device_handle *hdl,
431     struct iwmbt_version *version)
432 {
433 	int ret, transferred;
434 	struct iwmbt_hci_event_cmd_compl*event;
435 	struct iwmbt_hci_cmd cmd = {
436 		.opcode = htole16(0xfc05),
437 		.length = 0,
438 	};
439 	uint8_t buf[IWMBT_HCI_EVT_COMPL_SIZE(struct iwmbt_version)];
440 
441 	memset(buf, 0, sizeof(buf));
442 
443 	ret = iwmbt_hci_command(hdl,
444 	    &cmd,
445 	    buf,
446 	    sizeof(buf),
447 	    &transferred,
448 	    IWMBT_HCI_CMD_TIMEOUT);
449 
450 	if (ret < 0 || transferred != sizeof(buf)) {
451 		 iwmbt_debug("Can't get version: : code=%d, size=%d",
452 		     ret,
453 		     transferred);
454 		 return (-1);
455 	}
456 
457 	event = (struct iwmbt_hci_event_cmd_compl *)buf;
458 	memcpy(version, event->data, sizeof(struct iwmbt_version));
459 
460 	return (0);
461 }
462 
463 int
464 iwmbt_get_boot_params(struct libusb_device_handle *hdl,
465     struct iwmbt_boot_params *params)
466 {
467 	int ret, transferred = 0;
468 	struct iwmbt_hci_event_cmd_compl *event;
469 	struct iwmbt_hci_cmd cmd = {
470 		.opcode = htole16(0xfc0d),
471 		.length = 0,
472 	};
473 	uint8_t buf[IWMBT_HCI_EVT_COMPL_SIZE(struct iwmbt_boot_params)];
474 
475 	memset(buf, 0, sizeof(buf));
476 
477 	ret = iwmbt_hci_command(hdl,
478 	    &cmd,
479 	    buf,
480 	    sizeof(buf),
481 	    &transferred,
482 	    IWMBT_HCI_CMD_TIMEOUT);
483 
484 	if (ret < 0 || transferred != sizeof(buf)) {
485 		 iwmbt_debug("Can't get boot params: code=%d, size=%d",
486 		     ret,
487 		     transferred);
488 		 return (-1);
489 	}
490 
491 	event = (struct iwmbt_hci_event_cmd_compl *)buf;
492 	memcpy(params, event->data, sizeof(struct iwmbt_boot_params));
493 
494 	return (0);
495 }
496 
497 int
498 iwmbt_intel_reset(struct libusb_device_handle *hdl, uint32_t boot_param)
499 {
500 	int ret, transferred = 0;
501 	struct iwmbt_hci_event *event;
502 	static struct iwmbt_hci_cmd cmd = {
503 		.opcode = htole16(0xfc01),
504 		.length = 8,
505 		.data = { 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00 },
506 	};
507 	uint8_t buf[IWMBT_HCI_MAX_EVENT_SIZE];
508 
509 	le32enc(cmd.data + 4, boot_param);
510 	memset(buf, 0, sizeof(buf));
511 
512 	ret = iwmbt_hci_command(hdl,
513 	    &cmd,
514 	    buf,
515 	    sizeof(buf),
516 	    &transferred,
517 	    IWMBT_HCI_CMD_TIMEOUT);
518 
519 	if (ret < 0 || transferred < (int)sizeof(struct iwmbt_hci_event) + 1) {
520 		 iwmbt_debug("Intel Reset command failed: code=%d, size=%d",
521 		    ret,
522 		    transferred);
523 		 return (ret);
524 	}
525 
526 	/* expect Vendor Specific Event 0x02 */
527 	event = (struct iwmbt_hci_event *)buf;
528 	if (event->header.event != 0xFF || event->data[0] != 0x02) {
529 		iwmbt_err("Intel Reset completion event missed");
530 		return (-1);
531 	}
532 
533 	return (0);
534 }
535 
536 int
537 iwmbt_load_ddc(struct libusb_device_handle *hdl,
538     const struct iwmbt_firmware *ddc)
539 {
540 	int size, sent = 0;
541 	int ret, transferred;
542 	uint8_t buf[IWMBT_HCI_MAX_CMD_SIZE];
543 	struct iwmbt_hci_cmd *cmd = (struct iwmbt_hci_cmd *)buf;
544 
545 	size = ddc->len;
546 
547 	iwmbt_debug("file=%s, size=%d", ddc->fwname, size);
548 
549 	while (size > 0) {
550 
551 		memset(buf, 0, sizeof(buf));
552 		cmd->opcode = htole16(0xfc8b);
553 		cmd->length = ddc->buf[sent] + 1;
554 		memcpy(cmd->data, ddc->buf + sent, XMIN(ddc->buf[sent], size));
555 
556 		iwmbt_debug("transferring %d bytes, offset %d",
557 		    cmd->length,
558 		    sent);
559 
560 		size -= cmd->length;
561 		sent += cmd->length;
562 
563 		ret = iwmbt_hci_command(hdl,
564 		    cmd,
565 		    buf,
566 		    sizeof(buf),
567 		    &transferred,
568 		    IWMBT_HCI_CMD_TIMEOUT);
569 
570 		if (ret < 0) {
571 			 iwmbt_debug("Intel Write DDC failed: code=%d", ret);
572 			 return (-1);
573 		}
574 	}
575 
576 	return (0);
577 }
578 
579 int
580 iwmbt_set_event_mask(struct libusb_device_handle *hdl)
581 {
582 	int ret, transferred = 0;
583 	static struct iwmbt_hci_cmd cmd = {
584 		.opcode = htole16(0xfc52),
585 		.length = 8,
586 		.data = { 0x87, 0x0c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 },
587 	};
588 	uint8_t buf[IWMBT_HCI_MAX_EVENT_SIZE];
589 
590 	ret = iwmbt_hci_command(hdl,
591 	    &cmd,
592 	    buf,
593 	    sizeof(buf),
594 	    &transferred,
595 	    IWMBT_HCI_CMD_TIMEOUT);
596 
597 	if (ret < 0)
598 		 iwmbt_debug("Intel Set Event Mask failed: code=%d", ret);
599 
600 	return (ret);
601 }
602