xref: /openbmc/linux/drivers/s390/net/qeth_l2_main.c (revision 22ae2790)
14a71df50SFrank Blaschka /*
2bbcfcdc8SFrank Blaschka  *    Copyright IBM Corp. 2007, 2009
34a71df50SFrank Blaschka  *    Author(s): Utz Bacher <utz.bacher@de.ibm.com>,
44a71df50SFrank Blaschka  *		 Frank Pavlic <fpavlic@de.ibm.com>,
54a71df50SFrank Blaschka  *		 Thomas Spatzier <tspat@de.ibm.com>,
64a71df50SFrank Blaschka  *		 Frank Blaschka <frank.blaschka@de.ibm.com>
74a71df50SFrank Blaschka  */
84a71df50SFrank Blaschka 
974eacdb9SFrank Blaschka #define KMSG_COMPONENT "qeth"
1074eacdb9SFrank Blaschka #define pr_fmt(fmt) KMSG_COMPONENT ": " fmt
1174eacdb9SFrank Blaschka 
124a71df50SFrank Blaschka #include <linux/module.h>
134a71df50SFrank Blaschka #include <linux/moduleparam.h>
144a71df50SFrank Blaschka #include <linux/string.h>
154a71df50SFrank Blaschka #include <linux/errno.h>
164a71df50SFrank Blaschka #include <linux/kernel.h>
175a0e3ad6STejun Heo #include <linux/slab.h>
184a71df50SFrank Blaschka #include <linux/etherdevice.h>
194a71df50SFrank Blaschka #include <linux/mii.h>
204a71df50SFrank Blaschka #include <linux/ip.h>
21ccffad25SJiri Pirko #include <linux/list.h>
224a71df50SFrank Blaschka 
234a71df50SFrank Blaschka #include "qeth_core.h"
24b4d72c08SEugene Crosser #include "qeth_l2.h"
254a71df50SFrank Blaschka 
264a71df50SFrank Blaschka static int qeth_l2_set_offline(struct ccwgroup_device *);
274a71df50SFrank Blaschka static int qeth_l2_stop(struct net_device *);
284a71df50SFrank Blaschka static int qeth_l2_send_delmac(struct qeth_card *, __u8 *);
294a71df50SFrank Blaschka static int qeth_l2_send_setdelmac(struct qeth_card *, __u8 *,
304a71df50SFrank Blaschka 			   enum qeth_ipa_cmds,
314a71df50SFrank Blaschka 			   int (*reply_cb) (struct qeth_card *,
324a71df50SFrank Blaschka 					    struct qeth_reply*,
334a71df50SFrank Blaschka 					    unsigned long));
344a71df50SFrank Blaschka static void qeth_l2_set_multicast_list(struct net_device *);
354a71df50SFrank Blaschka static int qeth_l2_recover(void *);
36c044dc21SEugene Crosser static void qeth_bridgeport_query_support(struct qeth_card *card);
37c044dc21SEugene Crosser static void qeth_bridge_state_change(struct qeth_card *card,
38c044dc21SEugene Crosser 					struct qeth_ipa_cmd *cmd);
39c044dc21SEugene Crosser static void qeth_bridge_host_event(struct qeth_card *card,
40c044dc21SEugene Crosser 					struct qeth_ipa_cmd *cmd);
414a71df50SFrank Blaschka 
424a71df50SFrank Blaschka static int qeth_l2_do_ioctl(struct net_device *dev, struct ifreq *rq, int cmd)
434a71df50SFrank Blaschka {
44509e2562SHeiko Carstens 	struct qeth_card *card = dev->ml_priv;
454a71df50SFrank Blaschka 	struct mii_ioctl_data *mii_data;
464a71df50SFrank Blaschka 	int rc = 0;
474a71df50SFrank Blaschka 
484a71df50SFrank Blaschka 	if (!card)
494a71df50SFrank Blaschka 		return -ENODEV;
504a71df50SFrank Blaschka 
514a71df50SFrank Blaschka 	if ((card->state != CARD_STATE_UP) &&
524a71df50SFrank Blaschka 		(card->state != CARD_STATE_SOFTSETUP))
534a71df50SFrank Blaschka 		return -ENODEV;
544a71df50SFrank Blaschka 
554a71df50SFrank Blaschka 	if (card->info.type == QETH_CARD_TYPE_OSN)
564a71df50SFrank Blaschka 		return -EPERM;
574a71df50SFrank Blaschka 
584a71df50SFrank Blaschka 	switch (cmd) {
594a71df50SFrank Blaschka 	case SIOC_QETH_ADP_SET_SNMP_CONTROL:
604a71df50SFrank Blaschka 		rc = qeth_snmp_command(card, rq->ifr_ifru.ifru_data);
614a71df50SFrank Blaschka 		break;
624a71df50SFrank Blaschka 	case SIOC_QETH_GET_CARD_TYPE:
635113fec0SUrsula Braun 		if ((card->info.type == QETH_CARD_TYPE_OSD ||
645113fec0SUrsula Braun 		     card->info.type == QETH_CARD_TYPE_OSM ||
655113fec0SUrsula Braun 		     card->info.type == QETH_CARD_TYPE_OSX) &&
664a71df50SFrank Blaschka 		    !card->info.guestlan)
674a71df50SFrank Blaschka 			return 1;
684a71df50SFrank Blaschka 		return 0;
694a71df50SFrank Blaschka 		break;
704a71df50SFrank Blaschka 	case SIOCGMIIPHY:
714a71df50SFrank Blaschka 		mii_data = if_mii(rq);
724a71df50SFrank Blaschka 		mii_data->phy_id = 0;
734a71df50SFrank Blaschka 		break;
744a71df50SFrank Blaschka 	case SIOCGMIIREG:
754a71df50SFrank Blaschka 		mii_data = if_mii(rq);
764a71df50SFrank Blaschka 		if (mii_data->phy_id != 0)
774a71df50SFrank Blaschka 			rc = -EINVAL;
784a71df50SFrank Blaschka 		else
794a71df50SFrank Blaschka 			mii_data->val_out = qeth_mdio_read(dev,
804a71df50SFrank Blaschka 				mii_data->phy_id, mii_data->reg_num);
814a71df50SFrank Blaschka 		break;
82c3ab96f3SFrank Blaschka 	case SIOC_QETH_QUERY_OAT:
83c3ab96f3SFrank Blaschka 		rc = qeth_query_oat_command(card, rq->ifr_ifru.ifru_data);
84c3ab96f3SFrank Blaschka 		break;
854a71df50SFrank Blaschka 	default:
864a71df50SFrank Blaschka 		rc = -EOPNOTSUPP;
874a71df50SFrank Blaschka 	}
884a71df50SFrank Blaschka 	if (rc)
89847a50fdSCarsten Otte 		QETH_CARD_TEXT_(card, 2, "ioce%d", rc);
904a71df50SFrank Blaschka 	return rc;
914a71df50SFrank Blaschka }
924a71df50SFrank Blaschka 
934a71df50SFrank Blaschka static int qeth_l2_verify_dev(struct net_device *dev)
944a71df50SFrank Blaschka {
954a71df50SFrank Blaschka 	struct qeth_card *card;
964a71df50SFrank Blaschka 	unsigned long flags;
974a71df50SFrank Blaschka 	int rc = 0;
984a71df50SFrank Blaschka 
994a71df50SFrank Blaschka 	read_lock_irqsave(&qeth_core_card_list.rwlock, flags);
1004a71df50SFrank Blaschka 	list_for_each_entry(card, &qeth_core_card_list.list, list) {
1014a71df50SFrank Blaschka 		if (card->dev == dev) {
1024a71df50SFrank Blaschka 			rc = QETH_REAL_CARD;
1034a71df50SFrank Blaschka 			break;
1044a71df50SFrank Blaschka 		}
1054a71df50SFrank Blaschka 	}
1064a71df50SFrank Blaschka 	read_unlock_irqrestore(&qeth_core_card_list.rwlock, flags);
1074a71df50SFrank Blaschka 
1084a71df50SFrank Blaschka 	return rc;
1094a71df50SFrank Blaschka }
1104a71df50SFrank Blaschka 
1114a71df50SFrank Blaschka static struct net_device *qeth_l2_netdev_by_devno(unsigned char *read_dev_no)
1124a71df50SFrank Blaschka {
1134a71df50SFrank Blaschka 	struct qeth_card *card;
1144a71df50SFrank Blaschka 	struct net_device *ndev;
115f06f6f32SCornelia Huck 	__u16 temp_dev_no;
1164a71df50SFrank Blaschka 	unsigned long flags;
117f06f6f32SCornelia Huck 	struct ccw_dev_id read_devid;
1184a71df50SFrank Blaschka 
1194a71df50SFrank Blaschka 	ndev = NULL;
1204a71df50SFrank Blaschka 	memcpy(&temp_dev_no, read_dev_no, 2);
1214a71df50SFrank Blaschka 	read_lock_irqsave(&qeth_core_card_list.rwlock, flags);
1224a71df50SFrank Blaschka 	list_for_each_entry(card, &qeth_core_card_list.list, list) {
123f06f6f32SCornelia Huck 		ccw_device_get_id(CARD_RDEV(card), &read_devid);
124f06f6f32SCornelia Huck 		if (read_devid.devno == temp_dev_no) {
1254a71df50SFrank Blaschka 			ndev = card->dev;
1264a71df50SFrank Blaschka 			break;
1274a71df50SFrank Blaschka 		}
1284a71df50SFrank Blaschka 	}
1294a71df50SFrank Blaschka 	read_unlock_irqrestore(&qeth_core_card_list.rwlock, flags);
1304a71df50SFrank Blaschka 	return ndev;
1314a71df50SFrank Blaschka }
1324a71df50SFrank Blaschka 
1334a71df50SFrank Blaschka static int qeth_l2_send_setgroupmac_cb(struct qeth_card *card,
1344a71df50SFrank Blaschka 				struct qeth_reply *reply,
1354a71df50SFrank Blaschka 				unsigned long data)
1364a71df50SFrank Blaschka {
1374a71df50SFrank Blaschka 	struct qeth_ipa_cmd *cmd;
1384a71df50SFrank Blaschka 	__u8 *mac;
1394a71df50SFrank Blaschka 
140847a50fdSCarsten Otte 	QETH_CARD_TEXT(card, 2, "L2Sgmacb");
1414a71df50SFrank Blaschka 	cmd = (struct qeth_ipa_cmd *) data;
1424a71df50SFrank Blaschka 	mac = &cmd->data.setdelmac.mac[0];
1434a71df50SFrank Blaschka 	/* MAC already registered, needed in couple/uncouple case */
1440666eb06SUrsula Braun 	if (cmd->hdr.return_code ==  IPA_RC_L2_DUP_MAC) {
1457c510e4bSJohannes Berg 		QETH_DBF_MESSAGE(2, "Group MAC %pM already existing on %s \n",
1467c510e4bSJohannes Berg 			  mac, QETH_CARD_IFNAME(card));
1474a71df50SFrank Blaschka 		cmd->hdr.return_code = 0;
1484a71df50SFrank Blaschka 	}
1494a71df50SFrank Blaschka 	if (cmd->hdr.return_code)
1507c510e4bSJohannes Berg 		QETH_DBF_MESSAGE(2, "Could not set group MAC %pM on %s: %x\n",
1517c510e4bSJohannes Berg 			  mac, QETH_CARD_IFNAME(card), cmd->hdr.return_code);
1524a71df50SFrank Blaschka 	return 0;
1534a71df50SFrank Blaschka }
1544a71df50SFrank Blaschka 
1554a71df50SFrank Blaschka static int qeth_l2_send_setgroupmac(struct qeth_card *card, __u8 *mac)
1564a71df50SFrank Blaschka {
157847a50fdSCarsten Otte 	QETH_CARD_TEXT(card, 2, "L2Sgmac");
1584a71df50SFrank Blaschka 	return qeth_l2_send_setdelmac(card, mac, IPA_CMD_SETGMAC,
1594a71df50SFrank Blaschka 					  qeth_l2_send_setgroupmac_cb);
1604a71df50SFrank Blaschka }
1614a71df50SFrank Blaschka 
1624a71df50SFrank Blaschka static int qeth_l2_send_delgroupmac_cb(struct qeth_card *card,
1634a71df50SFrank Blaschka 				struct qeth_reply *reply,
1644a71df50SFrank Blaschka 				unsigned long data)
1654a71df50SFrank Blaschka {
1664a71df50SFrank Blaschka 	struct qeth_ipa_cmd *cmd;
1674a71df50SFrank Blaschka 	__u8 *mac;
1684a71df50SFrank Blaschka 
169847a50fdSCarsten Otte 	QETH_CARD_TEXT(card, 2, "L2Dgmacb");
1704a71df50SFrank Blaschka 	cmd = (struct qeth_ipa_cmd *) data;
1714a71df50SFrank Blaschka 	mac = &cmd->data.setdelmac.mac[0];
1724a71df50SFrank Blaschka 	if (cmd->hdr.return_code)
1737c510e4bSJohannes Berg 		QETH_DBF_MESSAGE(2, "Could not delete group MAC %pM on %s: %x\n",
1747c510e4bSJohannes Berg 			  mac, QETH_CARD_IFNAME(card), cmd->hdr.return_code);
1754a71df50SFrank Blaschka 	return 0;
1764a71df50SFrank Blaschka }
1774a71df50SFrank Blaschka 
1784a71df50SFrank Blaschka static int qeth_l2_send_delgroupmac(struct qeth_card *card, __u8 *mac)
1794a71df50SFrank Blaschka {
180847a50fdSCarsten Otte 	QETH_CARD_TEXT(card, 2, "L2Dgmac");
1814a71df50SFrank Blaschka 	return qeth_l2_send_setdelmac(card, mac, IPA_CMD_DELGMAC,
1824a71df50SFrank Blaschka 					  qeth_l2_send_delgroupmac_cb);
1834a71df50SFrank Blaschka }
1844a71df50SFrank Blaschka 
1857db2266aSFrank Blaschka static void qeth_l2_add_mc(struct qeth_card *card, __u8 *mac, int vmac)
1864a71df50SFrank Blaschka {
1874a71df50SFrank Blaschka 	struct qeth_mc_mac *mc;
1887db2266aSFrank Blaschka 	int rc;
1894a71df50SFrank Blaschka 
1904a71df50SFrank Blaschka 	mc = kmalloc(sizeof(struct qeth_mc_mac), GFP_ATOMIC);
1914a71df50SFrank Blaschka 
19214cc21b6SFrank Blaschka 	if (!mc)
1934a71df50SFrank Blaschka 		return;
1944a71df50SFrank Blaschka 
1954a71df50SFrank Blaschka 	memcpy(mc->mc_addr, mac, OSA_ADDR_LEN);
1964a71df50SFrank Blaschka 	mc->mc_addrlen = OSA_ADDR_LEN;
1977db2266aSFrank Blaschka 	mc->is_vmac = vmac;
1984a71df50SFrank Blaschka 
1997db2266aSFrank Blaschka 	if (vmac) {
2007db2266aSFrank Blaschka 		rc = qeth_l2_send_setdelmac(card, mac, IPA_CMD_SETVMAC,
2017db2266aSFrank Blaschka 					NULL);
2027db2266aSFrank Blaschka 	} else {
2037db2266aSFrank Blaschka 		rc = qeth_l2_send_setgroupmac(card, mac);
2047db2266aSFrank Blaschka 	}
2057db2266aSFrank Blaschka 
2067db2266aSFrank Blaschka 	if (!rc)
2074a71df50SFrank Blaschka 		list_add_tail(&mc->list, &card->mc_list);
2084a71df50SFrank Blaschka 	else
2094a71df50SFrank Blaschka 		kfree(mc);
2104a71df50SFrank Blaschka }
2114a71df50SFrank Blaschka 
21270919e23SUrsula Braun static void qeth_l2_del_all_mc(struct qeth_card *card, int del)
2134a71df50SFrank Blaschka {
2144a71df50SFrank Blaschka 	struct qeth_mc_mac *mc, *tmp;
2154a71df50SFrank Blaschka 
2164a71df50SFrank Blaschka 	spin_lock_bh(&card->mclock);
2174a71df50SFrank Blaschka 	list_for_each_entry_safe(mc, tmp, &card->mc_list, list) {
21870919e23SUrsula Braun 		if (del) {
2197db2266aSFrank Blaschka 			if (mc->is_vmac)
2207db2266aSFrank Blaschka 				qeth_l2_send_setdelmac(card, mc->mc_addr,
2217db2266aSFrank Blaschka 					IPA_CMD_DELVMAC, NULL);
2227db2266aSFrank Blaschka 			else
2234a71df50SFrank Blaschka 				qeth_l2_send_delgroupmac(card, mc->mc_addr);
22470919e23SUrsula Braun 		}
2254a71df50SFrank Blaschka 		list_del(&mc->list);
2264a71df50SFrank Blaschka 		kfree(mc);
2274a71df50SFrank Blaschka 	}
2284a71df50SFrank Blaschka 	spin_unlock_bh(&card->mclock);
2294a71df50SFrank Blaschka }
2304a71df50SFrank Blaschka 
231ce73e10eSKlaus-Dieter Wacker static inline int qeth_l2_get_cast_type(struct qeth_card *card,
232ce73e10eSKlaus-Dieter Wacker 			struct sk_buff *skb)
2334a71df50SFrank Blaschka {
234ce73e10eSKlaus-Dieter Wacker 	if (card->info.type == QETH_CARD_TYPE_OSN)
235ce73e10eSKlaus-Dieter Wacker 		return RTN_UNSPEC;
236ce73e10eSKlaus-Dieter Wacker 	if (is_broadcast_ether_addr(skb->data))
237ce73e10eSKlaus-Dieter Wacker 		return RTN_BROADCAST;
238ce73e10eSKlaus-Dieter Wacker 	if (is_multicast_ether_addr(skb->data))
239ce73e10eSKlaus-Dieter Wacker 		return RTN_MULTICAST;
240ce73e10eSKlaus-Dieter Wacker 	return RTN_UNSPEC;
2414a71df50SFrank Blaschka }
2424a71df50SFrank Blaschka 
2434a71df50SFrank Blaschka static void qeth_l2_fill_header(struct qeth_card *card, struct qeth_hdr *hdr,
2444a71df50SFrank Blaschka 			struct sk_buff *skb, int ipv, int cast_type)
2454a71df50SFrank Blaschka {
246683d718aSFrank Blaschka 	struct vlan_ethhdr *veth = (struct vlan_ethhdr *)skb_mac_header(skb);
2474a71df50SFrank Blaschka 
2484a71df50SFrank Blaschka 	memset(hdr, 0, sizeof(struct qeth_hdr));
2494a71df50SFrank Blaschka 	hdr->hdr.l2.id = QETH_HEADER_TYPE_LAYER2;
2504a71df50SFrank Blaschka 
2514a71df50SFrank Blaschka 	/* set byte byte 3 to casting flags */
2524a71df50SFrank Blaschka 	if (cast_type == RTN_MULTICAST)
2534a71df50SFrank Blaschka 		hdr->hdr.l2.flags[2] |= QETH_LAYER2_FLAG_MULTICAST;
2544a71df50SFrank Blaschka 	else if (cast_type == RTN_BROADCAST)
2554a71df50SFrank Blaschka 		hdr->hdr.l2.flags[2] |= QETH_LAYER2_FLAG_BROADCAST;
2564a71df50SFrank Blaschka 	else
257ce73e10eSKlaus-Dieter Wacker 		hdr->hdr.l2.flags[2] |= QETH_LAYER2_FLAG_UNICAST;
2584a71df50SFrank Blaschka 
2594a71df50SFrank Blaschka 	hdr->hdr.l2.pkt_length = skb->len-QETH_HEADER_SIZE;
2604a71df50SFrank Blaschka 	/* VSWITCH relies on the VLAN
2614a71df50SFrank Blaschka 	 * information to be present in
2624a71df50SFrank Blaschka 	 * the QDIO header */
2634a71df50SFrank Blaschka 	if (veth->h_vlan_proto == __constant_htons(ETH_P_8021Q)) {
2644a71df50SFrank Blaschka 		hdr->hdr.l2.flags[2] |= QETH_LAYER2_FLAG_VLAN;
2654a71df50SFrank Blaschka 		hdr->hdr.l2.vlan_id = ntohs(veth->h_vlan_TCI);
2664a71df50SFrank Blaschka 	}
2674a71df50SFrank Blaschka }
2684a71df50SFrank Blaschka 
2694a71df50SFrank Blaschka static int qeth_l2_send_setdelvlan_cb(struct qeth_card *card,
2704a71df50SFrank Blaschka 			struct qeth_reply *reply, unsigned long data)
2714a71df50SFrank Blaschka {
2724a71df50SFrank Blaschka 	struct qeth_ipa_cmd *cmd;
2734a71df50SFrank Blaschka 
274847a50fdSCarsten Otte 	QETH_CARD_TEXT(card, 2, "L2sdvcb");
2754a71df50SFrank Blaschka 	cmd = (struct qeth_ipa_cmd *) data;
2764a71df50SFrank Blaschka 	if (cmd->hdr.return_code) {
27714cc21b6SFrank Blaschka 		QETH_DBF_MESSAGE(2, "Error in processing VLAN %i on %s: 0x%x. "
2784a71df50SFrank Blaschka 			  "Continuing\n", cmd->data.setdelvlan.vlan_id,
2794a71df50SFrank Blaschka 			  QETH_CARD_IFNAME(card), cmd->hdr.return_code);
280847a50fdSCarsten Otte 		QETH_CARD_TEXT_(card, 2, "L2VL%4x", cmd->hdr.command);
281847a50fdSCarsten Otte 		QETH_CARD_TEXT_(card, 2, "err%d", cmd->hdr.return_code);
2824a71df50SFrank Blaschka 	}
2834a71df50SFrank Blaschka 	return 0;
2844a71df50SFrank Blaschka }
2854a71df50SFrank Blaschka 
2864a71df50SFrank Blaschka static int qeth_l2_send_setdelvlan(struct qeth_card *card, __u16 i,
2874a71df50SFrank Blaschka 				enum qeth_ipa_cmds ipacmd)
2884a71df50SFrank Blaschka {
2894a71df50SFrank Blaschka 	struct qeth_ipa_cmd *cmd;
2904a71df50SFrank Blaschka 	struct qeth_cmd_buffer *iob;
2914a71df50SFrank Blaschka 
292847a50fdSCarsten Otte 	QETH_CARD_TEXT_(card, 4, "L2sdv%x", ipacmd);
2934a71df50SFrank Blaschka 	iob = qeth_get_ipacmd_buffer(card, ipacmd, QETH_PROT_IPV4);
2944a71df50SFrank Blaschka 	cmd = (struct qeth_ipa_cmd *)(iob->data+IPA_PDU_HEADER_SIZE);
2954a71df50SFrank Blaschka 	cmd->data.setdelvlan.vlan_id = i;
2964a71df50SFrank Blaschka 	return qeth_send_ipa_cmd(card, iob,
2974a71df50SFrank Blaschka 				 qeth_l2_send_setdelvlan_cb, NULL);
2984a71df50SFrank Blaschka }
2994a71df50SFrank Blaschka 
30070919e23SUrsula Braun static void qeth_l2_process_vlans(struct qeth_card *card)
3014a71df50SFrank Blaschka {
3024a71df50SFrank Blaschka 	struct qeth_vlan_vid *id;
303847a50fdSCarsten Otte 	QETH_CARD_TEXT(card, 3, "L2prcvln");
3044a71df50SFrank Blaschka 	spin_lock_bh(&card->vlanlock);
3054a71df50SFrank Blaschka 	list_for_each_entry(id, &card->vid_list, list) {
30670919e23SUrsula Braun 		qeth_l2_send_setdelvlan(card, id->vid, IPA_CMD_SETVLAN);
3074a71df50SFrank Blaschka 	}
3084a71df50SFrank Blaschka 	spin_unlock_bh(&card->vlanlock);
3094a71df50SFrank Blaschka }
3104a71df50SFrank Blaschka 
31180d5c368SPatrick McHardy static int qeth_l2_vlan_rx_add_vid(struct net_device *dev,
31280d5c368SPatrick McHardy 				   __be16 proto, u16 vid)
3134a71df50SFrank Blaschka {
314509e2562SHeiko Carstens 	struct qeth_card *card = dev->ml_priv;
3154a71df50SFrank Blaschka 	struct qeth_vlan_vid *id;
3164a71df50SFrank Blaschka 
317847a50fdSCarsten Otte 	QETH_CARD_TEXT_(card, 4, "aid:%d", vid);
31810651db7SUrsula Braun 	if (!vid)
3198e586137SJiri Pirko 		return 0;
3205113fec0SUrsula Braun 	if (card->info.type == QETH_CARD_TYPE_OSM) {
321847a50fdSCarsten Otte 		QETH_CARD_TEXT(card, 3, "aidOSM");
3228e586137SJiri Pirko 		return 0;
3235113fec0SUrsula Braun 	}
3248e98ac48SUrsula Braun 	if (qeth_wait_for_threads(card, QETH_RECOVER_THREAD)) {
325847a50fdSCarsten Otte 		QETH_CARD_TEXT(card, 3, "aidREC");
3268e586137SJiri Pirko 		return 0;
3278e98ac48SUrsula Braun 	}
3284a71df50SFrank Blaschka 	id = kmalloc(sizeof(struct qeth_vlan_vid), GFP_ATOMIC);
3294a71df50SFrank Blaschka 	if (id) {
3304a71df50SFrank Blaschka 		id->vid = vid;
3314a71df50SFrank Blaschka 		qeth_l2_send_setdelvlan(card, vid, IPA_CMD_SETVLAN);
3324a71df50SFrank Blaschka 		spin_lock_bh(&card->vlanlock);
3334a71df50SFrank Blaschka 		list_add_tail(&id->list, &card->vid_list);
3344a71df50SFrank Blaschka 		spin_unlock_bh(&card->vlanlock);
3358e586137SJiri Pirko 	} else {
3368e586137SJiri Pirko 		return -ENOMEM;
3374a71df50SFrank Blaschka 	}
3388e586137SJiri Pirko 	return 0;
3394a71df50SFrank Blaschka }
3404a71df50SFrank Blaschka 
34180d5c368SPatrick McHardy static int qeth_l2_vlan_rx_kill_vid(struct net_device *dev,
34280d5c368SPatrick McHardy 				    __be16 proto, u16 vid)
3434a71df50SFrank Blaschka {
3444a71df50SFrank Blaschka 	struct qeth_vlan_vid *id, *tmpid = NULL;
345509e2562SHeiko Carstens 	struct qeth_card *card = dev->ml_priv;
3464a71df50SFrank Blaschka 
347847a50fdSCarsten Otte 	QETH_CARD_TEXT_(card, 4, "kid:%d", vid);
3485113fec0SUrsula Braun 	if (card->info.type == QETH_CARD_TYPE_OSM) {
349847a50fdSCarsten Otte 		QETH_CARD_TEXT(card, 3, "kidOSM");
3508e586137SJiri Pirko 		return 0;
3515113fec0SUrsula Braun 	}
3528e98ac48SUrsula Braun 	if (qeth_wait_for_threads(card, QETH_RECOVER_THREAD)) {
353847a50fdSCarsten Otte 		QETH_CARD_TEXT(card, 3, "kidREC");
3548e586137SJiri Pirko 		return 0;
3558e98ac48SUrsula Braun 	}
3564a71df50SFrank Blaschka 	spin_lock_bh(&card->vlanlock);
3574a71df50SFrank Blaschka 	list_for_each_entry(id, &card->vid_list, list) {
3584a71df50SFrank Blaschka 		if (id->vid == vid) {
3594a71df50SFrank Blaschka 			list_del(&id->list);
3604a71df50SFrank Blaschka 			tmpid = id;
3614a71df50SFrank Blaschka 			break;
3624a71df50SFrank Blaschka 		}
3634a71df50SFrank Blaschka 	}
3644a71df50SFrank Blaschka 	spin_unlock_bh(&card->vlanlock);
3654a71df50SFrank Blaschka 	if (tmpid) {
3664a71df50SFrank Blaschka 		qeth_l2_send_setdelvlan(card, vid, IPA_CMD_DELVLAN);
3674a71df50SFrank Blaschka 		kfree(tmpid);
3684a71df50SFrank Blaschka 	}
3694a71df50SFrank Blaschka 	qeth_l2_set_multicast_list(card->dev);
3708e586137SJiri Pirko 	return 0;
3714a71df50SFrank Blaschka }
3724a71df50SFrank Blaschka 
3734a71df50SFrank Blaschka static int qeth_l2_stop_card(struct qeth_card *card, int recovery_mode)
3744a71df50SFrank Blaschka {
3754a71df50SFrank Blaschka 	int rc = 0;
3764a71df50SFrank Blaschka 
377d11ba0c4SPeter Tiedemann 	QETH_DBF_TEXT(SETUP , 2, "stopcard");
378d11ba0c4SPeter Tiedemann 	QETH_DBF_HEX(SETUP, 2, &card, sizeof(void *));
3794a71df50SFrank Blaschka 
3804a71df50SFrank Blaschka 	qeth_set_allowed_threads(card, 0, 1);
3814a71df50SFrank Blaschka 	if (card->read.state == CH_STATE_UP &&
3824a71df50SFrank Blaschka 	    card->write.state == CH_STATE_UP &&
3834a71df50SFrank Blaschka 	    (card->state == CARD_STATE_UP)) {
3844a71df50SFrank Blaschka 		if (recovery_mode &&
3854a71df50SFrank Blaschka 		    card->info.type != QETH_CARD_TYPE_OSN) {
3864a71df50SFrank Blaschka 			qeth_l2_stop(card->dev);
3874a71df50SFrank Blaschka 		} else {
3884a71df50SFrank Blaschka 			rtnl_lock();
3894a71df50SFrank Blaschka 			dev_close(card->dev);
3904a71df50SFrank Blaschka 			rtnl_unlock();
3914a71df50SFrank Blaschka 		}
39270919e23SUrsula Braun 		card->info.mac_bits &= ~QETH_LAYER2_MAC_REGISTERED;
3934a71df50SFrank Blaschka 		card->state = CARD_STATE_SOFTSETUP;
3944a71df50SFrank Blaschka 	}
3954a71df50SFrank Blaschka 	if (card->state == CARD_STATE_SOFTSETUP) {
39670919e23SUrsula Braun 		qeth_l2_del_all_mc(card, 0);
3974a71df50SFrank Blaschka 		qeth_clear_ipacmd_list(card);
3984a71df50SFrank Blaschka 		card->state = CARD_STATE_HARDSETUP;
3994a71df50SFrank Blaschka 	}
4004a71df50SFrank Blaschka 	if (card->state == CARD_STATE_HARDSETUP) {
4014a71df50SFrank Blaschka 		qeth_qdio_clear_card(card, 0);
4024a71df50SFrank Blaschka 		qeth_clear_qdio_buffers(card);
4034a71df50SFrank Blaschka 		qeth_clear_working_pool_list(card);
4044a71df50SFrank Blaschka 		card->state = CARD_STATE_DOWN;
4054a71df50SFrank Blaschka 	}
4064a71df50SFrank Blaschka 	if (card->state == CARD_STATE_DOWN) {
4074a71df50SFrank Blaschka 		qeth_clear_cmd_buffers(&card->read);
4084a71df50SFrank Blaschka 		qeth_clear_cmd_buffers(&card->write);
4094a71df50SFrank Blaschka 	}
4104a71df50SFrank Blaschka 	return rc;
4114a71df50SFrank Blaschka }
4124a71df50SFrank Blaschka 
413a1c3ed4cSFrank Blaschka static int qeth_l2_process_inbound_buffer(struct qeth_card *card,
414a1c3ed4cSFrank Blaschka 				int budget, int *done)
4154a71df50SFrank Blaschka {
416a1c3ed4cSFrank Blaschka 	int work_done = 0;
4174a71df50SFrank Blaschka 	struct sk_buff *skb;
4184a71df50SFrank Blaschka 	struct qeth_hdr *hdr;
4194a71df50SFrank Blaschka 	unsigned int len;
4204a71df50SFrank Blaschka 
421a1c3ed4cSFrank Blaschka 	*done = 0;
42218af5c17SStefan Raspl 	WARN_ON_ONCE(!budget);
423a1c3ed4cSFrank Blaschka 	while (budget) {
424a1c3ed4cSFrank Blaschka 		skb = qeth_core_get_next_skb(card,
425b3332930SFrank Blaschka 			&card->qdio.in_q->bufs[card->rx.b_index],
426a1c3ed4cSFrank Blaschka 			&card->rx.b_element, &card->rx.e_offset, &hdr);
427a1c3ed4cSFrank Blaschka 		if (!skb) {
428a1c3ed4cSFrank Blaschka 			*done = 1;
429a1c3ed4cSFrank Blaschka 			break;
4304a71df50SFrank Blaschka 		}
431a1c3ed4cSFrank Blaschka 		skb->dev = card->dev;
4324a71df50SFrank Blaschka 		switch (hdr->hdr.l2.id) {
4334a71df50SFrank Blaschka 		case QETH_HEADER_TYPE_LAYER2:
4344a71df50SFrank Blaschka 			skb->pkt_type = PACKET_HOST;
4354a71df50SFrank Blaschka 			skb->protocol = eth_type_trans(skb, skb->dev);
4364a71df50SFrank Blaschka 			skb->ip_summed = CHECKSUM_NONE;
43712883725SUrsula Braun 			if (skb->protocol == htons(ETH_P_802_2))
4384a71df50SFrank Blaschka 				*((__u32 *)skb->cb) = ++card->seqno.pkt_seqno;
4394a71df50SFrank Blaschka 			len = skb->len;
440a1c3ed4cSFrank Blaschka 			netif_receive_skb(skb);
4414a71df50SFrank Blaschka 			break;
4424a71df50SFrank Blaschka 		case QETH_HEADER_TYPE_OSN:
4432d488c2fSUrsula Braun 			if (card->info.type == QETH_CARD_TYPE_OSN) {
4444a71df50SFrank Blaschka 				skb_push(skb, sizeof(struct qeth_hdr));
4454a71df50SFrank Blaschka 				skb_copy_to_linear_data(skb, hdr,
4464a71df50SFrank Blaschka 						sizeof(struct qeth_hdr));
4474a71df50SFrank Blaschka 				len = skb->len;
4484a71df50SFrank Blaschka 				card->osn_info.data_cb(skb);
4494a71df50SFrank Blaschka 				break;
4502d488c2fSUrsula Braun 			}
4512d488c2fSUrsula Braun 			/* else unknown */
4524a71df50SFrank Blaschka 		default:
4534a71df50SFrank Blaschka 			dev_kfree_skb_any(skb);
454847a50fdSCarsten Otte 			QETH_CARD_TEXT(card, 3, "inbunkno");
455d11ba0c4SPeter Tiedemann 			QETH_DBF_HEX(CTRL, 3, hdr, QETH_DBF_CTRL_LEN);
4564a71df50SFrank Blaschka 			continue;
4574a71df50SFrank Blaschka 		}
458a1c3ed4cSFrank Blaschka 		work_done++;
459a1c3ed4cSFrank Blaschka 		budget--;
4604a71df50SFrank Blaschka 		card->stats.rx_packets++;
4614a71df50SFrank Blaschka 		card->stats.rx_bytes += len;
4624a71df50SFrank Blaschka 	}
463a1c3ed4cSFrank Blaschka 	return work_done;
464a1c3ed4cSFrank Blaschka }
465a1c3ed4cSFrank Blaschka 
466a1c3ed4cSFrank Blaschka static int qeth_l2_poll(struct napi_struct *napi, int budget)
467a1c3ed4cSFrank Blaschka {
468a1c3ed4cSFrank Blaschka 	struct qeth_card *card = container_of(napi, struct qeth_card, napi);
469a1c3ed4cSFrank Blaschka 	int work_done = 0;
470a1c3ed4cSFrank Blaschka 	struct qeth_qdio_buffer *buffer;
471a1c3ed4cSFrank Blaschka 	int done;
472a1c3ed4cSFrank Blaschka 	int new_budget = budget;
473a1c3ed4cSFrank Blaschka 
474a1c3ed4cSFrank Blaschka 	if (card->options.performance_stats) {
475a1c3ed4cSFrank Blaschka 		card->perf_stats.inbound_cnt++;
476a1c3ed4cSFrank Blaschka 		card->perf_stats.inbound_start_time = qeth_get_micros();
477a1c3ed4cSFrank Blaschka 	}
478a1c3ed4cSFrank Blaschka 
479a1c3ed4cSFrank Blaschka 	while (1) {
480a1c3ed4cSFrank Blaschka 		if (!card->rx.b_count) {
481a1c3ed4cSFrank Blaschka 			card->rx.qdio_err = 0;
482a1c3ed4cSFrank Blaschka 			card->rx.b_count = qdio_get_next_buffers(
483a1c3ed4cSFrank Blaschka 				card->data.ccwdev, 0, &card->rx.b_index,
484a1c3ed4cSFrank Blaschka 				&card->rx.qdio_err);
485a1c3ed4cSFrank Blaschka 			if (card->rx.b_count <= 0) {
486a1c3ed4cSFrank Blaschka 				card->rx.b_count = 0;
487a1c3ed4cSFrank Blaschka 				break;
488a1c3ed4cSFrank Blaschka 			}
489a1c3ed4cSFrank Blaschka 			card->rx.b_element =
490a1c3ed4cSFrank Blaschka 				&card->qdio.in_q->bufs[card->rx.b_index]
491a1c3ed4cSFrank Blaschka 				.buffer->element[0];
492a1c3ed4cSFrank Blaschka 			card->rx.e_offset = 0;
493a1c3ed4cSFrank Blaschka 		}
494a1c3ed4cSFrank Blaschka 
495a1c3ed4cSFrank Blaschka 		while (card->rx.b_count) {
496a1c3ed4cSFrank Blaschka 			buffer = &card->qdio.in_q->bufs[card->rx.b_index];
497a1c3ed4cSFrank Blaschka 			if (!(card->rx.qdio_err &&
498a1c3ed4cSFrank Blaschka 			    qeth_check_qdio_errors(card, buffer->buffer,
499a1c3ed4cSFrank Blaschka 			    card->rx.qdio_err, "qinerr")))
500a1c3ed4cSFrank Blaschka 				work_done += qeth_l2_process_inbound_buffer(
501a1c3ed4cSFrank Blaschka 					card, new_budget, &done);
502a1c3ed4cSFrank Blaschka 			else
503a1c3ed4cSFrank Blaschka 				done = 1;
504a1c3ed4cSFrank Blaschka 
505a1c3ed4cSFrank Blaschka 			if (done) {
506a1c3ed4cSFrank Blaschka 				if (card->options.performance_stats)
507a1c3ed4cSFrank Blaschka 					card->perf_stats.bufs_rec++;
508a1c3ed4cSFrank Blaschka 				qeth_put_buffer_pool_entry(card,
509a1c3ed4cSFrank Blaschka 					buffer->pool_entry);
510a1c3ed4cSFrank Blaschka 				qeth_queue_input_buffer(card, card->rx.b_index);
511a1c3ed4cSFrank Blaschka 				card->rx.b_count--;
512a1c3ed4cSFrank Blaschka 				if (card->rx.b_count) {
513a1c3ed4cSFrank Blaschka 					card->rx.b_index =
514a1c3ed4cSFrank Blaschka 						(card->rx.b_index + 1) %
515a1c3ed4cSFrank Blaschka 						QDIO_MAX_BUFFERS_PER_Q;
516a1c3ed4cSFrank Blaschka 					card->rx.b_element =
517a1c3ed4cSFrank Blaschka 						&card->qdio.in_q
518a1c3ed4cSFrank Blaschka 						->bufs[card->rx.b_index]
519a1c3ed4cSFrank Blaschka 						.buffer->element[0];
520a1c3ed4cSFrank Blaschka 					card->rx.e_offset = 0;
521a1c3ed4cSFrank Blaschka 				}
522a1c3ed4cSFrank Blaschka 			}
523a1c3ed4cSFrank Blaschka 
524a1c3ed4cSFrank Blaschka 			if (work_done >= budget)
525a1c3ed4cSFrank Blaschka 				goto out;
526a1c3ed4cSFrank Blaschka 			else
527a1c3ed4cSFrank Blaschka 				new_budget = budget - work_done;
528a1c3ed4cSFrank Blaschka 		}
529a1c3ed4cSFrank Blaschka 	}
530a1c3ed4cSFrank Blaschka 
531a1c3ed4cSFrank Blaschka 	napi_complete(napi);
532a1c3ed4cSFrank Blaschka 	if (qdio_start_irq(card->data.ccwdev, 0))
533a1c3ed4cSFrank Blaschka 		napi_schedule(&card->napi);
534a1c3ed4cSFrank Blaschka out:
535a1c3ed4cSFrank Blaschka 	if (card->options.performance_stats)
536a1c3ed4cSFrank Blaschka 		card->perf_stats.inbound_time += qeth_get_micros() -
537a1c3ed4cSFrank Blaschka 			card->perf_stats.inbound_start_time;
538a1c3ed4cSFrank Blaschka 	return work_done;
5394a71df50SFrank Blaschka }
5404a71df50SFrank Blaschka 
5414a71df50SFrank Blaschka static int qeth_l2_send_setdelmac(struct qeth_card *card, __u8 *mac,
5424a71df50SFrank Blaschka 			   enum qeth_ipa_cmds ipacmd,
5434a71df50SFrank Blaschka 			   int (*reply_cb) (struct qeth_card *,
5444a71df50SFrank Blaschka 					    struct qeth_reply*,
5454a71df50SFrank Blaschka 					    unsigned long))
5464a71df50SFrank Blaschka {
5474a71df50SFrank Blaschka 	struct qeth_ipa_cmd *cmd;
5484a71df50SFrank Blaschka 	struct qeth_cmd_buffer *iob;
5494a71df50SFrank Blaschka 
550847a50fdSCarsten Otte 	QETH_CARD_TEXT(card, 2, "L2sdmac");
5514a71df50SFrank Blaschka 	iob = qeth_get_ipacmd_buffer(card, ipacmd, QETH_PROT_IPV4);
5524a71df50SFrank Blaschka 	cmd = (struct qeth_ipa_cmd *)(iob->data+IPA_PDU_HEADER_SIZE);
5534a71df50SFrank Blaschka 	cmd->data.setdelmac.mac_length = OSA_ADDR_LEN;
5544a71df50SFrank Blaschka 	memcpy(&cmd->data.setdelmac.mac, mac, OSA_ADDR_LEN);
5554a71df50SFrank Blaschka 	return qeth_send_ipa_cmd(card, iob, reply_cb, NULL);
5564a71df50SFrank Blaschka }
5574a71df50SFrank Blaschka 
5584a71df50SFrank Blaschka static int qeth_l2_send_setmac_cb(struct qeth_card *card,
5594a71df50SFrank Blaschka 			   struct qeth_reply *reply,
5604a71df50SFrank Blaschka 			   unsigned long data)
5614a71df50SFrank Blaschka {
5624a71df50SFrank Blaschka 	struct qeth_ipa_cmd *cmd;
5634a71df50SFrank Blaschka 
564847a50fdSCarsten Otte 	QETH_CARD_TEXT(card, 2, "L2Smaccb");
5654a71df50SFrank Blaschka 	cmd = (struct qeth_ipa_cmd *) data;
5664a71df50SFrank Blaschka 	if (cmd->hdr.return_code) {
567847a50fdSCarsten Otte 		QETH_CARD_TEXT_(card, 2, "L2er%x", cmd->hdr.return_code);
5684a71df50SFrank Blaschka 		card->info.mac_bits &= ~QETH_LAYER2_MAC_REGISTERED;
5690666eb06SUrsula Braun 		switch (cmd->hdr.return_code) {
5700666eb06SUrsula Braun 		case IPA_RC_L2_DUP_MAC:
5710666eb06SUrsula Braun 		case IPA_RC_L2_DUP_LAYER3_MAC:
5720666eb06SUrsula Braun 			dev_warn(&card->gdev->dev,
573d998ab0bSH Hartley Sweeten 				"MAC address %pM already exists\n",
574221c17feSUrsula Braun 				cmd->data.setdelmac.mac);
5750666eb06SUrsula Braun 			break;
5760666eb06SUrsula Braun 		case IPA_RC_L2_MAC_NOT_AUTH_BY_HYP:
5770666eb06SUrsula Braun 		case IPA_RC_L2_MAC_NOT_AUTH_BY_ADP:
5780666eb06SUrsula Braun 			dev_warn(&card->gdev->dev,
579d998ab0bSH Hartley Sweeten 				"MAC address %pM is not authorized\n",
580221c17feSUrsula Braun 				cmd->data.setdelmac.mac);
5810666eb06SUrsula Braun 			break;
5820666eb06SUrsula Braun 		default:
5830666eb06SUrsula Braun 			break;
5840666eb06SUrsula Braun 		}
5854a71df50SFrank Blaschka 	} else {
5864a71df50SFrank Blaschka 		card->info.mac_bits |= QETH_LAYER2_MAC_REGISTERED;
5874a71df50SFrank Blaschka 		memcpy(card->dev->dev_addr, cmd->data.setdelmac.mac,
5884a71df50SFrank Blaschka 		       OSA_ADDR_LEN);
58974eacdb9SFrank Blaschka 		dev_info(&card->gdev->dev,
590d998ab0bSH Hartley Sweeten 			"MAC address %pM successfully registered on device %s\n",
591d998ab0bSH Hartley Sweeten 			card->dev->dev_addr, card->dev->name);
5924a71df50SFrank Blaschka 	}
5934a71df50SFrank Blaschka 	return 0;
5944a71df50SFrank Blaschka }
5954a71df50SFrank Blaschka 
5964a71df50SFrank Blaschka static int qeth_l2_send_setmac(struct qeth_card *card, __u8 *mac)
5974a71df50SFrank Blaschka {
598847a50fdSCarsten Otte 	QETH_CARD_TEXT(card, 2, "L2Setmac");
5994a71df50SFrank Blaschka 	return qeth_l2_send_setdelmac(card, mac, IPA_CMD_SETVMAC,
6004a71df50SFrank Blaschka 					  qeth_l2_send_setmac_cb);
6014a71df50SFrank Blaschka }
6024a71df50SFrank Blaschka 
6034a71df50SFrank Blaschka static int qeth_l2_send_delmac_cb(struct qeth_card *card,
6044a71df50SFrank Blaschka 			   struct qeth_reply *reply,
6054a71df50SFrank Blaschka 			   unsigned long data)
6064a71df50SFrank Blaschka {
6074a71df50SFrank Blaschka 	struct qeth_ipa_cmd *cmd;
6084a71df50SFrank Blaschka 
609847a50fdSCarsten Otte 	QETH_CARD_TEXT(card, 2, "L2Dmaccb");
6104a71df50SFrank Blaschka 	cmd = (struct qeth_ipa_cmd *) data;
6114a71df50SFrank Blaschka 	if (cmd->hdr.return_code) {
612847a50fdSCarsten Otte 		QETH_CARD_TEXT_(card, 2, "err%d", cmd->hdr.return_code);
6134a71df50SFrank Blaschka 		return 0;
6144a71df50SFrank Blaschka 	}
6154a71df50SFrank Blaschka 	card->info.mac_bits &= ~QETH_LAYER2_MAC_REGISTERED;
6164a71df50SFrank Blaschka 
6174a71df50SFrank Blaschka 	return 0;
6184a71df50SFrank Blaschka }
6194a71df50SFrank Blaschka 
6204a71df50SFrank Blaschka static int qeth_l2_send_delmac(struct qeth_card *card, __u8 *mac)
6214a71df50SFrank Blaschka {
622847a50fdSCarsten Otte 	QETH_CARD_TEXT(card, 2, "L2Delmac");
6234a71df50SFrank Blaschka 	if (!(card->info.mac_bits & QETH_LAYER2_MAC_REGISTERED))
6244a71df50SFrank Blaschka 		return 0;
6254a71df50SFrank Blaschka 	return qeth_l2_send_setdelmac(card, mac, IPA_CMD_DELVMAC,
6264a71df50SFrank Blaschka 					  qeth_l2_send_delmac_cb);
6274a71df50SFrank Blaschka }
6284a71df50SFrank Blaschka 
6294a71df50SFrank Blaschka static int qeth_l2_request_initial_mac(struct qeth_card *card)
6304a71df50SFrank Blaschka {
6314a71df50SFrank Blaschka 	int rc = 0;
6324a71df50SFrank Blaschka 	char vendor_pre[] = {0x02, 0x00, 0x00};
6334a71df50SFrank Blaschka 
634d11ba0c4SPeter Tiedemann 	QETH_DBF_TEXT(SETUP, 2, "doL2init");
635d11ba0c4SPeter Tiedemann 	QETH_DBF_TEXT_(SETUP, 2, "doL2%s", CARD_BUS_ID(card));
6364a71df50SFrank Blaschka 
637a134884aSStefan Raspl 	if (qeth_is_supported(card, IPA_SETADAPTERPARMS)) {
6384a71df50SFrank Blaschka 		rc = qeth_query_setadapterparms(card);
6394a71df50SFrank Blaschka 		if (rc) {
640a134884aSStefan Raspl 			QETH_DBF_MESSAGE(2, "could not query adapter "
641a134884aSStefan Raspl 				"parameters on device %s: x%x\n",
642a134884aSStefan Raspl 				CARD_BUS_ID(card), rc);
643a134884aSStefan Raspl 		}
6444a71df50SFrank Blaschka 	}
6454a71df50SFrank Blaschka 
6465113fec0SUrsula Braun 	if (card->info.type == QETH_CARD_TYPE_IQD ||
6475113fec0SUrsula Braun 	    card->info.type == QETH_CARD_TYPE_OSM ||
6485113fec0SUrsula Braun 	    card->info.type == QETH_CARD_TYPE_OSX ||
6495113fec0SUrsula Braun 	    card->info.guestlan) {
6504a71df50SFrank Blaschka 		rc = qeth_setadpparms_change_macaddr(card);
6514a71df50SFrank Blaschka 		if (rc) {
65214cc21b6SFrank Blaschka 			QETH_DBF_MESSAGE(2, "couldn't get MAC address on "
65314cc21b6SFrank Blaschka 				"device %s: x%x\n", CARD_BUS_ID(card), rc);
654d11ba0c4SPeter Tiedemann 			QETH_DBF_TEXT_(SETUP, 2, "1err%d", rc);
6554a71df50SFrank Blaschka 			return rc;
6564a71df50SFrank Blaschka 		}
657d11ba0c4SPeter Tiedemann 		QETH_DBF_HEX(SETUP, 2, card->dev->dev_addr, OSA_ADDR_LEN);
6584a71df50SFrank Blaschka 	} else {
6591833611dSJoe Perches 		eth_random_addr(card->dev->dev_addr);
6604a71df50SFrank Blaschka 		memcpy(card->dev->dev_addr, vendor_pre, 3);
6614a71df50SFrank Blaschka 	}
6624a71df50SFrank Blaschka 	return 0;
6634a71df50SFrank Blaschka }
6644a71df50SFrank Blaschka 
6654a71df50SFrank Blaschka static int qeth_l2_set_mac_address(struct net_device *dev, void *p)
6664a71df50SFrank Blaschka {
6674a71df50SFrank Blaschka 	struct sockaddr *addr = p;
668509e2562SHeiko Carstens 	struct qeth_card *card = dev->ml_priv;
6694a71df50SFrank Blaschka 	int rc = 0;
6704a71df50SFrank Blaschka 
671847a50fdSCarsten Otte 	QETH_CARD_TEXT(card, 3, "setmac");
6724a71df50SFrank Blaschka 
6734a71df50SFrank Blaschka 	if (qeth_l2_verify_dev(dev) != QETH_REAL_CARD) {
674847a50fdSCarsten Otte 		QETH_CARD_TEXT(card, 3, "setmcINV");
6754a71df50SFrank Blaschka 		return -EOPNOTSUPP;
6764a71df50SFrank Blaschka 	}
6774a71df50SFrank Blaschka 
6785113fec0SUrsula Braun 	if (card->info.type == QETH_CARD_TYPE_OSN ||
6795113fec0SUrsula Braun 	    card->info.type == QETH_CARD_TYPE_OSM ||
6805113fec0SUrsula Braun 	    card->info.type == QETH_CARD_TYPE_OSX) {
681847a50fdSCarsten Otte 		QETH_CARD_TEXT(card, 3, "setmcTYP");
6824a71df50SFrank Blaschka 		return -EOPNOTSUPP;
6834a71df50SFrank Blaschka 	}
684847a50fdSCarsten Otte 	QETH_CARD_HEX(card, 3, addr->sa_data, OSA_ADDR_LEN);
6858e98ac48SUrsula Braun 	if (qeth_wait_for_threads(card, QETH_RECOVER_THREAD)) {
686847a50fdSCarsten Otte 		QETH_CARD_TEXT(card, 3, "setmcREC");
6878e98ac48SUrsula Braun 		return -ERESTARTSYS;
6888e98ac48SUrsula Braun 	}
6894a71df50SFrank Blaschka 	rc = qeth_l2_send_delmac(card, &card->dev->dev_addr[0]);
6907702745bSUrsula Braun 	if (!rc || (rc == IPA_RC_L2_MAC_NOT_FOUND))
6914a71df50SFrank Blaschka 		rc = qeth_l2_send_setmac(card, addr->sa_data);
692e0a8114cSUrsula Braun 	return rc ? -EINVAL : 0;
6934a71df50SFrank Blaschka }
6944a71df50SFrank Blaschka 
6954a71df50SFrank Blaschka static void qeth_l2_set_multicast_list(struct net_device *dev)
6964a71df50SFrank Blaschka {
697509e2562SHeiko Carstens 	struct qeth_card *card = dev->ml_priv;
698ccffad25SJiri Pirko 	struct netdev_hw_addr *ha;
6994a71df50SFrank Blaschka 
7004a71df50SFrank Blaschka 	if (card->info.type == QETH_CARD_TYPE_OSN)
7014a71df50SFrank Blaschka 		return ;
7024a71df50SFrank Blaschka 
703847a50fdSCarsten Otte 	QETH_CARD_TEXT(card, 3, "setmulti");
7048e98ac48SUrsula Braun 	if (qeth_threads_running(card, QETH_RECOVER_THREAD) &&
7058e98ac48SUrsula Braun 	    (card->state != CARD_STATE_UP))
7068e98ac48SUrsula Braun 		return;
70770919e23SUrsula Braun 	qeth_l2_del_all_mc(card, 1);
7084a71df50SFrank Blaschka 	spin_lock_bh(&card->mclock);
70922bedad3SJiri Pirko 	netdev_for_each_mc_addr(ha, dev)
71022bedad3SJiri Pirko 		qeth_l2_add_mc(card, ha->addr, 0);
7117db2266aSFrank Blaschka 
71232e7bfc4SJiri Pirko 	netdev_for_each_uc_addr(ha, dev)
713ccffad25SJiri Pirko 		qeth_l2_add_mc(card, ha->addr, 1);
7147db2266aSFrank Blaschka 
7154a71df50SFrank Blaschka 	spin_unlock_bh(&card->mclock);
7164a71df50SFrank Blaschka 	if (!qeth_adp_supported(card, IPA_SETADP_SET_PROMISC_MODE))
7174a71df50SFrank Blaschka 		return;
7184a71df50SFrank Blaschka 	qeth_setadp_promisc_mode(card);
7194a71df50SFrank Blaschka }
7204a71df50SFrank Blaschka 
7214a71df50SFrank Blaschka static int qeth_l2_hard_start_xmit(struct sk_buff *skb, struct net_device *dev)
7224a71df50SFrank Blaschka {
7234a71df50SFrank Blaschka 	int rc;
7244a71df50SFrank Blaschka 	struct qeth_hdr *hdr = NULL;
7254a71df50SFrank Blaschka 	int elements = 0;
726509e2562SHeiko Carstens 	struct qeth_card *card = dev->ml_priv;
7274a71df50SFrank Blaschka 	struct sk_buff *new_skb = skb;
7284a71df50SFrank Blaschka 	int ipv = qeth_get_ip_version(skb);
729ce73e10eSKlaus-Dieter Wacker 	int cast_type = qeth_l2_get_cast_type(card, skb);
7304a71df50SFrank Blaschka 	struct qeth_qdio_out_q *queue = card->qdio.out_qs
7314a71df50SFrank Blaschka 		[qeth_get_priority_queue(card, skb, ipv, cast_type)];
7324a71df50SFrank Blaschka 	int tx_bytes = skb->len;
733683d718aSFrank Blaschka 	int data_offset = -1;
734683d718aSFrank Blaschka 	int elements_needed = 0;
735683d718aSFrank Blaschka 	int hd_len = 0;
7364a71df50SFrank Blaschka 
7374a71df50SFrank Blaschka 	if ((card->state != CARD_STATE_UP) || !card->lan_online) {
7384a71df50SFrank Blaschka 		card->stats.tx_carrier_errors++;
7394a71df50SFrank Blaschka 		goto tx_drop;
7404a71df50SFrank Blaschka 	}
7414a71df50SFrank Blaschka 
7424a71df50SFrank Blaschka 	if ((card->info.type == QETH_CARD_TYPE_OSN) &&
7434a71df50SFrank Blaschka 	    (skb->protocol == htons(ETH_P_IPV6)))
7444a71df50SFrank Blaschka 		goto tx_drop;
7454a71df50SFrank Blaschka 
7464a71df50SFrank Blaschka 	if (card->options.performance_stats) {
7474a71df50SFrank Blaschka 		card->perf_stats.outbound_cnt++;
7484a71df50SFrank Blaschka 		card->perf_stats.outbound_start_time = qeth_get_micros();
7494a71df50SFrank Blaschka 	}
7504a71df50SFrank Blaschka 	netif_stop_queue(dev);
7514a71df50SFrank Blaschka 
7524a71df50SFrank Blaschka 	if (card->info.type == QETH_CARD_TYPE_OSN)
7534a71df50SFrank Blaschka 		hdr = (struct qeth_hdr *)skb->data;
7544a71df50SFrank Blaschka 	else {
75564ef8957SFrank Blaschka 		if (card->info.type == QETH_CARD_TYPE_IQD) {
756683d718aSFrank Blaschka 			new_skb = skb;
757683d718aSFrank Blaschka 			data_offset = ETH_HLEN;
758683d718aSFrank Blaschka 			hd_len = ETH_HLEN;
759683d718aSFrank Blaschka 			hdr = kmem_cache_alloc(qeth_core_header_cache,
760683d718aSFrank Blaschka 						GFP_ATOMIC);
761683d718aSFrank Blaschka 			if (!hdr)
762683d718aSFrank Blaschka 				goto tx_drop;
763683d718aSFrank Blaschka 			elements_needed++;
764683d718aSFrank Blaschka 			skb_reset_mac_header(new_skb);
765683d718aSFrank Blaschka 			qeth_l2_fill_header(card, hdr, new_skb, ipv, cast_type);
766683d718aSFrank Blaschka 			hdr->hdr.l2.pkt_length = new_skb->len;
767683d718aSFrank Blaschka 			memcpy(((char *)hdr) + sizeof(struct qeth_hdr),
768683d718aSFrank Blaschka 				skb_mac_header(new_skb), ETH_HLEN);
769683d718aSFrank Blaschka 		} else {
770f90b744eSFrank Blaschka 			/* create a clone with writeable headroom */
771683d718aSFrank Blaschka 			new_skb = skb_realloc_headroom(skb,
772683d718aSFrank Blaschka 						sizeof(struct qeth_hdr));
7734a71df50SFrank Blaschka 			if (!new_skb)
7744a71df50SFrank Blaschka 				goto tx_drop;
775f90b744eSFrank Blaschka 			hdr = (struct qeth_hdr *)skb_push(new_skb,
776f90b744eSFrank Blaschka 						sizeof(struct qeth_hdr));
777683d718aSFrank Blaschka 			skb_set_mac_header(new_skb, sizeof(struct qeth_hdr));
7784a71df50SFrank Blaschka 			qeth_l2_fill_header(card, hdr, new_skb, ipv, cast_type);
7794a71df50SFrank Blaschka 		}
780683d718aSFrank Blaschka 	}
7814a71df50SFrank Blaschka 
782065cc782SStefan Raspl 	elements = qeth_get_elements_no(card, new_skb, elements_needed);
783683d718aSFrank Blaschka 	if (!elements) {
784683d718aSFrank Blaschka 		if (data_offset >= 0)
785683d718aSFrank Blaschka 			kmem_cache_free(qeth_core_header_cache, hdr);
7864a71df50SFrank Blaschka 		goto tx_drop;
7874a71df50SFrank Blaschka 	}
7884a71df50SFrank Blaschka 
78951aa165cSFrank Blaschka 	if (card->info.type != QETH_CARD_TYPE_IQD) {
790d4ae1f5eSStefan Raspl 		if (qeth_hdr_chk_and_bounce(new_skb, &hdr,
79151aa165cSFrank Blaschka 		    sizeof(struct qeth_hdr_layer2)))
79251aa165cSFrank Blaschka 			goto tx_drop;
7934a71df50SFrank Blaschka 		rc = qeth_do_send_packet(card, queue, new_skb, hdr,
79464ef8957SFrank Blaschka 					 elements);
79551aa165cSFrank Blaschka 	} else
7964a71df50SFrank Blaschka 		rc = qeth_do_send_packet_fast(card, queue, new_skb, hdr,
79764ef8957SFrank Blaschka 					elements, data_offset, hd_len);
7984a71df50SFrank Blaschka 	if (!rc) {
7994a71df50SFrank Blaschka 		card->stats.tx_packets++;
8004a71df50SFrank Blaschka 		card->stats.tx_bytes += tx_bytes;
8014a71df50SFrank Blaschka 		if (new_skb != skb)
8024a71df50SFrank Blaschka 			dev_kfree_skb_any(skb);
803ec634fe3SPatrick McHardy 		rc = NETDEV_TX_OK;
8044a71df50SFrank Blaschka 	} else {
805683d718aSFrank Blaschka 		if (data_offset >= 0)
806683d718aSFrank Blaschka 			kmem_cache_free(qeth_core_header_cache, hdr);
807683d718aSFrank Blaschka 
8084a71df50SFrank Blaschka 		if (rc == -EBUSY) {
8094a71df50SFrank Blaschka 			if (new_skb != skb)
8104a71df50SFrank Blaschka 				dev_kfree_skb_any(new_skb);
8114a71df50SFrank Blaschka 			return NETDEV_TX_BUSY;
8124a71df50SFrank Blaschka 		} else
8134a71df50SFrank Blaschka 			goto tx_drop;
8144a71df50SFrank Blaschka 	}
8154a71df50SFrank Blaschka 
8164a71df50SFrank Blaschka 	netif_wake_queue(dev);
8174a71df50SFrank Blaschka 	if (card->options.performance_stats)
8184a71df50SFrank Blaschka 		card->perf_stats.outbound_time += qeth_get_micros() -
8194a71df50SFrank Blaschka 			card->perf_stats.outbound_start_time;
8204a71df50SFrank Blaschka 	return rc;
8214a71df50SFrank Blaschka 
8224a71df50SFrank Blaschka tx_drop:
8234a71df50SFrank Blaschka 	card->stats.tx_dropped++;
8244a71df50SFrank Blaschka 	card->stats.tx_errors++;
8254a71df50SFrank Blaschka 	if ((new_skb != skb) && new_skb)
8264a71df50SFrank Blaschka 		dev_kfree_skb_any(new_skb);
8274a71df50SFrank Blaschka 	dev_kfree_skb_any(skb);
828d0ec0f54SFrank Blaschka 	netif_wake_queue(dev);
8294a71df50SFrank Blaschka 	return NETDEV_TX_OK;
8304a71df50SFrank Blaschka }
8314a71df50SFrank Blaschka 
83239423440SUrsula Braun static int __qeth_l2_open(struct net_device *dev)
8334a71df50SFrank Blaschka {
834509e2562SHeiko Carstens 	struct qeth_card *card = dev->ml_priv;
835a1c3ed4cSFrank Blaschka 	int rc = 0;
8364a71df50SFrank Blaschka 
837847a50fdSCarsten Otte 	QETH_CARD_TEXT(card, 4, "qethopen");
83839423440SUrsula Braun 	if (card->state == CARD_STATE_UP)
83939423440SUrsula Braun 		return rc;
8404a71df50SFrank Blaschka 	if (card->state != CARD_STATE_SOFTSETUP)
8414a71df50SFrank Blaschka 		return -ENODEV;
8424a71df50SFrank Blaschka 
8434a71df50SFrank Blaschka 	if ((card->info.type != QETH_CARD_TYPE_OSN) &&
8444a71df50SFrank Blaschka 	     (!(card->info.mac_bits & QETH_LAYER2_MAC_REGISTERED))) {
845847a50fdSCarsten Otte 		QETH_CARD_TEXT(card, 4, "nomacadr");
8464a71df50SFrank Blaschka 		return -EPERM;
8474a71df50SFrank Blaschka 	}
8484a71df50SFrank Blaschka 	card->data.state = CH_STATE_UP;
8494a71df50SFrank Blaschka 	card->state = CARD_STATE_UP;
8504a71df50SFrank Blaschka 	netif_start_queue(dev);
8514a71df50SFrank Blaschka 
852a1c3ed4cSFrank Blaschka 	if (qdio_stop_irq(card->data.ccwdev, 0) >= 0) {
853a1c3ed4cSFrank Blaschka 		napi_enable(&card->napi);
854a1c3ed4cSFrank Blaschka 		napi_schedule(&card->napi);
855a1c3ed4cSFrank Blaschka 	} else
856a1c3ed4cSFrank Blaschka 		rc = -EIO;
857a1c3ed4cSFrank Blaschka 	return rc;
8584a71df50SFrank Blaschka }
8594a71df50SFrank Blaschka 
86039423440SUrsula Braun static int qeth_l2_open(struct net_device *dev)
86139423440SUrsula Braun {
86239423440SUrsula Braun 	struct qeth_card *card = dev->ml_priv;
86339423440SUrsula Braun 
86439423440SUrsula Braun 	QETH_CARD_TEXT(card, 5, "qethope_");
86539423440SUrsula Braun 	if (qeth_wait_for_threads(card, QETH_RECOVER_THREAD)) {
86639423440SUrsula Braun 		QETH_CARD_TEXT(card, 3, "openREC");
86739423440SUrsula Braun 		return -ERESTARTSYS;
86839423440SUrsula Braun 	}
86939423440SUrsula Braun 	return __qeth_l2_open(dev);
87039423440SUrsula Braun }
87139423440SUrsula Braun 
8724a71df50SFrank Blaschka static int qeth_l2_stop(struct net_device *dev)
8734a71df50SFrank Blaschka {
874509e2562SHeiko Carstens 	struct qeth_card *card = dev->ml_priv;
8754a71df50SFrank Blaschka 
876847a50fdSCarsten Otte 	QETH_CARD_TEXT(card, 4, "qethstop");
8774a71df50SFrank Blaschka 	netif_tx_disable(dev);
878a1c3ed4cSFrank Blaschka 	if (card->state == CARD_STATE_UP) {
8794a71df50SFrank Blaschka 		card->state = CARD_STATE_SOFTSETUP;
880a1c3ed4cSFrank Blaschka 		napi_disable(&card->napi);
881a1c3ed4cSFrank Blaschka 	}
8824a71df50SFrank Blaschka 	return 0;
8834a71df50SFrank Blaschka }
8844a71df50SFrank Blaschka 
8854a71df50SFrank Blaschka static int qeth_l2_probe_device(struct ccwgroup_device *gdev)
8864a71df50SFrank Blaschka {
8874a71df50SFrank Blaschka 	struct qeth_card *card = dev_get_drvdata(&gdev->dev);
8884a71df50SFrank Blaschka 
889b4d72c08SEugene Crosser 	qeth_l2_create_device_attributes(&gdev->dev);
8904a71df50SFrank Blaschka 	INIT_LIST_HEAD(&card->vid_list);
8914a71df50SFrank Blaschka 	INIT_LIST_HEAD(&card->mc_list);
8924a71df50SFrank Blaschka 	card->options.layer2 = 1;
8931da74b1cSFrank Blaschka 	card->info.hwtrap = 0;
8944a71df50SFrank Blaschka 	return 0;
8954a71df50SFrank Blaschka }
8964a71df50SFrank Blaschka 
8974a71df50SFrank Blaschka static void qeth_l2_remove_device(struct ccwgroup_device *cgdev)
8984a71df50SFrank Blaschka {
8994a71df50SFrank Blaschka 	struct qeth_card *card = dev_get_drvdata(&cgdev->dev);
9004a71df50SFrank Blaschka 
901b4d72c08SEugene Crosser 	qeth_l2_remove_device_attributes(&cgdev->dev);
902f2148565SUrsula Braun 	qeth_set_allowed_threads(card, 0, 1);
9034a71df50SFrank Blaschka 	wait_event(card->wait_q, qeth_threads_running(card, 0xffffffff) == 0);
9044a71df50SFrank Blaschka 
90570919e23SUrsula Braun 	if (cgdev->state == CCWGROUP_ONLINE)
9064a71df50SFrank Blaschka 		qeth_l2_set_offline(cgdev);
9074a71df50SFrank Blaschka 
9084a71df50SFrank Blaschka 	if (card->dev) {
9094a71df50SFrank Blaschka 		unregister_netdev(card->dev);
9104a71df50SFrank Blaschka 		card->dev = NULL;
9114a71df50SFrank Blaschka 	}
9124a71df50SFrank Blaschka 	return;
9134a71df50SFrank Blaschka }
9144a71df50SFrank Blaschka 
9150fc0b732SStephen Hemminger static const struct ethtool_ops qeth_l2_ethtool_ops = {
9164a71df50SFrank Blaschka 	.get_link = ethtool_op_get_link,
9174a71df50SFrank Blaschka 	.get_strings = qeth_core_get_strings,
9184a71df50SFrank Blaschka 	.get_ethtool_stats = qeth_core_get_ethtool_stats,
919df8b4ec8SBen Hutchings 	.get_sset_count = qeth_core_get_sset_count,
9204a71df50SFrank Blaschka 	.get_drvinfo = qeth_core_get_drvinfo,
9213f9975aaSFrank Blaschka 	.get_settings = qeth_core_ethtool_get_settings,
9224a71df50SFrank Blaschka };
9234a71df50SFrank Blaschka 
9240fc0b732SStephen Hemminger static const struct ethtool_ops qeth_l2_osn_ops = {
9254a71df50SFrank Blaschka 	.get_strings = qeth_core_get_strings,
9264a71df50SFrank Blaschka 	.get_ethtool_stats = qeth_core_get_ethtool_stats,
927df8b4ec8SBen Hutchings 	.get_sset_count = qeth_core_get_sset_count,
9284a71df50SFrank Blaschka 	.get_drvinfo = qeth_core_get_drvinfo,
9294a71df50SFrank Blaschka };
9304a71df50SFrank Blaschka 
9313d58cefdSFrank Blaschka static const struct net_device_ops qeth_l2_netdev_ops = {
9328403b13cSFrank Blaschka 	.ndo_open		= qeth_l2_open,
9338403b13cSFrank Blaschka 	.ndo_stop		= qeth_l2_stop,
9348403b13cSFrank Blaschka 	.ndo_get_stats		= qeth_get_stats,
9358403b13cSFrank Blaschka 	.ndo_start_xmit		= qeth_l2_hard_start_xmit,
9368403b13cSFrank Blaschka 	.ndo_validate_addr	= eth_validate_addr,
937afc4b13dSJiri Pirko 	.ndo_set_rx_mode	= qeth_l2_set_multicast_list,
9388403b13cSFrank Blaschka 	.ndo_do_ioctl	   	= qeth_l2_do_ioctl,
9398403b13cSFrank Blaschka 	.ndo_set_mac_address    = qeth_l2_set_mac_address,
9408403b13cSFrank Blaschka 	.ndo_change_mtu	   	= qeth_change_mtu,
9418403b13cSFrank Blaschka 	.ndo_vlan_rx_add_vid	= qeth_l2_vlan_rx_add_vid,
9428403b13cSFrank Blaschka 	.ndo_vlan_rx_kill_vid   = qeth_l2_vlan_rx_kill_vid,
9438403b13cSFrank Blaschka 	.ndo_tx_timeout	   	= qeth_tx_timeout,
9448403b13cSFrank Blaschka };
9458403b13cSFrank Blaschka 
9464a71df50SFrank Blaschka static int qeth_l2_setup_netdev(struct qeth_card *card)
9474a71df50SFrank Blaschka {
9484a71df50SFrank Blaschka 	switch (card->info.type) {
9494a71df50SFrank Blaschka 	case QETH_CARD_TYPE_IQD:
9504a71df50SFrank Blaschka 		card->dev = alloc_netdev(0, "hsi%d", ether_setup);
9514a71df50SFrank Blaschka 		break;
9524a71df50SFrank Blaschka 	case QETH_CARD_TYPE_OSN:
9534a71df50SFrank Blaschka 		card->dev = alloc_netdev(0, "osn%d", ether_setup);
9544a71df50SFrank Blaschka 		card->dev->flags |= IFF_NOARP;
9554a71df50SFrank Blaschka 		break;
9564a71df50SFrank Blaschka 	default:
9574a71df50SFrank Blaschka 		card->dev = alloc_etherdev(0);
9584a71df50SFrank Blaschka 	}
9594a71df50SFrank Blaschka 
9604a71df50SFrank Blaschka 	if (!card->dev)
9614a71df50SFrank Blaschka 		return -ENODEV;
9624a71df50SFrank Blaschka 
963509e2562SHeiko Carstens 	card->dev->ml_priv = card;
9644a71df50SFrank Blaschka 	card->dev->watchdog_timeo = QETH_TX_TIMEOUT;
9654a71df50SFrank Blaschka 	card->dev->mtu = card->info.initial_mtu;
9668403b13cSFrank Blaschka 	card->dev->netdev_ops = &qeth_l2_netdev_ops;
9674a71df50SFrank Blaschka 	if (card->info.type != QETH_CARD_TYPE_OSN)
9684a71df50SFrank Blaschka 		SET_ETHTOOL_OPS(card->dev, &qeth_l2_ethtool_ops);
9694a71df50SFrank Blaschka 	else
9704a71df50SFrank Blaschka 		SET_ETHTOOL_OPS(card->dev, &qeth_l2_osn_ops);
971f646968fSPatrick McHardy 	card->dev->features |= NETIF_F_HW_VLAN_CTAG_FILTER;
9724a71df50SFrank Blaschka 	card->info.broadcast_capable = 1;
9734a71df50SFrank Blaschka 	qeth_l2_request_initial_mac(card);
9744a71df50SFrank Blaschka 	SET_NETDEV_DEV(card->dev, &card->gdev->dev);
975a1c3ed4cSFrank Blaschka 	netif_napi_add(card->dev, &card->napi, qeth_l2_poll, QETH_NAPI_WEIGHT);
9764a71df50SFrank Blaschka 	return register_netdev(card->dev);
9774a71df50SFrank Blaschka }
9784a71df50SFrank Blaschka 
9794a71df50SFrank Blaschka static int __qeth_l2_set_online(struct ccwgroup_device *gdev, int recovery_mode)
9804a71df50SFrank Blaschka {
9814a71df50SFrank Blaschka 	struct qeth_card *card = dev_get_drvdata(&gdev->dev);
9824a71df50SFrank Blaschka 	int rc = 0;
9834a71df50SFrank Blaschka 	enum qeth_card_states recover_flag;
9844a71df50SFrank Blaschka 
9859dc48cccSUrsula Braun 	mutex_lock(&card->discipline_mutex);
986c4949f07SFrank Blaschka 	mutex_lock(&card->conf_mutex);
987d11ba0c4SPeter Tiedemann 	QETH_DBF_TEXT(SETUP, 2, "setonlin");
988d11ba0c4SPeter Tiedemann 	QETH_DBF_HEX(SETUP, 2, &card, sizeof(void *));
9894a71df50SFrank Blaschka 
9904a71df50SFrank Blaschka 	recover_flag = card->state;
9914a71df50SFrank Blaschka 	rc = qeth_core_hardsetup_card(card);
9924a71df50SFrank Blaschka 	if (rc) {
993d11ba0c4SPeter Tiedemann 		QETH_DBF_TEXT_(SETUP, 2, "2err%d", rc);
994aa909224SUrsula Braun 		rc = -ENODEV;
9954a71df50SFrank Blaschka 		goto out_remove;
9964a71df50SFrank Blaschka 	}
997c044dc21SEugene Crosser 	qeth_bridgeport_query_support(card);
998c044dc21SEugene Crosser 	if (card->options.sbp.supported_funcs)
999c044dc21SEugene Crosser 		dev_info(&card->gdev->dev,
1000c044dc21SEugene Crosser 		"The device represents a HiperSockets Bridge Capable Port\n");
1001395672e0SStefan Raspl 	qeth_trace_features(card);
10024a71df50SFrank Blaschka 
1003aa909224SUrsula Braun 	if (!card->dev && qeth_l2_setup_netdev(card)) {
1004aa909224SUrsula Braun 		rc = -ENODEV;
10054a71df50SFrank Blaschka 		goto out_remove;
1006aa909224SUrsula Braun 	}
10074a71df50SFrank Blaschka 
10084a71df50SFrank Blaschka 	if (card->info.type != QETH_CARD_TYPE_OSN)
10094a71df50SFrank Blaschka 		qeth_l2_send_setmac(card, &card->dev->dev_addr[0]);
10104a71df50SFrank Blaschka 
10111da74b1cSFrank Blaschka 	if (qeth_is_diagass_supported(card, QETH_DIAGS_CMD_TRAP)) {
10121da74b1cSFrank Blaschka 		if (card->info.hwtrap &&
10131da74b1cSFrank Blaschka 		    qeth_hw_trap(card, QETH_DIAGS_TRAP_ARM))
10141da74b1cSFrank Blaschka 			card->info.hwtrap = 0;
10151da74b1cSFrank Blaschka 	} else
10161da74b1cSFrank Blaschka 		card->info.hwtrap = 0;
10171da74b1cSFrank Blaschka 
1018b4d72c08SEugene Crosser 	qeth_l2_setup_bridgeport_attrs(card);
1019b4d72c08SEugene Crosser 
10204a71df50SFrank Blaschka 	card->state = CARD_STATE_HARDSETUP;
1021a1c3ed4cSFrank Blaschka 	memset(&card->rx, 0, sizeof(struct qeth_rx));
10224a71df50SFrank Blaschka 	qeth_print_status_message(card);
10234a71df50SFrank Blaschka 
10244a71df50SFrank Blaschka 	/* softsetup */
1025d11ba0c4SPeter Tiedemann 	QETH_DBF_TEXT(SETUP, 2, "softsetp");
10264a71df50SFrank Blaschka 
10274a71df50SFrank Blaschka 	rc = qeth_send_startlan(card);
10284a71df50SFrank Blaschka 	if (rc) {
1029d11ba0c4SPeter Tiedemann 		QETH_DBF_TEXT_(SETUP, 2, "1err%d", rc);
10304a71df50SFrank Blaschka 		if (rc == 0xe080) {
103174eacdb9SFrank Blaschka 			dev_warn(&card->gdev->dev,
103274eacdb9SFrank Blaschka 				"The LAN is offline\n");
10334a71df50SFrank Blaschka 			card->lan_online = 0;
10342b6203bbSUrsula Braun 			goto contin;
10354a71df50SFrank Blaschka 		}
1036aa909224SUrsula Braun 		rc = -ENODEV;
1037f2148565SUrsula Braun 		goto out_remove;
10384a71df50SFrank Blaschka 	} else
10394a71df50SFrank Blaschka 		card->lan_online = 1;
10404a71df50SFrank Blaschka 
10412b6203bbSUrsula Braun contin:
10425113fec0SUrsula Braun 	if ((card->info.type == QETH_CARD_TYPE_OSD) ||
10430f54761dSStefan Raspl 	    (card->info.type == QETH_CARD_TYPE_OSX)) {
1044d64ecc22SEinar Lueck 		/* configure isolation level */
10450f54761dSStefan Raspl 		rc = qeth_set_access_ctrl_online(card, 0);
10460f54761dSStefan Raspl 		if (rc) {
10470f54761dSStefan Raspl 			rc = -ENODEV;
10480f54761dSStefan Raspl 			goto out_remove;
10490f54761dSStefan Raspl 		}
10500f54761dSStefan Raspl 	}
10515113fec0SUrsula Braun 
10525113fec0SUrsula Braun 	if (card->info.type != QETH_CARD_TYPE_OSN &&
10535113fec0SUrsula Braun 	    card->info.type != QETH_CARD_TYPE_OSM)
105470919e23SUrsula Braun 		qeth_l2_process_vlans(card);
10554a71df50SFrank Blaschka 
10564a71df50SFrank Blaschka 	netif_tx_disable(card->dev);
10574a71df50SFrank Blaschka 
10584a71df50SFrank Blaschka 	rc = qeth_init_qdio_queues(card);
10594a71df50SFrank Blaschka 	if (rc) {
1060d11ba0c4SPeter Tiedemann 		QETH_DBF_TEXT_(SETUP, 2, "6err%d", rc);
1061aa909224SUrsula Braun 		rc = -ENODEV;
10624a71df50SFrank Blaschka 		goto out_remove;
10634a71df50SFrank Blaschka 	}
10644a71df50SFrank Blaschka 	card->state = CARD_STATE_SOFTSETUP;
10652b6203bbSUrsula Braun 	if (card->lan_online)
10664a71df50SFrank Blaschka 		netif_carrier_on(card->dev);
10672b6203bbSUrsula Braun 	else
10682b6203bbSUrsula Braun 		netif_carrier_off(card->dev);
10694a71df50SFrank Blaschka 
10704a71df50SFrank Blaschka 	qeth_set_allowed_threads(card, 0xffffffff, 0);
10714a71df50SFrank Blaschka 	if (recover_flag == CARD_STATE_RECOVER) {
10724a71df50SFrank Blaschka 		if (recovery_mode &&
10734a71df50SFrank Blaschka 		    card->info.type != QETH_CARD_TYPE_OSN) {
107439423440SUrsula Braun 			__qeth_l2_open(card->dev);
10754a71df50SFrank Blaschka 		} else {
10764a71df50SFrank Blaschka 			rtnl_lock();
10774a71df50SFrank Blaschka 			dev_open(card->dev);
10784a71df50SFrank Blaschka 			rtnl_unlock();
10794a71df50SFrank Blaschka 		}
10804a71df50SFrank Blaschka 		/* this also sets saved unicast addresses */
10814a71df50SFrank Blaschka 		qeth_l2_set_multicast_list(card->dev);
10824a71df50SFrank Blaschka 	}
10834a71df50SFrank Blaschka 	/* let user_space know that device is online */
10844a71df50SFrank Blaschka 	kobject_uevent(&gdev->dev.kobj, KOBJ_CHANGE);
1085c4949f07SFrank Blaschka 	mutex_unlock(&card->conf_mutex);
10869dc48cccSUrsula Braun 	mutex_unlock(&card->discipline_mutex);
10874a71df50SFrank Blaschka 	return 0;
1088aa909224SUrsula Braun 
10894a71df50SFrank Blaschka out_remove:
10904a71df50SFrank Blaschka 	qeth_l2_stop_card(card, 0);
10914a71df50SFrank Blaschka 	ccw_device_set_offline(CARD_DDEV(card));
10924a71df50SFrank Blaschka 	ccw_device_set_offline(CARD_WDEV(card));
10934a71df50SFrank Blaschka 	ccw_device_set_offline(CARD_RDEV(card));
109422ae2790SUrsula Braun 	qdio_free(CARD_DDEV(card));
10954a71df50SFrank Blaschka 	if (recover_flag == CARD_STATE_RECOVER)
10964a71df50SFrank Blaschka 		card->state = CARD_STATE_RECOVER;
10974a71df50SFrank Blaschka 	else
10984a71df50SFrank Blaschka 		card->state = CARD_STATE_DOWN;
1099c4949f07SFrank Blaschka 	mutex_unlock(&card->conf_mutex);
11009dc48cccSUrsula Braun 	mutex_unlock(&card->discipline_mutex);
1101aa909224SUrsula Braun 	return rc;
11024a71df50SFrank Blaschka }
11034a71df50SFrank Blaschka 
11044a71df50SFrank Blaschka static int qeth_l2_set_online(struct ccwgroup_device *gdev)
11054a71df50SFrank Blaschka {
11064a71df50SFrank Blaschka 	return __qeth_l2_set_online(gdev, 0);
11074a71df50SFrank Blaschka }
11084a71df50SFrank Blaschka 
11094a71df50SFrank Blaschka static int __qeth_l2_set_offline(struct ccwgroup_device *cgdev,
11104a71df50SFrank Blaschka 					int recovery_mode)
11114a71df50SFrank Blaschka {
11124a71df50SFrank Blaschka 	struct qeth_card *card = dev_get_drvdata(&cgdev->dev);
11134a71df50SFrank Blaschka 	int rc = 0, rc2 = 0, rc3 = 0;
11144a71df50SFrank Blaschka 	enum qeth_card_states recover_flag;
11154a71df50SFrank Blaschka 
11169dc48cccSUrsula Braun 	mutex_lock(&card->discipline_mutex);
1117c4949f07SFrank Blaschka 	mutex_lock(&card->conf_mutex);
1118d11ba0c4SPeter Tiedemann 	QETH_DBF_TEXT(SETUP, 3, "setoffl");
1119d11ba0c4SPeter Tiedemann 	QETH_DBF_HEX(SETUP, 3, &card, sizeof(void *));
11204a71df50SFrank Blaschka 
11214a71df50SFrank Blaschka 	if (card->dev && netif_carrier_ok(card->dev))
11224a71df50SFrank Blaschka 		netif_carrier_off(card->dev);
11234a71df50SFrank Blaschka 	recover_flag = card->state;
11241da74b1cSFrank Blaschka 	if ((!recovery_mode && card->info.hwtrap) || card->info.hwtrap == 2) {
11251da74b1cSFrank Blaschka 		qeth_hw_trap(card, QETH_DIAGS_TRAP_DISARM);
11261da74b1cSFrank Blaschka 		card->info.hwtrap = 1;
11271da74b1cSFrank Blaschka 	}
11280f5623c9SUrsula Braun 	qeth_l2_stop_card(card, recovery_mode);
11294a71df50SFrank Blaschka 	rc  = ccw_device_set_offline(CARD_DDEV(card));
11304a71df50SFrank Blaschka 	rc2 = ccw_device_set_offline(CARD_WDEV(card));
11314a71df50SFrank Blaschka 	rc3 = ccw_device_set_offline(CARD_RDEV(card));
11324a71df50SFrank Blaschka 	if (!rc)
11334a71df50SFrank Blaschka 		rc = (rc2) ? rc2 : rc3;
11344a71df50SFrank Blaschka 	if (rc)
1135d11ba0c4SPeter Tiedemann 		QETH_DBF_TEXT_(SETUP, 2, "1err%d", rc);
113622ae2790SUrsula Braun 	qdio_free(CARD_DDEV(card));
11374a71df50SFrank Blaschka 	if (recover_flag == CARD_STATE_UP)
11384a71df50SFrank Blaschka 		card->state = CARD_STATE_RECOVER;
11394a71df50SFrank Blaschka 	/* let user_space know that device is offline */
11404a71df50SFrank Blaschka 	kobject_uevent(&cgdev->dev.kobj, KOBJ_CHANGE);
1141c4949f07SFrank Blaschka 	mutex_unlock(&card->conf_mutex);
11429dc48cccSUrsula Braun 	mutex_unlock(&card->discipline_mutex);
11434a71df50SFrank Blaschka 	return 0;
11444a71df50SFrank Blaschka }
11454a71df50SFrank Blaschka 
11464a71df50SFrank Blaschka static int qeth_l2_set_offline(struct ccwgroup_device *cgdev)
11474a71df50SFrank Blaschka {
11484a71df50SFrank Blaschka 	return __qeth_l2_set_offline(cgdev, 0);
11494a71df50SFrank Blaschka }
11504a71df50SFrank Blaschka 
11514a71df50SFrank Blaschka static int qeth_l2_recover(void *ptr)
11524a71df50SFrank Blaschka {
11534a71df50SFrank Blaschka 	struct qeth_card *card;
11544a71df50SFrank Blaschka 	int rc = 0;
11554a71df50SFrank Blaschka 
11564a71df50SFrank Blaschka 	card = (struct qeth_card *) ptr;
1157847a50fdSCarsten Otte 	QETH_CARD_TEXT(card, 2, "recover1");
11584a71df50SFrank Blaschka 	if (!qeth_do_run_thread(card, QETH_RECOVER_THREAD))
11594a71df50SFrank Blaschka 		return 0;
1160847a50fdSCarsten Otte 	QETH_CARD_TEXT(card, 2, "recover2");
116174eacdb9SFrank Blaschka 	dev_warn(&card->gdev->dev,
116274eacdb9SFrank Blaschka 		"A recovery process has been started for the device\n");
116365d8013cSStefan Raspl 	qeth_set_recovery_task(card);
11644a71df50SFrank Blaschka 	__qeth_l2_set_offline(card->gdev, 1);
11654a71df50SFrank Blaschka 	rc = __qeth_l2_set_online(card->gdev, 1);
11664a71df50SFrank Blaschka 	if (!rc)
116774eacdb9SFrank Blaschka 		dev_info(&card->gdev->dev,
116874eacdb9SFrank Blaschka 			"Device successfully recovered!\n");
116928a7e4c9SUrsula Braun 	else {
11700f54761dSStefan Raspl 		qeth_close_dev(card);
117174eacdb9SFrank Blaschka 		dev_warn(&card->gdev->dev, "The qeth device driver "
117274eacdb9SFrank Blaschka 				"failed to recover an error on the device\n");
117328a7e4c9SUrsula Braun 	}
117465d8013cSStefan Raspl 	qeth_clear_recovery_task(card);
1175a1c3ed4cSFrank Blaschka 	qeth_clear_thread_start_bit(card, QETH_RECOVER_THREAD);
1176a1c3ed4cSFrank Blaschka 	qeth_clear_thread_running_bit(card, QETH_RECOVER_THREAD);
11774a71df50SFrank Blaschka 	return 0;
11784a71df50SFrank Blaschka }
11794a71df50SFrank Blaschka 
11804a71df50SFrank Blaschka static int __init qeth_l2_init(void)
11814a71df50SFrank Blaschka {
118274eacdb9SFrank Blaschka 	pr_info("register layer 2 discipline\n");
11834a71df50SFrank Blaschka 	return 0;
11844a71df50SFrank Blaschka }
11854a71df50SFrank Blaschka 
11864a71df50SFrank Blaschka static void __exit qeth_l2_exit(void)
11874a71df50SFrank Blaschka {
118874eacdb9SFrank Blaschka 	pr_info("unregister layer 2 discipline\n");
11894a71df50SFrank Blaschka }
11904a71df50SFrank Blaschka 
11914a71df50SFrank Blaschka static void qeth_l2_shutdown(struct ccwgroup_device *gdev)
11924a71df50SFrank Blaschka {
11934a71df50SFrank Blaschka 	struct qeth_card *card = dev_get_drvdata(&gdev->dev);
1194f78ac2bbSUrsula Braun 	qeth_set_allowed_threads(card, 0, 1);
11951da74b1cSFrank Blaschka 	if ((gdev->state == CCWGROUP_ONLINE) && card->info.hwtrap)
11961da74b1cSFrank Blaschka 		qeth_hw_trap(card, QETH_DIAGS_TRAP_DISARM);
11974a71df50SFrank Blaschka 	qeth_qdio_clear_card(card, 0);
11984a71df50SFrank Blaschka 	qeth_clear_qdio_buffers(card);
119922ae2790SUrsula Braun 	qdio_free(CARD_DDEV(card));
12004a71df50SFrank Blaschka }
12014a71df50SFrank Blaschka 
1202bbcfcdc8SFrank Blaschka static int qeth_l2_pm_suspend(struct ccwgroup_device *gdev)
1203bbcfcdc8SFrank Blaschka {
1204bbcfcdc8SFrank Blaschka 	struct qeth_card *card = dev_get_drvdata(&gdev->dev);
1205bbcfcdc8SFrank Blaschka 
1206bbcfcdc8SFrank Blaschka 	if (card->dev)
1207bbcfcdc8SFrank Blaschka 		netif_device_detach(card->dev);
1208bbcfcdc8SFrank Blaschka 	qeth_set_allowed_threads(card, 0, 1);
1209bbcfcdc8SFrank Blaschka 	wait_event(card->wait_q, qeth_threads_running(card, 0xffffffff) == 0);
1210bbcfcdc8SFrank Blaschka 	if (gdev->state == CCWGROUP_OFFLINE)
1211bbcfcdc8SFrank Blaschka 		return 0;
1212bbcfcdc8SFrank Blaschka 	if (card->state == CARD_STATE_UP) {
12131da74b1cSFrank Blaschka 		if (card->info.hwtrap)
12141da74b1cSFrank Blaschka 			qeth_hw_trap(card, QETH_DIAGS_TRAP_DISARM);
1215bbcfcdc8SFrank Blaschka 		__qeth_l2_set_offline(card->gdev, 1);
1216bbcfcdc8SFrank Blaschka 	} else
1217bbcfcdc8SFrank Blaschka 		__qeth_l2_set_offline(card->gdev, 0);
1218bbcfcdc8SFrank Blaschka 	return 0;
1219bbcfcdc8SFrank Blaschka }
1220bbcfcdc8SFrank Blaschka 
1221bbcfcdc8SFrank Blaschka static int qeth_l2_pm_resume(struct ccwgroup_device *gdev)
1222bbcfcdc8SFrank Blaschka {
1223bbcfcdc8SFrank Blaschka 	struct qeth_card *card = dev_get_drvdata(&gdev->dev);
1224bbcfcdc8SFrank Blaschka 	int rc = 0;
1225bbcfcdc8SFrank Blaschka 
1226bbcfcdc8SFrank Blaschka 	if (gdev->state == CCWGROUP_OFFLINE)
1227bbcfcdc8SFrank Blaschka 		goto out;
1228bbcfcdc8SFrank Blaschka 
1229bbcfcdc8SFrank Blaschka 	if (card->state == CARD_STATE_RECOVER) {
1230bbcfcdc8SFrank Blaschka 		rc = __qeth_l2_set_online(card->gdev, 1);
1231bbcfcdc8SFrank Blaschka 		if (rc) {
1232bbcfcdc8SFrank Blaschka 			rtnl_lock();
1233bbcfcdc8SFrank Blaschka 			dev_close(card->dev);
1234bbcfcdc8SFrank Blaschka 			rtnl_unlock();
1235bbcfcdc8SFrank Blaschka 		}
1236bbcfcdc8SFrank Blaschka 	} else
1237bbcfcdc8SFrank Blaschka 		rc = __qeth_l2_set_online(card->gdev, 0);
1238bbcfcdc8SFrank Blaschka out:
1239bbcfcdc8SFrank Blaschka 	qeth_set_allowed_threads(card, 0xffffffff, 0);
1240bbcfcdc8SFrank Blaschka 	if (card->dev)
1241bbcfcdc8SFrank Blaschka 		netif_device_attach(card->dev);
1242bbcfcdc8SFrank Blaschka 	if (rc)
1243bbcfcdc8SFrank Blaschka 		dev_warn(&card->gdev->dev, "The qeth device driver "
1244bbcfcdc8SFrank Blaschka 			"failed to recover an error on the device\n");
1245bbcfcdc8SFrank Blaschka 	return rc;
1246bbcfcdc8SFrank Blaschka }
1247bbcfcdc8SFrank Blaschka 
1248c044dc21SEugene Crosser /* Returns zero if the command is successfully "consumed" */
1249c044dc21SEugene Crosser static int qeth_l2_control_event(struct qeth_card *card,
1250c044dc21SEugene Crosser 					struct qeth_ipa_cmd *cmd)
1251c044dc21SEugene Crosser {
1252c044dc21SEugene Crosser 	switch (cmd->hdr.command) {
1253c044dc21SEugene Crosser 	case IPA_CMD_SETBRIDGEPORT:
1254c044dc21SEugene Crosser 		if (cmd->data.sbp.hdr.command_code ==
1255c044dc21SEugene Crosser 				IPA_SBP_BRIDGE_PORT_STATE_CHANGE) {
1256c044dc21SEugene Crosser 			qeth_bridge_state_change(card, cmd);
1257c044dc21SEugene Crosser 			return 0;
1258c044dc21SEugene Crosser 		} else
1259c044dc21SEugene Crosser 			return 1;
1260c044dc21SEugene Crosser 	case IPA_CMD_ADDRESS_CHANGE_NOTIF:
1261c044dc21SEugene Crosser 		qeth_bridge_host_event(card, cmd);
1262c044dc21SEugene Crosser 		return 0;
1263c044dc21SEugene Crosser 	default:
1264c044dc21SEugene Crosser 		return 1;
1265c044dc21SEugene Crosser 	}
1266c044dc21SEugene Crosser }
1267c044dc21SEugene Crosser 
1268c041f2d4SSebastian Ott struct qeth_discipline qeth_l2_discipline = {
1269c041f2d4SSebastian Ott 	.start_poll = qeth_qdio_start_poll,
1270c041f2d4SSebastian Ott 	.input_handler = (qdio_handler_t *) qeth_qdio_input_handler,
1271c041f2d4SSebastian Ott 	.output_handler = (qdio_handler_t *) qeth_qdio_output_handler,
1272c041f2d4SSebastian Ott 	.recover = qeth_l2_recover,
1273c041f2d4SSebastian Ott 	.setup = qeth_l2_probe_device,
12744a71df50SFrank Blaschka 	.remove = qeth_l2_remove_device,
12754a71df50SFrank Blaschka 	.set_online = qeth_l2_set_online,
12764a71df50SFrank Blaschka 	.set_offline = qeth_l2_set_offline,
12774a71df50SFrank Blaschka 	.shutdown = qeth_l2_shutdown,
1278bbcfcdc8SFrank Blaschka 	.freeze = qeth_l2_pm_suspend,
1279bbcfcdc8SFrank Blaschka 	.thaw = qeth_l2_pm_resume,
1280bbcfcdc8SFrank Blaschka 	.restore = qeth_l2_pm_resume,
1281c044dc21SEugene Crosser 	.control_event_handler = qeth_l2_control_event,
12824a71df50SFrank Blaschka };
1283c041f2d4SSebastian Ott EXPORT_SYMBOL_GPL(qeth_l2_discipline);
12844a71df50SFrank Blaschka 
12854a71df50SFrank Blaschka static int qeth_osn_send_control_data(struct qeth_card *card, int len,
12864a71df50SFrank Blaschka 			   struct qeth_cmd_buffer *iob)
12874a71df50SFrank Blaschka {
12884a71df50SFrank Blaschka 	unsigned long flags;
12894a71df50SFrank Blaschka 	int rc = 0;
12904a71df50SFrank Blaschka 
1291847a50fdSCarsten Otte 	QETH_CARD_TEXT(card, 5, "osndctrd");
12924a71df50SFrank Blaschka 
12934a71df50SFrank Blaschka 	wait_event(card->wait_q,
12944a71df50SFrank Blaschka 		   atomic_cmpxchg(&card->write.irq_pending, 0, 1) == 0);
12954a71df50SFrank Blaschka 	qeth_prepare_control_data(card, len, iob);
1296847a50fdSCarsten Otte 	QETH_CARD_TEXT(card, 6, "osnoirqp");
12974a71df50SFrank Blaschka 	spin_lock_irqsave(get_ccwdev_lock(card->write.ccwdev), flags);
12984a71df50SFrank Blaschka 	rc = ccw_device_start(card->write.ccwdev, &card->write.ccw,
12994a71df50SFrank Blaschka 			      (addr_t) iob, 0, 0);
13004a71df50SFrank Blaschka 	spin_unlock_irqrestore(get_ccwdev_lock(card->write.ccwdev), flags);
13014a71df50SFrank Blaschka 	if (rc) {
130214cc21b6SFrank Blaschka 		QETH_DBF_MESSAGE(2, "qeth_osn_send_control_data: "
13034a71df50SFrank Blaschka 			   "ccw_device_start rc = %i\n", rc);
1304847a50fdSCarsten Otte 		QETH_CARD_TEXT_(card, 2, " err%d", rc);
13054a71df50SFrank Blaschka 		qeth_release_buffer(iob->channel, iob);
13064a71df50SFrank Blaschka 		atomic_set(&card->write.irq_pending, 0);
13074a71df50SFrank Blaschka 		wake_up(&card->wait_q);
13084a71df50SFrank Blaschka 	}
13094a71df50SFrank Blaschka 	return rc;
13104a71df50SFrank Blaschka }
13114a71df50SFrank Blaschka 
13124a71df50SFrank Blaschka static int qeth_osn_send_ipa_cmd(struct qeth_card *card,
13134a71df50SFrank Blaschka 			struct qeth_cmd_buffer *iob, int data_len)
13144a71df50SFrank Blaschka {
13154a71df50SFrank Blaschka 	u16 s1, s2;
13164a71df50SFrank Blaschka 
1317847a50fdSCarsten Otte 	QETH_CARD_TEXT(card, 4, "osndipa");
13184a71df50SFrank Blaschka 
13194a71df50SFrank Blaschka 	qeth_prepare_ipa_cmd(card, iob, QETH_PROT_OSN2);
13204a71df50SFrank Blaschka 	s1 = (u16)(IPA_PDU_HEADER_SIZE + data_len);
13214a71df50SFrank Blaschka 	s2 = (u16)data_len;
13224a71df50SFrank Blaschka 	memcpy(QETH_IPA_PDU_LEN_TOTAL(iob->data), &s1, 2);
13234a71df50SFrank Blaschka 	memcpy(QETH_IPA_PDU_LEN_PDU1(iob->data), &s2, 2);
13244a71df50SFrank Blaschka 	memcpy(QETH_IPA_PDU_LEN_PDU2(iob->data), &s2, 2);
13254a71df50SFrank Blaschka 	memcpy(QETH_IPA_PDU_LEN_PDU3(iob->data), &s2, 2);
13264a71df50SFrank Blaschka 	return qeth_osn_send_control_data(card, s1, iob);
13274a71df50SFrank Blaschka }
13284a71df50SFrank Blaschka 
13294a71df50SFrank Blaschka int qeth_osn_assist(struct net_device *dev, void *data, int data_len)
13304a71df50SFrank Blaschka {
13314a71df50SFrank Blaschka 	struct qeth_cmd_buffer *iob;
13324a71df50SFrank Blaschka 	struct qeth_card *card;
13334a71df50SFrank Blaschka 	int rc;
13344a71df50SFrank Blaschka 
13354a71df50SFrank Blaschka 	if (!dev)
13364a71df50SFrank Blaschka 		return -ENODEV;
1337509e2562SHeiko Carstens 	card = dev->ml_priv;
13384a71df50SFrank Blaschka 	if (!card)
13394a71df50SFrank Blaschka 		return -ENODEV;
1340847a50fdSCarsten Otte 	QETH_CARD_TEXT(card, 2, "osnsdmc");
13414a71df50SFrank Blaschka 	if ((card->state != CARD_STATE_UP) &&
13424a71df50SFrank Blaschka 	    (card->state != CARD_STATE_SOFTSETUP))
13434a71df50SFrank Blaschka 		return -ENODEV;
13444a71df50SFrank Blaschka 	iob = qeth_wait_for_buffer(&card->write);
13454a71df50SFrank Blaschka 	memcpy(iob->data+IPA_PDU_HEADER_SIZE, data, data_len);
13464a71df50SFrank Blaschka 	rc = qeth_osn_send_ipa_cmd(card, iob, data_len);
13474a71df50SFrank Blaschka 	return rc;
13484a71df50SFrank Blaschka }
13494a71df50SFrank Blaschka EXPORT_SYMBOL(qeth_osn_assist);
13504a71df50SFrank Blaschka 
13514a71df50SFrank Blaschka int qeth_osn_register(unsigned char *read_dev_no, struct net_device **dev,
13524a71df50SFrank Blaschka 		  int (*assist_cb)(struct net_device *, void *),
13534a71df50SFrank Blaschka 		  int (*data_cb)(struct sk_buff *))
13544a71df50SFrank Blaschka {
13554a71df50SFrank Blaschka 	struct qeth_card *card;
13564a71df50SFrank Blaschka 
13574a71df50SFrank Blaschka 	*dev = qeth_l2_netdev_by_devno(read_dev_no);
13584a71df50SFrank Blaschka 	if (*dev == NULL)
13594a71df50SFrank Blaschka 		return -ENODEV;
1360509e2562SHeiko Carstens 	card = (*dev)->ml_priv;
13614a71df50SFrank Blaschka 	if (!card)
13624a71df50SFrank Blaschka 		return -ENODEV;
1363847a50fdSCarsten Otte 	QETH_CARD_TEXT(card, 2, "osnreg");
13644a71df50SFrank Blaschka 	if ((assist_cb == NULL) || (data_cb == NULL))
13654a71df50SFrank Blaschka 		return -EINVAL;
13664a71df50SFrank Blaschka 	card->osn_info.assist_cb = assist_cb;
13674a71df50SFrank Blaschka 	card->osn_info.data_cb = data_cb;
13684a71df50SFrank Blaschka 	return 0;
13694a71df50SFrank Blaschka }
13704a71df50SFrank Blaschka EXPORT_SYMBOL(qeth_osn_register);
13714a71df50SFrank Blaschka 
13724a71df50SFrank Blaschka void qeth_osn_deregister(struct net_device *dev)
13734a71df50SFrank Blaschka {
13744a71df50SFrank Blaschka 	struct qeth_card *card;
13754a71df50SFrank Blaschka 
13764a71df50SFrank Blaschka 	if (!dev)
13774a71df50SFrank Blaschka 		return;
1378509e2562SHeiko Carstens 	card = dev->ml_priv;
13794a71df50SFrank Blaschka 	if (!card)
13804a71df50SFrank Blaschka 		return;
1381847a50fdSCarsten Otte 	QETH_CARD_TEXT(card, 2, "osndereg");
13824a71df50SFrank Blaschka 	card->osn_info.assist_cb = NULL;
13834a71df50SFrank Blaschka 	card->osn_info.data_cb = NULL;
13844a71df50SFrank Blaschka 	return;
13854a71df50SFrank Blaschka }
13864a71df50SFrank Blaschka EXPORT_SYMBOL(qeth_osn_deregister);
13874a71df50SFrank Blaschka 
1388b4d72c08SEugene Crosser /* SETBRIDGEPORT support, async notifications */
1389b4d72c08SEugene Crosser 
13909f48b9dbSEugene Crosser enum qeth_an_event_type {anev_reg_unreg, anev_abort, anev_reset};
13919f48b9dbSEugene Crosser 
13929f48b9dbSEugene Crosser /**
13939f48b9dbSEugene Crosser  * qeth_bridge_emit_host_event() - bridgeport address change notification
13949f48b9dbSEugene Crosser  * @card:  qeth_card structure pointer, for udev events.
13959f48b9dbSEugene Crosser  * @evtype:  "normal" register/unregister, or abort, or reset. For abort
13969f48b9dbSEugene Crosser  *	      and reset token and addr_lnid are unused and may be NULL.
13979f48b9dbSEugene Crosser  * @code:  event bitmask: high order bit 0x80 value 1 means removal of an
13989f48b9dbSEugene Crosser  *			  object, 0 - addition of an object.
13999f48b9dbSEugene Crosser  *			  0x01 - VLAN, 0x02 - MAC, 0x03 - VLAN and MAC.
14009f48b9dbSEugene Crosser  * @token: "network token" structure identifying physical address of the port.
14019f48b9dbSEugene Crosser  * @addr_lnid: pointer to structure with MAC address and VLAN ID.
14029f48b9dbSEugene Crosser  *
14039f48b9dbSEugene Crosser  * This function is called when registrations and deregistrations are
14049f48b9dbSEugene Crosser  * reported by the hardware, and also when notifications are enabled -
14059f48b9dbSEugene Crosser  * for all currently registered addresses.
14069f48b9dbSEugene Crosser  */
14079f48b9dbSEugene Crosser static void qeth_bridge_emit_host_event(struct qeth_card *card,
14089f48b9dbSEugene Crosser 	enum qeth_an_event_type evtype,
14099f48b9dbSEugene Crosser 	u8 code, struct net_if_token *token, struct mac_addr_lnid *addr_lnid)
14109f48b9dbSEugene Crosser {
14119f48b9dbSEugene Crosser 	char str[7][32];
14129f48b9dbSEugene Crosser 	char *env[8];
14139f48b9dbSEugene Crosser 	int i = 0;
14149f48b9dbSEugene Crosser 
14159f48b9dbSEugene Crosser 	switch (evtype) {
14169f48b9dbSEugene Crosser 	case anev_reg_unreg:
14179f48b9dbSEugene Crosser 		snprintf(str[i], sizeof(str[i]), "BRIDGEDHOST=%s",
14189f48b9dbSEugene Crosser 				(code & IPA_ADDR_CHANGE_CODE_REMOVAL)
14199f48b9dbSEugene Crosser 				? "deregister" : "register");
14209f48b9dbSEugene Crosser 		env[i] = str[i]; i++;
14219f48b9dbSEugene Crosser 		if (code & IPA_ADDR_CHANGE_CODE_VLANID) {
14229f48b9dbSEugene Crosser 			snprintf(str[i], sizeof(str[i]), "VLAN=%d",
14239f48b9dbSEugene Crosser 				addr_lnid->lnid);
14249f48b9dbSEugene Crosser 			env[i] = str[i]; i++;
14259f48b9dbSEugene Crosser 		}
14269f48b9dbSEugene Crosser 		if (code & IPA_ADDR_CHANGE_CODE_MACADDR) {
14279f48b9dbSEugene Crosser 			snprintf(str[i], sizeof(str[i]), "MAC=%pM6",
14289f48b9dbSEugene Crosser 				&addr_lnid->mac);
14299f48b9dbSEugene Crosser 			env[i] = str[i]; i++;
14309f48b9dbSEugene Crosser 		}
14319f48b9dbSEugene Crosser 		snprintf(str[i], sizeof(str[i]), "NTOK_BUSID=%x.%x.%04x",
14329f48b9dbSEugene Crosser 			token->cssid, token->ssid, token->devnum);
14339f48b9dbSEugene Crosser 		env[i] = str[i]; i++;
14349f48b9dbSEugene Crosser 		snprintf(str[i], sizeof(str[i]), "NTOK_IID=%02x", token->iid);
14359f48b9dbSEugene Crosser 		env[i] = str[i]; i++;
14369f48b9dbSEugene Crosser 		snprintf(str[i], sizeof(str[i]), "NTOK_CHPID=%02x",
14379f48b9dbSEugene Crosser 				token->chpid);
14389f48b9dbSEugene Crosser 		env[i] = str[i]; i++;
14399f48b9dbSEugene Crosser 		snprintf(str[i], sizeof(str[i]), "NTOK_CHID=%04x", token->chid);
14409f48b9dbSEugene Crosser 		env[i] = str[i]; i++;
14419f48b9dbSEugene Crosser 		break;
14429f48b9dbSEugene Crosser 	case anev_abort:
14439f48b9dbSEugene Crosser 		snprintf(str[i], sizeof(str[i]), "BRIDGEDHOST=abort");
14449f48b9dbSEugene Crosser 		env[i] = str[i]; i++;
14459f48b9dbSEugene Crosser 		break;
14469f48b9dbSEugene Crosser 	case anev_reset:
14479f48b9dbSEugene Crosser 		snprintf(str[i], sizeof(str[i]), "BRIDGEDHOST=reset");
14489f48b9dbSEugene Crosser 		env[i] = str[i]; i++;
14499f48b9dbSEugene Crosser 		break;
14509f48b9dbSEugene Crosser 	}
14519f48b9dbSEugene Crosser 	env[i] = NULL;
14529f48b9dbSEugene Crosser 	kobject_uevent_env(&card->gdev->dev.kobj, KOBJ_CHANGE, env);
14539f48b9dbSEugene Crosser }
14549f48b9dbSEugene Crosser 
1455b4d72c08SEugene Crosser struct qeth_bridge_state_data {
1456b4d72c08SEugene Crosser 	struct work_struct worker;
1457b4d72c08SEugene Crosser 	struct qeth_card *card;
1458b4d72c08SEugene Crosser 	struct qeth_sbp_state_change qports;
1459b4d72c08SEugene Crosser };
1460b4d72c08SEugene Crosser 
1461b4d72c08SEugene Crosser static void qeth_bridge_state_change_worker(struct work_struct *work)
1462b4d72c08SEugene Crosser {
1463b4d72c08SEugene Crosser 	struct qeth_bridge_state_data *data =
1464b4d72c08SEugene Crosser 		container_of(work, struct qeth_bridge_state_data, worker);
1465b4d72c08SEugene Crosser 	/* We are only interested in the first entry - local port */
1466b4d72c08SEugene Crosser 	struct qeth_sbp_port_entry *entry = &data->qports.entry[0];
1467b4d72c08SEugene Crosser 	char env_locrem[32];
1468b4d72c08SEugene Crosser 	char env_role[32];
1469b4d72c08SEugene Crosser 	char env_state[32];
1470b4d72c08SEugene Crosser 	char *env[] = {
1471b4d72c08SEugene Crosser 		env_locrem,
1472b4d72c08SEugene Crosser 		env_role,
1473b4d72c08SEugene Crosser 		env_state,
1474b4d72c08SEugene Crosser 		NULL
1475b4d72c08SEugene Crosser 	};
1476b4d72c08SEugene Crosser 
1477b4d72c08SEugene Crosser 	/* Role should not change by itself, but if it did, */
1478b4d72c08SEugene Crosser 	/* information from the hardware is authoritative.  */
1479b4d72c08SEugene Crosser 	mutex_lock(&data->card->conf_mutex);
1480b4d72c08SEugene Crosser 	data->card->options.sbp.role = entry->role;
1481b4d72c08SEugene Crosser 	mutex_unlock(&data->card->conf_mutex);
1482b4d72c08SEugene Crosser 
1483b4d72c08SEugene Crosser 	snprintf(env_locrem, sizeof(env_locrem), "BRIDGEPORT=statechange");
1484b4d72c08SEugene Crosser 	snprintf(env_role, sizeof(env_role), "ROLE=%s",
1485b4d72c08SEugene Crosser 		(entry->role == QETH_SBP_ROLE_NONE) ? "none" :
1486b4d72c08SEugene Crosser 		(entry->role == QETH_SBP_ROLE_PRIMARY) ? "primary" :
1487b4d72c08SEugene Crosser 		(entry->role == QETH_SBP_ROLE_SECONDARY) ? "secondary" :
1488b4d72c08SEugene Crosser 		"<INVALID>");
1489b4d72c08SEugene Crosser 	snprintf(env_state, sizeof(env_state), "STATE=%s",
1490b4d72c08SEugene Crosser 		(entry->state == QETH_SBP_STATE_INACTIVE) ? "inactive" :
1491b4d72c08SEugene Crosser 		(entry->state == QETH_SBP_STATE_STANDBY) ? "standby" :
1492b4d72c08SEugene Crosser 		(entry->state == QETH_SBP_STATE_ACTIVE) ? "active" :
1493b4d72c08SEugene Crosser 		"<INVALID>");
1494b4d72c08SEugene Crosser 	kobject_uevent_env(&data->card->gdev->dev.kobj,
1495b4d72c08SEugene Crosser 				KOBJ_CHANGE, env);
1496b4d72c08SEugene Crosser 	kfree(data);
1497b4d72c08SEugene Crosser }
1498b4d72c08SEugene Crosser 
1499c044dc21SEugene Crosser static void qeth_bridge_state_change(struct qeth_card *card,
1500c044dc21SEugene Crosser 					struct qeth_ipa_cmd *cmd)
1501b4d72c08SEugene Crosser {
1502b4d72c08SEugene Crosser 	struct qeth_sbp_state_change *qports =
1503b4d72c08SEugene Crosser 		 &cmd->data.sbp.data.state_change;
1504b4d72c08SEugene Crosser 	struct qeth_bridge_state_data *data;
1505b4d72c08SEugene Crosser 	int extrasize;
1506b4d72c08SEugene Crosser 
1507b4d72c08SEugene Crosser 	QETH_CARD_TEXT(card, 2, "brstchng");
1508b4d72c08SEugene Crosser 	if (qports->entry_length != sizeof(struct qeth_sbp_port_entry)) {
1509b4d72c08SEugene Crosser 		QETH_CARD_TEXT_(card, 2, "BPsz%.8d", qports->entry_length);
1510b4d72c08SEugene Crosser 		return;
1511b4d72c08SEugene Crosser 	}
1512b4d72c08SEugene Crosser 	extrasize = sizeof(struct qeth_sbp_port_entry) * qports->num_entries;
1513b4d72c08SEugene Crosser 	data = kzalloc(sizeof(struct qeth_bridge_state_data) + extrasize,
1514b4d72c08SEugene Crosser 		GFP_ATOMIC);
1515b4d72c08SEugene Crosser 	if (!data) {
1516b4d72c08SEugene Crosser 		QETH_CARD_TEXT(card, 2, "BPSalloc");
1517b4d72c08SEugene Crosser 		return;
1518b4d72c08SEugene Crosser 	}
1519b4d72c08SEugene Crosser 	INIT_WORK(&data->worker, qeth_bridge_state_change_worker);
1520b4d72c08SEugene Crosser 	data->card = card;
1521b4d72c08SEugene Crosser 	memcpy(&data->qports, qports,
1522b4d72c08SEugene Crosser 			sizeof(struct qeth_sbp_state_change) + extrasize);
1523b4d72c08SEugene Crosser 	queue_work(qeth_wq, &data->worker);
1524b4d72c08SEugene Crosser }
1525b4d72c08SEugene Crosser 
15269f48b9dbSEugene Crosser struct qeth_bridge_host_data {
15279f48b9dbSEugene Crosser 	struct work_struct worker;
15289f48b9dbSEugene Crosser 	struct qeth_card *card;
15299f48b9dbSEugene Crosser 	struct qeth_ipacmd_addr_change hostevs;
15309f48b9dbSEugene Crosser };
15319f48b9dbSEugene Crosser 
15329f48b9dbSEugene Crosser static void qeth_bridge_host_event_worker(struct work_struct *work)
15339f48b9dbSEugene Crosser {
15349f48b9dbSEugene Crosser 	struct qeth_bridge_host_data *data =
15359f48b9dbSEugene Crosser 		container_of(work, struct qeth_bridge_host_data, worker);
15369f48b9dbSEugene Crosser 	int i;
15379f48b9dbSEugene Crosser 
15389f48b9dbSEugene Crosser 	if (data->hostevs.lost_event_mask) {
15399f48b9dbSEugene Crosser 		dev_info(&data->card->gdev->dev,
15409f48b9dbSEugene Crosser "Address notification from the HiperSockets Bridge Port stopped %s (%s)\n",
15419f48b9dbSEugene Crosser 			data->card->dev->name,
15429f48b9dbSEugene Crosser 			(data->hostevs.lost_event_mask == 0x01)
15439f48b9dbSEugene Crosser 			? "Overflow"
15449f48b9dbSEugene Crosser 			: (data->hostevs.lost_event_mask == 0x02)
15459f48b9dbSEugene Crosser 			? "Bridge port state change"
15469f48b9dbSEugene Crosser 			: "Unknown reason");
15479f48b9dbSEugene Crosser 		mutex_lock(&data->card->conf_mutex);
15489f48b9dbSEugene Crosser 		data->card->options.sbp.hostnotification = 0;
15499f48b9dbSEugene Crosser 		mutex_unlock(&data->card->conf_mutex);
15509f48b9dbSEugene Crosser 		qeth_bridge_emit_host_event(data->card, anev_abort,
15519f48b9dbSEugene Crosser 			0, NULL, NULL);
15529f48b9dbSEugene Crosser 	} else
15539f48b9dbSEugene Crosser 		for (i = 0; i < data->hostevs.num_entries; i++) {
15549f48b9dbSEugene Crosser 			struct qeth_ipacmd_addr_change_entry *entry =
15559f48b9dbSEugene Crosser 					&data->hostevs.entry[i];
15569f48b9dbSEugene Crosser 			qeth_bridge_emit_host_event(data->card,
15579f48b9dbSEugene Crosser 					anev_reg_unreg,
15589f48b9dbSEugene Crosser 					entry->change_code,
15599f48b9dbSEugene Crosser 					&entry->token, &entry->addr_lnid);
15609f48b9dbSEugene Crosser 		}
15619f48b9dbSEugene Crosser 	kfree(data);
15629f48b9dbSEugene Crosser }
15639f48b9dbSEugene Crosser 
1564c044dc21SEugene Crosser static void qeth_bridge_host_event(struct qeth_card *card,
1565c044dc21SEugene Crosser 					struct qeth_ipa_cmd *cmd)
15669f48b9dbSEugene Crosser {
15679f48b9dbSEugene Crosser 	struct qeth_ipacmd_addr_change *hostevs =
15689f48b9dbSEugene Crosser 		 &cmd->data.addrchange;
15699f48b9dbSEugene Crosser 	struct qeth_bridge_host_data *data;
15709f48b9dbSEugene Crosser 	int extrasize;
15719f48b9dbSEugene Crosser 
15729f48b9dbSEugene Crosser 	QETH_CARD_TEXT(card, 2, "brhostev");
15739f48b9dbSEugene Crosser 	if (cmd->hdr.return_code != 0x0000) {
15749f48b9dbSEugene Crosser 		if (cmd->hdr.return_code == 0x0010) {
15759f48b9dbSEugene Crosser 			if (hostevs->lost_event_mask == 0x00)
15769f48b9dbSEugene Crosser 				hostevs->lost_event_mask = 0xff;
15779f48b9dbSEugene Crosser 		} else {
15789f48b9dbSEugene Crosser 			QETH_CARD_TEXT_(card, 2, "BPHe%04x",
15799f48b9dbSEugene Crosser 				cmd->hdr.return_code);
15809f48b9dbSEugene Crosser 			return;
15819f48b9dbSEugene Crosser 		}
15829f48b9dbSEugene Crosser 	}
15839f48b9dbSEugene Crosser 	extrasize = sizeof(struct qeth_ipacmd_addr_change_entry) *
15849f48b9dbSEugene Crosser 						hostevs->num_entries;
15859f48b9dbSEugene Crosser 	data = kzalloc(sizeof(struct qeth_bridge_host_data) + extrasize,
15869f48b9dbSEugene Crosser 		GFP_ATOMIC);
15879f48b9dbSEugene Crosser 	if (!data) {
15889f48b9dbSEugene Crosser 		QETH_CARD_TEXT(card, 2, "BPHalloc");
15899f48b9dbSEugene Crosser 		return;
15909f48b9dbSEugene Crosser 	}
15919f48b9dbSEugene Crosser 	INIT_WORK(&data->worker, qeth_bridge_host_event_worker);
15929f48b9dbSEugene Crosser 	data->card = card;
15939f48b9dbSEugene Crosser 	memcpy(&data->hostevs, hostevs,
15949f48b9dbSEugene Crosser 			sizeof(struct qeth_ipacmd_addr_change) + extrasize);
15959f48b9dbSEugene Crosser 	queue_work(qeth_wq, &data->worker);
15969f48b9dbSEugene Crosser }
15979f48b9dbSEugene Crosser 
1598b4d72c08SEugene Crosser /* SETBRIDGEPORT support; sending commands */
1599b4d72c08SEugene Crosser 
1600b4d72c08SEugene Crosser struct _qeth_sbp_cbctl {
1601b4d72c08SEugene Crosser 	u16 ipa_rc;
1602b4d72c08SEugene Crosser 	u16 cmd_rc;
1603b4d72c08SEugene Crosser 	union {
1604b4d72c08SEugene Crosser 		u32 supported;
1605b4d72c08SEugene Crosser 		struct {
1606b4d72c08SEugene Crosser 			enum qeth_sbp_roles *role;
1607b4d72c08SEugene Crosser 			enum qeth_sbp_states *state;
1608b4d72c08SEugene Crosser 		} qports;
1609b4d72c08SEugene Crosser 	} data;
1610b4d72c08SEugene Crosser };
1611b4d72c08SEugene Crosser 
1612b4d72c08SEugene Crosser /**
1613b4d72c08SEugene Crosser  * qeth_bridgeport_makerc() - derive "traditional" error from hardware codes.
1614b4d72c08SEugene Crosser  * @card:		      qeth_card structure pointer, for debug messages.
1615b4d72c08SEugene Crosser  * @cbctl:		      state structure with hardware return codes.
1616b4d72c08SEugene Crosser  * @setcmd:		      IPA command code
1617b4d72c08SEugene Crosser  *
1618b4d72c08SEugene Crosser  * Returns negative errno-compatible error indication or 0 on success.
1619b4d72c08SEugene Crosser  */
1620b4d72c08SEugene Crosser static int qeth_bridgeport_makerc(struct qeth_card *card,
1621b4d72c08SEugene Crosser 	struct _qeth_sbp_cbctl *cbctl, enum qeth_ipa_sbp_cmd setcmd)
1622b4d72c08SEugene Crosser {
1623b4d72c08SEugene Crosser 	int rc;
1624b4d72c08SEugene Crosser 
1625b4d72c08SEugene Crosser 	switch (cbctl->ipa_rc) {
1626b4d72c08SEugene Crosser 	case IPA_RC_SUCCESS:
1627b4d72c08SEugene Crosser 		switch (cbctl->cmd_rc) {
1628b4d72c08SEugene Crosser 		case 0x0000:
1629b4d72c08SEugene Crosser 			rc = 0;
1630b4d72c08SEugene Crosser 			break;
1631b4d72c08SEugene Crosser 		case 0x0004:
1632b4d72c08SEugene Crosser 			rc = -ENOSYS;
1633b4d72c08SEugene Crosser 			break;
1634b4d72c08SEugene Crosser 		case 0x000C: /* Not configured as bridge Port */
1635b4d72c08SEugene Crosser 			rc = -ENODEV; /* maybe not the best code here? */
1636b4d72c08SEugene Crosser 			dev_err(&card->gdev->dev,
1637b4d72c08SEugene Crosser 	"The HiperSockets device is not configured as a Bridge Port\n");
1638b4d72c08SEugene Crosser 			break;
1639b4d72c08SEugene Crosser 		case 0x0014: /* Another device is Primary */
1640b4d72c08SEugene Crosser 			switch (setcmd) {
1641b4d72c08SEugene Crosser 			case IPA_SBP_SET_PRIMARY_BRIDGE_PORT:
1642b4d72c08SEugene Crosser 				rc = -EEXIST;
1643b4d72c08SEugene Crosser 				dev_err(&card->gdev->dev,
1644b4d72c08SEugene Crosser 	"The HiperSockets LAN already has a primary Bridge Port\n");
1645b4d72c08SEugene Crosser 				break;
1646b4d72c08SEugene Crosser 			case IPA_SBP_SET_SECONDARY_BRIDGE_PORT:
1647b4d72c08SEugene Crosser 				rc = -EBUSY;
1648b4d72c08SEugene Crosser 				dev_err(&card->gdev->dev,
1649b4d72c08SEugene Crosser 	"The HiperSockets device is already a primary Bridge Port\n");
1650b4d72c08SEugene Crosser 				break;
1651b4d72c08SEugene Crosser 			default:
1652b4d72c08SEugene Crosser 				rc = -EIO;
1653b4d72c08SEugene Crosser 			}
1654b4d72c08SEugene Crosser 			break;
1655b4d72c08SEugene Crosser 		case 0x0018: /* This device is currently Secondary */
1656b4d72c08SEugene Crosser 			rc = -EBUSY;
1657b4d72c08SEugene Crosser 			dev_err(&card->gdev->dev,
1658b4d72c08SEugene Crosser 	"The HiperSockets device is already a secondary Bridge Port\n");
1659b4d72c08SEugene Crosser 			break;
1660b4d72c08SEugene Crosser 		case 0x001C: /* Limit for Secondary devices reached */
1661b4d72c08SEugene Crosser 			rc = -EEXIST;
1662b4d72c08SEugene Crosser 			dev_err(&card->gdev->dev,
1663b4d72c08SEugene Crosser 	"The HiperSockets LAN cannot have more secondary Bridge Ports\n");
1664b4d72c08SEugene Crosser 			break;
1665b4d72c08SEugene Crosser 		case 0x0024: /* This device is currently Primary */
1666b4d72c08SEugene Crosser 			rc = -EBUSY;
1667b4d72c08SEugene Crosser 			dev_err(&card->gdev->dev,
1668b4d72c08SEugene Crosser 	"The HiperSockets device is already a primary Bridge Port\n");
1669b4d72c08SEugene Crosser 			break;
1670b4d72c08SEugene Crosser 		case 0x0020: /* Not authorized by zManager */
1671b4d72c08SEugene Crosser 			rc = -EACCES;
1672b4d72c08SEugene Crosser 			dev_err(&card->gdev->dev,
1673b4d72c08SEugene Crosser 	"The HiperSockets device is not authorized to be a Bridge Port\n");
1674b4d72c08SEugene Crosser 			break;
1675b4d72c08SEugene Crosser 		default:
1676b4d72c08SEugene Crosser 			rc = -EIO;
1677b4d72c08SEugene Crosser 		}
1678b4d72c08SEugene Crosser 		break;
1679b4d72c08SEugene Crosser 	case IPA_RC_NOTSUPP:
1680b4d72c08SEugene Crosser 		rc = -ENOSYS;
1681b4d72c08SEugene Crosser 		break;
1682b4d72c08SEugene Crosser 	case IPA_RC_UNSUPPORTED_COMMAND:
1683b4d72c08SEugene Crosser 		rc = -ENOSYS;
1684b4d72c08SEugene Crosser 		break;
1685b4d72c08SEugene Crosser 	default:
1686b4d72c08SEugene Crosser 		rc = -EIO;
1687b4d72c08SEugene Crosser 	}
1688b4d72c08SEugene Crosser 	if (rc) {
1689b4d72c08SEugene Crosser 		QETH_CARD_TEXT_(card, 2, "SBPi%04x", cbctl->ipa_rc);
1690b4d72c08SEugene Crosser 		QETH_CARD_TEXT_(card, 2, "SBPc%04x", cbctl->cmd_rc);
1691b4d72c08SEugene Crosser 	}
1692b4d72c08SEugene Crosser 	return rc;
1693b4d72c08SEugene Crosser }
1694b4d72c08SEugene Crosser 
1695b4d72c08SEugene Crosser static int qeth_bridgeport_query_support_cb(struct qeth_card *card,
1696b4d72c08SEugene Crosser 	struct qeth_reply *reply, unsigned long data)
1697b4d72c08SEugene Crosser {
1698b4d72c08SEugene Crosser 	struct qeth_ipa_cmd *cmd = (struct qeth_ipa_cmd *) data;
1699b4d72c08SEugene Crosser 	struct _qeth_sbp_cbctl *cbctl = (struct _qeth_sbp_cbctl *)reply->param;
1700b4d72c08SEugene Crosser 	QETH_CARD_TEXT(card, 2, "brqsupcb");
1701b4d72c08SEugene Crosser 	cbctl->ipa_rc = cmd->hdr.return_code;
1702b4d72c08SEugene Crosser 	cbctl->cmd_rc = cmd->data.sbp.hdr.return_code;
1703b4d72c08SEugene Crosser 	if ((cbctl->ipa_rc == 0) && (cbctl->cmd_rc == 0)) {
1704b4d72c08SEugene Crosser 		cbctl->data.supported =
1705b4d72c08SEugene Crosser 			cmd->data.sbp.data.query_cmds_supp.supported_cmds;
1706b4d72c08SEugene Crosser 	} else {
1707b4d72c08SEugene Crosser 		cbctl->data.supported = 0;
1708b4d72c08SEugene Crosser 	}
1709b4d72c08SEugene Crosser 	return 0;
1710b4d72c08SEugene Crosser }
1711b4d72c08SEugene Crosser 
1712b4d72c08SEugene Crosser /**
1713b4d72c08SEugene Crosser  * qeth_bridgeport_query_support() - store bitmask of supported subfunctions.
1714b4d72c08SEugene Crosser  * @card:			     qeth_card structure pointer.
1715b4d72c08SEugene Crosser  *
1716b4d72c08SEugene Crosser  * Sets bitmask of supported setbridgeport subfunctions in the qeth_card
1717b4d72c08SEugene Crosser  * strucutre: card->options.sbp.supported_funcs.
1718b4d72c08SEugene Crosser  */
1719c044dc21SEugene Crosser static void qeth_bridgeport_query_support(struct qeth_card *card)
1720b4d72c08SEugene Crosser {
1721b4d72c08SEugene Crosser 	struct qeth_cmd_buffer *iob;
1722b4d72c08SEugene Crosser 	struct qeth_ipa_cmd *cmd;
1723b4d72c08SEugene Crosser 	struct _qeth_sbp_cbctl cbctl;
1724b4d72c08SEugene Crosser 
1725b4d72c08SEugene Crosser 	QETH_CARD_TEXT(card, 2, "brqsuppo");
1726b4d72c08SEugene Crosser 	iob = qeth_get_ipacmd_buffer(card, IPA_CMD_SETBRIDGEPORT, 0);
1727b4d72c08SEugene Crosser 	cmd = (struct qeth_ipa_cmd *)(iob->data+IPA_PDU_HEADER_SIZE);
1728b4d72c08SEugene Crosser 	cmd->data.sbp.hdr.cmdlength =
1729b4d72c08SEugene Crosser 		sizeof(struct qeth_ipacmd_sbp_hdr) +
1730b4d72c08SEugene Crosser 		sizeof(struct qeth_sbp_query_cmds_supp);
1731b4d72c08SEugene Crosser 	cmd->data.sbp.hdr.command_code =
1732b4d72c08SEugene Crosser 		IPA_SBP_QUERY_COMMANDS_SUPPORTED;
1733b4d72c08SEugene Crosser 	cmd->data.sbp.hdr.used_total = 1;
1734b4d72c08SEugene Crosser 	cmd->data.sbp.hdr.seq_no = 1;
1735b4d72c08SEugene Crosser 	if (qeth_send_ipa_cmd(card, iob, qeth_bridgeport_query_support_cb,
1736b4d72c08SEugene Crosser 							(void *)&cbctl) ||
1737b4d72c08SEugene Crosser 	    qeth_bridgeport_makerc(card, &cbctl,
1738b4d72c08SEugene Crosser 					IPA_SBP_QUERY_COMMANDS_SUPPORTED)) {
1739b4d72c08SEugene Crosser 		/* non-zero makerc signifies failure, and produce messages */
1740b4d72c08SEugene Crosser 		card->options.sbp.role = QETH_SBP_ROLE_NONE;
1741b4d72c08SEugene Crosser 		return;
1742b4d72c08SEugene Crosser 	}
1743b4d72c08SEugene Crosser 	card->options.sbp.supported_funcs = cbctl.data.supported;
1744b4d72c08SEugene Crosser }
1745b4d72c08SEugene Crosser 
1746b4d72c08SEugene Crosser static int qeth_bridgeport_query_ports_cb(struct qeth_card *card,
1747b4d72c08SEugene Crosser 	struct qeth_reply *reply, unsigned long data)
1748b4d72c08SEugene Crosser {
1749b4d72c08SEugene Crosser 	struct qeth_ipa_cmd *cmd = (struct qeth_ipa_cmd *) data;
1750b4d72c08SEugene Crosser 	struct qeth_sbp_query_ports *qports = &cmd->data.sbp.data.query_ports;
1751b4d72c08SEugene Crosser 	struct _qeth_sbp_cbctl *cbctl = (struct _qeth_sbp_cbctl *)reply->param;
1752b4d72c08SEugene Crosser 
1753b4d72c08SEugene Crosser 	QETH_CARD_TEXT(card, 2, "brqprtcb");
1754b4d72c08SEugene Crosser 	cbctl->ipa_rc = cmd->hdr.return_code;
1755b4d72c08SEugene Crosser 	cbctl->cmd_rc = cmd->data.sbp.hdr.return_code;
1756b4d72c08SEugene Crosser 	if ((cbctl->ipa_rc != 0) || (cbctl->cmd_rc != 0))
1757b4d72c08SEugene Crosser 		return 0;
1758b4d72c08SEugene Crosser 	if (qports->entry_length != sizeof(struct qeth_sbp_port_entry)) {
1759b4d72c08SEugene Crosser 		cbctl->cmd_rc = 0xffff;
1760b4d72c08SEugene Crosser 		QETH_CARD_TEXT_(card, 2, "SBPs%04x", qports->entry_length);
1761b4d72c08SEugene Crosser 		return 0;
1762b4d72c08SEugene Crosser 	}
1763b4d72c08SEugene Crosser 	/* first entry contains the state of the local port */
1764b4d72c08SEugene Crosser 	if (qports->num_entries > 0) {
1765b4d72c08SEugene Crosser 		if (cbctl->data.qports.role)
1766b4d72c08SEugene Crosser 			*cbctl->data.qports.role = qports->entry[0].role;
1767b4d72c08SEugene Crosser 		if (cbctl->data.qports.state)
1768b4d72c08SEugene Crosser 			*cbctl->data.qports.state = qports->entry[0].state;
1769b4d72c08SEugene Crosser 	}
1770b4d72c08SEugene Crosser 	return 0;
1771b4d72c08SEugene Crosser }
1772b4d72c08SEugene Crosser 
1773b4d72c08SEugene Crosser /**
1774b4d72c08SEugene Crosser  * qeth_bridgeport_query_ports() - query local bridgeport status.
1775b4d72c08SEugene Crosser  * @card:			   qeth_card structure pointer.
1776b4d72c08SEugene Crosser  * @role:   Role of the port: 0-none, 1-primary, 2-secondary.
1777b4d72c08SEugene Crosser  * @state:  State of the port: 0-inactive, 1-standby, 2-active.
1778b4d72c08SEugene Crosser  *
1779b4d72c08SEugene Crosser  * Returns negative errno-compatible error indication or 0 on success.
1780b4d72c08SEugene Crosser  *
1781b4d72c08SEugene Crosser  * 'role' and 'state' are not updated in case of hardware operation failure.
1782b4d72c08SEugene Crosser  */
1783b4d72c08SEugene Crosser int qeth_bridgeport_query_ports(struct qeth_card *card,
1784b4d72c08SEugene Crosser 	enum qeth_sbp_roles *role, enum qeth_sbp_states *state)
1785b4d72c08SEugene Crosser {
1786b4d72c08SEugene Crosser 	int rc = 0;
1787b4d72c08SEugene Crosser 	struct qeth_cmd_buffer *iob;
1788b4d72c08SEugene Crosser 	struct qeth_ipa_cmd *cmd;
1789b4d72c08SEugene Crosser 	struct _qeth_sbp_cbctl cbctl = {
1790b4d72c08SEugene Crosser 		.data = {
1791b4d72c08SEugene Crosser 			.qports = {
1792b4d72c08SEugene Crosser 				.role = role,
1793b4d72c08SEugene Crosser 				.state = state,
1794b4d72c08SEugene Crosser 			},
1795b4d72c08SEugene Crosser 		},
1796b4d72c08SEugene Crosser 	};
1797b4d72c08SEugene Crosser 
1798b4d72c08SEugene Crosser 	QETH_CARD_TEXT(card, 2, "brqports");
1799b4d72c08SEugene Crosser 	if (!(card->options.sbp.supported_funcs & IPA_SBP_QUERY_BRIDGE_PORTS))
1800b4d72c08SEugene Crosser 		return -EOPNOTSUPP;
1801b4d72c08SEugene Crosser 	iob = qeth_get_ipacmd_buffer(card, IPA_CMD_SETBRIDGEPORT, 0);
1802b4d72c08SEugene Crosser 	cmd = (struct qeth_ipa_cmd *)(iob->data+IPA_PDU_HEADER_SIZE);
1803b4d72c08SEugene Crosser 	cmd->data.sbp.hdr.cmdlength =
1804b4d72c08SEugene Crosser 		sizeof(struct qeth_ipacmd_sbp_hdr);
1805b4d72c08SEugene Crosser 	cmd->data.sbp.hdr.command_code =
1806b4d72c08SEugene Crosser 		IPA_SBP_QUERY_BRIDGE_PORTS;
1807b4d72c08SEugene Crosser 	cmd->data.sbp.hdr.used_total = 1;
1808b4d72c08SEugene Crosser 	cmd->data.sbp.hdr.seq_no = 1;
1809b4d72c08SEugene Crosser 	rc = qeth_send_ipa_cmd(card, iob, qeth_bridgeport_query_ports_cb,
1810b4d72c08SEugene Crosser 				(void *)&cbctl);
1811b4d72c08SEugene Crosser 	if (rc)
1812b4d72c08SEugene Crosser 		return rc;
1813b4d72c08SEugene Crosser 	rc = qeth_bridgeport_makerc(card, &cbctl, IPA_SBP_QUERY_BRIDGE_PORTS);
1814b4d72c08SEugene Crosser 	if (rc)
1815b4d72c08SEugene Crosser 		return rc;
1816b4d72c08SEugene Crosser 	return 0;
1817b4d72c08SEugene Crosser }
1818b4d72c08SEugene Crosser EXPORT_SYMBOL_GPL(qeth_bridgeport_query_ports);
1819b4d72c08SEugene Crosser 
1820b4d72c08SEugene Crosser static int qeth_bridgeport_set_cb(struct qeth_card *card,
1821b4d72c08SEugene Crosser 	struct qeth_reply *reply, unsigned long data)
1822b4d72c08SEugene Crosser {
1823b4d72c08SEugene Crosser 	struct qeth_ipa_cmd *cmd = (struct qeth_ipa_cmd *)data;
1824b4d72c08SEugene Crosser 	struct _qeth_sbp_cbctl *cbctl = (struct _qeth_sbp_cbctl *)reply->param;
1825b4d72c08SEugene Crosser 	QETH_CARD_TEXT(card, 2, "brsetrcb");
1826b4d72c08SEugene Crosser 	cbctl->ipa_rc = cmd->hdr.return_code;
1827b4d72c08SEugene Crosser 	cbctl->cmd_rc = cmd->data.sbp.hdr.return_code;
1828b4d72c08SEugene Crosser 	return 0;
1829b4d72c08SEugene Crosser }
1830b4d72c08SEugene Crosser 
1831b4d72c08SEugene Crosser /**
1832b4d72c08SEugene Crosser  * qeth_bridgeport_setrole() - Assign primary role to the port.
1833b4d72c08SEugene Crosser  * @card:		       qeth_card structure pointer.
1834b4d72c08SEugene Crosser  * @role:		       Role to assign.
1835b4d72c08SEugene Crosser  *
1836b4d72c08SEugene Crosser  * Returns negative errno-compatible error indication or 0 on success.
1837b4d72c08SEugene Crosser  */
1838b4d72c08SEugene Crosser int qeth_bridgeport_setrole(struct qeth_card *card, enum qeth_sbp_roles role)
1839b4d72c08SEugene Crosser {
1840b4d72c08SEugene Crosser 	int rc = 0;
1841b4d72c08SEugene Crosser 	int cmdlength;
1842b4d72c08SEugene Crosser 	struct qeth_cmd_buffer *iob;
1843b4d72c08SEugene Crosser 	struct qeth_ipa_cmd *cmd;
1844b4d72c08SEugene Crosser 	struct _qeth_sbp_cbctl cbctl;
1845b4d72c08SEugene Crosser 	enum qeth_ipa_sbp_cmd setcmd;
1846b4d72c08SEugene Crosser 
1847b4d72c08SEugene Crosser 	QETH_CARD_TEXT(card, 2, "brsetrol");
1848b4d72c08SEugene Crosser 	switch (role) {
1849b4d72c08SEugene Crosser 	case QETH_SBP_ROLE_NONE:
1850b4d72c08SEugene Crosser 		setcmd = IPA_SBP_RESET_BRIDGE_PORT_ROLE;
1851b4d72c08SEugene Crosser 		cmdlength =  sizeof(struct qeth_ipacmd_sbp_hdr) +
1852b4d72c08SEugene Crosser 			sizeof(struct qeth_sbp_reset_role);
1853b4d72c08SEugene Crosser 		break;
1854b4d72c08SEugene Crosser 	case QETH_SBP_ROLE_PRIMARY:
1855b4d72c08SEugene Crosser 		setcmd = IPA_SBP_SET_PRIMARY_BRIDGE_PORT;
1856b4d72c08SEugene Crosser 		cmdlength =  sizeof(struct qeth_ipacmd_sbp_hdr) +
1857b4d72c08SEugene Crosser 			sizeof(struct qeth_sbp_set_primary);
1858b4d72c08SEugene Crosser 		break;
1859b4d72c08SEugene Crosser 	case QETH_SBP_ROLE_SECONDARY:
1860b4d72c08SEugene Crosser 		setcmd = IPA_SBP_SET_SECONDARY_BRIDGE_PORT;
1861b4d72c08SEugene Crosser 		cmdlength =  sizeof(struct qeth_ipacmd_sbp_hdr) +
1862b4d72c08SEugene Crosser 			sizeof(struct qeth_sbp_set_secondary);
1863b4d72c08SEugene Crosser 		break;
1864b4d72c08SEugene Crosser 	default:
1865b4d72c08SEugene Crosser 		return -EINVAL;
1866b4d72c08SEugene Crosser 	}
1867b4d72c08SEugene Crosser 	if (!(card->options.sbp.supported_funcs & setcmd))
1868b4d72c08SEugene Crosser 		return -EOPNOTSUPP;
1869b4d72c08SEugene Crosser 	iob = qeth_get_ipacmd_buffer(card, IPA_CMD_SETBRIDGEPORT, 0);
1870b4d72c08SEugene Crosser 	cmd = (struct qeth_ipa_cmd *)(iob->data+IPA_PDU_HEADER_SIZE);
1871b4d72c08SEugene Crosser 	cmd->data.sbp.hdr.cmdlength = cmdlength;
1872b4d72c08SEugene Crosser 	cmd->data.sbp.hdr.command_code = setcmd;
1873b4d72c08SEugene Crosser 	cmd->data.sbp.hdr.used_total = 1;
1874b4d72c08SEugene Crosser 	cmd->data.sbp.hdr.seq_no = 1;
1875b4d72c08SEugene Crosser 	rc = qeth_send_ipa_cmd(card, iob, qeth_bridgeport_set_cb,
1876b4d72c08SEugene Crosser 				(void *)&cbctl);
1877b4d72c08SEugene Crosser 	if (rc)
1878b4d72c08SEugene Crosser 		return rc;
1879b4d72c08SEugene Crosser 	rc = qeth_bridgeport_makerc(card, &cbctl, setcmd);
1880b4d72c08SEugene Crosser 	return rc;
1881b4d72c08SEugene Crosser }
1882b4d72c08SEugene Crosser 
18839f48b9dbSEugene Crosser /**
18849f48b9dbSEugene Crosser  * qeth_anset_makerc() - derive "traditional" error from hardware codes.
18859f48b9dbSEugene Crosser  * @card:		      qeth_card structure pointer, for debug messages.
18869f48b9dbSEugene Crosser  *
18879f48b9dbSEugene Crosser  * Returns negative errno-compatible error indication or 0 on success.
18889f48b9dbSEugene Crosser  */
18899f48b9dbSEugene Crosser static int qeth_anset_makerc(struct qeth_card *card, int pnso_rc, u16 response)
18909f48b9dbSEugene Crosser {
18919f48b9dbSEugene Crosser 	int rc;
18929f48b9dbSEugene Crosser 
18939f48b9dbSEugene Crosser 	if (pnso_rc == 0)
18949f48b9dbSEugene Crosser 		switch (response) {
18959f48b9dbSEugene Crosser 		case 0x0001:
18969f48b9dbSEugene Crosser 			rc = 0;
18979f48b9dbSEugene Crosser 			break;
18989f48b9dbSEugene Crosser 		case 0x0004:
18999f48b9dbSEugene Crosser 		case 0x0100:
19009f48b9dbSEugene Crosser 		case 0x0106:
19019f48b9dbSEugene Crosser 			rc = -ENOSYS;
19029f48b9dbSEugene Crosser 			dev_err(&card->gdev->dev,
19039f48b9dbSEugene Crosser 				"Setting address notification failed\n");
19049f48b9dbSEugene Crosser 			break;
19059f48b9dbSEugene Crosser 		case 0x0107:
19069f48b9dbSEugene Crosser 			rc = -EAGAIN;
19079f48b9dbSEugene Crosser 			break;
19089f48b9dbSEugene Crosser 		default:
19099f48b9dbSEugene Crosser 			rc = -EIO;
19109f48b9dbSEugene Crosser 		}
19119f48b9dbSEugene Crosser 	else
19129f48b9dbSEugene Crosser 		rc = -EIO;
19139f48b9dbSEugene Crosser 
19149f48b9dbSEugene Crosser 	if (rc) {
19159f48b9dbSEugene Crosser 		QETH_CARD_TEXT_(card, 2, "SBPp%04x", pnso_rc);
19169f48b9dbSEugene Crosser 		QETH_CARD_TEXT_(card, 2, "SBPr%04x", response);
19179f48b9dbSEugene Crosser 	}
19189f48b9dbSEugene Crosser 	return rc;
19199f48b9dbSEugene Crosser }
19209f48b9dbSEugene Crosser 
19219f48b9dbSEugene Crosser static void qeth_bridgeport_an_set_cb(void *priv,
19229f48b9dbSEugene Crosser 		enum qdio_brinfo_entry_type type, void *entry)
19239f48b9dbSEugene Crosser {
19249f48b9dbSEugene Crosser 	struct qeth_card *card = (struct qeth_card *)priv;
19259f48b9dbSEugene Crosser 	struct qdio_brinfo_entry_l2 *l2entry;
19269f48b9dbSEugene Crosser 	u8 code;
19279f48b9dbSEugene Crosser 
19289f48b9dbSEugene Crosser 	if (type != l2_addr_lnid) {
19299f48b9dbSEugene Crosser 		WARN_ON_ONCE(1);
19309f48b9dbSEugene Crosser 		return;
19319f48b9dbSEugene Crosser 	}
19329f48b9dbSEugene Crosser 
19339f48b9dbSEugene Crosser 	l2entry = (struct qdio_brinfo_entry_l2 *)entry;
19349f48b9dbSEugene Crosser 	code = IPA_ADDR_CHANGE_CODE_MACADDR;
19359f48b9dbSEugene Crosser 	if (l2entry->addr_lnid.lnid)
19369f48b9dbSEugene Crosser 		code |= IPA_ADDR_CHANGE_CODE_VLANID;
19379f48b9dbSEugene Crosser 	qeth_bridge_emit_host_event(card, anev_reg_unreg, code,
19389f48b9dbSEugene Crosser 		(struct net_if_token *)&l2entry->nit,
19399f48b9dbSEugene Crosser 		(struct mac_addr_lnid *)&l2entry->addr_lnid);
19409f48b9dbSEugene Crosser }
19419f48b9dbSEugene Crosser 
19429f48b9dbSEugene Crosser /**
19439f48b9dbSEugene Crosser  * qeth_bridgeport_an_set() - Enable or disable bridgeport address notification
19449f48b9dbSEugene Crosser  * @card:		      qeth_card structure pointer.
19459f48b9dbSEugene Crosser  * @enable:		      0 - disable, non-zero - enable notifications
19469f48b9dbSEugene Crosser  *
19479f48b9dbSEugene Crosser  * Returns negative errno-compatible error indication or 0 on success.
19489f48b9dbSEugene Crosser  *
19499f48b9dbSEugene Crosser  * On enable, emits a series of address notifications udev events for all
19509f48b9dbSEugene Crosser  * currently registered hosts.
19519f48b9dbSEugene Crosser  */
19529f48b9dbSEugene Crosser int qeth_bridgeport_an_set(struct qeth_card *card, int enable)
19539f48b9dbSEugene Crosser {
19549f48b9dbSEugene Crosser 	int rc;
19559f48b9dbSEugene Crosser 	u16 response;
19569f48b9dbSEugene Crosser 	struct ccw_device *ddev;
19579f48b9dbSEugene Crosser 	struct subchannel_id schid;
19589f48b9dbSEugene Crosser 
19599f48b9dbSEugene Crosser 	if (!card)
19609f48b9dbSEugene Crosser 		return -EINVAL;
19619f48b9dbSEugene Crosser 	if (!card->options.sbp.supported_funcs)
19629f48b9dbSEugene Crosser 		return -EOPNOTSUPP;
19639f48b9dbSEugene Crosser 	ddev = CARD_DDEV(card);
19649f48b9dbSEugene Crosser 	ccw_device_get_schid(ddev, &schid);
19659f48b9dbSEugene Crosser 
19669f48b9dbSEugene Crosser 	if (enable) {
19679f48b9dbSEugene Crosser 		qeth_bridge_emit_host_event(card, anev_reset, 0, NULL, NULL);
19689f48b9dbSEugene Crosser 		rc = qdio_pnso_brinfo(schid, 1, &response,
19699f48b9dbSEugene Crosser 			qeth_bridgeport_an_set_cb, card);
19709f48b9dbSEugene Crosser 	} else
19719f48b9dbSEugene Crosser 		rc = qdio_pnso_brinfo(schid, 0, &response, NULL, NULL);
19729f48b9dbSEugene Crosser 	return qeth_anset_makerc(card, rc, response);
19739f48b9dbSEugene Crosser }
19749f48b9dbSEugene Crosser EXPORT_SYMBOL_GPL(qeth_bridgeport_an_set);
19759f48b9dbSEugene Crosser 
19764a71df50SFrank Blaschka module_init(qeth_l2_init);
19774a71df50SFrank Blaschka module_exit(qeth_l2_exit);
19784a71df50SFrank Blaschka MODULE_AUTHOR("Frank Blaschka <frank.blaschka@de.ibm.com>");
19794a71df50SFrank Blaschka MODULE_DESCRIPTION("qeth layer 2 discipline");
19804a71df50SFrank Blaschka MODULE_LICENSE("GPL");
1981