xref: /openbmc/linux/drivers/s390/net/qeth_l2_main.c (revision 817741a8)
1ab9953ffSGreg Kroah-Hartman // SPDX-License-Identifier: GPL-2.0
24a71df50SFrank Blaschka /*
3bbcfcdc8SFrank Blaschka  *    Copyright IBM Corp. 2007, 2009
44a71df50SFrank Blaschka  *    Author(s): Utz Bacher <utz.bacher@de.ibm.com>,
54a71df50SFrank Blaschka  *		 Frank Pavlic <fpavlic@de.ibm.com>,
64a71df50SFrank Blaschka  *		 Thomas Spatzier <tspat@de.ibm.com>,
74a71df50SFrank Blaschka  *		 Frank Blaschka <frank.blaschka@de.ibm.com>
84a71df50SFrank Blaschka  */
94a71df50SFrank Blaschka 
1074eacdb9SFrank Blaschka #define KMSG_COMPONENT "qeth"
1174eacdb9SFrank Blaschka #define pr_fmt(fmt) KMSG_COMPONENT ": " fmt
1274eacdb9SFrank Blaschka 
134a71df50SFrank Blaschka #include <linux/module.h>
144a71df50SFrank Blaschka #include <linux/moduleparam.h>
154a71df50SFrank Blaschka #include <linux/string.h>
164a71df50SFrank Blaschka #include <linux/errno.h>
174a71df50SFrank Blaschka #include <linux/kernel.h>
185a0e3ad6STejun Heo #include <linux/slab.h>
194a71df50SFrank Blaschka #include <linux/etherdevice.h>
20fa115adfSAlexandra Winter #include <linux/if_bridge.h>
21ccffad25SJiri Pirko #include <linux/list.h>
22fe5c8028SLakhvich Dmitriy #include <linux/hash.h>
23fe5c8028SLakhvich Dmitriy #include <linux/hashtable.h>
2410a6cfc0SAlexandra Winter #include <net/switchdev.h>
25a0138f59SAlexandra Winter #include <asm/chsc.h>
26fa115adfSAlexandra Winter #include <asm/css_chars.h>
27ec61bd2fSJulian Wiedmann #include <asm/setup.h>
284a71df50SFrank Blaschka #include "qeth_core.h"
29b4d72c08SEugene Crosser #include "qeth_l2.h"
304a71df50SFrank Blaschka 
31c044dc21SEugene Crosser static void qeth_bridgeport_query_support(struct qeth_card *card);
32c044dc21SEugene Crosser static void qeth_bridge_state_change(struct qeth_card *card,
33c044dc21SEugene Crosser 					struct qeth_ipa_cmd *cmd);
34a0138f59SAlexandra Winter static void qeth_addr_change_event(struct qeth_card *card,
35c044dc21SEugene Crosser 				   struct qeth_ipa_cmd *cmd);
36caa1f0b1SHans Wippel static void qeth_l2_vnicc_set_defaults(struct qeth_card *card);
37a45b3fafSHans Wippel static void qeth_l2_vnicc_init(struct qeth_card *card);
38349d13d5SHans Wippel static bool qeth_l2_vnicc_recover_timeout(struct qeth_card *card, u32 vnicc,
39349d13d5SHans Wippel 					  u32 *timeout);
404a71df50SFrank Blaschka 
41742d4d40SJulian Wiedmann static int qeth_l2_setdelmac_makerc(struct qeth_card *card, u16 retcode)
424a71df50SFrank Blaschka {
43efbbc1d5SEugene Crosser 	int rc;
444a71df50SFrank Blaschka 
45efbbc1d5SEugene Crosser 	if (retcode)
461aec42bcSThomas Richter 		QETH_CARD_TEXT_(card, 2, "err%04x", retcode);
47efbbc1d5SEugene Crosser 	switch (retcode) {
48efbbc1d5SEugene Crosser 	case IPA_RC_SUCCESS:
49efbbc1d5SEugene Crosser 		rc = 0;
50efbbc1d5SEugene Crosser 		break;
51efbbc1d5SEugene Crosser 	case IPA_RC_L2_UNSUPPORTED_CMD:
52ffb95251SEugene Crosser 		rc = -EOPNOTSUPP;
53efbbc1d5SEugene Crosser 		break;
54efbbc1d5SEugene Crosser 	case IPA_RC_L2_ADDR_TABLE_FULL:
55efbbc1d5SEugene Crosser 		rc = -ENOSPC;
56efbbc1d5SEugene Crosser 		break;
57efbbc1d5SEugene Crosser 	case IPA_RC_L2_DUP_MAC:
58efbbc1d5SEugene Crosser 	case IPA_RC_L2_DUP_LAYER3_MAC:
59bdb0cc12SJulian Wiedmann 		rc = -EADDRINUSE;
60efbbc1d5SEugene Crosser 		break;
61efbbc1d5SEugene Crosser 	case IPA_RC_L2_MAC_NOT_AUTH_BY_HYP:
62efbbc1d5SEugene Crosser 	case IPA_RC_L2_MAC_NOT_AUTH_BY_ADP:
63bdb0cc12SJulian Wiedmann 		rc = -EADDRNOTAVAIL;
64efbbc1d5SEugene Crosser 		break;
65efbbc1d5SEugene Crosser 	case IPA_RC_L2_MAC_NOT_FOUND:
66efbbc1d5SEugene Crosser 		rc = -ENOENT;
67efbbc1d5SEugene Crosser 		break;
68efbbc1d5SEugene Crosser 	default:
69efbbc1d5SEugene Crosser 		rc = -EIO;
70efbbc1d5SEugene Crosser 		break;
714a71df50SFrank Blaschka 	}
72efbbc1d5SEugene Crosser 	return rc;
734a71df50SFrank Blaschka }
744a71df50SFrank Blaschka 
75742d4d40SJulian Wiedmann static int qeth_l2_send_setdelmac_cb(struct qeth_card *card,
76742d4d40SJulian Wiedmann 				     struct qeth_reply *reply,
77742d4d40SJulian Wiedmann 				     unsigned long data)
78742d4d40SJulian Wiedmann {
79742d4d40SJulian Wiedmann 	struct qeth_ipa_cmd *cmd = (struct qeth_ipa_cmd *) data;
80742d4d40SJulian Wiedmann 
81742d4d40SJulian Wiedmann 	return qeth_l2_setdelmac_makerc(card, cmd->hdr.return_code);
82742d4d40SJulian Wiedmann }
83742d4d40SJulian Wiedmann 
84ac988d78SJulian Wiedmann static int qeth_l2_send_setdelmac(struct qeth_card *card, __u8 *mac,
85ac988d78SJulian Wiedmann 			   enum qeth_ipa_cmds ipacmd)
86ac988d78SJulian Wiedmann {
87ac988d78SJulian Wiedmann 	struct qeth_ipa_cmd *cmd;
88ac988d78SJulian Wiedmann 	struct qeth_cmd_buffer *iob;
89ac988d78SJulian Wiedmann 
90ac988d78SJulian Wiedmann 	QETH_CARD_TEXT(card, 2, "L2sdmac");
91a59d121dSJulian Wiedmann 	iob = qeth_ipa_alloc_cmd(card, ipacmd, QETH_PROT_IPV4,
92a59d121dSJulian Wiedmann 				 IPA_DATA_SIZEOF(setdelmac));
93ac988d78SJulian Wiedmann 	if (!iob)
94ac988d78SJulian Wiedmann 		return -ENOMEM;
95ff5caa7aSJulian Wiedmann 	cmd = __ipa_cmd(iob);
9699f0b85dSJulian Wiedmann 	cmd->data.setdelmac.mac_length = ETH_ALEN;
9799f0b85dSJulian Wiedmann 	ether_addr_copy(cmd->data.setdelmac.mac, mac);
98742d4d40SJulian Wiedmann 	return qeth_send_ipa_cmd(card, iob, qeth_l2_send_setdelmac_cb, NULL);
99ac988d78SJulian Wiedmann }
100ac988d78SJulian Wiedmann 
101ac988d78SJulian Wiedmann static int qeth_l2_send_setmac(struct qeth_card *card, __u8 *mac)
102ac988d78SJulian Wiedmann {
103ac988d78SJulian Wiedmann 	int rc;
104ac988d78SJulian Wiedmann 
105ac988d78SJulian Wiedmann 	QETH_CARD_TEXT(card, 2, "L2Setmac");
106ac988d78SJulian Wiedmann 	rc = qeth_l2_send_setdelmac(card, mac, IPA_CMD_SETVMAC);
107ac988d78SJulian Wiedmann 	if (rc == 0) {
108ac988d78SJulian Wiedmann 		dev_info(&card->gdev->dev,
109654e3d48SJulian Wiedmann 			 "MAC address %pM successfully registered\n", mac);
110ac988d78SJulian Wiedmann 	} else {
111ac988d78SJulian Wiedmann 		switch (rc) {
112bdb0cc12SJulian Wiedmann 		case -EADDRINUSE:
113ac988d78SJulian Wiedmann 			dev_warn(&card->gdev->dev,
114ac988d78SJulian Wiedmann 				"MAC address %pM already exists\n", mac);
115ac988d78SJulian Wiedmann 			break;
116bdb0cc12SJulian Wiedmann 		case -EADDRNOTAVAIL:
117ac988d78SJulian Wiedmann 			dev_warn(&card->gdev->dev,
118ac988d78SJulian Wiedmann 				"MAC address %pM is not authorized\n", mac);
119ac988d78SJulian Wiedmann 			break;
120ac988d78SJulian Wiedmann 		}
121ac988d78SJulian Wiedmann 	}
122ac988d78SJulian Wiedmann 	return rc;
123ac988d78SJulian Wiedmann }
124ac988d78SJulian Wiedmann 
1258174aa8aSJulian Wiedmann static int qeth_l2_write_mac(struct qeth_card *card, u8 *mac)
1264a71df50SFrank Blaschka {
1279d0a58fbSVasily Gorbik 	enum qeth_ipa_cmds cmd = is_multicast_ether_addr(mac) ?
1288174aa8aSJulian Wiedmann 					IPA_CMD_SETGMAC : IPA_CMD_SETVMAC;
129efbbc1d5SEugene Crosser 	int rc;
130efbbc1d5SEugene Crosser 
1318174aa8aSJulian Wiedmann 	QETH_CARD_TEXT(card, 2, "L2Wmac");
1328174aa8aSJulian Wiedmann 	rc = qeth_l2_send_setdelmac(card, mac, cmd);
133bdb0cc12SJulian Wiedmann 	if (rc == -EADDRINUSE)
134e19e5be8SJulian Wiedmann 		QETH_DBF_MESSAGE(2, "MAC already registered on device %x\n",
135e19e5be8SJulian Wiedmann 				 CARD_DEVID(card));
136efbbc1d5SEugene Crosser 	else if (rc)
137e19e5be8SJulian Wiedmann 		QETH_DBF_MESSAGE(2, "Failed to register MAC on device %x: %d\n",
138e19e5be8SJulian Wiedmann 				 CARD_DEVID(card), rc);
139efbbc1d5SEugene Crosser 	return rc;
1404a71df50SFrank Blaschka }
1414a71df50SFrank Blaschka 
1428174aa8aSJulian Wiedmann static int qeth_l2_remove_mac(struct qeth_card *card, u8 *mac)
1434a71df50SFrank Blaschka {
1449d0a58fbSVasily Gorbik 	enum qeth_ipa_cmds cmd = is_multicast_ether_addr(mac) ?
1458174aa8aSJulian Wiedmann 					IPA_CMD_DELGMAC : IPA_CMD_DELVMAC;
146efbbc1d5SEugene Crosser 	int rc;
147efbbc1d5SEugene Crosser 
1488174aa8aSJulian Wiedmann 	QETH_CARD_TEXT(card, 2, "L2Rmac");
1498174aa8aSJulian Wiedmann 	rc = qeth_l2_send_setdelmac(card, mac, cmd);
150efbbc1d5SEugene Crosser 	if (rc)
151e19e5be8SJulian Wiedmann 		QETH_DBF_MESSAGE(2, "Failed to delete MAC on device %u: %d\n",
152e19e5be8SJulian Wiedmann 				 CARD_DEVID(card), rc);
153efbbc1d5SEugene Crosser 	return rc;
1544a71df50SFrank Blaschka }
1554a71df50SFrank Blaschka 
156d0c74825SJulian Wiedmann static void qeth_l2_drain_rx_mode_cache(struct qeth_card *card)
1574a71df50SFrank Blaschka {
158fe5c8028SLakhvich Dmitriy 	struct qeth_mac *mac;
159fe5c8028SLakhvich Dmitriy 	struct hlist_node *tmp;
160fe5c8028SLakhvich Dmitriy 	int i;
1614a71df50SFrank Blaschka 
1620973292fSJulian Wiedmann 	hash_for_each_safe(card->rx_mode_addrs, i, tmp, mac, hnode) {
163fe5c8028SLakhvich Dmitriy 		hash_del(&mac->hnode);
164fe5c8028SLakhvich Dmitriy 		kfree(mac);
1654a71df50SFrank Blaschka 	}
1664a71df50SFrank Blaschka }
1674a71df50SFrank Blaschka 
168b0abc4f5SJulian Wiedmann static void qeth_l2_fill_header(struct qeth_qdio_out_q *queue,
169b0abc4f5SJulian Wiedmann 				struct qeth_hdr *hdr, struct sk_buff *skb,
170eca1d5c2SJulian Wiedmann 				int ipv, unsigned int data_len)
1714a71df50SFrank Blaschka {
172eca1d5c2SJulian Wiedmann 	int cast_type = qeth_get_ether_cast_type(skb);
1730aef8392SJulian Wiedmann 	struct vlan_ethhdr *veth = vlan_eth_hdr(skb);
1744a71df50SFrank Blaschka 
175ae79fe03SJulian Wiedmann 	hdr->hdr.l2.pkt_length = data_len;
1764a71df50SFrank Blaschka 
1770aef8392SJulian Wiedmann 	if (skb_is_gso(skb)) {
1780aef8392SJulian Wiedmann 		hdr->hdr.l2.id = QETH_HEADER_TYPE_L2_TSO;
1790aef8392SJulian Wiedmann 	} else {
1800aef8392SJulian Wiedmann 		hdr->hdr.l2.id = QETH_HEADER_TYPE_LAYER2;
181eeac0e20SJulian Wiedmann 		if (skb->ip_summed == CHECKSUM_PARTIAL)
182fc69660bSJulian Wiedmann 			qeth_tx_csum(skb, &hdr->hdr.l2.flags[1], ipv);
1830aef8392SJulian Wiedmann 	}
184fc69660bSJulian Wiedmann 
1854a71df50SFrank Blaschka 	/* set byte byte 3 to casting flags */
1864a71df50SFrank Blaschka 	if (cast_type == RTN_MULTICAST)
1874a71df50SFrank Blaschka 		hdr->hdr.l2.flags[2] |= QETH_LAYER2_FLAG_MULTICAST;
1884a71df50SFrank Blaschka 	else if (cast_type == RTN_BROADCAST)
1894a71df50SFrank Blaschka 		hdr->hdr.l2.flags[2] |= QETH_LAYER2_FLAG_BROADCAST;
1904a71df50SFrank Blaschka 	else
191ce73e10eSKlaus-Dieter Wacker 		hdr->hdr.l2.flags[2] |= QETH_LAYER2_FLAG_UNICAST;
1924a71df50SFrank Blaschka 
1934a71df50SFrank Blaschka 	/* VSWITCH relies on the VLAN
1944a71df50SFrank Blaschka 	 * information to be present in
1954a71df50SFrank Blaschka 	 * the QDIO header */
1964a71df50SFrank Blaschka 	if (veth->h_vlan_proto == __constant_htons(ETH_P_8021Q)) {
1974a71df50SFrank Blaschka 		hdr->hdr.l2.flags[2] |= QETH_LAYER2_FLAG_VLAN;
1984a71df50SFrank Blaschka 		hdr->hdr.l2.vlan_id = ntohs(veth->h_vlan_TCI);
1994a71df50SFrank Blaschka 	}
2004a71df50SFrank Blaschka }
2014a71df50SFrank Blaschka 
202742d4d40SJulian Wiedmann static int qeth_l2_setdelvlan_makerc(struct qeth_card *card, u16 retcode)
2034a71df50SFrank Blaschka {
2042aa48671SJulian Wiedmann 	if (retcode)
2052aa48671SJulian Wiedmann 		QETH_CARD_TEXT_(card, 2, "err%04x", retcode);
2062aa48671SJulian Wiedmann 
2072aa48671SJulian Wiedmann 	switch (retcode) {
2082aa48671SJulian Wiedmann 	case IPA_RC_SUCCESS:
2092aa48671SJulian Wiedmann 		return 0;
2102aa48671SJulian Wiedmann 	case IPA_RC_L2_INVALID_VLAN_ID:
2112aa48671SJulian Wiedmann 		return -EINVAL;
2122aa48671SJulian Wiedmann 	case IPA_RC_L2_DUP_VLAN_ID:
2132aa48671SJulian Wiedmann 		return -EEXIST;
2142aa48671SJulian Wiedmann 	case IPA_RC_L2_VLAN_ID_NOT_FOUND:
2152aa48671SJulian Wiedmann 		return -ENOENT;
2162aa48671SJulian Wiedmann 	case IPA_RC_L2_VLAN_ID_NOT_ALLOWED:
2172aa48671SJulian Wiedmann 		return -EPERM;
2182aa48671SJulian Wiedmann 	default:
2192aa48671SJulian Wiedmann 		return -EIO;
2202aa48671SJulian Wiedmann 	}
2212aa48671SJulian Wiedmann }
2222aa48671SJulian Wiedmann 
2232aa48671SJulian Wiedmann static int qeth_l2_send_setdelvlan_cb(struct qeth_card *card,
2242aa48671SJulian Wiedmann 				      struct qeth_reply *reply,
2252aa48671SJulian Wiedmann 				      unsigned long data)
2262aa48671SJulian Wiedmann {
2272aa48671SJulian Wiedmann 	struct qeth_ipa_cmd *cmd = (struct qeth_ipa_cmd *) data;
2284a71df50SFrank Blaschka 
229847a50fdSCarsten Otte 	QETH_CARD_TEXT(card, 2, "L2sdvcb");
2304a71df50SFrank Blaschka 	if (cmd->hdr.return_code) {
231e19e5be8SJulian Wiedmann 		QETH_DBF_MESSAGE(2, "Error in processing VLAN %u on device %x: %#x.\n",
2322aa48671SJulian Wiedmann 				 cmd->data.setdelvlan.vlan_id,
233e19e5be8SJulian Wiedmann 				 CARD_DEVID(card), cmd->hdr.return_code);
234847a50fdSCarsten Otte 		QETH_CARD_TEXT_(card, 2, "L2VL%4x", cmd->hdr.command);
2354a71df50SFrank Blaschka 	}
236742d4d40SJulian Wiedmann 	return qeth_l2_setdelvlan_makerc(card, cmd->hdr.return_code);
2374a71df50SFrank Blaschka }
2384a71df50SFrank Blaschka 
2394a71df50SFrank Blaschka static int qeth_l2_send_setdelvlan(struct qeth_card *card, __u16 i,
2404a71df50SFrank Blaschka 				   enum qeth_ipa_cmds ipacmd)
2414a71df50SFrank Blaschka {
2424a71df50SFrank Blaschka 	struct qeth_ipa_cmd *cmd;
2434a71df50SFrank Blaschka 	struct qeth_cmd_buffer *iob;
2444a71df50SFrank Blaschka 
245847a50fdSCarsten Otte 	QETH_CARD_TEXT_(card, 4, "L2sdv%x", ipacmd);
246a59d121dSJulian Wiedmann 	iob = qeth_ipa_alloc_cmd(card, ipacmd, QETH_PROT_IPV4,
247a59d121dSJulian Wiedmann 				 IPA_DATA_SIZEOF(setdelvlan));
2481aec42bcSThomas Richter 	if (!iob)
2491aec42bcSThomas Richter 		return -ENOMEM;
250ff5caa7aSJulian Wiedmann 	cmd = __ipa_cmd(iob);
2514a71df50SFrank Blaschka 	cmd->data.setdelvlan.vlan_id = i;
252742d4d40SJulian Wiedmann 	return qeth_send_ipa_cmd(card, iob, qeth_l2_send_setdelvlan_cb, NULL);
2534a71df50SFrank Blaschka }
2544a71df50SFrank Blaschka 
25580d5c368SPatrick McHardy static int qeth_l2_vlan_rx_add_vid(struct net_device *dev,
25680d5c368SPatrick McHardy 				   __be16 proto, u16 vid)
2574a71df50SFrank Blaschka {
258509e2562SHeiko Carstens 	struct qeth_card *card = dev->ml_priv;
2594a71df50SFrank Blaschka 
260847a50fdSCarsten Otte 	QETH_CARD_TEXT_(card, 4, "aid:%d", vid);
26110651db7SUrsula Braun 	if (!vid)
2628e586137SJiri Pirko 		return 0;
263e6e771b3SJulian Wiedmann 
2645fc692a7SJulian Wiedmann 	return qeth_l2_send_setdelvlan(card, vid, IPA_CMD_SETVLAN);
2654a71df50SFrank Blaschka }
2664a71df50SFrank Blaschka 
26780d5c368SPatrick McHardy static int qeth_l2_vlan_rx_kill_vid(struct net_device *dev,
26880d5c368SPatrick McHardy 				    __be16 proto, u16 vid)
2694a71df50SFrank Blaschka {
270509e2562SHeiko Carstens 	struct qeth_card *card = dev->ml_priv;
2714a71df50SFrank Blaschka 
272847a50fdSCarsten Otte 	QETH_CARD_TEXT_(card, 4, "kid:%d", vid);
2735fc692a7SJulian Wiedmann 	if (!vid)
2745fc692a7SJulian Wiedmann 		return 0;
275e6e771b3SJulian Wiedmann 
2765fc692a7SJulian Wiedmann 	return qeth_l2_send_setdelvlan(card, vid, IPA_CMD_DELVLAN);
2774a71df50SFrank Blaschka }
2784a71df50SFrank Blaschka 
2799d6a569aSJulian Wiedmann static void qeth_l2_set_pnso_mode(struct qeth_card *card,
2809d6a569aSJulian Wiedmann 				  enum qeth_pnso_mode mode)
2819d6a569aSJulian Wiedmann {
2829d6a569aSJulian Wiedmann 	spin_lock_irq(get_ccwdev_lock(CARD_RDEV(card)));
2839d6a569aSJulian Wiedmann 	WRITE_ONCE(card->info.pnso_mode, mode);
2849d6a569aSJulian Wiedmann 	spin_unlock_irq(get_ccwdev_lock(CARD_RDEV(card)));
2859d6a569aSJulian Wiedmann 
2869d6a569aSJulian Wiedmann 	if (mode == QETH_PNSO_NONE)
2879d6a569aSJulian Wiedmann 		drain_workqueue(card->event_wq);
2889d6a569aSJulian Wiedmann }
2899d6a569aSJulian Wiedmann 
290817741a8SAlexandra Winter static void qeth_l2_dev2br_fdb_flush(struct qeth_card *card)
291817741a8SAlexandra Winter {
292817741a8SAlexandra Winter 	struct switchdev_notifier_fdb_info info;
293817741a8SAlexandra Winter 
294817741a8SAlexandra Winter 	QETH_CARD_TEXT(card, 2, "fdbflush");
295817741a8SAlexandra Winter 
296817741a8SAlexandra Winter 	info.addr = NULL;
297817741a8SAlexandra Winter 	/* flush all VLANs: */
298817741a8SAlexandra Winter 	info.vid = 0;
299817741a8SAlexandra Winter 	info.added_by_user = false;
300817741a8SAlexandra Winter 	info.offloaded = true;
301817741a8SAlexandra Winter 
302817741a8SAlexandra Winter 	call_switchdev_notifiers(SWITCHDEV_FDB_FLUSH_TO_BRIDGE,
303817741a8SAlexandra Winter 				 card->dev, &info.info, NULL);
304817741a8SAlexandra Winter }
305817741a8SAlexandra Winter 
306d4560150SJulian Wiedmann static void qeth_l2_stop_card(struct qeth_card *card)
3074a71df50SFrank Blaschka {
30857a688aaSJulian Wiedmann 	QETH_CARD_TEXT(card, 2, "stopcard");
3094a71df50SFrank Blaschka 
3104a71df50SFrank Blaschka 	qeth_set_allowed_threads(card, 0, 1);
3110f7aedbdSJulian Wiedmann 
312d0c74825SJulian Wiedmann 	cancel_work_sync(&card->rx_mode_work);
313d0c74825SJulian Wiedmann 	qeth_l2_drain_rx_mode_cache(card);
314d0c74825SJulian Wiedmann 
3154a71df50SFrank Blaschka 	if (card->state == CARD_STATE_SOFTSETUP) {
3164a71df50SFrank Blaschka 		qeth_clear_ipacmd_list(card);
31741c47da3SJulian Wiedmann 		qeth_drain_output_queues(card);
3184a71df50SFrank Blaschka 		card->state = CARD_STATE_DOWN;
3194a71df50SFrank Blaschka 	}
320ad160872SJulian Wiedmann 
3218b5026bcSJulian Wiedmann 	qeth_qdio_clear_card(card, 0);
322aa3ad392SJulian Wiedmann 	qeth_clear_working_pool_list(card);
3239d6a569aSJulian Wiedmann 	qeth_l2_set_pnso_mode(card, QETH_PNSO_NONE);
3240d49c06bSJulian Wiedmann 	qeth_flush_local_addrs(card);
3250f399305SJulian Wiedmann 	card->info.promisc_mode = 0;
3264a71df50SFrank Blaschka }
3274a71df50SFrank Blaschka 
3284a71df50SFrank Blaschka static int qeth_l2_request_initial_mac(struct qeth_card *card)
3294a71df50SFrank Blaschka {
3304a71df50SFrank Blaschka 	int rc = 0;
3314a71df50SFrank Blaschka 
33257a688aaSJulian Wiedmann 	QETH_CARD_TEXT(card, 2, "l2reqmac");
3334a71df50SFrank Blaschka 
334ec61bd2fSJulian Wiedmann 	if (MACHINE_IS_VM) {
335ec61bd2fSJulian Wiedmann 		rc = qeth_vm_request_mac(card);
336ec61bd2fSJulian Wiedmann 		if (!rc)
337ec61bd2fSJulian Wiedmann 			goto out;
338e19e5be8SJulian Wiedmann 		QETH_DBF_MESSAGE(2, "z/VM MAC Service failed on device %x: %#x\n",
339e19e5be8SJulian Wiedmann 				 CARD_DEVID(card), rc);
34057a688aaSJulian Wiedmann 		QETH_CARD_TEXT_(card, 2, "err%04x", rc);
341ec61bd2fSJulian Wiedmann 		/* fall back to alternative mechanism: */
342ec61bd2fSJulian Wiedmann 	}
343ec61bd2fSJulian Wiedmann 
344b144b99fSJulian Wiedmann 	if (!IS_OSN(card)) {
3454a71df50SFrank Blaschka 		rc = qeth_setadpparms_change_macaddr(card);
3464b7ae122SJulian Wiedmann 		if (!rc)
34721b1702aSJulian Wiedmann 			goto out;
348e19e5be8SJulian Wiedmann 		QETH_DBF_MESSAGE(2, "READ_MAC Assist failed on device %x: %#x\n",
349e19e5be8SJulian Wiedmann 				 CARD_DEVID(card), rc);
35057a688aaSJulian Wiedmann 		QETH_CARD_TEXT_(card, 2, "1err%04x", rc);
35121b1702aSJulian Wiedmann 		/* fall back once more: */
3524a71df50SFrank Blaschka 	}
35321b1702aSJulian Wiedmann 
35421b1702aSJulian Wiedmann 	/* some devices don't support a custom MAC address: */
355379ac99eSJulian Wiedmann 	if (IS_OSM(card) || IS_OSX(card))
35621b1702aSJulian Wiedmann 		return (rc) ? rc : -EADDRNOTAVAIL;
35721b1702aSJulian Wiedmann 	eth_hw_addr_random(card->dev);
35821b1702aSJulian Wiedmann 
359ec61bd2fSJulian Wiedmann out:
36057a688aaSJulian Wiedmann 	QETH_CARD_HEX(card, 2, card->dev->dev_addr, card->dev->addr_len);
3614a71df50SFrank Blaschka 	return 0;
3624a71df50SFrank Blaschka }
3634a71df50SFrank Blaschka 
364654e3d48SJulian Wiedmann static void qeth_l2_register_dev_addr(struct qeth_card *card)
365654e3d48SJulian Wiedmann {
366654e3d48SJulian Wiedmann 	if (!is_valid_ether_addr(card->dev->dev_addr))
367654e3d48SJulian Wiedmann 		qeth_l2_request_initial_mac(card);
368654e3d48SJulian Wiedmann 
369654e3d48SJulian Wiedmann 	if (!IS_OSN(card) && !qeth_l2_send_setmac(card, card->dev->dev_addr))
3709de15117SJulian Wiedmann 		card->info.dev_addr_is_registered = 1;
3719de15117SJulian Wiedmann 	else
3729de15117SJulian Wiedmann 		card->info.dev_addr_is_registered = 0;
373654e3d48SJulian Wiedmann }
374654e3d48SJulian Wiedmann 
375e22355eaSJulian Wiedmann static int qeth_l2_validate_addr(struct net_device *dev)
376e22355eaSJulian Wiedmann {
377e22355eaSJulian Wiedmann 	struct qeth_card *card = dev->ml_priv;
378e22355eaSJulian Wiedmann 
3799de15117SJulian Wiedmann 	if (card->info.dev_addr_is_registered)
380e22355eaSJulian Wiedmann 		return eth_validate_addr(dev);
381e22355eaSJulian Wiedmann 
382e22355eaSJulian Wiedmann 	QETH_CARD_TEXT(card, 4, "nomacadr");
383e22355eaSJulian Wiedmann 	return -EPERM;
384e22355eaSJulian Wiedmann }
385e22355eaSJulian Wiedmann 
3864a71df50SFrank Blaschka static int qeth_l2_set_mac_address(struct net_device *dev, void *p)
3874a71df50SFrank Blaschka {
3884a71df50SFrank Blaschka 	struct sockaddr *addr = p;
389509e2562SHeiko Carstens 	struct qeth_card *card = dev->ml_priv;
390bcacfcbcSJulian Wiedmann 	u8 old_addr[ETH_ALEN];
3914a71df50SFrank Blaschka 	int rc = 0;
3924a71df50SFrank Blaschka 
393847a50fdSCarsten Otte 	QETH_CARD_TEXT(card, 3, "setmac");
3944a71df50SFrank Blaschka 
3958024cc9eSJulian Wiedmann 	if (IS_OSM(card) || IS_OSX(card)) {
396847a50fdSCarsten Otte 		QETH_CARD_TEXT(card, 3, "setmcTYP");
3974a71df50SFrank Blaschka 		return -EOPNOTSUPP;
3984a71df50SFrank Blaschka 	}
39999f0b85dSJulian Wiedmann 	QETH_CARD_HEX(card, 3, addr->sa_data, ETH_ALEN);
400bcacfcbcSJulian Wiedmann 	if (!is_valid_ether_addr(addr->sa_data))
401bcacfcbcSJulian Wiedmann 		return -EADDRNOTAVAIL;
402bcacfcbcSJulian Wiedmann 
403bcacfcbcSJulian Wiedmann 	/* don't register the same address twice */
404bcacfcbcSJulian Wiedmann 	if (ether_addr_equal_64bits(dev->dev_addr, addr->sa_data) &&
4059de15117SJulian Wiedmann 	    card->info.dev_addr_is_registered)
406e6e771b3SJulian Wiedmann 		return 0;
407bcacfcbcSJulian Wiedmann 
408bcacfcbcSJulian Wiedmann 	/* add the new address, switch over, drop the old */
4094a71df50SFrank Blaschka 	rc = qeth_l2_send_setmac(card, addr->sa_data);
410bcacfcbcSJulian Wiedmann 	if (rc)
411e6e771b3SJulian Wiedmann 		return rc;
412bcacfcbcSJulian Wiedmann 	ether_addr_copy(old_addr, dev->dev_addr);
413bcacfcbcSJulian Wiedmann 	ether_addr_copy(dev->dev_addr, addr->sa_data);
414bcacfcbcSJulian Wiedmann 
4159de15117SJulian Wiedmann 	if (card->info.dev_addr_is_registered)
416bcacfcbcSJulian Wiedmann 		qeth_l2_remove_mac(card, old_addr);
4179de15117SJulian Wiedmann 	card->info.dev_addr_is_registered = 1;
418e6e771b3SJulian Wiedmann 	return 0;
4194a71df50SFrank Blaschka }
4204a71df50SFrank Blaschka 
42159b757a9SJulian Wiedmann static void qeth_l2_promisc_to_bridge(struct qeth_card *card, bool enable)
4220db587b0SEugene Crosser {
4230db587b0SEugene Crosser 	int role;
4240db587b0SEugene Crosser 	int rc;
4250db587b0SEugene Crosser 
4260db587b0SEugene Crosser 	QETH_CARD_TEXT(card, 3, "pmisc2br");
4270db587b0SEugene Crosser 
42859b757a9SJulian Wiedmann 	if (enable) {
4290db587b0SEugene Crosser 		if (card->options.sbp.reflect_promisc_primary)
4300db587b0SEugene Crosser 			role = QETH_SBP_ROLE_PRIMARY;
4310db587b0SEugene Crosser 		else
4320db587b0SEugene Crosser 			role = QETH_SBP_ROLE_SECONDARY;
4330db587b0SEugene Crosser 	} else
4340db587b0SEugene Crosser 		role = QETH_SBP_ROLE_NONE;
4350db587b0SEugene Crosser 
4360db587b0SEugene Crosser 	rc = qeth_bridgeport_setrole(card, role);
43759b757a9SJulian Wiedmann 	QETH_CARD_TEXT_(card, 2, "bpm%c%04x", enable ? '+' : '-', rc);
4380db587b0SEugene Crosser 	if (!rc) {
4390db587b0SEugene Crosser 		card->options.sbp.role = role;
44059b757a9SJulian Wiedmann 		card->info.promisc_mode = enable;
44159b757a9SJulian Wiedmann 	}
4420db587b0SEugene Crosser }
443fe5c8028SLakhvich Dmitriy 
44459b757a9SJulian Wiedmann static void qeth_l2_set_promisc_mode(struct qeth_card *card)
44559b757a9SJulian Wiedmann {
44659b757a9SJulian Wiedmann 	bool enable = card->dev->flags & IFF_PROMISC;
44759b757a9SJulian Wiedmann 
44859b757a9SJulian Wiedmann 	if (card->info.promisc_mode == enable)
44959b757a9SJulian Wiedmann 		return;
45059b757a9SJulian Wiedmann 
451c8183f54SJulian Wiedmann 	if (qeth_adp_supported(card, IPA_SETADP_SET_PROMISC_MODE)) {
45259b757a9SJulian Wiedmann 		qeth_setadp_promisc_mode(card, enable);
453c8183f54SJulian Wiedmann 	} else {
454c8183f54SJulian Wiedmann 		mutex_lock(&card->sbp_lock);
455c8183f54SJulian Wiedmann 		if (card->options.sbp.reflect_promisc)
45659b757a9SJulian Wiedmann 			qeth_l2_promisc_to_bridge(card, enable);
457c8183f54SJulian Wiedmann 		mutex_unlock(&card->sbp_lock);
458c8183f54SJulian Wiedmann 	}
459fe5c8028SLakhvich Dmitriy }
46059b757a9SJulian Wiedmann 
461fe5c8028SLakhvich Dmitriy /* New MAC address is added to the hash table and marked to be written on card
462fe5c8028SLakhvich Dmitriy  * only if there is not in the hash table storage already
463fe5c8028SLakhvich Dmitriy  *
464fe5c8028SLakhvich Dmitriy */
4654641b027SJulian Wiedmann static void qeth_l2_add_mac(struct qeth_card *card, struct netdev_hw_addr *ha)
466fe5c8028SLakhvich Dmitriy {
467cef6ff22SJulian Wiedmann 	u32 mac_hash = get_unaligned((u32 *)(&ha->addr[2]));
468fe5c8028SLakhvich Dmitriy 	struct qeth_mac *mac;
469fe5c8028SLakhvich Dmitriy 
4700973292fSJulian Wiedmann 	hash_for_each_possible(card->rx_mode_addrs, mac, hnode, mac_hash) {
47199f0b85dSJulian Wiedmann 		if (ether_addr_equal_64bits(ha->addr, mac->mac_addr)) {
4725f78e29cSLakhvich Dmitriy 			mac->disp_flag = QETH_DISP_ADDR_DO_NOTHING;
473fe5c8028SLakhvich Dmitriy 			return;
474fe5c8028SLakhvich Dmitriy 		}
4750db587b0SEugene Crosser 	}
4760db587b0SEugene Crosser 
477fe5c8028SLakhvich Dmitriy 	mac = kzalloc(sizeof(struct qeth_mac), GFP_ATOMIC);
478fe5c8028SLakhvich Dmitriy 	if (!mac)
479fe5c8028SLakhvich Dmitriy 		return;
480fe5c8028SLakhvich Dmitriy 
48199f0b85dSJulian Wiedmann 	ether_addr_copy(mac->mac_addr, ha->addr);
4825f78e29cSLakhvich Dmitriy 	mac->disp_flag = QETH_DISP_ADDR_ADD;
483fe5c8028SLakhvich Dmitriy 
4840973292fSJulian Wiedmann 	hash_add(card->rx_mode_addrs, &mac->hnode, mac_hash);
485fe5c8028SLakhvich Dmitriy }
486fe5c8028SLakhvich Dmitriy 
487d0c74825SJulian Wiedmann static void qeth_l2_rx_mode_work(struct work_struct *work)
4884a71df50SFrank Blaschka {
489d0c74825SJulian Wiedmann 	struct qeth_card *card = container_of(work, struct qeth_card,
490d0c74825SJulian Wiedmann 					      rx_mode_work);
491d0c74825SJulian Wiedmann 	struct net_device *dev = card->dev;
492ccffad25SJiri Pirko 	struct netdev_hw_addr *ha;
493fe5c8028SLakhvich Dmitriy 	struct qeth_mac *mac;
494fe5c8028SLakhvich Dmitriy 	struct hlist_node *tmp;
495fe5c8028SLakhvich Dmitriy 	int i;
496fe5c8028SLakhvich Dmitriy 	int rc;
4974a71df50SFrank Blaschka 
498847a50fdSCarsten Otte 	QETH_CARD_TEXT(card, 3, "setmulti");
499fe5c8028SLakhvich Dmitriy 
500d0c74825SJulian Wiedmann 	netif_addr_lock_bh(dev);
50122bedad3SJiri Pirko 	netdev_for_each_mc_addr(ha, dev)
5024641b027SJulian Wiedmann 		qeth_l2_add_mac(card, ha);
50332e7bfc4SJiri Pirko 	netdev_for_each_uc_addr(ha, dev)
5044641b027SJulian Wiedmann 		qeth_l2_add_mac(card, ha);
505d0c74825SJulian Wiedmann 	netif_addr_unlock_bh(dev);
506fe5c8028SLakhvich Dmitriy 
5070973292fSJulian Wiedmann 	hash_for_each_safe(card->rx_mode_addrs, i, tmp, mac, hnode) {
50800c163f1SJulian Wiedmann 		switch (mac->disp_flag) {
50900c163f1SJulian Wiedmann 		case QETH_DISP_ADDR_DELETE:
5108174aa8aSJulian Wiedmann 			qeth_l2_remove_mac(card, mac->mac_addr);
511fe5c8028SLakhvich Dmitriy 			hash_del(&mac->hnode);
512fe5c8028SLakhvich Dmitriy 			kfree(mac);
51300c163f1SJulian Wiedmann 			break;
51400c163f1SJulian Wiedmann 		case QETH_DISP_ADDR_ADD:
5158174aa8aSJulian Wiedmann 			rc = qeth_l2_write_mac(card, mac->mac_addr);
516fe5c8028SLakhvich Dmitriy 			if (rc) {
517fe5c8028SLakhvich Dmitriy 				hash_del(&mac->hnode);
518fe5c8028SLakhvich Dmitriy 				kfree(mac);
51900c163f1SJulian Wiedmann 				break;
52000c163f1SJulian Wiedmann 			}
521df561f66SGustavo A. R. Silva 			fallthrough;
52200c163f1SJulian Wiedmann 		default:
52300c163f1SJulian Wiedmann 			/* for next call to set_rx_mode(): */
5245f78e29cSLakhvich Dmitriy 			mac->disp_flag = QETH_DISP_ADDR_DELETE;
52500c163f1SJulian Wiedmann 		}
526fe5c8028SLakhvich Dmitriy 	}
5277db2266aSFrank Blaschka 
52859b757a9SJulian Wiedmann 	qeth_l2_set_promisc_mode(card);
5294a71df50SFrank Blaschka }
5304a71df50SFrank Blaschka 
5317286384bSJulian Wiedmann static int qeth_l2_xmit_osn(struct qeth_card *card, struct sk_buff *skb,
5327286384bSJulian Wiedmann 			    struct qeth_qdio_out_q *queue)
5337286384bSJulian Wiedmann {
534b413ff8aSJulian Wiedmann 	gfp_t gfp = GFP_ATOMIC | (skb_pfmemalloc(skb) ? __GFP_MEMALLOC : 0);
53569d7ce80SJulian Wiedmann 	struct qeth_hdr *hdr = (struct qeth_hdr *)skb->data;
53669d7ce80SJulian Wiedmann 	addr_t end = (addr_t)(skb->data + sizeof(*hdr));
53769d7ce80SJulian Wiedmann 	addr_t start = (addr_t)skb->data;
53869d7ce80SJulian Wiedmann 	unsigned int elements = 0;
53969d7ce80SJulian Wiedmann 	unsigned int hd_len = 0;
54069d7ce80SJulian Wiedmann 	int rc;
5417286384bSJulian Wiedmann 
5427286384bSJulian Wiedmann 	if (skb->protocol == htons(ETH_P_IPV6))
5437286384bSJulian Wiedmann 		return -EPROTONOSUPPORT;
5447286384bSJulian Wiedmann 
54569d7ce80SJulian Wiedmann 	if (qeth_get_elements_for_range(start, end) > 1) {
54669d7ce80SJulian Wiedmann 		/* Misaligned HW header, move it to its own buffer element. */
547b413ff8aSJulian Wiedmann 		hdr = kmem_cache_alloc(qeth_core_header_cache, gfp);
54869d7ce80SJulian Wiedmann 		if (!hdr)
54969d7ce80SJulian Wiedmann 			return -ENOMEM;
55069d7ce80SJulian Wiedmann 		hd_len = sizeof(*hdr);
55169d7ce80SJulian Wiedmann 		skb_copy_from_linear_data(skb, (char *)hdr, hd_len);
55269d7ce80SJulian Wiedmann 		elements++;
55369d7ce80SJulian Wiedmann 	}
55469d7ce80SJulian Wiedmann 
55569d7ce80SJulian Wiedmann 	elements += qeth_count_elements(skb, hd_len);
5564e26c5feSJulian Wiedmann 	if (elements > queue->max_elements) {
55769d7ce80SJulian Wiedmann 		rc = -E2BIG;
55869d7ce80SJulian Wiedmann 		goto out;
55969d7ce80SJulian Wiedmann 	}
56069d7ce80SJulian Wiedmann 
56169d7ce80SJulian Wiedmann 	rc = qeth_do_send_packet(card, queue, skb, hdr, hd_len, hd_len,
56269d7ce80SJulian Wiedmann 				 elements);
56369d7ce80SJulian Wiedmann out:
56469d7ce80SJulian Wiedmann 	if (rc && hd_len)
56569d7ce80SJulian Wiedmann 		kmem_cache_free(qeth_core_header_cache, hdr);
56669d7ce80SJulian Wiedmann 	return rc;
5677286384bSJulian Wiedmann }
5687286384bSJulian Wiedmann 
569e38db6beSJulian Wiedmann static netdev_tx_t qeth_l2_hard_start_xmit(struct sk_buff *skb,
570e38db6beSJulian Wiedmann 					   struct net_device *dev)
5714a71df50SFrank Blaschka {
572509e2562SHeiko Carstens 	struct qeth_card *card = dev->ml_priv;
5733a18d754SJulian Wiedmann 	u16 txq = skb_get_queue_mapping(skb);
574290b8348SStefan Raspl 	struct qeth_qdio_out_q *queue;
5757286384bSJulian Wiedmann 	int rc;
5764a71df50SFrank Blaschka 
577eeac0e20SJulian Wiedmann 	if (!skb_is_gso(skb))
578eeac0e20SJulian Wiedmann 		qdisc_skb_cb(skb)->pkt_len = skb->len;
5793a18d754SJulian Wiedmann 	if (IS_IQD(card))
58054a50941SJulian Wiedmann 		txq = qeth_iqd_translate_txq(dev, txq);
58173dc2dafSJulian Wiedmann 	queue = card->qdio.out_qs[txq];
582b0abc4f5SJulian Wiedmann 
5835f89eca5SJulian Wiedmann 	if (IS_OSN(card))
5847286384bSJulian Wiedmann 		rc = qeth_l2_xmit_osn(card, skb, queue);
5855f89eca5SJulian Wiedmann 	else
58673dc2dafSJulian Wiedmann 		rc = qeth_xmit(card, skb, queue, qeth_get_ip_version(skb),
58758aa2491SJulian Wiedmann 			       qeth_l2_fill_header);
5884a71df50SFrank Blaschka 
589eeac0e20SJulian Wiedmann 	if (!rc)
5907286384bSJulian Wiedmann 		return NETDEV_TX_OK;
5914a71df50SFrank Blaschka 
592b0abc4f5SJulian Wiedmann 	QETH_TXQ_STAT_INC(queue, tx_dropped);
593104b4859SJulian Wiedmann 	kfree_skb(skb);
5944a71df50SFrank Blaschka 	return NETDEV_TX_OK;
5954a71df50SFrank Blaschka }
5964a71df50SFrank Blaschka 
5973a18d754SJulian Wiedmann static u16 qeth_l2_select_queue(struct net_device *dev, struct sk_buff *skb,
5983a18d754SJulian Wiedmann 				struct net_device *sb_dev)
5993a18d754SJulian Wiedmann {
60073dc2dafSJulian Wiedmann 	struct qeth_card *card = dev->ml_priv;
60173dc2dafSJulian Wiedmann 
60273dc2dafSJulian Wiedmann 	if (IS_IQD(card))
60373dc2dafSJulian Wiedmann 		return qeth_iqd_select_queue(dev, skb,
60458aa2491SJulian Wiedmann 					     qeth_get_ether_cast_type(skb),
6053a18d754SJulian Wiedmann 					     sb_dev);
6061c103cf8SJulian Wiedmann 
6071c103cf8SJulian Wiedmann 	return IS_VM_NIC(card) ? netdev_pick_tx(dev, skb, sb_dev) :
6081c103cf8SJulian Wiedmann 				 qeth_get_priority_queue(card, skb);
6093a18d754SJulian Wiedmann }
6103a18d754SJulian Wiedmann 
6112d2ebb3eSJulian Wiedmann static const struct device_type qeth_l2_devtype = {
6122d2ebb3eSJulian Wiedmann 	.name = "qeth_layer2",
6132d2ebb3eSJulian Wiedmann 	.groups = qeth_l2_attr_groups,
6142d2ebb3eSJulian Wiedmann };
6152d2ebb3eSJulian Wiedmann 
6164a71df50SFrank Blaschka static int qeth_l2_probe_device(struct ccwgroup_device *gdev)
6174a71df50SFrank Blaschka {
6184a71df50SFrank Blaschka 	struct qeth_card *card = dev_get_drvdata(&gdev->dev);
6199111e788SUrsula Braun 	int rc;
6204a71df50SFrank Blaschka 
621fb64de1bSJulian Wiedmann 	if (IS_OSN(card))
622fb64de1bSJulian Wiedmann 		dev_notice(&gdev->dev, "OSN support will be dropped in 2021\n");
623fb64de1bSJulian Wiedmann 
62412b409ddSJulian Wiedmann 	qeth_l2_vnicc_set_defaults(card);
625c8183f54SJulian Wiedmann 	mutex_init(&card->sbp_lock);
62612b409ddSJulian Wiedmann 
6272d2ebb3eSJulian Wiedmann 	if (gdev->dev.type == &qeth_generic_devtype) {
6289111e788SUrsula Braun 		rc = qeth_l2_create_device_attributes(&gdev->dev);
6299111e788SUrsula Braun 		if (rc)
6309111e788SUrsula Braun 			return rc;
6312d2ebb3eSJulian Wiedmann 	}
6325fc692a7SJulian Wiedmann 
633d0c74825SJulian Wiedmann 	INIT_WORK(&card->rx_mode_work, qeth_l2_rx_mode_work);
6344a71df50SFrank Blaschka 	return 0;
6354a71df50SFrank Blaschka }
6364a71df50SFrank Blaschka 
6374a71df50SFrank Blaschka static void qeth_l2_remove_device(struct ccwgroup_device *cgdev)
6384a71df50SFrank Blaschka {
6394a71df50SFrank Blaschka 	struct qeth_card *card = dev_get_drvdata(&cgdev->dev);
6404a71df50SFrank Blaschka 
6412d2ebb3eSJulian Wiedmann 	if (cgdev->dev.type == &qeth_generic_devtype)
642b4d72c08SEugene Crosser 		qeth_l2_remove_device_attributes(&cgdev->dev);
643f2148565SUrsula Braun 	qeth_set_allowed_threads(card, 0, 1);
6444a71df50SFrank Blaschka 	wait_event(card->wait_q, qeth_threads_running(card, 0xffffffff) == 0);
6454a71df50SFrank Blaschka 
64670919e23SUrsula Braun 	if (cgdev->state == CCWGROUP_ONLINE)
64791003f35SJulian Wiedmann 		qeth_set_offline(card, false);
648c2780c1aSJulian Wiedmann 
649c2780c1aSJulian Wiedmann 	cancel_work_sync(&card->close_dev_work);
650cd652be5SJulian Wiedmann 	if (card->dev->reg_state == NETREG_REGISTERED)
6514a71df50SFrank Blaschka 		unregister_netdev(card->dev);
6524a71df50SFrank Blaschka }
6534a71df50SFrank Blaschka 
654d0c74825SJulian Wiedmann static void qeth_l2_set_rx_mode(struct net_device *dev)
655d0c74825SJulian Wiedmann {
656d0c74825SJulian Wiedmann 	struct qeth_card *card = dev->ml_priv;
657d0c74825SJulian Wiedmann 
658d0c74825SJulian Wiedmann 	schedule_work(&card->rx_mode_work);
659d0c74825SJulian Wiedmann }
660d0c74825SJulian Wiedmann 
661a0138f59SAlexandra Winter /**
662a0138f59SAlexandra Winter  *	qeth_l2_pnso() - perform network subchannel operation
663a0138f59SAlexandra Winter  *	@card: qeth_card structure pointer
6644fea49a7SAlexandra Winter  *	@oc: Operation Code
665a0138f59SAlexandra Winter  *	@cnc: Boolean Change-Notification Control
666a0138f59SAlexandra Winter  *	@cb: Callback function will be executed for each element
667a0138f59SAlexandra Winter  *		of the address list
668a0138f59SAlexandra Winter  *	@priv: Pointer to pass to the callback function.
669a0138f59SAlexandra Winter  *
670a0138f59SAlexandra Winter  *	Collects network information in a network address list and calls the
671a0138f59SAlexandra Winter  *	callback function for every entry in the list. If "change-notification-
672a0138f59SAlexandra Winter  *	control" is set, further changes in the address list will be reported
673a0138f59SAlexandra Winter  *	via the IPA command.
674a0138f59SAlexandra Winter  */
6754fea49a7SAlexandra Winter static int qeth_l2_pnso(struct qeth_card *card, u8 oc, int cnc,
676a0138f59SAlexandra Winter 			void (*cb)(void *priv, struct chsc_pnso_naid_l2 *entry),
677a0138f59SAlexandra Winter 			void *priv)
678a0138f59SAlexandra Winter {
679a0138f59SAlexandra Winter 	struct ccw_device *ddev = CARD_DDEV(card);
680a0138f59SAlexandra Winter 	struct chsc_pnso_area *rr;
681a0138f59SAlexandra Winter 	u32 prev_instance = 0;
682a0138f59SAlexandra Winter 	int isfirstblock = 1;
683a0138f59SAlexandra Winter 	int i, size, elems;
684a0138f59SAlexandra Winter 	int rc;
685a0138f59SAlexandra Winter 
686a0138f59SAlexandra Winter 	rr = (struct chsc_pnso_area *)get_zeroed_page(GFP_KERNEL);
687a0138f59SAlexandra Winter 	if (rr == NULL)
688a0138f59SAlexandra Winter 		return -ENOMEM;
689a0138f59SAlexandra Winter 	do {
6904fea49a7SAlexandra Winter 		QETH_CARD_TEXT(card, 2, "PNSO");
691a0138f59SAlexandra Winter 		/* on the first iteration, naihdr.resume_token will be zero */
6924fea49a7SAlexandra Winter 		rc = ccw_device_pnso(ddev, rr, oc, rr->naihdr.resume_token,
6934fea49a7SAlexandra Winter 				     cnc);
694a0138f59SAlexandra Winter 		if (rc)
695a0138f59SAlexandra Winter 			continue;
696a0138f59SAlexandra Winter 		if (cb == NULL)
697a0138f59SAlexandra Winter 			continue;
698a0138f59SAlexandra Winter 
699a0138f59SAlexandra Winter 		size = rr->naihdr.naids;
700a0138f59SAlexandra Winter 		if (size != sizeof(struct chsc_pnso_naid_l2)) {
701a0138f59SAlexandra Winter 			WARN_ON_ONCE(1);
702a0138f59SAlexandra Winter 			continue;
703a0138f59SAlexandra Winter 		}
704a0138f59SAlexandra Winter 
705a0138f59SAlexandra Winter 		elems = (rr->response.length - sizeof(struct chsc_header) -
706a0138f59SAlexandra Winter 			 sizeof(struct chsc_pnso_naihdr)) / size;
707a0138f59SAlexandra Winter 
708a0138f59SAlexandra Winter 		if (!isfirstblock && (rr->naihdr.instance != prev_instance)) {
709a0138f59SAlexandra Winter 			/* Inform the caller that they need to scrap */
710a0138f59SAlexandra Winter 			/* the data that was already reported via cb */
711a0138f59SAlexandra Winter 			rc = -EAGAIN;
712a0138f59SAlexandra Winter 			break;
713a0138f59SAlexandra Winter 		}
714a0138f59SAlexandra Winter 		isfirstblock = 0;
715a0138f59SAlexandra Winter 		prev_instance = rr->naihdr.instance;
716a0138f59SAlexandra Winter 		for (i = 0; i < elems; i++)
717a0138f59SAlexandra Winter 			(*cb)(priv, &rr->entries[i]);
718a0138f59SAlexandra Winter 	} while ((rc == -EBUSY) || (!rc && /* list stored */
719a0138f59SAlexandra Winter 		   /* resume token is non-zero => list incomplete */
720a0138f59SAlexandra Winter 		   (rr->naihdr.resume_token.t1 || rr->naihdr.resume_token.t2)));
721a0138f59SAlexandra Winter 
722a0138f59SAlexandra Winter 	if (rc)
723a0138f59SAlexandra Winter 		QETH_CARD_TEXT_(card, 2, "PNrp%04x", rr->response.code);
724a0138f59SAlexandra Winter 
725a0138f59SAlexandra Winter 	free_page((unsigned long)rr);
726a0138f59SAlexandra Winter 	return rc;
727a0138f59SAlexandra Winter }
728a0138f59SAlexandra Winter 
72910a6cfc0SAlexandra Winter static bool qeth_is_my_net_if_token(struct qeth_card *card,
73010a6cfc0SAlexandra Winter 				    struct net_if_token *token)
73110a6cfc0SAlexandra Winter {
73210a6cfc0SAlexandra Winter 	return ((card->info.ddev_devno == token->devnum) &&
73310a6cfc0SAlexandra Winter 		(card->info.cssid == token->cssid) &&
73410a6cfc0SAlexandra Winter 		(card->info.iid == token->iid) &&
73510a6cfc0SAlexandra Winter 		(card->info.ssid == token->ssid) &&
73610a6cfc0SAlexandra Winter 		(card->info.chpid == token->chpid) &&
73710a6cfc0SAlexandra Winter 		(card->info.chid == token->chid));
73810a6cfc0SAlexandra Winter }
73910a6cfc0SAlexandra Winter 
74010a6cfc0SAlexandra Winter /**
74110a6cfc0SAlexandra Winter  *	qeth_l2_dev2br_fdb_notify() - update fdb of master bridge
74210a6cfc0SAlexandra Winter  *	@card:	qeth_card structure pointer
74310a6cfc0SAlexandra Winter  *	@code:	event bitmask: high order bit 0x80 set to
74410a6cfc0SAlexandra Winter  *				1 - removal of an object
74510a6cfc0SAlexandra Winter  *				0 - addition of an object
74610a6cfc0SAlexandra Winter  *			       Object type(s):
74710a6cfc0SAlexandra Winter  *				0x01 - VLAN, 0x02 - MAC, 0x03 - VLAN and MAC
74810a6cfc0SAlexandra Winter  *	@token: "network token" structure identifying 'physical' location
74910a6cfc0SAlexandra Winter  *		of the target
75010a6cfc0SAlexandra Winter  *	@addr_lnid: structure with MAC address and VLAN ID of the target
75110a6cfc0SAlexandra Winter  */
75210a6cfc0SAlexandra Winter static void qeth_l2_dev2br_fdb_notify(struct qeth_card *card, u8 code,
75310a6cfc0SAlexandra Winter 				      struct net_if_token *token,
75410a6cfc0SAlexandra Winter 				      struct mac_addr_lnid *addr_lnid)
75510a6cfc0SAlexandra Winter {
75610a6cfc0SAlexandra Winter 	struct switchdev_notifier_fdb_info info;
75710a6cfc0SAlexandra Winter 	u8 ntfy_mac[ETH_ALEN];
75810a6cfc0SAlexandra Winter 
75910a6cfc0SAlexandra Winter 	ether_addr_copy(ntfy_mac, addr_lnid->mac);
76010a6cfc0SAlexandra Winter 	/* Ignore VLAN only changes */
76110a6cfc0SAlexandra Winter 	if (!(code & IPA_ADDR_CHANGE_CODE_MACADDR))
76210a6cfc0SAlexandra Winter 		return;
76310a6cfc0SAlexandra Winter 	/* Ignore mcast entries */
76410a6cfc0SAlexandra Winter 	if (is_multicast_ether_addr(ntfy_mac))
76510a6cfc0SAlexandra Winter 		return;
76610a6cfc0SAlexandra Winter 	/* Ignore my own addresses */
76710a6cfc0SAlexandra Winter 	if (qeth_is_my_net_if_token(card, token))
76810a6cfc0SAlexandra Winter 		return;
76910a6cfc0SAlexandra Winter 
77010a6cfc0SAlexandra Winter 	info.addr = ntfy_mac;
77110a6cfc0SAlexandra Winter 	/* don't report VLAN IDs */
77210a6cfc0SAlexandra Winter 	info.vid = 0;
77310a6cfc0SAlexandra Winter 	info.added_by_user = false;
77410a6cfc0SAlexandra Winter 	info.offloaded = true;
77510a6cfc0SAlexandra Winter 
77610a6cfc0SAlexandra Winter 	if (code & IPA_ADDR_CHANGE_CODE_REMOVAL) {
77710a6cfc0SAlexandra Winter 		call_switchdev_notifiers(SWITCHDEV_FDB_DEL_TO_BRIDGE,
77810a6cfc0SAlexandra Winter 					 card->dev, &info.info, NULL);
77910a6cfc0SAlexandra Winter 		QETH_CARD_TEXT(card, 4, "andelmac");
78010a6cfc0SAlexandra Winter 		QETH_CARD_TEXT_(card, 4,
78110a6cfc0SAlexandra Winter 				"mc%012lx", ether_addr_to_u64(ntfy_mac));
78210a6cfc0SAlexandra Winter 	} else {
78310a6cfc0SAlexandra Winter 		call_switchdev_notifiers(SWITCHDEV_FDB_ADD_TO_BRIDGE,
78410a6cfc0SAlexandra Winter 					 card->dev, &info.info, NULL);
78510a6cfc0SAlexandra Winter 		QETH_CARD_TEXT(card, 4, "anaddmac");
78610a6cfc0SAlexandra Winter 		QETH_CARD_TEXT_(card, 4,
78710a6cfc0SAlexandra Winter 				"mc%012lx", ether_addr_to_u64(ntfy_mac));
78810a6cfc0SAlexandra Winter 	}
78910a6cfc0SAlexandra Winter }
79010a6cfc0SAlexandra Winter 
791817741a8SAlexandra Winter static void qeth_l2_dev2br_an_set_cb(void *priv,
792817741a8SAlexandra Winter 				     struct chsc_pnso_naid_l2 *entry)
793817741a8SAlexandra Winter {
794817741a8SAlexandra Winter 	u8 code = IPA_ADDR_CHANGE_CODE_MACADDR;
795817741a8SAlexandra Winter 	struct qeth_card *card = priv;
796817741a8SAlexandra Winter 
797817741a8SAlexandra Winter 	if (entry->addr_lnid.lnid < VLAN_N_VID)
798817741a8SAlexandra Winter 		code |= IPA_ADDR_CHANGE_CODE_VLANID;
799817741a8SAlexandra Winter 	qeth_l2_dev2br_fdb_notify(card, code,
800817741a8SAlexandra Winter 				  (struct net_if_token *)&entry->nit,
801817741a8SAlexandra Winter 				  (struct mac_addr_lnid *)&entry->addr_lnid);
802817741a8SAlexandra Winter }
803817741a8SAlexandra Winter 
804817741a8SAlexandra Winter /**
805817741a8SAlexandra Winter  *	qeth_l2_dev2br_an_set() -
806817741a8SAlexandra Winter  *	Enable or disable 'dev to bridge network address notification'
807817741a8SAlexandra Winter  *	@card: qeth_card structure pointer
808817741a8SAlexandra Winter  *	@enable: Enable or disable 'dev to bridge network address notification'
809817741a8SAlexandra Winter  *
810817741a8SAlexandra Winter  *	Returns negative errno-compatible error indication or 0 on success.
811817741a8SAlexandra Winter  *
812817741a8SAlexandra Winter  *	On enable, emits a series of address notifications for all
813817741a8SAlexandra Winter  *	currently registered hosts.
814817741a8SAlexandra Winter  *
815817741a8SAlexandra Winter  *	Must be called under rtnl_lock
816817741a8SAlexandra Winter  */
817817741a8SAlexandra Winter static int qeth_l2_dev2br_an_set(struct qeth_card *card, bool enable)
818817741a8SAlexandra Winter {
819817741a8SAlexandra Winter 	int rc;
820817741a8SAlexandra Winter 
821817741a8SAlexandra Winter 	if (enable) {
822817741a8SAlexandra Winter 		QETH_CARD_TEXT(card, 2, "anseton");
823817741a8SAlexandra Winter 		rc = qeth_l2_pnso(card, PNSO_OC_NET_ADDR_INFO, 1,
824817741a8SAlexandra Winter 				  qeth_l2_dev2br_an_set_cb, card);
825817741a8SAlexandra Winter 		if (rc == -EAGAIN)
826817741a8SAlexandra Winter 			/* address notification enabled, but inconsistent
827817741a8SAlexandra Winter 			 * addresses reported -> disable address notification
828817741a8SAlexandra Winter 			 */
829817741a8SAlexandra Winter 			qeth_l2_pnso(card, PNSO_OC_NET_ADDR_INFO, 0,
830817741a8SAlexandra Winter 				     NULL, NULL);
831817741a8SAlexandra Winter 	} else {
832817741a8SAlexandra Winter 		QETH_CARD_TEXT(card, 2, "ansetoff");
833817741a8SAlexandra Winter 		rc = qeth_l2_pnso(card, PNSO_OC_NET_ADDR_INFO, 0, NULL, NULL);
834817741a8SAlexandra Winter 	}
835817741a8SAlexandra Winter 
836817741a8SAlexandra Winter 	return rc;
837817741a8SAlexandra Winter }
838817741a8SAlexandra Winter 
8393d58cefdSFrank Blaschka static const struct net_device_ops qeth_l2_netdev_ops = {
840e22355eaSJulian Wiedmann 	.ndo_open		= qeth_open,
841e22355eaSJulian Wiedmann 	.ndo_stop		= qeth_stop,
842b0abc4f5SJulian Wiedmann 	.ndo_get_stats64	= qeth_get_stats64,
8438403b13cSFrank Blaschka 	.ndo_start_xmit		= qeth_l2_hard_start_xmit,
8446d69b1f1SJulian Wiedmann 	.ndo_features_check	= qeth_features_check,
8453a18d754SJulian Wiedmann 	.ndo_select_queue	= qeth_l2_select_queue,
846e22355eaSJulian Wiedmann 	.ndo_validate_addr	= qeth_l2_validate_addr,
847fe5c8028SLakhvich Dmitriy 	.ndo_set_rx_mode	= qeth_l2_set_rx_mode,
848942d6984SJulian Wiedmann 	.ndo_do_ioctl		= qeth_do_ioctl,
8498403b13cSFrank Blaschka 	.ndo_set_mac_address    = qeth_l2_set_mac_address,
8508403b13cSFrank Blaschka 	.ndo_vlan_rx_add_vid	= qeth_l2_vlan_rx_add_vid,
8518403b13cSFrank Blaschka 	.ndo_vlan_rx_kill_vid   = qeth_l2_vlan_rx_kill_vid,
8528403b13cSFrank Blaschka 	.ndo_tx_timeout	   	= qeth_tx_timeout,
8538f43fb00SThomas Richter 	.ndo_fix_features	= qeth_fix_features,
8548f43fb00SThomas Richter 	.ndo_set_features	= qeth_set_features
8558403b13cSFrank Blaschka };
8568403b13cSFrank Blaschka 
8578024cc9eSJulian Wiedmann static const struct net_device_ops qeth_osn_netdev_ops = {
8588024cc9eSJulian Wiedmann 	.ndo_open		= qeth_open,
8598024cc9eSJulian Wiedmann 	.ndo_stop		= qeth_stop,
8608024cc9eSJulian Wiedmann 	.ndo_get_stats64	= qeth_get_stats64,
8618024cc9eSJulian Wiedmann 	.ndo_start_xmit		= qeth_l2_hard_start_xmit,
8628024cc9eSJulian Wiedmann 	.ndo_validate_addr	= eth_validate_addr,
8638024cc9eSJulian Wiedmann 	.ndo_tx_timeout		= qeth_tx_timeout,
8648024cc9eSJulian Wiedmann };
8658024cc9eSJulian Wiedmann 
866cd652be5SJulian Wiedmann static int qeth_l2_setup_netdev(struct qeth_card *card)
8674a71df50SFrank Blaschka {
868d3d1b205SJulian Wiedmann 	int rc;
8694a71df50SFrank Blaschka 
8708024cc9eSJulian Wiedmann 	if (IS_OSN(card)) {
8718024cc9eSJulian Wiedmann 		card->dev->netdev_ops = &qeth_osn_netdev_ops;
87225e2c341SJulian Wiedmann 		card->dev->flags |= IFF_NOARP;
8738024cc9eSJulian Wiedmann 		goto add_napi;
8748024cc9eSJulian Wiedmann 	}
8758024cc9eSJulian Wiedmann 
8761c103cf8SJulian Wiedmann 	rc = qeth_setup_netdev(card);
8771c103cf8SJulian Wiedmann 	if (rc)
8781c103cf8SJulian Wiedmann 		return rc;
8791c103cf8SJulian Wiedmann 
8805f89eca5SJulian Wiedmann 	card->dev->needed_headroom = sizeof(struct qeth_hdr);
8818024cc9eSJulian Wiedmann 	card->dev->netdev_ops = &qeth_l2_netdev_ops;
8828024cc9eSJulian Wiedmann 	card->dev->priv_flags |= IFF_UNICAST_FLT;
8839400c53fSJulian Wiedmann 
8845fc692a7SJulian Wiedmann 	if (IS_OSM(card)) {
8859400c53fSJulian Wiedmann 		card->dev->features |= NETIF_F_VLAN_CHALLENGED;
8865fc692a7SJulian Wiedmann 	} else {
8875fc692a7SJulian Wiedmann 		if (!IS_VM_NIC(card))
8885fc692a7SJulian Wiedmann 			card->dev->hw_features |= NETIF_F_HW_VLAN_CTAG_FILTER;
889f646968fSPatrick McHardy 		card->dev->features |= NETIF_F_HW_VLAN_CTAG_FILTER;
8905fc692a7SJulian Wiedmann 	}
8910f342945SJulian Wiedmann 
892379ac99eSJulian Wiedmann 	if (IS_OSD(card) && !IS_VM_NIC(card)) {
8936d69b1f1SJulian Wiedmann 		card->dev->features |= NETIF_F_SG;
8948f43fb00SThomas Richter 		/* OSA 3S and earlier has no RX/TX support */
8959bdc4411SHans Wippel 		if (qeth_is_supported(card, IPA_OUTBOUND_CHECKSUM)) {
8968f43fb00SThomas Richter 			card->dev->hw_features |= NETIF_F_IP_CSUM;
8979bdc4411SHans Wippel 			card->dev->vlan_features |= NETIF_F_IP_CSUM;
8989bdc4411SHans Wippel 		}
8994d7def2aSThomas Richter 	}
900571f9dd8SKittipon Meesompop 	if (qeth_is_supported6(card, IPA_OUTBOUND_CHECKSUM_V6)) {
901571f9dd8SKittipon Meesompop 		card->dev->hw_features |= NETIF_F_IPV6_CSUM;
902571f9dd8SKittipon Meesompop 		card->dev->vlan_features |= NETIF_F_IPV6_CSUM;
903571f9dd8SKittipon Meesompop 	}
904d7e6ed97SKittipon Meesompop 	if (qeth_is_supported(card, IPA_INBOUND_CHECKSUM) ||
905d7e6ed97SKittipon Meesompop 	    qeth_is_supported6(card, IPA_INBOUND_CHECKSUM_V6)) {
906d7e6ed97SKittipon Meesompop 		card->dev->hw_features |= NETIF_F_RXCSUM;
907d7e6ed97SKittipon Meesompop 		card->dev->vlan_features |= NETIF_F_RXCSUM;
908d7e6ed97SKittipon Meesompop 	}
9090aef8392SJulian Wiedmann 	if (qeth_is_supported(card, IPA_OUTBOUND_TSO)) {
9100aef8392SJulian Wiedmann 		card->dev->hw_features |= NETIF_F_TSO;
9110aef8392SJulian Wiedmann 		card->dev->vlan_features |= NETIF_F_TSO;
9120aef8392SJulian Wiedmann 	}
9130aef8392SJulian Wiedmann 	if (qeth_is_supported6(card, IPA_OUTBOUND_TSO)) {
9140aef8392SJulian Wiedmann 		card->dev->hw_features |= NETIF_F_TSO6;
9150aef8392SJulian Wiedmann 		card->dev->vlan_features |= NETIF_F_TSO6;
9160aef8392SJulian Wiedmann 	}
9170aef8392SJulian Wiedmann 
9180aef8392SJulian Wiedmann 	if (card->dev->hw_features & (NETIF_F_TSO | NETIF_F_TSO6)) {
9190aef8392SJulian Wiedmann 		card->dev->needed_headroom = sizeof(struct qeth_hdr_tso);
920c619e9a6SJulian Wiedmann 		netif_keep_dst(card->dev);
9210aef8392SJulian Wiedmann 		netif_set_gso_max_size(card->dev,
9220aef8392SJulian Wiedmann 				       PAGE_SIZE * (QDIO_MAX_ELEMENTS_PER_BUFFER - 1));
9230aef8392SJulian Wiedmann 	}
9240d6f02d3SJulian Wiedmann 
9258024cc9eSJulian Wiedmann add_napi:
926d73ef324SJulian Wiedmann 	netif_napi_add(card->dev, &card->napi, qeth_poll, QETH_NAPI_WEIGHT);
927cd652be5SJulian Wiedmann 	return register_netdev(card->dev);
9284a71df50SFrank Blaschka }
9294a71df50SFrank Blaschka 
930521c10eaSJulian Wiedmann static void qeth_l2_trace_features(struct qeth_card *card)
931521c10eaSJulian Wiedmann {
932a45b3fafSHans Wippel 	/* Set BridgePort features */
933a45b3fafSHans Wippel 	QETH_CARD_TEXT(card, 2, "featuSBP");
934521c10eaSJulian Wiedmann 	QETH_CARD_HEX(card, 2, &card->options.sbp.supported_funcs,
935521c10eaSJulian Wiedmann 		      sizeof(card->options.sbp.supported_funcs));
936a45b3fafSHans Wippel 	/* VNIC Characteristics features */
937a45b3fafSHans Wippel 	QETH_CARD_TEXT(card, 2, "feaVNICC");
938a45b3fafSHans Wippel 	QETH_CARD_HEX(card, 2, &card->options.vnicc.sup_chars,
939a45b3fafSHans Wippel 		      sizeof(card->options.vnicc.sup_chars));
940521c10eaSJulian Wiedmann }
941521c10eaSJulian Wiedmann 
9428ca769e2SJulian Wiedmann static void qeth_l2_setup_bridgeport_attrs(struct qeth_card *card)
9438ca769e2SJulian Wiedmann {
9448ca769e2SJulian Wiedmann 	if (!card->options.sbp.reflect_promisc &&
9458ca769e2SJulian Wiedmann 	    card->options.sbp.role != QETH_SBP_ROLE_NONE) {
9468ca769e2SJulian Wiedmann 		/* Conditional to avoid spurious error messages */
9478ca769e2SJulian Wiedmann 		qeth_bridgeport_setrole(card, card->options.sbp.role);
9488ca769e2SJulian Wiedmann 		/* Let the callback function refresh the stored role value. */
9498ca769e2SJulian Wiedmann 		qeth_bridgeport_query_ports(card, &card->options.sbp.role,
9508ca769e2SJulian Wiedmann 					    NULL);
9518ca769e2SJulian Wiedmann 	}
9528ca769e2SJulian Wiedmann 	if (card->options.sbp.hostnotification) {
9538ca769e2SJulian Wiedmann 		if (qeth_bridgeport_an_set(card, 1))
9548ca769e2SJulian Wiedmann 			card->options.sbp.hostnotification = 0;
9558ca769e2SJulian Wiedmann 	}
9568ca769e2SJulian Wiedmann }
9578ca769e2SJulian Wiedmann 
958fa115adfSAlexandra Winter /**
959fa115adfSAlexandra Winter  *	qeth_l2_detect_dev2br_support() -
960fa115adfSAlexandra Winter  *	Detect whether this card supports 'dev to bridge fdb network address
961fa115adfSAlexandra Winter  *	change notification' and thus can support the learning_sync bridgeport
962fa115adfSAlexandra Winter  *	attribute
963fa115adfSAlexandra Winter  *	@card: qeth_card structure pointer
964fa115adfSAlexandra Winter  *
965fa115adfSAlexandra Winter  *	This is a destructive test and must be called before dev2br or
966fa115adfSAlexandra Winter  *	bridgeport address notification is enabled!
967fa115adfSAlexandra Winter  */
968fa115adfSAlexandra Winter static void qeth_l2_detect_dev2br_support(struct qeth_card *card)
969fa115adfSAlexandra Winter {
970fa115adfSAlexandra Winter 	struct qeth_priv *priv = netdev_priv(card->dev);
971fa115adfSAlexandra Winter 	bool dev2br_supported;
972fa115adfSAlexandra Winter 	int rc;
973fa115adfSAlexandra Winter 
974fa115adfSAlexandra Winter 	QETH_CARD_TEXT(card, 2, "d2brsup");
975fa115adfSAlexandra Winter 	if (!IS_IQD(card))
976fa115adfSAlexandra Winter 		return;
977fa115adfSAlexandra Winter 
978fa115adfSAlexandra Winter 	/* dev2br requires valid cssid,iid,chid */
979fa115adfSAlexandra Winter 	if (!card->info.ids_valid) {
980fa115adfSAlexandra Winter 		dev2br_supported = false;
981fa115adfSAlexandra Winter 	} else if (css_general_characteristics.enarf) {
982fa115adfSAlexandra Winter 		dev2br_supported = true;
983fa115adfSAlexandra Winter 	} else {
984fa115adfSAlexandra Winter 		/* Old machines don't have the feature bit:
985fa115adfSAlexandra Winter 		 * Probe by testing whether a disable succeeds
986fa115adfSAlexandra Winter 		 */
987fa115adfSAlexandra Winter 		rc = qeth_l2_pnso(card, PNSO_OC_NET_ADDR_INFO, 0, NULL, NULL);
988fa115adfSAlexandra Winter 		dev2br_supported = !rc;
989fa115adfSAlexandra Winter 	}
990fa115adfSAlexandra Winter 	QETH_CARD_TEXT_(card, 2, "D2Bsup%02x", dev2br_supported);
991fa115adfSAlexandra Winter 
992fa115adfSAlexandra Winter 	if (dev2br_supported)
993fa115adfSAlexandra Winter 		priv->brport_hw_features |= BR_LEARNING_SYNC;
994fa115adfSAlexandra Winter 	else
995fa115adfSAlexandra Winter 		priv->brport_hw_features &= ~BR_LEARNING_SYNC;
996fa115adfSAlexandra Winter }
997fa115adfSAlexandra Winter 
99891003f35SJulian Wiedmann static int qeth_l2_set_online(struct qeth_card *card)
9994a71df50SFrank Blaschka {
100091003f35SJulian Wiedmann 	struct ccwgroup_device *gdev = card->gdev;
1001fa3d2e60SJulian Wiedmann 	struct net_device *dev = card->dev;
10024a71df50SFrank Blaschka 	int rc = 0;
10039fae5c3bSJulian Wiedmann 	bool carrier_ok;
10044a71df50SFrank Blaschka 
10059fae5c3bSJulian Wiedmann 	rc = qeth_core_hardsetup_card(card, &carrier_ok);
10064a71df50SFrank Blaschka 	if (rc) {
100757a688aaSJulian Wiedmann 		QETH_CARD_TEXT_(card, 2, "2err%04x", rc);
1008aa909224SUrsula Braun 		rc = -ENODEV;
10094a71df50SFrank Blaschka 		goto out_remove;
10104a71df50SFrank Blaschka 	}
1011654e3d48SJulian Wiedmann 
1012fa115adfSAlexandra Winter 	/* query before bridgeport_notification may be enabled */
1013fa115adfSAlexandra Winter 	qeth_l2_detect_dev2br_support(card);
1014fa115adfSAlexandra Winter 
1015c8183f54SJulian Wiedmann 	mutex_lock(&card->sbp_lock);
1016d7ef489fSJulian Wiedmann 	qeth_bridgeport_query_support(card);
10178ca769e2SJulian Wiedmann 	if (card->options.sbp.supported_funcs) {
10188ca769e2SJulian Wiedmann 		qeth_l2_setup_bridgeport_attrs(card);
1019d7ef489fSJulian Wiedmann 		dev_info(&card->gdev->dev,
1020d7ef489fSJulian Wiedmann 			 "The device represents a Bridge Capable Port\n");
10218ca769e2SJulian Wiedmann 	}
1022c8183f54SJulian Wiedmann 	mutex_unlock(&card->sbp_lock);
1023d7ef489fSJulian Wiedmann 
1024d7ef489fSJulian Wiedmann 	qeth_l2_register_dev_addr(card);
1025d7ef489fSJulian Wiedmann 
1026a45b3fafSHans Wippel 	/* for the rx_bcast characteristic, init VNICC after setmac */
1027a45b3fafSHans Wippel 	qeth_l2_vnicc_init(card);
1028a45b3fafSHans Wippel 
1029a45b3fafSHans Wippel 	qeth_trace_features(card);
1030a45b3fafSHans Wippel 	qeth_l2_trace_features(card);
1031a45b3fafSHans Wippel 
10324a71df50SFrank Blaschka 	qeth_print_status_message(card);
10334a71df50SFrank Blaschka 
10344a71df50SFrank Blaschka 	/* softsetup */
103557a688aaSJulian Wiedmann 	QETH_CARD_TEXT(card, 2, "softsetp");
10364a71df50SFrank Blaschka 
10374a71df50SFrank Blaschka 	card->state = CARD_STATE_SOFTSETUP;
10384a71df50SFrank Blaschka 
10394a71df50SFrank Blaschka 	qeth_set_allowed_threads(card, 0xffffffff, 0);
1040d025da9eSJulian Wiedmann 
1041cd652be5SJulian Wiedmann 	if (dev->reg_state != NETREG_REGISTERED) {
1042cd652be5SJulian Wiedmann 		rc = qeth_l2_setup_netdev(card);
1043fa3d2e60SJulian Wiedmann 		if (rc)
1044fa3d2e60SJulian Wiedmann 			goto out_remove;
1045cd652be5SJulian Wiedmann 
1046cd652be5SJulian Wiedmann 		if (carrier_ok)
1047cd652be5SJulian Wiedmann 			netif_carrier_on(dev);
10484a71df50SFrank Blaschka 	} else {
10494a71df50SFrank Blaschka 		rtnl_lock();
1050fa3d2e60SJulian Wiedmann 		if (carrier_ok)
1051fa3d2e60SJulian Wiedmann 			netif_carrier_on(dev);
1052fa3d2e60SJulian Wiedmann 		else
1053fa3d2e60SJulian Wiedmann 			netif_carrier_off(dev);
1054fa3d2e60SJulian Wiedmann 
1055e6e771b3SJulian Wiedmann 		netif_device_attach(dev);
1056fa3d2e60SJulian Wiedmann 		qeth_enable_hw_features(dev);
1057fa3d2e60SJulian Wiedmann 
1058d7d543f2SJulian Wiedmann 		if (card->info.open_when_online) {
1059d7d543f2SJulian Wiedmann 			card->info.open_when_online = 0;
1060fa3d2e60SJulian Wiedmann 			dev_open(dev, NULL);
1061fa3d2e60SJulian Wiedmann 		}
1062fa3d2e60SJulian Wiedmann 		rtnl_unlock();
10634a71df50SFrank Blaschka 	}
10644a71df50SFrank Blaschka 	/* let user_space know that device is online */
10654a71df50SFrank Blaschka 	kobject_uevent(&gdev->dev.kobj, KOBJ_CHANGE);
10664a71df50SFrank Blaschka 	return 0;
1067aa909224SUrsula Braun 
10684a71df50SFrank Blaschka out_remove:
1069d4560150SJulian Wiedmann 	qeth_l2_stop_card(card);
1070f9e50b02SJulian Wiedmann 	qeth_stop_channel(&card->data);
1071f9e50b02SJulian Wiedmann 	qeth_stop_channel(&card->write);
1072f9e50b02SJulian Wiedmann 	qeth_stop_channel(&card->read);
107322ae2790SUrsula Braun 	qdio_free(CARD_DDEV(card));
1074aa909224SUrsula Braun 	return rc;
10754a71df50SFrank Blaschka }
10764a71df50SFrank Blaschka 
107791003f35SJulian Wiedmann static void qeth_l2_set_offline(struct qeth_card *card)
10784a71df50SFrank Blaschka {
1079d4560150SJulian Wiedmann 	qeth_l2_stop_card(card);
10804a71df50SFrank Blaschka }
10814a71df50SFrank Blaschka 
10824a71df50SFrank Blaschka static int __init qeth_l2_init(void)
10834a71df50SFrank Blaschka {
108474eacdb9SFrank Blaschka 	pr_info("register layer 2 discipline\n");
10854a71df50SFrank Blaschka 	return 0;
10864a71df50SFrank Blaschka }
10874a71df50SFrank Blaschka 
10884a71df50SFrank Blaschka static void __exit qeth_l2_exit(void)
10894a71df50SFrank Blaschka {
109074eacdb9SFrank Blaschka 	pr_info("unregister layer 2 discipline\n");
10914a71df50SFrank Blaschka }
10924a71df50SFrank Blaschka 
1093c044dc21SEugene Crosser /* Returns zero if the command is successfully "consumed" */
1094c044dc21SEugene Crosser static int qeth_l2_control_event(struct qeth_card *card,
1095c044dc21SEugene Crosser 					struct qeth_ipa_cmd *cmd)
1096c044dc21SEugene Crosser {
1097c044dc21SEugene Crosser 	switch (cmd->hdr.command) {
10989c23f4daSEugene Crosser 	case IPA_CMD_SETBRIDGEPORT_OSA:
10999c23f4daSEugene Crosser 	case IPA_CMD_SETBRIDGEPORT_IQD:
1100c044dc21SEugene Crosser 		if (cmd->data.sbp.hdr.command_code ==
1101c044dc21SEugene Crosser 				IPA_SBP_BRIDGE_PORT_STATE_CHANGE) {
1102c044dc21SEugene Crosser 			qeth_bridge_state_change(card, cmd);
1103c044dc21SEugene Crosser 			return 0;
1104c044dc21SEugene Crosser 		} else
1105c044dc21SEugene Crosser 			return 1;
1106c044dc21SEugene Crosser 	case IPA_CMD_ADDRESS_CHANGE_NOTIF:
1107a0138f59SAlexandra Winter 		qeth_addr_change_event(card, cmd);
1108c044dc21SEugene Crosser 		return 0;
1109c044dc21SEugene Crosser 	default:
1110c044dc21SEugene Crosser 		return 1;
1111c044dc21SEugene Crosser 	}
1112c044dc21SEugene Crosser }
1113c044dc21SEugene Crosser 
1114c041f2d4SSebastian Ott struct qeth_discipline qeth_l2_discipline = {
11152d2ebb3eSJulian Wiedmann 	.devtype = &qeth_l2_devtype,
1116c041f2d4SSebastian Ott 	.setup = qeth_l2_probe_device,
11174a71df50SFrank Blaschka 	.remove = qeth_l2_remove_device,
11184a71df50SFrank Blaschka 	.set_online = qeth_l2_set_online,
11194a71df50SFrank Blaschka 	.set_offline = qeth_l2_set_offline,
1120942d6984SJulian Wiedmann 	.do_ioctl = NULL,
1121c044dc21SEugene Crosser 	.control_event_handler = qeth_l2_control_event,
11224a71df50SFrank Blaschka };
1123c041f2d4SSebastian Ott EXPORT_SYMBOL_GPL(qeth_l2_discipline);
11244a71df50SFrank Blaschka 
11254e2b5aa5SJulian Wiedmann #ifdef CONFIG_QETH_OSN
11260ce37ec2SJulian Wiedmann static void qeth_osn_assist_cb(struct qeth_card *card,
112712fc286fSJulian Wiedmann 			       struct qeth_cmd_buffer *iob,
112812fc286fSJulian Wiedmann 			       unsigned int data_length)
11294a71df50SFrank Blaschka {
1130308946b0SJulian Wiedmann 	qeth_notify_cmd(iob, 0);
1131c3b2218dSJulian Wiedmann 	qeth_put_cmd(iob);
11324a71df50SFrank Blaschka }
11334a71df50SFrank Blaschka 
11344a71df50SFrank Blaschka int qeth_osn_assist(struct net_device *dev, void *data, int data_len)
11354a71df50SFrank Blaschka {
11364a71df50SFrank Blaschka 	struct qeth_cmd_buffer *iob;
11374a71df50SFrank Blaschka 	struct qeth_card *card;
11384a71df50SFrank Blaschka 
1139a59d121dSJulian Wiedmann 	if (data_len < 0)
1140a59d121dSJulian Wiedmann 		return -EINVAL;
11414a71df50SFrank Blaschka 	if (!dev)
11424a71df50SFrank Blaschka 		return -ENODEV;
1143509e2562SHeiko Carstens 	card = dev->ml_priv;
11444a71df50SFrank Blaschka 	if (!card)
11454a71df50SFrank Blaschka 		return -ENODEV;
1146847a50fdSCarsten Otte 	QETH_CARD_TEXT(card, 2, "osnsdmc");
1147c3521254SEugene Crosser 	if (!qeth_card_hw_is_reachable(card))
11484a71df50SFrank Blaschka 		return -ENODEV;
1149a59d121dSJulian Wiedmann 
1150a59d121dSJulian Wiedmann 	iob = qeth_alloc_cmd(&card->write, IPA_PDU_HEADER_SIZE + data_len, 1,
1151a59d121dSJulian Wiedmann 			     QETH_IPA_TIMEOUT);
11521273a800SJulian Wiedmann 	if (!iob)
11531273a800SJulian Wiedmann 		return -ENOMEM;
11541273a800SJulian Wiedmann 
11557f92316cSJulian Wiedmann 	qeth_prepare_ipa_cmd(card, iob, (u16) data_len, NULL);
11567f92316cSJulian Wiedmann 
1157ff5caa7aSJulian Wiedmann 	memcpy(__ipa_cmd(iob), data, data_len);
11580ce37ec2SJulian Wiedmann 	iob->callback = qeth_osn_assist_cb;
11590ce37ec2SJulian Wiedmann 	return qeth_send_ipa_cmd(card, iob, NULL, NULL);
11604a71df50SFrank Blaschka }
11614a71df50SFrank Blaschka EXPORT_SYMBOL(qeth_osn_assist);
11624a71df50SFrank Blaschka 
11634a71df50SFrank Blaschka int qeth_osn_register(unsigned char *read_dev_no, struct net_device **dev,
11644a71df50SFrank Blaschka 		  int (*assist_cb)(struct net_device *, void *),
11654a71df50SFrank Blaschka 		  int (*data_cb)(struct sk_buff *))
11664a71df50SFrank Blaschka {
11674a71df50SFrank Blaschka 	struct qeth_card *card;
1168d7d18da1SJulian Wiedmann 	char bus_id[16];
1169d7d18da1SJulian Wiedmann 	u16 devno;
11704a71df50SFrank Blaschka 
1171d7d18da1SJulian Wiedmann 	memcpy(&devno, read_dev_no, 2);
1172d7d18da1SJulian Wiedmann 	sprintf(bus_id, "0.0.%04x", devno);
1173d7d18da1SJulian Wiedmann 	card = qeth_get_card_by_busid(bus_id);
1174d7d18da1SJulian Wiedmann 	if (!card || !IS_OSN(card))
11754a71df50SFrank Blaschka 		return -ENODEV;
1176d7d18da1SJulian Wiedmann 	*dev = card->dev;
1177d7d18da1SJulian Wiedmann 
1178847a50fdSCarsten Otte 	QETH_CARD_TEXT(card, 2, "osnreg");
11794a71df50SFrank Blaschka 	if ((assist_cb == NULL) || (data_cb == NULL))
11804a71df50SFrank Blaschka 		return -EINVAL;
11814a71df50SFrank Blaschka 	card->osn_info.assist_cb = assist_cb;
11824a71df50SFrank Blaschka 	card->osn_info.data_cb = data_cb;
11834a71df50SFrank Blaschka 	return 0;
11844a71df50SFrank Blaschka }
11854a71df50SFrank Blaschka EXPORT_SYMBOL(qeth_osn_register);
11864a71df50SFrank Blaschka 
11874a71df50SFrank Blaschka void qeth_osn_deregister(struct net_device *dev)
11884a71df50SFrank Blaschka {
11894a71df50SFrank Blaschka 	struct qeth_card *card;
11904a71df50SFrank Blaschka 
11914a71df50SFrank Blaschka 	if (!dev)
11924a71df50SFrank Blaschka 		return;
1193509e2562SHeiko Carstens 	card = dev->ml_priv;
11944a71df50SFrank Blaschka 	if (!card)
11954a71df50SFrank Blaschka 		return;
1196847a50fdSCarsten Otte 	QETH_CARD_TEXT(card, 2, "osndereg");
11974a71df50SFrank Blaschka 	card->osn_info.assist_cb = NULL;
11984a71df50SFrank Blaschka 	card->osn_info.data_cb = NULL;
11994a71df50SFrank Blaschka 	return;
12004a71df50SFrank Blaschka }
12014a71df50SFrank Blaschka EXPORT_SYMBOL(qeth_osn_deregister);
12024e2b5aa5SJulian Wiedmann #endif
12034a71df50SFrank Blaschka 
1204b4d72c08SEugene Crosser /* SETBRIDGEPORT support, async notifications */
1205b4d72c08SEugene Crosser 
12069f48b9dbSEugene Crosser enum qeth_an_event_type {anev_reg_unreg, anev_abort, anev_reset};
12079f48b9dbSEugene Crosser 
12089f48b9dbSEugene Crosser /**
12099f48b9dbSEugene Crosser  * qeth_bridge_emit_host_event() - bridgeport address change notification
12109f48b9dbSEugene Crosser  * @card:  qeth_card structure pointer, for udev events.
12119f48b9dbSEugene Crosser  * @evtype:  "normal" register/unregister, or abort, or reset. For abort
12129f48b9dbSEugene Crosser  *	      and reset token and addr_lnid are unused and may be NULL.
12139f48b9dbSEugene Crosser  * @code:  event bitmask: high order bit 0x80 value 1 means removal of an
12149f48b9dbSEugene Crosser  *			  object, 0 - addition of an object.
12159f48b9dbSEugene Crosser  *			  0x01 - VLAN, 0x02 - MAC, 0x03 - VLAN and MAC.
12169f48b9dbSEugene Crosser  * @token: "network token" structure identifying physical address of the port.
12179f48b9dbSEugene Crosser  * @addr_lnid: pointer to structure with MAC address and VLAN ID.
12189f48b9dbSEugene Crosser  *
12199f48b9dbSEugene Crosser  * This function is called when registrations and deregistrations are
12209f48b9dbSEugene Crosser  * reported by the hardware, and also when notifications are enabled -
12219f48b9dbSEugene Crosser  * for all currently registered addresses.
12229f48b9dbSEugene Crosser  */
12239f48b9dbSEugene Crosser static void qeth_bridge_emit_host_event(struct qeth_card *card,
12249f48b9dbSEugene Crosser 					enum qeth_an_event_type evtype,
1225a0138f59SAlexandra Winter 					u8 code,
1226a0138f59SAlexandra Winter 					struct net_if_token *token,
1227a0138f59SAlexandra Winter 					struct mac_addr_lnid *addr_lnid)
12289f48b9dbSEugene Crosser {
12299f48b9dbSEugene Crosser 	char str[7][32];
12309f48b9dbSEugene Crosser 	char *env[8];
12319f48b9dbSEugene Crosser 	int i = 0;
12329f48b9dbSEugene Crosser 
12339f48b9dbSEugene Crosser 	switch (evtype) {
12349f48b9dbSEugene Crosser 	case anev_reg_unreg:
12359f48b9dbSEugene Crosser 		snprintf(str[i], sizeof(str[i]), "BRIDGEDHOST=%s",
12369f48b9dbSEugene Crosser 				(code & IPA_ADDR_CHANGE_CODE_REMOVAL)
12379f48b9dbSEugene Crosser 				? "deregister" : "register");
12389f48b9dbSEugene Crosser 		env[i] = str[i]; i++;
12399f48b9dbSEugene Crosser 		if (code & IPA_ADDR_CHANGE_CODE_VLANID) {
12409f48b9dbSEugene Crosser 			snprintf(str[i], sizeof(str[i]), "VLAN=%d",
12419f48b9dbSEugene Crosser 				addr_lnid->lnid);
12429f48b9dbSEugene Crosser 			env[i] = str[i]; i++;
12439f48b9dbSEugene Crosser 		}
12449f48b9dbSEugene Crosser 		if (code & IPA_ADDR_CHANGE_CODE_MACADDR) {
12459846e70bSEugene Crosser 			snprintf(str[i], sizeof(str[i]), "MAC=%pM",
12469846e70bSEugene Crosser 				addr_lnid->mac);
12479f48b9dbSEugene Crosser 			env[i] = str[i]; i++;
12489f48b9dbSEugene Crosser 		}
12499f48b9dbSEugene Crosser 		snprintf(str[i], sizeof(str[i]), "NTOK_BUSID=%x.%x.%04x",
12509f48b9dbSEugene Crosser 			token->cssid, token->ssid, token->devnum);
12519f48b9dbSEugene Crosser 		env[i] = str[i]; i++;
12529f48b9dbSEugene Crosser 		snprintf(str[i], sizeof(str[i]), "NTOK_IID=%02x", token->iid);
12539f48b9dbSEugene Crosser 		env[i] = str[i]; i++;
12549f48b9dbSEugene Crosser 		snprintf(str[i], sizeof(str[i]), "NTOK_CHPID=%02x",
12559f48b9dbSEugene Crosser 				token->chpid);
12569f48b9dbSEugene Crosser 		env[i] = str[i]; i++;
12579f48b9dbSEugene Crosser 		snprintf(str[i], sizeof(str[i]), "NTOK_CHID=%04x", token->chid);
12589f48b9dbSEugene Crosser 		env[i] = str[i]; i++;
12599f48b9dbSEugene Crosser 		break;
12609f48b9dbSEugene Crosser 	case anev_abort:
12619f48b9dbSEugene Crosser 		snprintf(str[i], sizeof(str[i]), "BRIDGEDHOST=abort");
12629f48b9dbSEugene Crosser 		env[i] = str[i]; i++;
12639f48b9dbSEugene Crosser 		break;
12649f48b9dbSEugene Crosser 	case anev_reset:
12659f48b9dbSEugene Crosser 		snprintf(str[i], sizeof(str[i]), "BRIDGEDHOST=reset");
12669f48b9dbSEugene Crosser 		env[i] = str[i]; i++;
12679f48b9dbSEugene Crosser 		break;
12689f48b9dbSEugene Crosser 	}
12699f48b9dbSEugene Crosser 	env[i] = NULL;
12709f48b9dbSEugene Crosser 	kobject_uevent_env(&card->gdev->dev.kobj, KOBJ_CHANGE, env);
12719f48b9dbSEugene Crosser }
12729f48b9dbSEugene Crosser 
1273b4d72c08SEugene Crosser struct qeth_bridge_state_data {
1274b4d72c08SEugene Crosser 	struct work_struct worker;
1275b4d72c08SEugene Crosser 	struct qeth_card *card;
127661c6f217SJulian Wiedmann 	u8 role;
127761c6f217SJulian Wiedmann 	u8 state;
1278b4d72c08SEugene Crosser };
1279b4d72c08SEugene Crosser 
1280b4d72c08SEugene Crosser static void qeth_bridge_state_change_worker(struct work_struct *work)
1281b4d72c08SEugene Crosser {
1282b4d72c08SEugene Crosser 	struct qeth_bridge_state_data *data =
1283b4d72c08SEugene Crosser 		container_of(work, struct qeth_bridge_state_data, worker);
1284b4d72c08SEugene Crosser 	char env_locrem[32];
1285b4d72c08SEugene Crosser 	char env_role[32];
1286b4d72c08SEugene Crosser 	char env_state[32];
1287b4d72c08SEugene Crosser 	char *env[] = {
1288b4d72c08SEugene Crosser 		env_locrem,
1289b4d72c08SEugene Crosser 		env_role,
1290b4d72c08SEugene Crosser 		env_state,
1291b4d72c08SEugene Crosser 		NULL
1292b4d72c08SEugene Crosser 	};
1293b4d72c08SEugene Crosser 
1294b4d72c08SEugene Crosser 	snprintf(env_locrem, sizeof(env_locrem), "BRIDGEPORT=statechange");
1295b4d72c08SEugene Crosser 	snprintf(env_role, sizeof(env_role), "ROLE=%s",
129661c6f217SJulian Wiedmann 		(data->role == QETH_SBP_ROLE_NONE) ? "none" :
129761c6f217SJulian Wiedmann 		(data->role == QETH_SBP_ROLE_PRIMARY) ? "primary" :
129861c6f217SJulian Wiedmann 		(data->role == QETH_SBP_ROLE_SECONDARY) ? "secondary" :
1299b4d72c08SEugene Crosser 		"<INVALID>");
1300b4d72c08SEugene Crosser 	snprintf(env_state, sizeof(env_state), "STATE=%s",
130161c6f217SJulian Wiedmann 		(data->state == QETH_SBP_STATE_INACTIVE) ? "inactive" :
130261c6f217SJulian Wiedmann 		(data->state == QETH_SBP_STATE_STANDBY) ? "standby" :
130361c6f217SJulian Wiedmann 		(data->state == QETH_SBP_STATE_ACTIVE) ? "active" :
1304b4d72c08SEugene Crosser 		"<INVALID>");
1305b4d72c08SEugene Crosser 	kobject_uevent_env(&data->card->gdev->dev.kobj,
1306b4d72c08SEugene Crosser 				KOBJ_CHANGE, env);
1307b4d72c08SEugene Crosser 	kfree(data);
1308b4d72c08SEugene Crosser }
1309b4d72c08SEugene Crosser 
1310c044dc21SEugene Crosser static void qeth_bridge_state_change(struct qeth_card *card,
1311c044dc21SEugene Crosser 					struct qeth_ipa_cmd *cmd)
1312b4d72c08SEugene Crosser {
131365b0494eSJulian Wiedmann 	struct qeth_sbp_port_data *qports = &cmd->data.sbp.data.port_data;
1314b4d72c08SEugene Crosser 	struct qeth_bridge_state_data *data;
1315b4d72c08SEugene Crosser 
1316b4d72c08SEugene Crosser 	QETH_CARD_TEXT(card, 2, "brstchng");
131702472e28SJulian Wiedmann 	if (qports->num_entries == 0) {
131802472e28SJulian Wiedmann 		QETH_CARD_TEXT(card, 2, "BPempty");
131902472e28SJulian Wiedmann 		return;
132002472e28SJulian Wiedmann 	}
1321b4d72c08SEugene Crosser 	if (qports->entry_length != sizeof(struct qeth_sbp_port_entry)) {
13228a593148SThomas Richter 		QETH_CARD_TEXT_(card, 2, "BPsz%04x", qports->entry_length);
1323b4d72c08SEugene Crosser 		return;
1324b4d72c08SEugene Crosser 	}
132561c6f217SJulian Wiedmann 
132661c6f217SJulian Wiedmann 	data = kzalloc(sizeof(*data), GFP_ATOMIC);
1327b4d72c08SEugene Crosser 	if (!data) {
1328b4d72c08SEugene Crosser 		QETH_CARD_TEXT(card, 2, "BPSalloc");
1329b4d72c08SEugene Crosser 		return;
1330b4d72c08SEugene Crosser 	}
1331b4d72c08SEugene Crosser 	INIT_WORK(&data->worker, qeth_bridge_state_change_worker);
1332b4d72c08SEugene Crosser 	data->card = card;
133361c6f217SJulian Wiedmann 	/* Information for the local port: */
133461c6f217SJulian Wiedmann 	data->role = qports->entry[0].role;
133561c6f217SJulian Wiedmann 	data->state = qports->entry[0].state;
133661c6f217SJulian Wiedmann 
1337c0a2e4d1SJulian Wiedmann 	queue_work(card->event_wq, &data->worker);
1338b4d72c08SEugene Crosser }
1339b4d72c08SEugene Crosser 
1340a0138f59SAlexandra Winter struct qeth_addr_change_data {
13419d6a569aSJulian Wiedmann 	struct delayed_work dwork;
13429f48b9dbSEugene Crosser 	struct qeth_card *card;
1343a0138f59SAlexandra Winter 	struct qeth_ipacmd_addr_change ac_event;
13449f48b9dbSEugene Crosser };
13459f48b9dbSEugene Crosser 
134610a6cfc0SAlexandra Winter static void qeth_l2_dev2br_worker(struct work_struct *work)
134710a6cfc0SAlexandra Winter {
134810a6cfc0SAlexandra Winter 	struct delayed_work *dwork = to_delayed_work(work);
134910a6cfc0SAlexandra Winter 	struct qeth_addr_change_data *data;
135010a6cfc0SAlexandra Winter 	struct qeth_card *card;
1351817741a8SAlexandra Winter 	struct qeth_priv *priv;
135210a6cfc0SAlexandra Winter 	unsigned int i;
1353817741a8SAlexandra Winter 	int rc;
135410a6cfc0SAlexandra Winter 
135510a6cfc0SAlexandra Winter 	data = container_of(dwork, struct qeth_addr_change_data, dwork);
135610a6cfc0SAlexandra Winter 	card = data->card;
1357817741a8SAlexandra Winter 	priv = netdev_priv(card->dev);
135810a6cfc0SAlexandra Winter 
135910a6cfc0SAlexandra Winter 	QETH_CARD_TEXT(card, 4, "dev2brew");
136010a6cfc0SAlexandra Winter 
136110a6cfc0SAlexandra Winter 	if (READ_ONCE(card->info.pnso_mode) == QETH_PNSO_NONE)
136210a6cfc0SAlexandra Winter 		goto free;
136310a6cfc0SAlexandra Winter 
136410a6cfc0SAlexandra Winter 	/* Potential re-config in progress, try again later: */
136510a6cfc0SAlexandra Winter 	if (!rtnl_trylock()) {
136610a6cfc0SAlexandra Winter 		queue_delayed_work(card->event_wq, dwork,
136710a6cfc0SAlexandra Winter 				   msecs_to_jiffies(100));
136810a6cfc0SAlexandra Winter 		return;
136910a6cfc0SAlexandra Winter 	}
1370817741a8SAlexandra Winter 	if (!netif_device_present(card->dev))
1371817741a8SAlexandra Winter 		goto out_unlock;
137210a6cfc0SAlexandra Winter 
137310a6cfc0SAlexandra Winter 	if (data->ac_event.lost_event_mask) {
137410a6cfc0SAlexandra Winter 		QETH_DBF_MESSAGE(3,
137510a6cfc0SAlexandra Winter 				 "Address change notification overflow on device %x\n",
137610a6cfc0SAlexandra Winter 				 CARD_DEVID(card));
1377817741a8SAlexandra Winter 		/* Card fdb and bridge fdb are out of sync, card has stopped
1378817741a8SAlexandra Winter 		 * notifications (no need to drain_workqueue). Purge all
1379817741a8SAlexandra Winter 		 * 'extern_learn' entries from the parent bridge and restart
1380817741a8SAlexandra Winter 		 * the notifications.
1381817741a8SAlexandra Winter 		 */
1382817741a8SAlexandra Winter 		qeth_l2_dev2br_fdb_flush(card);
1383817741a8SAlexandra Winter 		rc = qeth_l2_dev2br_an_set(card, true);
1384817741a8SAlexandra Winter 		if (rc) {
1385817741a8SAlexandra Winter 			/* TODO: if we want to retry after -EAGAIN, be
1386817741a8SAlexandra Winter 			 * aware there could be stale entries in the
1387817741a8SAlexandra Winter 			 * workqueue now, that need to be drained.
1388817741a8SAlexandra Winter 			 * For now we give up:
1389817741a8SAlexandra Winter 			 */
1390817741a8SAlexandra Winter 			netdev_err(card->dev,
1391817741a8SAlexandra Winter 				   "bridge learning_sync failed to recover: %d\n",
1392817741a8SAlexandra Winter 				   rc);
1393817741a8SAlexandra Winter 			WRITE_ONCE(card->info.pnso_mode,
1394817741a8SAlexandra Winter 				   QETH_PNSO_NONE);
1395817741a8SAlexandra Winter 			/* To remove fdb entries reported by an_set: */
1396817741a8SAlexandra Winter 			qeth_l2_dev2br_fdb_flush(card);
1397817741a8SAlexandra Winter 			priv->brport_features ^= BR_LEARNING_SYNC;
1398817741a8SAlexandra Winter 		} else {
1399817741a8SAlexandra Winter 			QETH_DBF_MESSAGE(3,
1400817741a8SAlexandra Winter 					 "Address Notification resynced on device %x\n",
1401817741a8SAlexandra Winter 					 CARD_DEVID(card));
1402817741a8SAlexandra Winter 		}
140310a6cfc0SAlexandra Winter 	} else {
140410a6cfc0SAlexandra Winter 		for (i = 0; i < data->ac_event.num_entries; i++) {
140510a6cfc0SAlexandra Winter 			struct qeth_ipacmd_addr_change_entry *entry =
140610a6cfc0SAlexandra Winter 					&data->ac_event.entry[i];
140710a6cfc0SAlexandra Winter 			qeth_l2_dev2br_fdb_notify(card,
140810a6cfc0SAlexandra Winter 						  entry->change_code,
140910a6cfc0SAlexandra Winter 						  &entry->token,
141010a6cfc0SAlexandra Winter 						  &entry->addr_lnid);
141110a6cfc0SAlexandra Winter 		}
141210a6cfc0SAlexandra Winter 	}
1413817741a8SAlexandra Winter 
1414817741a8SAlexandra Winter out_unlock:
141510a6cfc0SAlexandra Winter 	rtnl_unlock();
141610a6cfc0SAlexandra Winter 
141710a6cfc0SAlexandra Winter free:
141810a6cfc0SAlexandra Winter 	kfree(data);
141910a6cfc0SAlexandra Winter }
142010a6cfc0SAlexandra Winter 
1421a0138f59SAlexandra Winter static void qeth_addr_change_event_worker(struct work_struct *work)
14229f48b9dbSEugene Crosser {
14239d6a569aSJulian Wiedmann 	struct delayed_work *dwork = to_delayed_work(work);
14249d6a569aSJulian Wiedmann 	struct qeth_addr_change_data *data;
14259d6a569aSJulian Wiedmann 	struct qeth_card *card;
14269f48b9dbSEugene Crosser 	int i;
14279f48b9dbSEugene Crosser 
14289d6a569aSJulian Wiedmann 	data = container_of(dwork, struct qeth_addr_change_data, dwork);
14299d6a569aSJulian Wiedmann 	card = data->card;
14309d6a569aSJulian Wiedmann 
1431a0138f59SAlexandra Winter 	QETH_CARD_TEXT(data->card, 4, "adrchgew");
14329d6a569aSJulian Wiedmann 
14339d6a569aSJulian Wiedmann 	if (READ_ONCE(card->info.pnso_mode) == QETH_PNSO_NONE)
14349d6a569aSJulian Wiedmann 		goto free;
14359d6a569aSJulian Wiedmann 
1436a0138f59SAlexandra Winter 	if (data->ac_event.lost_event_mask) {
14379d6a569aSJulian Wiedmann 		/* Potential re-config in progress, try again later: */
14389d6a569aSJulian Wiedmann 		if (!mutex_trylock(&card->sbp_lock)) {
14399d6a569aSJulian Wiedmann 			queue_delayed_work(card->event_wq, dwork,
14409d6a569aSJulian Wiedmann 					   msecs_to_jiffies(100));
14419d6a569aSJulian Wiedmann 			return;
14429d6a569aSJulian Wiedmann 		}
14439d6a569aSJulian Wiedmann 
14449f48b9dbSEugene Crosser 		dev_info(&data->card->gdev->dev,
1445a0138f59SAlexandra Winter 			 "Address change notification stopped on %s (%s)\n",
14469f48b9dbSEugene Crosser 			 data->card->dev->name,
1447a0138f59SAlexandra Winter 			(data->ac_event.lost_event_mask == 0x01)
14489f48b9dbSEugene Crosser 			? "Overflow"
1449a0138f59SAlexandra Winter 			: (data->ac_event.lost_event_mask == 0x02)
14509f48b9dbSEugene Crosser 			? "Bridge port state change"
14519f48b9dbSEugene Crosser 			: "Unknown reason");
14529d6a569aSJulian Wiedmann 
14539f48b9dbSEugene Crosser 		data->card->options.sbp.hostnotification = 0;
14549d6a569aSJulian Wiedmann 		card->info.pnso_mode = QETH_PNSO_NONE;
1455c8183f54SJulian Wiedmann 		mutex_unlock(&data->card->sbp_lock);
14569f48b9dbSEugene Crosser 		qeth_bridge_emit_host_event(data->card, anev_abort,
14579f48b9dbSEugene Crosser 					    0, NULL, NULL);
14589f48b9dbSEugene Crosser 	} else
1459a0138f59SAlexandra Winter 		for (i = 0; i < data->ac_event.num_entries; i++) {
14609f48b9dbSEugene Crosser 			struct qeth_ipacmd_addr_change_entry *entry =
1461a0138f59SAlexandra Winter 					&data->ac_event.entry[i];
14629f48b9dbSEugene Crosser 			qeth_bridge_emit_host_event(data->card,
14639f48b9dbSEugene Crosser 						    anev_reg_unreg,
14649f48b9dbSEugene Crosser 						    entry->change_code,
1465a0138f59SAlexandra Winter 						    &entry->token,
1466a0138f59SAlexandra Winter 						    &entry->addr_lnid);
14679f48b9dbSEugene Crosser 		}
14689d6a569aSJulian Wiedmann 
14699d6a569aSJulian Wiedmann free:
14709f48b9dbSEugene Crosser 	kfree(data);
14719f48b9dbSEugene Crosser }
14729f48b9dbSEugene Crosser 
1473a0138f59SAlexandra Winter static void qeth_addr_change_event(struct qeth_card *card,
1474c044dc21SEugene Crosser 				   struct qeth_ipa_cmd *cmd)
14759f48b9dbSEugene Crosser {
14769f48b9dbSEugene Crosser 	struct qeth_ipacmd_addr_change *hostevs =
14779f48b9dbSEugene Crosser 		 &cmd->data.addrchange;
1478a0138f59SAlexandra Winter 	struct qeth_addr_change_data *data;
14799f48b9dbSEugene Crosser 	int extrasize;
14809f48b9dbSEugene Crosser 
14819d6a569aSJulian Wiedmann 	if (card->info.pnso_mode == QETH_PNSO_NONE)
14829d6a569aSJulian Wiedmann 		return;
14839d6a569aSJulian Wiedmann 
1484a0138f59SAlexandra Winter 	QETH_CARD_TEXT(card, 4, "adrchgev");
14859f48b9dbSEugene Crosser 	if (cmd->hdr.return_code != 0x0000) {
14869f48b9dbSEugene Crosser 		if (cmd->hdr.return_code == 0x0010) {
14879f48b9dbSEugene Crosser 			if (hostevs->lost_event_mask == 0x00)
14889f48b9dbSEugene Crosser 				hostevs->lost_event_mask = 0xff;
14899f48b9dbSEugene Crosser 		} else {
1490a0138f59SAlexandra Winter 			QETH_CARD_TEXT_(card, 2, "ACHN%04x",
14919f48b9dbSEugene Crosser 				cmd->hdr.return_code);
14929f48b9dbSEugene Crosser 			return;
14939f48b9dbSEugene Crosser 		}
14949f48b9dbSEugene Crosser 	}
14959f48b9dbSEugene Crosser 	extrasize = sizeof(struct qeth_ipacmd_addr_change_entry) *
14969f48b9dbSEugene Crosser 						hostevs->num_entries;
1497a0138f59SAlexandra Winter 	data = kzalloc(sizeof(struct qeth_addr_change_data) + extrasize,
14989f48b9dbSEugene Crosser 		       GFP_ATOMIC);
14999f48b9dbSEugene Crosser 	if (!data) {
1500a0138f59SAlexandra Winter 		QETH_CARD_TEXT(card, 2, "ACNalloc");
15019f48b9dbSEugene Crosser 		return;
15029f48b9dbSEugene Crosser 	}
150310a6cfc0SAlexandra Winter 	if (card->info.pnso_mode == QETH_PNSO_BRIDGEPORT)
15049d6a569aSJulian Wiedmann 		INIT_DELAYED_WORK(&data->dwork, qeth_addr_change_event_worker);
150510a6cfc0SAlexandra Winter 	else
150610a6cfc0SAlexandra Winter 		INIT_DELAYED_WORK(&data->dwork, qeth_l2_dev2br_worker);
15079f48b9dbSEugene Crosser 	data->card = card;
1508a0138f59SAlexandra Winter 	memcpy(&data->ac_event, hostevs,
15099f48b9dbSEugene Crosser 			sizeof(struct qeth_ipacmd_addr_change) + extrasize);
15109d6a569aSJulian Wiedmann 	queue_delayed_work(card->event_wq, &data->dwork, 0);
15119f48b9dbSEugene Crosser }
15129f48b9dbSEugene Crosser 
1513b4d72c08SEugene Crosser /* SETBRIDGEPORT support; sending commands */
1514b4d72c08SEugene Crosser 
1515b4d72c08SEugene Crosser struct _qeth_sbp_cbctl {
1516b4d72c08SEugene Crosser 	union {
1517b4d72c08SEugene Crosser 		u32 supported;
1518b4d72c08SEugene Crosser 		struct {
1519b4d72c08SEugene Crosser 			enum qeth_sbp_roles *role;
1520b4d72c08SEugene Crosser 			enum qeth_sbp_states *state;
1521b4d72c08SEugene Crosser 		} qports;
1522b4d72c08SEugene Crosser 	} data;
1523b4d72c08SEugene Crosser };
1524b4d72c08SEugene Crosser 
1525b4d72c08SEugene Crosser static int qeth_bridgeport_makerc(struct qeth_card *card,
15261709ff8dSJulian Wiedmann 				  struct qeth_ipa_cmd *cmd)
1527b4d72c08SEugene Crosser {
15281709ff8dSJulian Wiedmann 	struct qeth_ipacmd_setbridgeport *sbp = &cmd->data.sbp;
15291709ff8dSJulian Wiedmann 	enum qeth_ipa_sbp_cmd setcmd = sbp->hdr.command_code;
15301709ff8dSJulian Wiedmann 	u16 ipa_rc = cmd->hdr.return_code;
15311709ff8dSJulian Wiedmann 	u16 sbp_rc = sbp->hdr.return_code;
1532b4d72c08SEugene Crosser 	int rc;
1533b4d72c08SEugene Crosser 
15341709ff8dSJulian Wiedmann 	if (ipa_rc == IPA_RC_SUCCESS && sbp_rc == IPA_RC_SUCCESS)
15351709ff8dSJulian Wiedmann 		return 0;
15361709ff8dSJulian Wiedmann 
15371709ff8dSJulian Wiedmann 	if ((IS_IQD(card) && ipa_rc == IPA_RC_SUCCESS) ||
15381709ff8dSJulian Wiedmann 	    (!IS_IQD(card) && ipa_rc == sbp_rc)) {
15391709ff8dSJulian Wiedmann 		switch (sbp_rc) {
15402063a5f5SKittipon Meesompop 		case IPA_RC_SUCCESS:
1541b4d72c08SEugene Crosser 			rc = 0;
1542b4d72c08SEugene Crosser 			break;
15432063a5f5SKittipon Meesompop 		case IPA_RC_L2_UNSUPPORTED_CMD:
15442063a5f5SKittipon Meesompop 		case IPA_RC_UNSUPPORTED_COMMAND:
1545ffb95251SEugene Crosser 			rc = -EOPNOTSUPP;
1546b4d72c08SEugene Crosser 			break;
15472063a5f5SKittipon Meesompop 		case IPA_RC_SBP_OSA_NOT_CONFIGURED:
15482063a5f5SKittipon Meesompop 		case IPA_RC_SBP_IQD_NOT_CONFIGURED:
1549b4d72c08SEugene Crosser 			rc = -ENODEV; /* maybe not the best code here? */
1550b4d72c08SEugene Crosser 			dev_err(&card->gdev->dev,
15519c23f4daSEugene Crosser 	"The device is not configured as a Bridge Port\n");
1552b4d72c08SEugene Crosser 			break;
15532063a5f5SKittipon Meesompop 		case IPA_RC_SBP_OSA_OS_MISMATCH:
15542063a5f5SKittipon Meesompop 		case IPA_RC_SBP_IQD_OS_MISMATCH:
15551b05cf62SEugene Crosser 			rc = -EPERM;
15561b05cf62SEugene Crosser 			dev_err(&card->gdev->dev,
15571b05cf62SEugene Crosser 	"A Bridge Port is already configured by a different operating system\n");
15581b05cf62SEugene Crosser 			break;
15592063a5f5SKittipon Meesompop 		case IPA_RC_SBP_OSA_ANO_DEV_PRIMARY:
15602063a5f5SKittipon Meesompop 		case IPA_RC_SBP_IQD_ANO_DEV_PRIMARY:
1561b4d72c08SEugene Crosser 			switch (setcmd) {
1562b4d72c08SEugene Crosser 			case IPA_SBP_SET_PRIMARY_BRIDGE_PORT:
1563b4d72c08SEugene Crosser 				rc = -EEXIST;
1564b4d72c08SEugene Crosser 				dev_err(&card->gdev->dev,
15659c23f4daSEugene Crosser 	"The LAN already has a primary Bridge Port\n");
1566b4d72c08SEugene Crosser 				break;
1567b4d72c08SEugene Crosser 			case IPA_SBP_SET_SECONDARY_BRIDGE_PORT:
1568b4d72c08SEugene Crosser 				rc = -EBUSY;
1569b4d72c08SEugene Crosser 				dev_err(&card->gdev->dev,
15709c23f4daSEugene Crosser 	"The device is already a primary Bridge Port\n");
1571b4d72c08SEugene Crosser 				break;
1572b4d72c08SEugene Crosser 			default:
1573b4d72c08SEugene Crosser 				rc = -EIO;
1574b4d72c08SEugene Crosser 			}
1575b4d72c08SEugene Crosser 			break;
15762063a5f5SKittipon Meesompop 		case IPA_RC_SBP_OSA_CURRENT_SECOND:
15772063a5f5SKittipon Meesompop 		case IPA_RC_SBP_IQD_CURRENT_SECOND:
1578b4d72c08SEugene Crosser 			rc = -EBUSY;
1579b4d72c08SEugene Crosser 			dev_err(&card->gdev->dev,
15809c23f4daSEugene Crosser 	"The device is already a secondary Bridge Port\n");
1581b4d72c08SEugene Crosser 			break;
15822063a5f5SKittipon Meesompop 		case IPA_RC_SBP_OSA_LIMIT_SECOND:
15832063a5f5SKittipon Meesompop 		case IPA_RC_SBP_IQD_LIMIT_SECOND:
1584b4d72c08SEugene Crosser 			rc = -EEXIST;
1585b4d72c08SEugene Crosser 			dev_err(&card->gdev->dev,
15869c23f4daSEugene Crosser 	"The LAN cannot have more secondary Bridge Ports\n");
1587b4d72c08SEugene Crosser 			break;
15882063a5f5SKittipon Meesompop 		case IPA_RC_SBP_OSA_CURRENT_PRIMARY:
15892063a5f5SKittipon Meesompop 		case IPA_RC_SBP_IQD_CURRENT_PRIMARY:
1590b4d72c08SEugene Crosser 			rc = -EBUSY;
1591b4d72c08SEugene Crosser 			dev_err(&card->gdev->dev,
15929c23f4daSEugene Crosser 	"The device is already a primary Bridge Port\n");
1593b4d72c08SEugene Crosser 			break;
15942063a5f5SKittipon Meesompop 		case IPA_RC_SBP_OSA_NOT_AUTHD_BY_ZMAN:
15952063a5f5SKittipon Meesompop 		case IPA_RC_SBP_IQD_NOT_AUTHD_BY_ZMAN:
1596b4d72c08SEugene Crosser 			rc = -EACCES;
1597b4d72c08SEugene Crosser 			dev_err(&card->gdev->dev,
15989c23f4daSEugene Crosser 	"The device is not authorized to be a Bridge Port\n");
1599b4d72c08SEugene Crosser 			break;
1600b4d72c08SEugene Crosser 		default:
1601b4d72c08SEugene Crosser 			rc = -EIO;
1602b4d72c08SEugene Crosser 		}
16031709ff8dSJulian Wiedmann 	} else {
16041709ff8dSJulian Wiedmann 		switch (ipa_rc) {
1605b4d72c08SEugene Crosser 		case IPA_RC_NOTSUPP:
1606ffb95251SEugene Crosser 			rc = -EOPNOTSUPP;
1607b4d72c08SEugene Crosser 			break;
1608b4d72c08SEugene Crosser 		case IPA_RC_UNSUPPORTED_COMMAND:
1609ffb95251SEugene Crosser 			rc = -EOPNOTSUPP;
1610b4d72c08SEugene Crosser 			break;
1611b4d72c08SEugene Crosser 		default:
1612b4d72c08SEugene Crosser 			rc = -EIO;
1613b4d72c08SEugene Crosser 		}
16141709ff8dSJulian Wiedmann 	}
16159c23f4daSEugene Crosser 
1616b4d72c08SEugene Crosser 	if (rc) {
16171709ff8dSJulian Wiedmann 		QETH_CARD_TEXT_(card, 2, "SBPi%04x", ipa_rc);
16181709ff8dSJulian Wiedmann 		QETH_CARD_TEXT_(card, 2, "SBPc%04x", sbp_rc);
1619b4d72c08SEugene Crosser 	}
1620b4d72c08SEugene Crosser 	return rc;
1621b4d72c08SEugene Crosser }
1622b4d72c08SEugene Crosser 
1623d65626adSJulian Wiedmann static struct qeth_cmd_buffer *qeth_sbp_build_cmd(struct qeth_card *card,
1624d65626adSJulian Wiedmann 						  enum qeth_ipa_sbp_cmd sbp_cmd,
1625b9150461SJulian Wiedmann 						  unsigned int data_length)
16269c23f4daSEugene Crosser {
1627379ac99eSJulian Wiedmann 	enum qeth_ipa_cmds ipa_cmd = IS_IQD(card) ? IPA_CMD_SETBRIDGEPORT_IQD :
16289c23f4daSEugene Crosser 						    IPA_CMD_SETBRIDGEPORT_OSA;
1629b9150461SJulian Wiedmann 	struct qeth_ipacmd_sbp_hdr *hdr;
1630d65626adSJulian Wiedmann 	struct qeth_cmd_buffer *iob;
1631d65626adSJulian Wiedmann 
1632b9150461SJulian Wiedmann 	iob = qeth_ipa_alloc_cmd(card, ipa_cmd, QETH_PROT_NONE,
1633b9150461SJulian Wiedmann 				 data_length +
1634b9150461SJulian Wiedmann 				 offsetof(struct qeth_ipacmd_setbridgeport,
1635b9150461SJulian Wiedmann 					  data));
1636d65626adSJulian Wiedmann 	if (!iob)
1637d65626adSJulian Wiedmann 		return iob;
1638b9150461SJulian Wiedmann 
1639b9150461SJulian Wiedmann 	hdr = &__ipa_cmd(iob)->data.sbp.hdr;
1640b9150461SJulian Wiedmann 	hdr->cmdlength = sizeof(*hdr) + data_length;
1641b9150461SJulian Wiedmann 	hdr->command_code = sbp_cmd;
1642b9150461SJulian Wiedmann 	hdr->used_total = 1;
1643b9150461SJulian Wiedmann 	hdr->seq_no = 1;
1644d65626adSJulian Wiedmann 	return iob;
16459c23f4daSEugene Crosser }
16469c23f4daSEugene Crosser 
1647b4d72c08SEugene Crosser static int qeth_bridgeport_query_support_cb(struct qeth_card *card,
1648b4d72c08SEugene Crosser 	struct qeth_reply *reply, unsigned long data)
1649b4d72c08SEugene Crosser {
1650b4d72c08SEugene Crosser 	struct qeth_ipa_cmd *cmd = (struct qeth_ipa_cmd *) data;
1651b4d72c08SEugene Crosser 	struct _qeth_sbp_cbctl *cbctl = (struct _qeth_sbp_cbctl *)reply->param;
16521709ff8dSJulian Wiedmann 	int rc;
16531709ff8dSJulian Wiedmann 
1654b4d72c08SEugene Crosser 	QETH_CARD_TEXT(card, 2, "brqsupcb");
16551709ff8dSJulian Wiedmann 	rc = qeth_bridgeport_makerc(card, cmd);
16561709ff8dSJulian Wiedmann 	if (rc)
16571709ff8dSJulian Wiedmann 		return rc;
16581709ff8dSJulian Wiedmann 
1659b4d72c08SEugene Crosser 	cbctl->data.supported =
1660b4d72c08SEugene Crosser 		cmd->data.sbp.data.query_cmds_supp.supported_cmds;
1661b4d72c08SEugene Crosser 	return 0;
1662b4d72c08SEugene Crosser }
1663b4d72c08SEugene Crosser 
1664b4d72c08SEugene Crosser /**
1665b4d72c08SEugene Crosser  * qeth_bridgeport_query_support() - store bitmask of supported subfunctions.
1666b4d72c08SEugene Crosser  * @card:			     qeth_card structure pointer.
1667b4d72c08SEugene Crosser  *
1668b4d72c08SEugene Crosser  * Sets bitmask of supported setbridgeport subfunctions in the qeth_card
1669b4d72c08SEugene Crosser  * strucutre: card->options.sbp.supported_funcs.
1670b4d72c08SEugene Crosser  */
1671c044dc21SEugene Crosser static void qeth_bridgeport_query_support(struct qeth_card *card)
1672b4d72c08SEugene Crosser {
1673b4d72c08SEugene Crosser 	struct qeth_cmd_buffer *iob;
1674b4d72c08SEugene Crosser 	struct _qeth_sbp_cbctl cbctl;
1675b4d72c08SEugene Crosser 
1676b4d72c08SEugene Crosser 	QETH_CARD_TEXT(card, 2, "brqsuppo");
1677d65626adSJulian Wiedmann 	iob = qeth_sbp_build_cmd(card, IPA_SBP_QUERY_COMMANDS_SUPPORTED,
1678b9150461SJulian Wiedmann 				 SBP_DATA_SIZEOF(query_cmds_supp));
16791aec42bcSThomas Richter 	if (!iob)
16801aec42bcSThomas Richter 		return;
16811709ff8dSJulian Wiedmann 
1682b4d72c08SEugene Crosser 	if (qeth_send_ipa_cmd(card, iob, qeth_bridgeport_query_support_cb,
16831709ff8dSJulian Wiedmann 			      &cbctl)) {
1684b4d72c08SEugene Crosser 		card->options.sbp.role = QETH_SBP_ROLE_NONE;
16851709ff8dSJulian Wiedmann 		card->options.sbp.supported_funcs = 0;
1686b4d72c08SEugene Crosser 		return;
1687b4d72c08SEugene Crosser 	}
1688b4d72c08SEugene Crosser 	card->options.sbp.supported_funcs = cbctl.data.supported;
1689b4d72c08SEugene Crosser }
1690b4d72c08SEugene Crosser 
1691b4d72c08SEugene Crosser static int qeth_bridgeport_query_ports_cb(struct qeth_card *card,
1692b4d72c08SEugene Crosser 	struct qeth_reply *reply, unsigned long data)
1693b4d72c08SEugene Crosser {
1694b4d72c08SEugene Crosser 	struct qeth_ipa_cmd *cmd = (struct qeth_ipa_cmd *) data;
1695b4d72c08SEugene Crosser 	struct _qeth_sbp_cbctl *cbctl = (struct _qeth_sbp_cbctl *)reply->param;
169665b0494eSJulian Wiedmann 	struct qeth_sbp_port_data *qports;
16971709ff8dSJulian Wiedmann 	int rc;
1698b4d72c08SEugene Crosser 
1699b4d72c08SEugene Crosser 	QETH_CARD_TEXT(card, 2, "brqprtcb");
17001709ff8dSJulian Wiedmann 	rc = qeth_bridgeport_makerc(card, cmd);
17011709ff8dSJulian Wiedmann 	if (rc)
17021709ff8dSJulian Wiedmann 		return rc;
17031709ff8dSJulian Wiedmann 
170465b0494eSJulian Wiedmann 	qports = &cmd->data.sbp.data.port_data;
1705b4d72c08SEugene Crosser 	if (qports->entry_length != sizeof(struct qeth_sbp_port_entry)) {
1706b4d72c08SEugene Crosser 		QETH_CARD_TEXT_(card, 2, "SBPs%04x", qports->entry_length);
17071709ff8dSJulian Wiedmann 		return -EINVAL;
1708b4d72c08SEugene Crosser 	}
1709b4d72c08SEugene Crosser 	/* first entry contains the state of the local port */
1710b4d72c08SEugene Crosser 	if (qports->num_entries > 0) {
1711b4d72c08SEugene Crosser 		if (cbctl->data.qports.role)
1712b4d72c08SEugene Crosser 			*cbctl->data.qports.role = qports->entry[0].role;
1713b4d72c08SEugene Crosser 		if (cbctl->data.qports.state)
1714b4d72c08SEugene Crosser 			*cbctl->data.qports.state = qports->entry[0].state;
1715b4d72c08SEugene Crosser 	}
1716b4d72c08SEugene Crosser 	return 0;
1717b4d72c08SEugene Crosser }
1718b4d72c08SEugene Crosser 
1719b4d72c08SEugene Crosser /**
1720b4d72c08SEugene Crosser  * qeth_bridgeport_query_ports() - query local bridgeport status.
1721b4d72c08SEugene Crosser  * @card:			   qeth_card structure pointer.
1722b4d72c08SEugene Crosser  * @role:   Role of the port: 0-none, 1-primary, 2-secondary.
1723b4d72c08SEugene Crosser  * @state:  State of the port: 0-inactive, 1-standby, 2-active.
1724b4d72c08SEugene Crosser  *
1725b4d72c08SEugene Crosser  * Returns negative errno-compatible error indication or 0 on success.
1726b4d72c08SEugene Crosser  *
1727b4d72c08SEugene Crosser  * 'role' and 'state' are not updated in case of hardware operation failure.
1728b4d72c08SEugene Crosser  */
1729b4d72c08SEugene Crosser int qeth_bridgeport_query_ports(struct qeth_card *card,
1730b4d72c08SEugene Crosser 	enum qeth_sbp_roles *role, enum qeth_sbp_states *state)
1731b4d72c08SEugene Crosser {
1732b4d72c08SEugene Crosser 	struct qeth_cmd_buffer *iob;
1733b4d72c08SEugene Crosser 	struct _qeth_sbp_cbctl cbctl = {
1734b4d72c08SEugene Crosser 		.data = {
1735b4d72c08SEugene Crosser 			.qports = {
1736b4d72c08SEugene Crosser 				.role = role,
1737b4d72c08SEugene Crosser 				.state = state,
1738b4d72c08SEugene Crosser 			},
1739b4d72c08SEugene Crosser 		},
1740b4d72c08SEugene Crosser 	};
1741b4d72c08SEugene Crosser 
1742b4d72c08SEugene Crosser 	QETH_CARD_TEXT(card, 2, "brqports");
1743b4d72c08SEugene Crosser 	if (!(card->options.sbp.supported_funcs & IPA_SBP_QUERY_BRIDGE_PORTS))
1744b4d72c08SEugene Crosser 		return -EOPNOTSUPP;
1745d65626adSJulian Wiedmann 	iob = qeth_sbp_build_cmd(card, IPA_SBP_QUERY_BRIDGE_PORTS, 0);
17461aec42bcSThomas Richter 	if (!iob)
17471aec42bcSThomas Richter 		return -ENOMEM;
17481709ff8dSJulian Wiedmann 
17491709ff8dSJulian Wiedmann 	return qeth_send_ipa_cmd(card, iob, qeth_bridgeport_query_ports_cb,
17501709ff8dSJulian Wiedmann 				 &cbctl);
1751b4d72c08SEugene Crosser }
1752b4d72c08SEugene Crosser 
1753b4d72c08SEugene Crosser static int qeth_bridgeport_set_cb(struct qeth_card *card,
1754b4d72c08SEugene Crosser 	struct qeth_reply *reply, unsigned long data)
1755b4d72c08SEugene Crosser {
1756b4d72c08SEugene Crosser 	struct qeth_ipa_cmd *cmd = (struct qeth_ipa_cmd *)data;
17571709ff8dSJulian Wiedmann 
1758b4d72c08SEugene Crosser 	QETH_CARD_TEXT(card, 2, "brsetrcb");
17591709ff8dSJulian Wiedmann 	return qeth_bridgeport_makerc(card, cmd);
1760b4d72c08SEugene Crosser }
1761b4d72c08SEugene Crosser 
1762b4d72c08SEugene Crosser /**
1763b4d72c08SEugene Crosser  * qeth_bridgeport_setrole() - Assign primary role to the port.
1764b4d72c08SEugene Crosser  * @card:		       qeth_card structure pointer.
1765b4d72c08SEugene Crosser  * @role:		       Role to assign.
1766b4d72c08SEugene Crosser  *
1767b4d72c08SEugene Crosser  * Returns negative errno-compatible error indication or 0 on success.
1768b4d72c08SEugene Crosser  */
1769b4d72c08SEugene Crosser int qeth_bridgeport_setrole(struct qeth_card *card, enum qeth_sbp_roles role)
1770b4d72c08SEugene Crosser {
1771b4d72c08SEugene Crosser 	struct qeth_cmd_buffer *iob;
1772b4d72c08SEugene Crosser 	enum qeth_ipa_sbp_cmd setcmd;
1773b9150461SJulian Wiedmann 	unsigned int cmdlength = 0;
1774b4d72c08SEugene Crosser 
1775b4d72c08SEugene Crosser 	QETH_CARD_TEXT(card, 2, "brsetrol");
1776b4d72c08SEugene Crosser 	switch (role) {
1777b4d72c08SEugene Crosser 	case QETH_SBP_ROLE_NONE:
1778b4d72c08SEugene Crosser 		setcmd = IPA_SBP_RESET_BRIDGE_PORT_ROLE;
1779b4d72c08SEugene Crosser 		break;
1780b4d72c08SEugene Crosser 	case QETH_SBP_ROLE_PRIMARY:
1781b4d72c08SEugene Crosser 		setcmd = IPA_SBP_SET_PRIMARY_BRIDGE_PORT;
1782b9150461SJulian Wiedmann 		cmdlength = SBP_DATA_SIZEOF(set_primary);
1783b4d72c08SEugene Crosser 		break;
1784b4d72c08SEugene Crosser 	case QETH_SBP_ROLE_SECONDARY:
1785b4d72c08SEugene Crosser 		setcmd = IPA_SBP_SET_SECONDARY_BRIDGE_PORT;
1786b4d72c08SEugene Crosser 		break;
1787b4d72c08SEugene Crosser 	default:
1788b4d72c08SEugene Crosser 		return -EINVAL;
1789b4d72c08SEugene Crosser 	}
1790b4d72c08SEugene Crosser 	if (!(card->options.sbp.supported_funcs & setcmd))
1791b4d72c08SEugene Crosser 		return -EOPNOTSUPP;
1792d65626adSJulian Wiedmann 	iob = qeth_sbp_build_cmd(card, setcmd, cmdlength);
17931aec42bcSThomas Richter 	if (!iob)
17941aec42bcSThomas Richter 		return -ENOMEM;
17951709ff8dSJulian Wiedmann 
17961709ff8dSJulian Wiedmann 	return qeth_send_ipa_cmd(card, iob, qeth_bridgeport_set_cb, NULL);
1797b4d72c08SEugene Crosser }
1798b4d72c08SEugene Crosser 
17999f48b9dbSEugene Crosser static void qeth_bridgeport_an_set_cb(void *priv,
1800a0138f59SAlexandra Winter 				      struct chsc_pnso_naid_l2 *entry)
18019f48b9dbSEugene Crosser {
18029f48b9dbSEugene Crosser 	struct qeth_card *card = (struct qeth_card *)priv;
18039f48b9dbSEugene Crosser 	u8 code;
18049f48b9dbSEugene Crosser 
18059f48b9dbSEugene Crosser 	code = IPA_ADDR_CHANGE_CODE_MACADDR;
1806a0138f59SAlexandra Winter 	if (entry->addr_lnid.lnid < VLAN_N_VID)
18079f48b9dbSEugene Crosser 		code |= IPA_ADDR_CHANGE_CODE_VLANID;
18089f48b9dbSEugene Crosser 	qeth_bridge_emit_host_event(card, anev_reg_unreg, code,
1809a0138f59SAlexandra Winter 				    (struct net_if_token *)&entry->nit,
1810a0138f59SAlexandra Winter 				    (struct mac_addr_lnid *)&entry->addr_lnid);
18119f48b9dbSEugene Crosser }
18129f48b9dbSEugene Crosser 
18139f48b9dbSEugene Crosser /**
18149f48b9dbSEugene Crosser  * qeth_bridgeport_an_set() - Enable or disable bridgeport address notification
18159f48b9dbSEugene Crosser  * @card:		      qeth_card structure pointer.
18169f48b9dbSEugene Crosser  * @enable:		      0 - disable, non-zero - enable notifications
18179f48b9dbSEugene Crosser  *
18189f48b9dbSEugene Crosser  * Returns negative errno-compatible error indication or 0 on success.
18199f48b9dbSEugene Crosser  *
18209f48b9dbSEugene Crosser  * On enable, emits a series of address notifications udev events for all
18219f48b9dbSEugene Crosser  * currently registered hosts.
18229f48b9dbSEugene Crosser  */
18239f48b9dbSEugene Crosser int qeth_bridgeport_an_set(struct qeth_card *card, int enable)
18249f48b9dbSEugene Crosser {
18259f48b9dbSEugene Crosser 	int rc;
18269f48b9dbSEugene Crosser 
18279f48b9dbSEugene Crosser 	if (!card->options.sbp.supported_funcs)
18289f48b9dbSEugene Crosser 		return -EOPNOTSUPP;
18299f48b9dbSEugene Crosser 
18309f48b9dbSEugene Crosser 	if (enable) {
18319f48b9dbSEugene Crosser 		qeth_bridge_emit_host_event(card, anev_reset, 0, NULL, NULL);
18329d6a569aSJulian Wiedmann 		qeth_l2_set_pnso_mode(card, QETH_PNSO_BRIDGEPORT);
18334fea49a7SAlexandra Winter 		rc = qeth_l2_pnso(card, PNSO_OC_NET_BRIDGE_INFO, 1,
18344fea49a7SAlexandra Winter 				  qeth_bridgeport_an_set_cb, card);
18359d6a569aSJulian Wiedmann 		if (rc)
18369d6a569aSJulian Wiedmann 			qeth_l2_set_pnso_mode(card, QETH_PNSO_NONE);
18379d6a569aSJulian Wiedmann 	} else {
18384fea49a7SAlexandra Winter 		rc = qeth_l2_pnso(card, PNSO_OC_NET_BRIDGE_INFO, 0, NULL, NULL);
18399d6a569aSJulian Wiedmann 		qeth_l2_set_pnso_mode(card, QETH_PNSO_NONE);
18409d6a569aSJulian Wiedmann 	}
1841a0138f59SAlexandra Winter 	return rc;
18429f48b9dbSEugene Crosser }
18439f48b9dbSEugene Crosser 
1844caa1f0b1SHans Wippel static bool qeth_bridgeport_is_in_use(struct qeth_card *card)
1845caa1f0b1SHans Wippel {
1846caa1f0b1SHans Wippel 	return (card->options.sbp.role || card->options.sbp.reflect_promisc ||
1847caa1f0b1SHans Wippel 		card->options.sbp.hostnotification);
1848caa1f0b1SHans Wippel }
1849caa1f0b1SHans Wippel 
1850a45b3fafSHans Wippel /* VNIC Characteristics support */
1851a45b3fafSHans Wippel 
1852a45b3fafSHans Wippel /* handle VNICC IPA command return codes; convert to error codes */
1853742d4d40SJulian Wiedmann static int qeth_l2_vnicc_makerc(struct qeth_card *card, u16 ipa_rc)
1854a45b3fafSHans Wippel {
1855a45b3fafSHans Wippel 	int rc;
1856a45b3fafSHans Wippel 
1857a45b3fafSHans Wippel 	switch (ipa_rc) {
1858a45b3fafSHans Wippel 	case IPA_RC_SUCCESS:
1859a45b3fafSHans Wippel 		return ipa_rc;
1860a45b3fafSHans Wippel 	case IPA_RC_L2_UNSUPPORTED_CMD:
1861a45b3fafSHans Wippel 	case IPA_RC_NOTSUPP:
1862a45b3fafSHans Wippel 		rc = -EOPNOTSUPP;
1863a45b3fafSHans Wippel 		break;
1864a45b3fafSHans Wippel 	case IPA_RC_VNICC_OOSEQ:
1865a45b3fafSHans Wippel 		rc = -EALREADY;
1866a45b3fafSHans Wippel 		break;
1867a45b3fafSHans Wippel 	case IPA_RC_VNICC_VNICBP:
1868a45b3fafSHans Wippel 		rc = -EBUSY;
1869a45b3fafSHans Wippel 		break;
1870a45b3fafSHans Wippel 	case IPA_RC_L2_ADDR_TABLE_FULL:
1871a45b3fafSHans Wippel 		rc = -ENOSPC;
1872a45b3fafSHans Wippel 		break;
1873a45b3fafSHans Wippel 	case IPA_RC_L2_MAC_NOT_AUTH_BY_ADP:
1874a45b3fafSHans Wippel 		rc = -EACCES;
1875a45b3fafSHans Wippel 		break;
1876a45b3fafSHans Wippel 	default:
1877a45b3fafSHans Wippel 		rc = -EIO;
1878a45b3fafSHans Wippel 	}
1879a45b3fafSHans Wippel 
1880a45b3fafSHans Wippel 	QETH_CARD_TEXT_(card, 2, "err%04x", ipa_rc);
1881a45b3fafSHans Wippel 	return rc;
1882a45b3fafSHans Wippel }
1883a45b3fafSHans Wippel 
1884a45b3fafSHans Wippel /* generic VNICC request call back */
1885a45b3fafSHans Wippel static int qeth_l2_vnicc_request_cb(struct qeth_card *card,
1886a45b3fafSHans Wippel 				    struct qeth_reply *reply,
1887a45b3fafSHans Wippel 				    unsigned long data)
1888a45b3fafSHans Wippel {
1889a45b3fafSHans Wippel 	struct qeth_ipa_cmd *cmd = (struct qeth_ipa_cmd *) data;
1890a45b3fafSHans Wippel 	struct qeth_ipacmd_vnicc *rep = &cmd->data.vnicc;
18912744d811SJulian Wiedmann 	u32 sub_cmd = cmd->data.vnicc.hdr.sub_command;
1892a45b3fafSHans Wippel 
1893a45b3fafSHans Wippel 	QETH_CARD_TEXT(card, 2, "vniccrcb");
1894a45b3fafSHans Wippel 	if (cmd->hdr.return_code)
1895742d4d40SJulian Wiedmann 		return qeth_l2_vnicc_makerc(card, cmd->hdr.return_code);
1896a45b3fafSHans Wippel 	/* return results to caller */
18972cfb4810SJulian Wiedmann 	card->options.vnicc.sup_chars = rep->vnicc_cmds.supported;
18982cfb4810SJulian Wiedmann 	card->options.vnicc.cur_chars = rep->vnicc_cmds.enabled;
1899a45b3fafSHans Wippel 
19002744d811SJulian Wiedmann 	if (sub_cmd == IPA_VNICC_QUERY_CMDS)
190149f42f5dSJulian Wiedmann 		*(u32 *)reply->param = rep->data.query_cmds.sup_cmds;
19022744d811SJulian Wiedmann 	else if (sub_cmd == IPA_VNICC_GET_TIMEOUT)
190349f42f5dSJulian Wiedmann 		*(u32 *)reply->param = rep->data.getset_timeout.timeout;
1904349d13d5SHans Wippel 
1905a45b3fafSHans Wippel 	return 0;
1906a45b3fafSHans Wippel }
1907a45b3fafSHans Wippel 
19082cfb4810SJulian Wiedmann static struct qeth_cmd_buffer *qeth_l2_vnicc_build_cmd(struct qeth_card *card,
19092cfb4810SJulian Wiedmann 						       u32 vnicc_cmd,
19102cfb4810SJulian Wiedmann 						       unsigned int data_length)
1911a45b3fafSHans Wippel {
19122cfb4810SJulian Wiedmann 	struct qeth_ipacmd_vnicc_hdr *hdr;
1913a45b3fafSHans Wippel 	struct qeth_cmd_buffer *iob;
1914a45b3fafSHans Wippel 
19152cfb4810SJulian Wiedmann 	iob = qeth_ipa_alloc_cmd(card, IPA_CMD_VNICC, QETH_PROT_NONE,
19162cfb4810SJulian Wiedmann 				 data_length +
19172cfb4810SJulian Wiedmann 				 offsetof(struct qeth_ipacmd_vnicc, data));
1918a45b3fafSHans Wippel 	if (!iob)
19192cfb4810SJulian Wiedmann 		return NULL;
1920a45b3fafSHans Wippel 
19212cfb4810SJulian Wiedmann 	hdr = &__ipa_cmd(iob)->data.vnicc.hdr;
19222cfb4810SJulian Wiedmann 	hdr->data_length = sizeof(*hdr) + data_length;
19232cfb4810SJulian Wiedmann 	hdr->sub_command = vnicc_cmd;
19242cfb4810SJulian Wiedmann 	return iob;
1925a45b3fafSHans Wippel }
1926a45b3fafSHans Wippel 
1927a45b3fafSHans Wippel /* VNICC query VNIC characteristics request */
1928a45b3fafSHans Wippel static int qeth_l2_vnicc_query_chars(struct qeth_card *card)
1929a45b3fafSHans Wippel {
19302cfb4810SJulian Wiedmann 	struct qeth_cmd_buffer *iob;
19312cfb4810SJulian Wiedmann 
19322cfb4810SJulian Wiedmann 	QETH_CARD_TEXT(card, 2, "vniccqch");
19332cfb4810SJulian Wiedmann 	iob = qeth_l2_vnicc_build_cmd(card, IPA_VNICC_QUERY_CHARS, 0);
19342cfb4810SJulian Wiedmann 	if (!iob)
19352cfb4810SJulian Wiedmann 		return -ENOMEM;
1936a45b3fafSHans Wippel 
19372744d811SJulian Wiedmann 	return qeth_send_ipa_cmd(card, iob, qeth_l2_vnicc_request_cb, NULL);
1938a45b3fafSHans Wippel }
1939a45b3fafSHans Wippel 
1940caa1f0b1SHans Wippel /* VNICC query sub commands request */
1941caa1f0b1SHans Wippel static int qeth_l2_vnicc_query_cmds(struct qeth_card *card, u32 vnic_char,
1942caa1f0b1SHans Wippel 				    u32 *sup_cmds)
1943caa1f0b1SHans Wippel {
19442cfb4810SJulian Wiedmann 	struct qeth_cmd_buffer *iob;
19452cfb4810SJulian Wiedmann 
19462cfb4810SJulian Wiedmann 	QETH_CARD_TEXT(card, 2, "vniccqcm");
19472cfb4810SJulian Wiedmann 	iob = qeth_l2_vnicc_build_cmd(card, IPA_VNICC_QUERY_CMDS,
19482cfb4810SJulian Wiedmann 				      VNICC_DATA_SIZEOF(query_cmds));
19492cfb4810SJulian Wiedmann 	if (!iob)
19502cfb4810SJulian Wiedmann 		return -ENOMEM;
19512cfb4810SJulian Wiedmann 
19522cfb4810SJulian Wiedmann 	__ipa_cmd(iob)->data.vnicc.data.query_cmds.vnic_char = vnic_char;
1953caa1f0b1SHans Wippel 
195449f42f5dSJulian Wiedmann 	return qeth_send_ipa_cmd(card, iob, qeth_l2_vnicc_request_cb, sup_cmds);
1955caa1f0b1SHans Wippel }
1956caa1f0b1SHans Wippel 
1957caa1f0b1SHans Wippel /* VNICC enable/disable characteristic request */
1958caa1f0b1SHans Wippel static int qeth_l2_vnicc_set_char(struct qeth_card *card, u32 vnic_char,
1959caa1f0b1SHans Wippel 				      u32 cmd)
1960caa1f0b1SHans Wippel {
19612cfb4810SJulian Wiedmann 	struct qeth_cmd_buffer *iob;
19622cfb4810SJulian Wiedmann 
19632cfb4810SJulian Wiedmann 	QETH_CARD_TEXT(card, 2, "vniccedc");
19642cfb4810SJulian Wiedmann 	iob = qeth_l2_vnicc_build_cmd(card, cmd, VNICC_DATA_SIZEOF(set_char));
19652cfb4810SJulian Wiedmann 	if (!iob)
19662cfb4810SJulian Wiedmann 		return -ENOMEM;
19672cfb4810SJulian Wiedmann 
19682cfb4810SJulian Wiedmann 	__ipa_cmd(iob)->data.vnicc.data.set_char.vnic_char = vnic_char;
1969caa1f0b1SHans Wippel 
19702744d811SJulian Wiedmann 	return qeth_send_ipa_cmd(card, iob, qeth_l2_vnicc_request_cb, NULL);
1971caa1f0b1SHans Wippel }
1972caa1f0b1SHans Wippel 
1973349d13d5SHans Wippel /* VNICC get/set timeout for characteristic request */
1974349d13d5SHans Wippel static int qeth_l2_vnicc_getset_timeout(struct qeth_card *card, u32 vnicc,
1975349d13d5SHans Wippel 					u32 cmd, u32 *timeout)
1976349d13d5SHans Wippel {
19772cfb4810SJulian Wiedmann 	struct qeth_vnicc_getset_timeout *getset_timeout;
19782cfb4810SJulian Wiedmann 	struct qeth_cmd_buffer *iob;
19792cfb4810SJulian Wiedmann 
19802cfb4810SJulian Wiedmann 	QETH_CARD_TEXT(card, 2, "vniccgst");
19812cfb4810SJulian Wiedmann 	iob = qeth_l2_vnicc_build_cmd(card, cmd,
19822cfb4810SJulian Wiedmann 				      VNICC_DATA_SIZEOF(getset_timeout));
19832cfb4810SJulian Wiedmann 	if (!iob)
19842cfb4810SJulian Wiedmann 		return -ENOMEM;
19852cfb4810SJulian Wiedmann 
19862cfb4810SJulian Wiedmann 	getset_timeout = &__ipa_cmd(iob)->data.vnicc.data.getset_timeout;
19872cfb4810SJulian Wiedmann 	getset_timeout->vnic_char = vnicc;
19882cfb4810SJulian Wiedmann 
19892cfb4810SJulian Wiedmann 	if (cmd == IPA_VNICC_SET_TIMEOUT)
19902cfb4810SJulian Wiedmann 		getset_timeout->timeout = *timeout;
1991349d13d5SHans Wippel 
199249f42f5dSJulian Wiedmann 	return qeth_send_ipa_cmd(card, iob, qeth_l2_vnicc_request_cb, timeout);
1993349d13d5SHans Wippel }
1994349d13d5SHans Wippel 
1995caa1f0b1SHans Wippel /* set current VNICC flag state; called from sysfs store function */
1996caa1f0b1SHans Wippel int qeth_l2_vnicc_set_state(struct qeth_card *card, u32 vnicc, bool state)
1997caa1f0b1SHans Wippel {
1998caa1f0b1SHans Wippel 	int rc = 0;
1999caa1f0b1SHans Wippel 	u32 cmd;
2000caa1f0b1SHans Wippel 
2001caa1f0b1SHans Wippel 	QETH_CARD_TEXT(card, 2, "vniccsch");
2002caa1f0b1SHans Wippel 
2003caa1f0b1SHans Wippel 	/* check if characteristic and enable/disable are supported */
2004caa1f0b1SHans Wippel 	if (!(card->options.vnicc.sup_chars & vnicc) ||
2005caa1f0b1SHans Wippel 	    !(card->options.vnicc.set_char_sup & vnicc))
2006caa1f0b1SHans Wippel 		return -EOPNOTSUPP;
2007caa1f0b1SHans Wippel 
20086f3846f0SAlexandra Winter 	if (qeth_bridgeport_is_in_use(card))
20096f3846f0SAlexandra Winter 		return -EBUSY;
20106f3846f0SAlexandra Winter 
2011caa1f0b1SHans Wippel 	/* set enable/disable command and store wanted characteristic */
2012caa1f0b1SHans Wippel 	if (state) {
2013caa1f0b1SHans Wippel 		cmd = IPA_VNICC_ENABLE;
2014caa1f0b1SHans Wippel 		card->options.vnicc.wanted_chars |= vnicc;
2015caa1f0b1SHans Wippel 	} else {
2016caa1f0b1SHans Wippel 		cmd = IPA_VNICC_DISABLE;
2017caa1f0b1SHans Wippel 		card->options.vnicc.wanted_chars &= ~vnicc;
2018caa1f0b1SHans Wippel 	}
2019caa1f0b1SHans Wippel 
2020caa1f0b1SHans Wippel 	/* do we need to do anything? */
2021caa1f0b1SHans Wippel 	if (card->options.vnicc.cur_chars == card->options.vnicc.wanted_chars)
2022caa1f0b1SHans Wippel 		return rc;
2023caa1f0b1SHans Wippel 
2024caa1f0b1SHans Wippel 	/* if card is not ready, simply stop here */
2025caa1f0b1SHans Wippel 	if (!qeth_card_hw_is_reachable(card)) {
2026caa1f0b1SHans Wippel 		if (state)
2027caa1f0b1SHans Wippel 			card->options.vnicc.cur_chars |= vnicc;
2028caa1f0b1SHans Wippel 		else
2029caa1f0b1SHans Wippel 			card->options.vnicc.cur_chars &= ~vnicc;
2030caa1f0b1SHans Wippel 		return rc;
2031caa1f0b1SHans Wippel 	}
2032caa1f0b1SHans Wippel 
2033caa1f0b1SHans Wippel 	rc = qeth_l2_vnicc_set_char(card, vnicc, cmd);
2034caa1f0b1SHans Wippel 	if (rc)
2035caa1f0b1SHans Wippel 		card->options.vnicc.wanted_chars =
2036caa1f0b1SHans Wippel 			card->options.vnicc.cur_chars;
2037349d13d5SHans Wippel 	else {
2038349d13d5SHans Wippel 		/* successful online VNICC change; handle special cases */
2039349d13d5SHans Wippel 		if (state && vnicc == QETH_VNICC_RX_BCAST)
2040caa1f0b1SHans Wippel 			card->options.vnicc.rx_bcast_enabled = true;
2041349d13d5SHans Wippel 		if (!state && vnicc == QETH_VNICC_LEARNING)
2042349d13d5SHans Wippel 			qeth_l2_vnicc_recover_timeout(card, vnicc,
2043349d13d5SHans Wippel 					&card->options.vnicc.learning_timeout);
2044349d13d5SHans Wippel 	}
2045caa1f0b1SHans Wippel 
2046caa1f0b1SHans Wippel 	return rc;
2047caa1f0b1SHans Wippel }
2048caa1f0b1SHans Wippel 
2049caa1f0b1SHans Wippel /* get current VNICC flag state; called from sysfs show function */
2050caa1f0b1SHans Wippel int qeth_l2_vnicc_get_state(struct qeth_card *card, u32 vnicc, bool *state)
2051caa1f0b1SHans Wippel {
2052caa1f0b1SHans Wippel 	int rc = 0;
2053caa1f0b1SHans Wippel 
2054caa1f0b1SHans Wippel 	QETH_CARD_TEXT(card, 2, "vniccgch");
2055caa1f0b1SHans Wippel 
2056caa1f0b1SHans Wippel 	/* check if characteristic is supported */
2057caa1f0b1SHans Wippel 	if (!(card->options.vnicc.sup_chars & vnicc))
2058caa1f0b1SHans Wippel 		return -EOPNOTSUPP;
2059caa1f0b1SHans Wippel 
20606f3846f0SAlexandra Winter 	if (qeth_bridgeport_is_in_use(card))
20616f3846f0SAlexandra Winter 		return -EBUSY;
20626f3846f0SAlexandra Winter 
2063caa1f0b1SHans Wippel 	/* if card is ready, query current VNICC state */
2064caa1f0b1SHans Wippel 	if (qeth_card_hw_is_reachable(card))
2065caa1f0b1SHans Wippel 		rc = qeth_l2_vnicc_query_chars(card);
2066caa1f0b1SHans Wippel 
2067caa1f0b1SHans Wippel 	*state = (card->options.vnicc.cur_chars & vnicc) ? true : false;
2068caa1f0b1SHans Wippel 	return rc;
2069caa1f0b1SHans Wippel }
2070caa1f0b1SHans Wippel 
2071349d13d5SHans Wippel /* set VNICC timeout; called from sysfs store function. Currently, only learning
2072349d13d5SHans Wippel  * supports timeout
2073349d13d5SHans Wippel  */
2074349d13d5SHans Wippel int qeth_l2_vnicc_set_timeout(struct qeth_card *card, u32 timeout)
2075349d13d5SHans Wippel {
2076349d13d5SHans Wippel 	int rc = 0;
2077349d13d5SHans Wippel 
2078349d13d5SHans Wippel 	QETH_CARD_TEXT(card, 2, "vniccsto");
2079349d13d5SHans Wippel 
2080349d13d5SHans Wippel 	/* check if characteristic and set_timeout are supported */
2081349d13d5SHans Wippel 	if (!(card->options.vnicc.sup_chars & QETH_VNICC_LEARNING) ||
2082349d13d5SHans Wippel 	    !(card->options.vnicc.getset_timeout_sup & QETH_VNICC_LEARNING))
2083349d13d5SHans Wippel 		return -EOPNOTSUPP;
2084349d13d5SHans Wippel 
20856f3846f0SAlexandra Winter 	if (qeth_bridgeport_is_in_use(card))
20866f3846f0SAlexandra Winter 		return -EBUSY;
20876f3846f0SAlexandra Winter 
2088349d13d5SHans Wippel 	/* do we need to do anything? */
2089349d13d5SHans Wippel 	if (card->options.vnicc.learning_timeout == timeout)
2090349d13d5SHans Wippel 		return rc;
2091349d13d5SHans Wippel 
2092349d13d5SHans Wippel 	/* if card is not ready, simply store the value internally and return */
2093349d13d5SHans Wippel 	if (!qeth_card_hw_is_reachable(card)) {
2094349d13d5SHans Wippel 		card->options.vnicc.learning_timeout = timeout;
2095349d13d5SHans Wippel 		return rc;
2096349d13d5SHans Wippel 	}
2097349d13d5SHans Wippel 
2098349d13d5SHans Wippel 	/* send timeout value to card; if successful, store value internally */
2099349d13d5SHans Wippel 	rc = qeth_l2_vnicc_getset_timeout(card, QETH_VNICC_LEARNING,
2100349d13d5SHans Wippel 					  IPA_VNICC_SET_TIMEOUT, &timeout);
2101349d13d5SHans Wippel 	if (!rc)
2102349d13d5SHans Wippel 		card->options.vnicc.learning_timeout = timeout;
2103349d13d5SHans Wippel 
2104349d13d5SHans Wippel 	return rc;
2105349d13d5SHans Wippel }
2106349d13d5SHans Wippel 
2107349d13d5SHans Wippel /* get current VNICC timeout; called from sysfs show function. Currently, only
2108349d13d5SHans Wippel  * learning supports timeout
2109349d13d5SHans Wippel  */
2110349d13d5SHans Wippel int qeth_l2_vnicc_get_timeout(struct qeth_card *card, u32 *timeout)
2111349d13d5SHans Wippel {
2112349d13d5SHans Wippel 	int rc = 0;
2113349d13d5SHans Wippel 
2114349d13d5SHans Wippel 	QETH_CARD_TEXT(card, 2, "vniccgto");
2115349d13d5SHans Wippel 
2116349d13d5SHans Wippel 	/* check if characteristic and get_timeout are supported */
2117349d13d5SHans Wippel 	if (!(card->options.vnicc.sup_chars & QETH_VNICC_LEARNING) ||
2118349d13d5SHans Wippel 	    !(card->options.vnicc.getset_timeout_sup & QETH_VNICC_LEARNING))
2119349d13d5SHans Wippel 		return -EOPNOTSUPP;
21206f3846f0SAlexandra Winter 
21216f3846f0SAlexandra Winter 	if (qeth_bridgeport_is_in_use(card))
21226f3846f0SAlexandra Winter 		return -EBUSY;
21236f3846f0SAlexandra Winter 
2124349d13d5SHans Wippel 	/* if card is ready, get timeout. Otherwise, just return stored value */
2125349d13d5SHans Wippel 	*timeout = card->options.vnicc.learning_timeout;
2126349d13d5SHans Wippel 	if (qeth_card_hw_is_reachable(card))
2127349d13d5SHans Wippel 		rc = qeth_l2_vnicc_getset_timeout(card, QETH_VNICC_LEARNING,
2128349d13d5SHans Wippel 						  IPA_VNICC_GET_TIMEOUT,
2129349d13d5SHans Wippel 						  timeout);
2130349d13d5SHans Wippel 
2131349d13d5SHans Wippel 	return rc;
2132349d13d5SHans Wippel }
2133349d13d5SHans Wippel 
2134caa1f0b1SHans Wippel /* check if VNICC is currently enabled */
2135817741a8SAlexandra Winter static bool _qeth_l2_vnicc_is_in_use(struct qeth_card *card)
2136caa1f0b1SHans Wippel {
2137e8a66d80SAlexandra Winter 	if (!card->options.vnicc.sup_chars)
2138caa1f0b1SHans Wippel 		return false;
2139caa1f0b1SHans Wippel 	/* default values are only OK if rx_bcast was not enabled by user
2140caa1f0b1SHans Wippel 	 * or the card is offline.
2141caa1f0b1SHans Wippel 	 */
2142caa1f0b1SHans Wippel 	if (card->options.vnicc.cur_chars == QETH_VNICC_DEFAULT) {
2143caa1f0b1SHans Wippel 		if (!card->options.vnicc.rx_bcast_enabled ||
2144caa1f0b1SHans Wippel 		    !qeth_card_hw_is_reachable(card))
2145caa1f0b1SHans Wippel 			return false;
2146caa1f0b1SHans Wippel 	}
2147caa1f0b1SHans Wippel 	return true;
2148caa1f0b1SHans Wippel }
2149caa1f0b1SHans Wippel 
2150817741a8SAlexandra Winter /**
2151817741a8SAlexandra Winter  *	qeth_bridgeport_allowed - are any qeth_bridgeport functions allowed?
2152817741a8SAlexandra Winter  *	@card: qeth_card structure pointer
2153817741a8SAlexandra Winter  *
2154817741a8SAlexandra Winter  *	qeth_bridgeport functionality is mutually exclusive with usage of the
2155817741a8SAlexandra Winter  *	VNIC Characteristics and dev2br address notifications
2156817741a8SAlexandra Winter  */
2157817741a8SAlexandra Winter bool qeth_bridgeport_allowed(struct qeth_card *card)
2158817741a8SAlexandra Winter {
2159817741a8SAlexandra Winter 	struct qeth_priv *priv = netdev_priv(card->dev);
2160817741a8SAlexandra Winter 
2161817741a8SAlexandra Winter 	return (!_qeth_l2_vnicc_is_in_use(card) &&
2162817741a8SAlexandra Winter 		!(priv->brport_features & BR_LEARNING_SYNC));
2163817741a8SAlexandra Winter }
2164817741a8SAlexandra Winter 
2165349d13d5SHans Wippel /* recover user timeout setting */
2166349d13d5SHans Wippel static bool qeth_l2_vnicc_recover_timeout(struct qeth_card *card, u32 vnicc,
2167349d13d5SHans Wippel 					  u32 *timeout)
2168349d13d5SHans Wippel {
2169349d13d5SHans Wippel 	if (card->options.vnicc.sup_chars & vnicc &&
2170349d13d5SHans Wippel 	    card->options.vnicc.getset_timeout_sup & vnicc &&
2171349d13d5SHans Wippel 	    !qeth_l2_vnicc_getset_timeout(card, vnicc, IPA_VNICC_SET_TIMEOUT,
2172349d13d5SHans Wippel 					  timeout))
2173349d13d5SHans Wippel 		return false;
2174349d13d5SHans Wippel 	*timeout = QETH_VNICC_DEFAULT_TIMEOUT;
2175349d13d5SHans Wippel 	return true;
2176349d13d5SHans Wippel }
2177349d13d5SHans Wippel 
2178caa1f0b1SHans Wippel /* recover user characteristic setting */
2179caa1f0b1SHans Wippel static bool qeth_l2_vnicc_recover_char(struct qeth_card *card, u32 vnicc,
2180caa1f0b1SHans Wippel 				       bool enable)
2181caa1f0b1SHans Wippel {
2182caa1f0b1SHans Wippel 	u32 cmd = enable ? IPA_VNICC_ENABLE : IPA_VNICC_DISABLE;
2183caa1f0b1SHans Wippel 
2184caa1f0b1SHans Wippel 	if (card->options.vnicc.sup_chars & vnicc &&
2185caa1f0b1SHans Wippel 	    card->options.vnicc.set_char_sup & vnicc &&
2186caa1f0b1SHans Wippel 	    !qeth_l2_vnicc_set_char(card, vnicc, cmd))
2187caa1f0b1SHans Wippel 		return false;
2188caa1f0b1SHans Wippel 	card->options.vnicc.wanted_chars &= ~vnicc;
2189caa1f0b1SHans Wippel 	card->options.vnicc.wanted_chars |= QETH_VNICC_DEFAULT & vnicc;
2190caa1f0b1SHans Wippel 	return true;
2191caa1f0b1SHans Wippel }
2192caa1f0b1SHans Wippel 
2193a45b3fafSHans Wippel /* (re-)initialize VNICC */
2194a45b3fafSHans Wippel static void qeth_l2_vnicc_init(struct qeth_card *card)
2195a45b3fafSHans Wippel {
2196349d13d5SHans Wippel 	u32 *timeout = &card->options.vnicc.learning_timeout;
2197b528965bSAlexandra Winter 	bool enable, error = false;
2198caa1f0b1SHans Wippel 	unsigned int chars_len, i;
2199caa1f0b1SHans Wippel 	unsigned long chars_tmp;
2200caa1f0b1SHans Wippel 	u32 sup_cmds, vnicc;
2201caa1f0b1SHans Wippel 
2202a45b3fafSHans Wippel 	QETH_CARD_TEXT(card, 2, "vniccini");
2203caa1f0b1SHans Wippel 	/* reset rx_bcast */
2204caa1f0b1SHans Wippel 	card->options.vnicc.rx_bcast_enabled = 0;
2205a45b3fafSHans Wippel 	/* initial query and storage of VNIC characteristics */
2206a45b3fafSHans Wippel 	if (qeth_l2_vnicc_query_chars(card)) {
2207349d13d5SHans Wippel 		if (card->options.vnicc.wanted_chars != QETH_VNICC_DEFAULT ||
2208349d13d5SHans Wippel 		    *timeout != QETH_VNICC_DEFAULT_TIMEOUT)
2209caa1f0b1SHans Wippel 			dev_err(&card->gdev->dev, "Configuring the VNIC characteristics failed\n");
2210caa1f0b1SHans Wippel 		/* fail quietly if user didn't change the default config */
2211a45b3fafSHans Wippel 		card->options.vnicc.sup_chars = 0;
2212a45b3fafSHans Wippel 		card->options.vnicc.cur_chars = 0;
2213caa1f0b1SHans Wippel 		card->options.vnicc.wanted_chars = QETH_VNICC_DEFAULT;
2214caa1f0b1SHans Wippel 		return;
2215a45b3fafSHans Wippel 	}
2216caa1f0b1SHans Wippel 	/* get supported commands for each supported characteristic */
2217caa1f0b1SHans Wippel 	chars_tmp = card->options.vnicc.sup_chars;
2218caa1f0b1SHans Wippel 	chars_len = sizeof(card->options.vnicc.sup_chars) * BITS_PER_BYTE;
2219caa1f0b1SHans Wippel 	for_each_set_bit(i, &chars_tmp, chars_len) {
2220caa1f0b1SHans Wippel 		vnicc = BIT(i);
2221b528965bSAlexandra Winter 		if (qeth_l2_vnicc_query_cmds(card, vnicc, &sup_cmds)) {
2222b528965bSAlexandra Winter 			sup_cmds = 0;
2223b528965bSAlexandra Winter 			error = true;
2224b528965bSAlexandra Winter 		}
2225be40a86cSAlexandra Winter 		if ((sup_cmds & IPA_VNICC_SET_TIMEOUT) &&
2226be40a86cSAlexandra Winter 		    (sup_cmds & IPA_VNICC_GET_TIMEOUT))
2227be40a86cSAlexandra Winter 			card->options.vnicc.getset_timeout_sup |= vnicc;
2228be40a86cSAlexandra Winter 		else
2229349d13d5SHans Wippel 			card->options.vnicc.getset_timeout_sup &= ~vnicc;
2230be40a86cSAlexandra Winter 		if ((sup_cmds & IPA_VNICC_ENABLE) &&
2231be40a86cSAlexandra Winter 		    (sup_cmds & IPA_VNICC_DISABLE))
2232be40a86cSAlexandra Winter 			card->options.vnicc.set_char_sup |= vnicc;
2233be40a86cSAlexandra Winter 		else
2234caa1f0b1SHans Wippel 			card->options.vnicc.set_char_sup &= ~vnicc;
2235caa1f0b1SHans Wippel 	}
2236caa1f0b1SHans Wippel 	/* enforce assumed default values and recover settings, if changed  */
2237b528965bSAlexandra Winter 	error |= qeth_l2_vnicc_recover_timeout(card, QETH_VNICC_LEARNING,
2238349d13d5SHans Wippel 					       timeout);
2239d1b9ae18SAlexandra Winter 	/* Change chars, if necessary  */
2240d1b9ae18SAlexandra Winter 	chars_tmp = card->options.vnicc.wanted_chars ^
2241d1b9ae18SAlexandra Winter 		    card->options.vnicc.cur_chars;
2242caa1f0b1SHans Wippel 	chars_len = sizeof(card->options.vnicc.wanted_chars) * BITS_PER_BYTE;
2243caa1f0b1SHans Wippel 	for_each_set_bit(i, &chars_tmp, chars_len) {
2244caa1f0b1SHans Wippel 		vnicc = BIT(i);
2245caa1f0b1SHans Wippel 		enable = card->options.vnicc.wanted_chars & vnicc;
2246caa1f0b1SHans Wippel 		error |= qeth_l2_vnicc_recover_char(card, vnicc, enable);
2247caa1f0b1SHans Wippel 	}
2248caa1f0b1SHans Wippel 	if (error)
2249caa1f0b1SHans Wippel 		dev_err(&card->gdev->dev, "Configuring the VNIC characteristics failed\n");
2250caa1f0b1SHans Wippel }
2251caa1f0b1SHans Wippel 
2252caa1f0b1SHans Wippel /* configure default values of VNIC characteristics */
2253caa1f0b1SHans Wippel static void qeth_l2_vnicc_set_defaults(struct qeth_card *card)
2254caa1f0b1SHans Wippel {
2255caa1f0b1SHans Wippel 	/* characteristics values */
2256caa1f0b1SHans Wippel 	card->options.vnicc.sup_chars = QETH_VNICC_ALL;
2257caa1f0b1SHans Wippel 	card->options.vnicc.cur_chars = QETH_VNICC_DEFAULT;
2258349d13d5SHans Wippel 	card->options.vnicc.learning_timeout = QETH_VNICC_DEFAULT_TIMEOUT;
2259caa1f0b1SHans Wippel 	/* supported commands */
2260caa1f0b1SHans Wippel 	card->options.vnicc.set_char_sup = QETH_VNICC_ALL;
2261349d13d5SHans Wippel 	card->options.vnicc.getset_timeout_sup = QETH_VNICC_LEARNING;
2262caa1f0b1SHans Wippel 	/* settings wanted by users */
2263caa1f0b1SHans Wippel 	card->options.vnicc.wanted_chars = QETH_VNICC_DEFAULT;
2264a45b3fafSHans Wippel }
2265a45b3fafSHans Wippel 
22664a71df50SFrank Blaschka module_init(qeth_l2_init);
22674a71df50SFrank Blaschka module_exit(qeth_l2_exit);
22684a71df50SFrank Blaschka MODULE_AUTHOR("Frank Blaschka <frank.blaschka@de.ibm.com>");
22694a71df50SFrank Blaschka MODULE_DESCRIPTION("qeth layer 2 discipline");
22704a71df50SFrank Blaschka MODULE_LICENSE("GPL");
2271