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