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