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
50static int
51iwmbt_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
94static int
95iwmbt_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
129int
130iwmbt_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
220int
221iwmbt_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
254int
255iwmbt_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
288int
289iwmbt_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
327int
328iwmbt_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
370int
371iwmbt_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