1 /*
2 * Copyright (c) 2009-2015 Solarflare Communications Inc.
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions are met:
7 *
8 * 1. Redistributions of source code must retain the above copyright notice,
9 * this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright notice,
11 * this list of conditions and the following disclaimer in the documentation
12 * and/or other materials provided with the distribution.
13 *
14 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
15 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
16 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
17 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
18 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
19 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
20 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
21 * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
22 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
23 * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
24 * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
25 *
26 * The views and conclusions contained in the software and documentation are
27 * those of the authors and should not be interpreted as representing official
28 * policies, either expressed or implied, of the FreeBSD Project.
29 */
30
31 #include "efx.h"
32 #include "efx_impl.h"
33
34 #if EFSYS_OPT_WOL
35
36 __checkReturn efx_rc_t
efx_wol_init(__in efx_nic_t * enp)37 efx_wol_init(
38 __in efx_nic_t *enp)
39 {
40 efx_nic_cfg_t *encp = &(enp->en_nic_cfg);
41 efx_rc_t rc;
42
43 EFSYS_ASSERT3U(enp->en_magic, ==, EFX_NIC_MAGIC);
44 EFSYS_ASSERT3U(enp->en_mod_flags, &, EFX_MOD_PROBE);
45 EFSYS_ASSERT(!(enp->en_mod_flags & EFX_MOD_WOL));
46
47 if (~(encp->enc_features) & EFX_FEATURE_WOL) {
48 rc = ENOTSUP;
49 goto fail1;
50 }
51
52 /* Current implementation is Siena specific */
53 EFSYS_ASSERT3U(enp->en_family, ==, EFX_FAMILY_SIENA);
54
55 enp->en_mod_flags |= EFX_MOD_WOL;
56
57 return (0);
58
59 fail1:
60 EFSYS_PROBE1(fail1, efx_rc_t, rc);
61
62 return (rc);
63 }
64
65 __checkReturn efx_rc_t
efx_wol_filter_clear(__in efx_nic_t * enp)66 efx_wol_filter_clear(
67 __in efx_nic_t *enp)
68 {
69 efx_mcdi_req_t req;
70 uint8_t payload[MAX(MC_CMD_WOL_FILTER_RESET_IN_LEN,
71 MC_CMD_WOL_FILTER_RESET_OUT_LEN)];
72 efx_rc_t rc;
73
74 EFSYS_ASSERT3U(enp->en_magic, ==, EFX_NIC_MAGIC);
75 EFSYS_ASSERT3U(enp->en_mod_flags, &, EFX_MOD_WOL);
76
77 (void) memset(payload, 0, sizeof (payload));
78 req.emr_cmd = MC_CMD_WOL_FILTER_RESET;
79 req.emr_in_buf = payload;
80 req.emr_in_length = MC_CMD_WOL_FILTER_RESET_IN_LEN;
81 req.emr_out_buf = payload;
82 req.emr_out_length = MC_CMD_WOL_FILTER_RESET_OUT_LEN;
83
84 MCDI_IN_SET_DWORD(req, WOL_FILTER_RESET_IN_MASK,
85 MC_CMD_WOL_FILTER_RESET_IN_WAKE_FILTERS |
86 MC_CMD_WOL_FILTER_RESET_IN_LIGHTSOUT_OFFLOADS);
87
88 efx_mcdi_execute(enp, &req);
89
90 if (req.emr_rc != 0) {
91 rc = req.emr_rc;
92 goto fail1;
93 }
94
95 return (0);
96
97 fail1:
98 EFSYS_PROBE1(fail1, efx_rc_t, rc);
99
100 return (rc);
101 }
102
103 __checkReturn efx_rc_t
efx_wol_filter_add(__in efx_nic_t * enp,__in efx_wol_type_t type,__in efx_wol_param_t * paramp,__out uint32_t * filter_idp)104 efx_wol_filter_add(
105 __in efx_nic_t *enp,
106 __in efx_wol_type_t type,
107 __in efx_wol_param_t *paramp,
108 __out uint32_t *filter_idp)
109 {
110 efx_mcdi_req_t req;
111 uint8_t payload[MAX(MC_CMD_WOL_FILTER_SET_IN_LEN,
112 MC_CMD_WOL_FILTER_SET_OUT_LEN)];
113 efx_byte_t link_mask;
114 efx_rc_t rc;
115
116 EFSYS_ASSERT3U(enp->en_magic, ==, EFX_NIC_MAGIC);
117 EFSYS_ASSERT3U(enp->en_mod_flags, &, EFX_MOD_WOL);
118
119 (void) memset(payload, 0, sizeof (payload));
120 req.emr_cmd = MC_CMD_WOL_FILTER_SET;
121 req.emr_in_buf = payload;
122 req.emr_in_length = MC_CMD_WOL_FILTER_SET_IN_LEN;
123 req.emr_out_buf = payload;
124 req.emr_out_length = MC_CMD_WOL_FILTER_SET_OUT_LEN;
125
126 switch (type) {
127 case EFX_WOL_TYPE_MAGIC:
128 MCDI_IN_SET_DWORD(req, WOL_FILTER_SET_IN_FILTER_MODE,
129 MC_CMD_FILTER_MODE_SIMPLE);
130 MCDI_IN_SET_DWORD(req, WOL_FILTER_SET_IN_WOL_TYPE,
131 MC_CMD_WOL_TYPE_MAGIC);
132 EFX_MAC_ADDR_COPY(
133 MCDI_IN2(req, uint8_t, WOL_FILTER_SET_IN_MAGIC_MAC),
134 paramp->ewp_magic.mac_addr);
135 break;
136
137 case EFX_WOL_TYPE_BITMAP: {
138 uint32_t swapped = 0;
139 efx_dword_t *dwordp;
140 unsigned int pos, bit;
141
142 MCDI_IN_SET_DWORD(req, WOL_FILTER_SET_IN_FILTER_MODE,
143 MC_CMD_FILTER_MODE_SIMPLE);
144 MCDI_IN_SET_DWORD(req, WOL_FILTER_SET_IN_WOL_TYPE,
145 MC_CMD_WOL_TYPE_BITMAP);
146
147 /*
148 * MC bitmask is supposed to be bit swapped
149 * amongst 32 bit words(!)
150 */
151
152 dwordp = MCDI_IN2(req, efx_dword_t,
153 WOL_FILTER_SET_IN_BITMAP_MASK);
154
155 EFSYS_ASSERT3U(EFX_WOL_BITMAP_MASK_SIZE % 4, ==, 0);
156
157 for (pos = 0; pos < EFX_WOL_BITMAP_MASK_SIZE; ++pos) {
158 uint8_t native = paramp->ewp_bitmap.mask[pos];
159
160 for (bit = 0; bit < 8; ++bit) {
161 swapped <<= 1;
162 swapped |= (native & 0x1);
163 native >>= 1;
164 }
165
166 if ((pos & 3) == 3) {
167 EFX_POPULATE_DWORD_1(dwordp[pos >> 2],
168 EFX_DWORD_0, swapped);
169 swapped = 0;
170 }
171 }
172
173 (void) memcpy(MCDI_IN2(req, uint8_t,
174 WOL_FILTER_SET_IN_BITMAP_BITMAP),
175 paramp->ewp_bitmap.value,
176 sizeof (paramp->ewp_bitmap.value));
177
178 EFSYS_ASSERT3U(paramp->ewp_bitmap.value_len, <=,
179 sizeof (paramp->ewp_bitmap.value));
180 MCDI_IN_SET_DWORD(req, WOL_FILTER_SET_IN_BITMAP_LEN,
181 paramp->ewp_bitmap.value_len);
182 }
183 break;
184
185 case EFX_WOL_TYPE_LINK:
186 MCDI_IN_SET_DWORD(req, WOL_FILTER_SET_IN_FILTER_MODE,
187 MC_CMD_FILTER_MODE_SIMPLE);
188 MCDI_IN_SET_DWORD(req, WOL_FILTER_SET_IN_WOL_TYPE,
189 MC_CMD_WOL_TYPE_LINK);
190
191 EFX_ZERO_BYTE(link_mask);
192 EFX_SET_BYTE_FIELD(link_mask, MC_CMD_WOL_FILTER_SET_IN_LINK_UP,
193 1);
194 MCDI_IN_SET_BYTE(req, WOL_FILTER_SET_IN_LINK_MASK,
195 link_mask.eb_u8[0]);
196 break;
197
198 default:
199 EFSYS_ASSERT3U(type, !=, type);
200 }
201
202 efx_mcdi_execute(enp, &req);
203
204 if (req.emr_rc != 0) {
205 rc = req.emr_rc;
206 goto fail1;
207 }
208
209 if (req.emr_out_length_used < MC_CMD_WOL_FILTER_SET_OUT_LEN) {
210 rc = EMSGSIZE;
211 goto fail2;
212 }
213
214 *filter_idp = MCDI_OUT_DWORD(req, WOL_FILTER_SET_OUT_FILTER_ID);
215
216 return (0);
217
218 fail2:
219 EFSYS_PROBE(fail2);
220 fail1:
221 EFSYS_PROBE1(fail1, efx_rc_t, rc);
222
223 return (rc);
224 }
225
226 __checkReturn efx_rc_t
efx_wol_filter_remove(__in efx_nic_t * enp,__in uint32_t filter_id)227 efx_wol_filter_remove(
228 __in efx_nic_t *enp,
229 __in uint32_t filter_id)
230 {
231 efx_mcdi_req_t req;
232 uint8_t payload[MAX(MC_CMD_WOL_FILTER_REMOVE_IN_LEN,
233 MC_CMD_WOL_FILTER_REMOVE_OUT_LEN)];
234 efx_rc_t rc;
235
236 EFSYS_ASSERT3U(enp->en_magic, ==, EFX_NIC_MAGIC);
237 EFSYS_ASSERT3U(enp->en_mod_flags, &, EFX_MOD_WOL);
238
239 (void) memset(payload, 0, sizeof (payload));
240 req.emr_cmd = MC_CMD_WOL_FILTER_REMOVE;
241 req.emr_in_buf = payload;
242 req.emr_in_length = MC_CMD_WOL_FILTER_REMOVE_IN_LEN;
243 req.emr_out_buf = payload;
244 req.emr_out_length = MC_CMD_WOL_FILTER_REMOVE_OUT_LEN;
245
246 MCDI_IN_SET_DWORD(req, WOL_FILTER_REMOVE_IN_FILTER_ID, filter_id);
247
248 efx_mcdi_execute(enp, &req);
249
250 if (req.emr_rc != 0) {
251 rc = req.emr_rc;
252 goto fail1;
253 }
254
255 return (0);
256
257 fail1:
258 EFSYS_PROBE1(fail1, efx_rc_t, rc);
259
260 return (rc);
261 }
262
263
264 __checkReturn efx_rc_t
efx_lightsout_offload_add(__in efx_nic_t * enp,__in efx_lightsout_offload_type_t type,__in efx_lightsout_offload_param_t * paramp,__out uint32_t * filter_idp)265 efx_lightsout_offload_add(
266 __in efx_nic_t *enp,
267 __in efx_lightsout_offload_type_t type,
268 __in efx_lightsout_offload_param_t *paramp,
269 __out uint32_t *filter_idp)
270 {
271 efx_mcdi_req_t req;
272 uint8_t payload[MAX(MAX(MC_CMD_ADD_LIGHTSOUT_OFFLOAD_IN_ARP_LEN,
273 MC_CMD_ADD_LIGHTSOUT_OFFLOAD_IN_NS_LEN),
274 MC_CMD_ADD_LIGHTSOUT_OFFLOAD_OUT_LEN)];
275 efx_rc_t rc;
276
277 EFSYS_ASSERT3U(enp->en_magic, ==, EFX_NIC_MAGIC);
278 EFSYS_ASSERT3U(enp->en_mod_flags, &, EFX_MOD_WOL);
279
280 (void) memset(payload, 0, sizeof (payload));
281 req.emr_cmd = MC_CMD_ADD_LIGHTSOUT_OFFLOAD;
282 req.emr_in_buf = payload;
283 req.emr_in_length = sizeof (type);
284 req.emr_out_buf = payload;
285 req.emr_out_length = MC_CMD_ADD_LIGHTSOUT_OFFLOAD_OUT_LEN;
286
287 switch (type) {
288 case EFX_LIGHTSOUT_OFFLOAD_TYPE_ARP:
289 req.emr_in_length = MC_CMD_ADD_LIGHTSOUT_OFFLOAD_IN_ARP_LEN;
290
291 MCDI_IN_SET_DWORD(req, ADD_LIGHTSOUT_OFFLOAD_IN_PROTOCOL,
292 MC_CMD_LIGHTSOUT_OFFLOAD_PROTOCOL_ARP);
293 EFX_MAC_ADDR_COPY(MCDI_IN2(req, uint8_t,
294 ADD_LIGHTSOUT_OFFLOAD_IN_ARP_MAC),
295 paramp->elop_arp.mac_addr);
296 MCDI_IN_SET_DWORD(req, ADD_LIGHTSOUT_OFFLOAD_IN_ARP_IP,
297 paramp->elop_arp.ip);
298 break;
299 case EFX_LIGHTSOUT_OFFLOAD_TYPE_NS:
300 req.emr_in_length = MC_CMD_ADD_LIGHTSOUT_OFFLOAD_IN_NS_LEN;
301
302 MCDI_IN_SET_DWORD(req, ADD_LIGHTSOUT_OFFLOAD_IN_PROTOCOL,
303 MC_CMD_LIGHTSOUT_OFFLOAD_PROTOCOL_NS);
304 EFX_MAC_ADDR_COPY(MCDI_IN2(req, uint8_t,
305 ADD_LIGHTSOUT_OFFLOAD_IN_NS_MAC),
306 paramp->elop_ns.mac_addr);
307 (void) memcpy(MCDI_IN2(req, uint8_t,
308 ADD_LIGHTSOUT_OFFLOAD_IN_NS_SNIPV6),
309 paramp->elop_ns.solicited_node,
310 sizeof (paramp->elop_ns.solicited_node));
311 (void) memcpy(MCDI_IN2(req, uint8_t,
312 ADD_LIGHTSOUT_OFFLOAD_IN_NS_IPV6),
313 paramp->elop_ns.ip, sizeof (paramp->elop_ns.ip));
314 break;
315 default:
316 rc = EINVAL;
317 goto fail1;
318 }
319
320 efx_mcdi_execute(enp, &req);
321
322 if (req.emr_rc != 0) {
323 rc = req.emr_rc;
324 goto fail2;
325 }
326
327 if (req.emr_out_length_used < MC_CMD_ADD_LIGHTSOUT_OFFLOAD_OUT_LEN) {
328 rc = EMSGSIZE;
329 goto fail3;
330 }
331
332 *filter_idp = MCDI_OUT_DWORD(req, ADD_LIGHTSOUT_OFFLOAD_OUT_FILTER_ID);
333
334 return (0);
335
336 fail3:
337 EFSYS_PROBE(fail3);
338 fail2:
339 EFSYS_PROBE(fail2);
340 fail1:
341 EFSYS_PROBE1(fail1, efx_rc_t, rc);
342
343 return (rc);
344 }
345
346
347 __checkReturn efx_rc_t
efx_lightsout_offload_remove(__in efx_nic_t * enp,__in efx_lightsout_offload_type_t type,__in uint32_t filter_id)348 efx_lightsout_offload_remove(
349 __in efx_nic_t *enp,
350 __in efx_lightsout_offload_type_t type,
351 __in uint32_t filter_id)
352 {
353 efx_mcdi_req_t req;
354 uint8_t payload[MAX(MC_CMD_REMOVE_LIGHTSOUT_OFFLOAD_IN_LEN,
355 MC_CMD_REMOVE_LIGHTSOUT_OFFLOAD_OUT_LEN)];
356 efx_rc_t rc;
357
358 EFSYS_ASSERT3U(enp->en_magic, ==, EFX_NIC_MAGIC);
359 EFSYS_ASSERT3U(enp->en_mod_flags, &, EFX_MOD_WOL);
360
361 (void) memset(payload, 0, sizeof (payload));
362 req.emr_cmd = MC_CMD_REMOVE_LIGHTSOUT_OFFLOAD;
363 req.emr_in_buf = payload;
364 req.emr_in_length = MC_CMD_REMOVE_LIGHTSOUT_OFFLOAD_IN_LEN;
365 req.emr_out_buf = payload;
366 req.emr_out_length = MC_CMD_REMOVE_LIGHTSOUT_OFFLOAD_OUT_LEN;
367
368 switch (type) {
369 case EFX_LIGHTSOUT_OFFLOAD_TYPE_ARP:
370 MCDI_IN_SET_DWORD(req, REMOVE_LIGHTSOUT_OFFLOAD_IN_PROTOCOL,
371 MC_CMD_LIGHTSOUT_OFFLOAD_PROTOCOL_ARP);
372 break;
373 case EFX_LIGHTSOUT_OFFLOAD_TYPE_NS:
374 MCDI_IN_SET_DWORD(req, REMOVE_LIGHTSOUT_OFFLOAD_IN_PROTOCOL,
375 MC_CMD_LIGHTSOUT_OFFLOAD_PROTOCOL_NS);
376 break;
377 default:
378 rc = EINVAL;
379 goto fail1;
380 }
381
382 MCDI_IN_SET_DWORD(req, REMOVE_LIGHTSOUT_OFFLOAD_IN_FILTER_ID,
383 filter_id);
384
385 efx_mcdi_execute(enp, &req);
386
387 if (req.emr_rc != 0) {
388 rc = req.emr_rc;
389 goto fail2;
390 }
391
392 return (0);
393
394 fail2:
395 EFSYS_PROBE(fail2);
396 fail1:
397 EFSYS_PROBE1(fail1, efx_rc_t, rc);
398
399 return (rc);
400 }
401
402
403 void
efx_wol_fini(__in efx_nic_t * enp)404 efx_wol_fini(
405 __in efx_nic_t *enp)
406 {
407 EFSYS_ASSERT3U(enp->en_magic, ==, EFX_NIC_MAGIC);
408 EFSYS_ASSERT3U(enp->en_mod_flags, &, EFX_MOD_PROBE);
409 EFSYS_ASSERT3U(enp->en_mod_flags, &, EFX_MOD_WOL);
410
411 enp->en_mod_flags &= ~EFX_MOD_WOL;
412 }
413
414 #endif /* EFSYS_OPT_WOL */
415