]>
Commit | Line | Data |
---|---|---|
04672fe6 | 1 | // SPDX-License-Identifier: GPL-2.0-only |
1ba47da5 IPG |
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 | * | |
1ba47da5 IPG |
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/uwb.h> | |
21 | #include <linux/random.h> | |
475c0a6b | 22 | #include <linux/export.h> |
1ba47da5 | 23 | |
bce83697 | 24 | /* |
b5784f10 AL |
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 | */ | |
1ba47da5 | 38 | int i1480_rceb_check(const struct i1480 *i1480, const struct uwb_rceb *rceb, |
b5784f10 AL |
39 | const char *cmd, u8 context, u8 expected_type, |
40 | unsigned expected_event) | |
1ba47da5 IPG |
41 | { |
42 | int result = 0; | |
43 | struct device *dev = i1480->dev; | |
44 | if (rceb->bEventContext != context) { | |
b5784f10 AL |
45 | if (cmd) |
46 | dev_err(dev, "%s: unexpected context id 0x%02x " | |
47 | "(expected 0x%02x)\n", cmd, | |
48 | rceb->bEventContext, context); | |
1ba47da5 IPG |
49 | result = -EINVAL; |
50 | } | |
51 | if (rceb->bEventType != expected_type) { | |
b5784f10 AL |
52 | if (cmd) |
53 | dev_err(dev, "%s: unexpected event type 0x%02x " | |
54 | "(expected 0x%02x)\n", cmd, | |
55 | rceb->bEventType, expected_type); | |
1ba47da5 IPG |
56 | result = -EINVAL; |
57 | } | |
58 | if (le16_to_cpu(rceb->wEvent) != expected_event) { | |
b5784f10 AL |
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); | |
1ba47da5 IPG |
63 | result = -EINVAL; |
64 | } | |
65 | return result; | |
66 | } | |
67 | EXPORT_SYMBOL_GPL(i1480_rceb_check); | |
68 | ||
69 | ||
bce83697 | 70 | /* |
1ba47da5 IPG |
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 | */ | |
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 | ||
1ba47da5 IPG |
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 | } | |
b5784f10 AL |
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 | } | |
1ba47da5 IPG |
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: | |
1ba47da5 IPG |
136 | return result; |
137 | } | |
138 | EXPORT_SYMBOL_GPL(i1480_cmd); | |
139 | ||
140 | ||
1ba47da5 IPG |
141 | static |
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 | */ | |
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 | } | |
8c7e8cb8 AL |
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 | */ | |
1ba47da5 IPG |
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); |