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