1/*-
2 * Copyright (c) 2009-2016 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 <sys/cdefs.h>
32__FBSDID("$FreeBSD: releng/11.0/sys/dev/sfxge/common/efx_wol.c 300607 2016-05-24 12:16:57Z arybchik $");
33
34#include "efx.h"
35#include "efx_impl.h"
36
37#if EFSYS_OPT_WOL
38
39	__checkReturn	efx_rc_t
40efx_wol_init(
41	__in		efx_nic_t *enp)
42{
43	efx_nic_cfg_t *encp = &(enp->en_nic_cfg);
44	efx_rc_t rc;
45
46	EFSYS_ASSERT3U(enp->en_magic, ==, EFX_NIC_MAGIC);
47	EFSYS_ASSERT3U(enp->en_mod_flags, &, EFX_MOD_PROBE);
48	EFSYS_ASSERT(!(enp->en_mod_flags & EFX_MOD_WOL));
49
50	if (~(encp->enc_features) & EFX_FEATURE_WOL) {
51		rc = ENOTSUP;
52		goto fail1;
53	}
54
55	/* Current implementation is Siena specific */
56	EFSYS_ASSERT3U(enp->en_family, ==, EFX_FAMILY_SIENA);
57
58	enp->en_mod_flags |= EFX_MOD_WOL;
59
60	return (0);
61
62fail1:
63	EFSYS_PROBE1(fail1, efx_rc_t, rc);
64
65	return (rc);
66}
67
68	__checkReturn	efx_rc_t
69efx_wol_filter_clear(
70	__in		efx_nic_t *enp)
71{
72	efx_mcdi_req_t req;
73	uint8_t payload[MAX(MC_CMD_WOL_FILTER_RESET_IN_LEN,
74			    MC_CMD_WOL_FILTER_RESET_OUT_LEN)];
75	efx_rc_t rc;
76
77	EFSYS_ASSERT3U(enp->en_magic, ==, EFX_NIC_MAGIC);
78	EFSYS_ASSERT3U(enp->en_mod_flags, &, EFX_MOD_WOL);
79
80	(void) memset(payload, 0, sizeof (payload));
81	req.emr_cmd = MC_CMD_WOL_FILTER_RESET;
82	req.emr_in_buf = payload;
83	req.emr_in_length = MC_CMD_WOL_FILTER_RESET_IN_LEN;
84	req.emr_out_buf = payload;
85	req.emr_out_length = MC_CMD_WOL_FILTER_RESET_OUT_LEN;
86
87	MCDI_IN_SET_DWORD(req, WOL_FILTER_RESET_IN_MASK,
88			    MC_CMD_WOL_FILTER_RESET_IN_WAKE_FILTERS |
89			    MC_CMD_WOL_FILTER_RESET_IN_LIGHTSOUT_OFFLOADS);
90
91	efx_mcdi_execute(enp, &req);
92
93	if (req.emr_rc != 0) {
94		rc = req.emr_rc;
95		goto fail1;
96	}
97
98	return (0);
99
100fail1:
101	EFSYS_PROBE1(fail1, efx_rc_t, rc);
102
103	return (rc);
104}
105
106	__checkReturn	efx_rc_t
107efx_wol_filter_add(
108	__in		efx_nic_t *enp,
109	__in		efx_wol_type_t type,
110	__in		efx_wol_param_t *paramp,
111	__out		uint32_t *filter_idp)
112{
113	efx_mcdi_req_t req;
114	uint8_t payload[MAX(MC_CMD_WOL_FILTER_SET_IN_LEN,
115			    MC_CMD_WOL_FILTER_SET_OUT_LEN)];
116	efx_byte_t link_mask;
117	efx_rc_t rc;
118
119	EFSYS_ASSERT3U(enp->en_magic, ==, EFX_NIC_MAGIC);
120	EFSYS_ASSERT3U(enp->en_mod_flags, &, EFX_MOD_WOL);
121
122	(void) memset(payload, 0, sizeof (payload));
123	req.emr_cmd = MC_CMD_WOL_FILTER_SET;
124	req.emr_in_buf = payload;
125	req.emr_in_length = MC_CMD_WOL_FILTER_SET_IN_LEN;
126	req.emr_out_buf = payload;
127	req.emr_out_length = MC_CMD_WOL_FILTER_SET_OUT_LEN;
128
129	switch (type) {
130	case EFX_WOL_TYPE_MAGIC:
131		MCDI_IN_SET_DWORD(req, WOL_FILTER_SET_IN_FILTER_MODE,
132				    MC_CMD_FILTER_MODE_SIMPLE);
133		MCDI_IN_SET_DWORD(req, WOL_FILTER_SET_IN_WOL_TYPE,
134				    MC_CMD_WOL_TYPE_MAGIC);
135		EFX_MAC_ADDR_COPY(
136			MCDI_IN2(req, uint8_t, WOL_FILTER_SET_IN_MAGIC_MAC),
137			paramp->ewp_magic.mac_addr);
138		break;
139
140	case EFX_WOL_TYPE_BITMAP: {
141		uint32_t swapped = 0;
142		efx_dword_t *dwordp;
143		unsigned int pos, bit;
144
145		MCDI_IN_SET_DWORD(req, WOL_FILTER_SET_IN_FILTER_MODE,
146				    MC_CMD_FILTER_MODE_SIMPLE);
147		MCDI_IN_SET_DWORD(req, WOL_FILTER_SET_IN_WOL_TYPE,
148				    MC_CMD_WOL_TYPE_BITMAP);
149
150		/*
151		 * MC bitmask is supposed to be bit swapped
152		 * amongst 32 bit words(!)
153		 */
154
155		dwordp = MCDI_IN2(req, efx_dword_t,
156				    WOL_FILTER_SET_IN_BITMAP_MASK);
157
158		EFSYS_ASSERT3U(EFX_WOL_BITMAP_MASK_SIZE % 4, ==, 0);
159
160		for (pos = 0; pos < EFX_WOL_BITMAP_MASK_SIZE; ++pos) {
161			uint8_t native = paramp->ewp_bitmap.mask[pos];
162
163			for (bit = 0; bit < 8; ++bit) {
164				swapped <<= 1;
165				swapped |= (native & 0x1);
166				native >>= 1;
167			}
168
169			if ((pos & 3) == 3) {
170				EFX_POPULATE_DWORD_1(dwordp[pos >> 2],
171				    EFX_DWORD_0, swapped);
172				swapped = 0;
173			}
174		}
175
176		memcpy(MCDI_IN2(req, uint8_t, WOL_FILTER_SET_IN_BITMAP_BITMAP),
177		    paramp->ewp_bitmap.value,
178		    sizeof (paramp->ewp_bitmap.value));
179
180		EFSYS_ASSERT3U(paramp->ewp_bitmap.value_len, <=,
181				    sizeof (paramp->ewp_bitmap.value));
182		MCDI_IN_SET_DWORD(req, WOL_FILTER_SET_IN_BITMAP_LEN,
183				    paramp->ewp_bitmap.value_len);
184		}
185		break;
186
187	case EFX_WOL_TYPE_LINK:
188		MCDI_IN_SET_DWORD(req, WOL_FILTER_SET_IN_FILTER_MODE,
189				    MC_CMD_FILTER_MODE_SIMPLE);
190		MCDI_IN_SET_DWORD(req, WOL_FILTER_SET_IN_WOL_TYPE,
191				    MC_CMD_WOL_TYPE_LINK);
192
193		EFX_ZERO_BYTE(link_mask);
194		EFX_SET_BYTE_FIELD(link_mask, MC_CMD_WOL_FILTER_SET_IN_LINK_UP,
195		    1);
196		MCDI_IN_SET_BYTE(req, WOL_FILTER_SET_IN_LINK_MASK,
197		    link_mask.eb_u8[0]);
198		break;
199
200	default:
201		EFSYS_ASSERT3U(type, !=, type);
202	}
203
204	efx_mcdi_execute(enp, &req);
205
206	if (req.emr_rc != 0) {
207		rc = req.emr_rc;
208		goto fail1;
209	}
210
211	if (req.emr_out_length_used < MC_CMD_WOL_FILTER_SET_OUT_LEN) {
212		rc = EMSGSIZE;
213		goto fail2;
214	}
215
216	*filter_idp = MCDI_OUT_DWORD(req, WOL_FILTER_SET_OUT_FILTER_ID);
217
218	return (0);
219
220fail2:
221	EFSYS_PROBE(fail2);
222fail1:
223	EFSYS_PROBE1(fail1, efx_rc_t, rc);
224
225	return (rc);
226}
227
228	__checkReturn	efx_rc_t
229efx_wol_filter_remove(
230	__in		efx_nic_t *enp,
231	__in		uint32_t filter_id)
232{
233	efx_mcdi_req_t req;
234	uint8_t payload[MAX(MC_CMD_WOL_FILTER_REMOVE_IN_LEN,
235			    MC_CMD_WOL_FILTER_REMOVE_OUT_LEN)];
236	efx_rc_t rc;
237
238	EFSYS_ASSERT3U(enp->en_magic, ==, EFX_NIC_MAGIC);
239	EFSYS_ASSERT3U(enp->en_mod_flags, &, EFX_MOD_WOL);
240
241	(void) memset(payload, 0, sizeof (payload));
242	req.emr_cmd = MC_CMD_WOL_FILTER_REMOVE;
243	req.emr_in_buf = payload;
244	req.emr_in_length = MC_CMD_WOL_FILTER_REMOVE_IN_LEN;
245	req.emr_out_buf = payload;
246	req.emr_out_length = MC_CMD_WOL_FILTER_REMOVE_OUT_LEN;
247
248	MCDI_IN_SET_DWORD(req, WOL_FILTER_REMOVE_IN_FILTER_ID, filter_id);
249
250	efx_mcdi_execute(enp, &req);
251
252	if (req.emr_rc != 0) {
253		rc = req.emr_rc;
254		goto fail1;
255	}
256
257	return (0);
258
259fail1:
260	EFSYS_PROBE1(fail1, efx_rc_t, rc);
261
262	return (rc);
263}
264
265
266	__checkReturn	efx_rc_t
267efx_lightsout_offload_add(
268	__in		efx_nic_t *enp,
269	__in		efx_lightsout_offload_type_t type,
270	__in		efx_lightsout_offload_param_t *paramp,
271	__out		uint32_t *filter_idp)
272{
273	efx_mcdi_req_t req;
274	uint8_t payload[MAX(MAX(MC_CMD_ADD_LIGHTSOUT_OFFLOAD_IN_ARP_LEN,
275				MC_CMD_ADD_LIGHTSOUT_OFFLOAD_IN_NS_LEN),
276			    MC_CMD_ADD_LIGHTSOUT_OFFLOAD_OUT_LEN)];
277	efx_rc_t rc;
278
279	EFSYS_ASSERT3U(enp->en_magic, ==, EFX_NIC_MAGIC);
280	EFSYS_ASSERT3U(enp->en_mod_flags, &, EFX_MOD_WOL);
281
282	(void) memset(payload, 0, sizeof (payload));
283	req.emr_cmd = MC_CMD_ADD_LIGHTSOUT_OFFLOAD;
284	req.emr_in_buf = payload;
285	req.emr_in_length = sizeof (type);
286	req.emr_out_buf = payload;
287	req.emr_out_length = MC_CMD_ADD_LIGHTSOUT_OFFLOAD_OUT_LEN;
288
289	switch (type) {
290	case EFX_LIGHTSOUT_OFFLOAD_TYPE_ARP:
291		req.emr_in_length = MC_CMD_ADD_LIGHTSOUT_OFFLOAD_IN_ARP_LEN;
292
293		MCDI_IN_SET_DWORD(req, ADD_LIGHTSOUT_OFFLOAD_IN_PROTOCOL,
294				    MC_CMD_LIGHTSOUT_OFFLOAD_PROTOCOL_ARP);
295		EFX_MAC_ADDR_COPY(MCDI_IN2(req, uint8_t,
296					    ADD_LIGHTSOUT_OFFLOAD_IN_ARP_MAC),
297				    paramp->elop_arp.mac_addr);
298		MCDI_IN_SET_DWORD(req, ADD_LIGHTSOUT_OFFLOAD_IN_ARP_IP,
299				    paramp->elop_arp.ip);
300		break;
301	case EFX_LIGHTSOUT_OFFLOAD_TYPE_NS:
302		req.emr_in_length = MC_CMD_ADD_LIGHTSOUT_OFFLOAD_IN_NS_LEN;
303
304		MCDI_IN_SET_DWORD(req, ADD_LIGHTSOUT_OFFLOAD_IN_PROTOCOL,
305				    MC_CMD_LIGHTSOUT_OFFLOAD_PROTOCOL_NS);
306		EFX_MAC_ADDR_COPY(MCDI_IN2(req, uint8_t,
307					    ADD_LIGHTSOUT_OFFLOAD_IN_NS_MAC),
308				    paramp->elop_ns.mac_addr);
309		memcpy(MCDI_IN2(req, uint8_t,
310		    ADD_LIGHTSOUT_OFFLOAD_IN_NS_SNIPV6),
311		    paramp->elop_ns.solicited_node,
312		    sizeof (paramp->elop_ns.solicited_node));
313		memcpy(MCDI_IN2(req, uint8_t, ADD_LIGHTSOUT_OFFLOAD_IN_NS_IPV6),
314		    paramp->elop_ns.ip, sizeof (paramp->elop_ns.ip));
315		break;
316	default:
317		rc = EINVAL;
318		goto fail1;
319	}
320
321	efx_mcdi_execute(enp, &req);
322
323	if (req.emr_rc != 0) {
324		rc = req.emr_rc;
325		goto fail2;
326	}
327
328	if (req.emr_out_length_used < MC_CMD_ADD_LIGHTSOUT_OFFLOAD_OUT_LEN) {
329		rc = EMSGSIZE;
330		goto fail3;
331	}
332
333	*filter_idp = MCDI_OUT_DWORD(req, ADD_LIGHTSOUT_OFFLOAD_OUT_FILTER_ID);
334
335	return (0);
336
337fail3:
338	EFSYS_PROBE(fail3);
339fail2:
340	EFSYS_PROBE(fail2);
341fail1:
342	EFSYS_PROBE1(fail1, efx_rc_t, rc);
343
344	return (rc);
345}
346
347
348	__checkReturn	efx_rc_t
349efx_lightsout_offload_remove(
350	__in		efx_nic_t *enp,
351	__in		efx_lightsout_offload_type_t type,
352	__in		uint32_t filter_id)
353{
354	efx_mcdi_req_t req;
355	uint8_t payload[MAX(MC_CMD_REMOVE_LIGHTSOUT_OFFLOAD_IN_LEN,
356			    MC_CMD_REMOVE_LIGHTSOUT_OFFLOAD_OUT_LEN)];
357	efx_rc_t rc;
358
359	EFSYS_ASSERT3U(enp->en_magic, ==, EFX_NIC_MAGIC);
360	EFSYS_ASSERT3U(enp->en_mod_flags, &, EFX_MOD_WOL);
361
362	(void) memset(payload, 0, sizeof (payload));
363	req.emr_cmd = MC_CMD_REMOVE_LIGHTSOUT_OFFLOAD;
364	req.emr_in_buf = payload;
365	req.emr_in_length = MC_CMD_REMOVE_LIGHTSOUT_OFFLOAD_IN_LEN;
366	req.emr_out_buf = payload;
367	req.emr_out_length = MC_CMD_REMOVE_LIGHTSOUT_OFFLOAD_OUT_LEN;
368
369	switch (type) {
370	case EFX_LIGHTSOUT_OFFLOAD_TYPE_ARP:
371		MCDI_IN_SET_DWORD(req, REMOVE_LIGHTSOUT_OFFLOAD_IN_PROTOCOL,
372				    MC_CMD_LIGHTSOUT_OFFLOAD_PROTOCOL_ARP);
373		break;
374	case EFX_LIGHTSOUT_OFFLOAD_TYPE_NS:
375		MCDI_IN_SET_DWORD(req, REMOVE_LIGHTSOUT_OFFLOAD_IN_PROTOCOL,
376				    MC_CMD_LIGHTSOUT_OFFLOAD_PROTOCOL_NS);
377		break;
378	default:
379		rc = EINVAL;
380		goto fail1;
381	}
382
383	MCDI_IN_SET_DWORD(req, REMOVE_LIGHTSOUT_OFFLOAD_IN_FILTER_ID,
384			    filter_id);
385
386	efx_mcdi_execute(enp, &req);
387
388	if (req.emr_rc != 0) {
389		rc = req.emr_rc;
390		goto fail2;
391	}
392
393	return (0);
394
395fail2:
396	EFSYS_PROBE(fail2);
397fail1:
398	EFSYS_PROBE1(fail1, efx_rc_t, rc);
399
400	return (rc);
401}
402
403
404			void
405efx_wol_fini(
406	__in		efx_nic_t *enp)
407{
408	EFSYS_ASSERT3U(enp->en_magic, ==, EFX_NIC_MAGIC);
409	EFSYS_ASSERT3U(enp->en_mod_flags, &, EFX_MOD_PROBE);
410	EFSYS_ASSERT3U(enp->en_mod_flags, &, EFX_MOD_WOL);
411
412	enp->en_mod_flags &= ~EFX_MOD_WOL;
413}
414
415#endif	/* EFSYS_OPT_WOL */
416