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