• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // SPDX-License-Identifier: GPL-2.0-only
2 /*
3  * Intel Wireless UWB Link 1480
4  * Main driver
5  *
6  * Copyright (C) 2005-2006 Intel Corporation
7  * Inaky Perez-Gonzalez <inaky.perez-gonzalez@intel.com>
8  *
9  * Common code for firmware upload used by the USB and PCI version;
10  * i1480_fw_upload() takes a device descriptor and uses the function
11  * pointers it provides to upload firmware and prepare the PHY.
12  *
13  * As well, provides common functions used by the rest of the code.
14  */
15 #include "i1480-dfu.h"
16 #include <linux/errno.h>
17 #include <linux/delay.h>
18 #include <linux/pci.h>
19 #include <linux/device.h>
20 #include <linux/random.h>
21 #include <linux/export.h>
22 #include "../../uwb.h"
23 
24 /*
25  * i1480_rceb_check - Check RCEB for expected field values
26  * @i1480: pointer to device for which RCEB is being checked
27  * @rceb: RCEB being checked
28  * @cmd: which command the RCEB is related to
29  * @context: expected context
30  * @expected_type: expected event type
31  * @expected_event: expected event
32  *
33  * If @cmd is NULL, do not print error messages, but still return an error
34  * code.
35  *
36  * Return 0 if @rceb matches the expected values, -EINVAL otherwise.
37  */
i1480_rceb_check(const struct i1480 * i1480,const struct uwb_rceb * rceb,const char * cmd,u8 context,u8 expected_type,unsigned expected_event)38 int i1480_rceb_check(const struct i1480 *i1480, const struct uwb_rceb *rceb,
39 		     const char *cmd, u8 context, u8 expected_type,
40 		     unsigned expected_event)
41 {
42 	int result = 0;
43 	struct device *dev = i1480->dev;
44 	if (rceb->bEventContext != context) {
45 		if (cmd)
46 			dev_err(dev, "%s: unexpected context id 0x%02x "
47 				"(expected 0x%02x)\n", cmd,
48 				rceb->bEventContext, context);
49 		result = -EINVAL;
50 	}
51 	if (rceb->bEventType != expected_type) {
52 		if (cmd)
53 			dev_err(dev, "%s: unexpected event type 0x%02x "
54 				"(expected 0x%02x)\n", cmd,
55 				rceb->bEventType, expected_type);
56 		result = -EINVAL;
57 	}
58 	if (le16_to_cpu(rceb->wEvent) != expected_event) {
59 		if (cmd)
60 			dev_err(dev, "%s: unexpected event 0x%04x "
61 				"(expected 0x%04x)\n", cmd,
62 				le16_to_cpu(rceb->wEvent), expected_event);
63 		result = -EINVAL;
64 	}
65 	return result;
66 }
67 EXPORT_SYMBOL_GPL(i1480_rceb_check);
68 
69 
70 /*
71  * Execute a Radio Control Command
72  *
73  * Command data has to be in i1480->cmd_buf.
74  *
75  * @returns size of the reply data filled in i1480->evt_buf or < 0 errno
76  *          code on error.
77  */
i1480_cmd(struct i1480 * i1480,const char * cmd_name,size_t cmd_size,size_t reply_size)78 ssize_t i1480_cmd(struct i1480 *i1480, const char *cmd_name, size_t cmd_size,
79 		  size_t reply_size)
80 {
81 	ssize_t result;
82 	struct uwb_rceb *reply = i1480->evt_buf;
83 	struct uwb_rccb *cmd = i1480->cmd_buf;
84 	u16 expected_event = reply->wEvent;
85 	u8 expected_type = reply->bEventType;
86 	u8 context;
87 
88 	init_completion(&i1480->evt_complete);
89 	i1480->evt_result = -EINPROGRESS;
90 	do {
91 		get_random_bytes(&context, 1);
92 	} while (context == 0x00 || context == 0xff);
93 	cmd->bCommandContext = context;
94 	result = i1480->cmd(i1480, cmd_name, cmd_size);
95 	if (result < 0)
96 		goto error;
97 	/* wait for the callback to report a event was received */
98 	result = wait_for_completion_interruptible_timeout(
99 		&i1480->evt_complete, HZ);
100 	if (result == 0) {
101 		result = -ETIMEDOUT;
102 		goto error;
103 	}
104 	if (result < 0)
105 		goto error;
106 	result = i1480->evt_result;
107 	if (result < 0) {
108 		dev_err(i1480->dev, "%s: command reply reception failed: %zd\n",
109 			cmd_name, result);
110 		goto error;
111 	}
112 	/*
113 	 * Firmware versions >= 1.4.12224 for IOGear GUWA100U generate a
114 	 * spurious notification after firmware is downloaded. So check whether
115 	 * the receibed RCEB is such notification before assuming that the
116 	 * command has failed.
117 	 */
118 	if (i1480_rceb_check(i1480, i1480->evt_buf, NULL,
119 			     0, 0xfd, 0x0022) == 0) {
120 		/* Now wait for the actual RCEB for this command. */
121 		result = i1480->wait_init_done(i1480);
122 		if (result < 0)
123 			goto error;
124 		result = i1480->evt_result;
125 	}
126 	if (result != reply_size) {
127 		dev_err(i1480->dev, "%s returned only %zu bytes, %zu expected\n",
128 			cmd_name, result, reply_size);
129 		result = -EINVAL;
130 		goto error;
131 	}
132 	/* Verify we got the right event in response */
133 	result = i1480_rceb_check(i1480, i1480->evt_buf, cmd_name, context,
134 				  expected_type, expected_event);
135 error:
136 	return result;
137 }
138 EXPORT_SYMBOL_GPL(i1480_cmd);
139 
140 
141 static
i1480_print_state(struct i1480 * i1480)142 int i1480_print_state(struct i1480 *i1480)
143 {
144 	int result;
145 	u32 *buf = (u32 *) i1480->cmd_buf;
146 
147 	result = i1480->read(i1480, 0x80080000, 2 * sizeof(*buf));
148 	if (result < 0) {
149 		dev_err(i1480->dev, "cannot read U & L states: %d\n", result);
150 		goto error;
151 	}
152 	dev_info(i1480->dev, "state U 0x%08x, L 0x%08x\n", buf[0], buf[1]);
153 error:
154 	return result;
155 }
156 
157 
158 /*
159  * PCI probe, firmware uploader
160  *
161  * _mac_fw_upload() will call rc_setup(), which needs an rc_release().
162  */
i1480_fw_upload(struct i1480 * i1480)163 int i1480_fw_upload(struct i1480 *i1480)
164 {
165 	int result;
166 
167 	result = i1480_pre_fw_upload(i1480);	/* PHY pre fw */
168 	if (result < 0 && result != -ENOENT) {
169 		i1480_print_state(i1480);
170 		goto error;
171 	}
172 	result = i1480_mac_fw_upload(i1480);	/* MAC fw */
173 	if (result < 0) {
174 		if (result == -ENOENT)
175 			dev_err(i1480->dev, "Cannot locate MAC FW file '%s'\n",
176 				i1480->mac_fw_name);
177 		else
178 			i1480_print_state(i1480);
179 		goto error;
180 	}
181 	result = i1480_phy_fw_upload(i1480);	/* PHY fw */
182 	if (result < 0 && result != -ENOENT) {
183 		i1480_print_state(i1480);
184 		goto error_rc_release;
185 	}
186 	/*
187 	 * FIXME: find some reliable way to check whether firmware is running
188 	 * properly. Maybe use some standard request that has no side effects?
189 	 */
190 	dev_info(i1480->dev, "firmware uploaded successfully\n");
191 error_rc_release:
192 	if (i1480->rc_release)
193 		i1480->rc_release(i1480);
194 	result = 0;
195 error:
196 	return result;
197 }
198 EXPORT_SYMBOL_GPL(i1480_fw_upload);
199