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