18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Copyright IBM Corp. 2007, 2009 48c2ecf20Sopenharmony_ci * Author(s): Utz Bacher <utz.bacher@de.ibm.com>, 58c2ecf20Sopenharmony_ci * Frank Pavlic <fpavlic@de.ibm.com>, 68c2ecf20Sopenharmony_ci * Thomas Spatzier <tspat@de.ibm.com>, 78c2ecf20Sopenharmony_ci * Frank Blaschka <frank.blaschka@de.ibm.com> 88c2ecf20Sopenharmony_ci */ 98c2ecf20Sopenharmony_ci 108c2ecf20Sopenharmony_ci#define KMSG_COMPONENT "qeth" 118c2ecf20Sopenharmony_ci#define pr_fmt(fmt) KMSG_COMPONENT ": " fmt 128c2ecf20Sopenharmony_ci 138c2ecf20Sopenharmony_ci#include <linux/module.h> 148c2ecf20Sopenharmony_ci#include <linux/moduleparam.h> 158c2ecf20Sopenharmony_ci#include <linux/string.h> 168c2ecf20Sopenharmony_ci#include <linux/errno.h> 178c2ecf20Sopenharmony_ci#include <linux/kernel.h> 188c2ecf20Sopenharmony_ci#include <linux/slab.h> 198c2ecf20Sopenharmony_ci#include <linux/etherdevice.h> 208c2ecf20Sopenharmony_ci#include <linux/if_bridge.h> 218c2ecf20Sopenharmony_ci#include <linux/list.h> 228c2ecf20Sopenharmony_ci#include <linux/hash.h> 238c2ecf20Sopenharmony_ci#include <linux/hashtable.h> 248c2ecf20Sopenharmony_ci#include <net/switchdev.h> 258c2ecf20Sopenharmony_ci#include <asm/chsc.h> 268c2ecf20Sopenharmony_ci#include <asm/css_chars.h> 278c2ecf20Sopenharmony_ci#include <asm/setup.h> 288c2ecf20Sopenharmony_ci#include "qeth_core.h" 298c2ecf20Sopenharmony_ci#include "qeth_l2.h" 308c2ecf20Sopenharmony_ci 318c2ecf20Sopenharmony_cistatic int qeth_l2_setdelmac_makerc(struct qeth_card *card, u16 retcode) 328c2ecf20Sopenharmony_ci{ 338c2ecf20Sopenharmony_ci int rc; 348c2ecf20Sopenharmony_ci 358c2ecf20Sopenharmony_ci if (retcode) 368c2ecf20Sopenharmony_ci QETH_CARD_TEXT_(card, 2, "err%04x", retcode); 378c2ecf20Sopenharmony_ci switch (retcode) { 388c2ecf20Sopenharmony_ci case IPA_RC_SUCCESS: 398c2ecf20Sopenharmony_ci rc = 0; 408c2ecf20Sopenharmony_ci break; 418c2ecf20Sopenharmony_ci case IPA_RC_L2_UNSUPPORTED_CMD: 428c2ecf20Sopenharmony_ci rc = -EOPNOTSUPP; 438c2ecf20Sopenharmony_ci break; 448c2ecf20Sopenharmony_ci case IPA_RC_L2_ADDR_TABLE_FULL: 458c2ecf20Sopenharmony_ci rc = -ENOSPC; 468c2ecf20Sopenharmony_ci break; 478c2ecf20Sopenharmony_ci case IPA_RC_L2_DUP_MAC: 488c2ecf20Sopenharmony_ci case IPA_RC_L2_DUP_LAYER3_MAC: 498c2ecf20Sopenharmony_ci rc = -EADDRINUSE; 508c2ecf20Sopenharmony_ci break; 518c2ecf20Sopenharmony_ci case IPA_RC_L2_MAC_NOT_AUTH_BY_HYP: 528c2ecf20Sopenharmony_ci case IPA_RC_L2_MAC_NOT_AUTH_BY_ADP: 538c2ecf20Sopenharmony_ci rc = -EADDRNOTAVAIL; 548c2ecf20Sopenharmony_ci break; 558c2ecf20Sopenharmony_ci case IPA_RC_L2_MAC_NOT_FOUND: 568c2ecf20Sopenharmony_ci rc = -ENOENT; 578c2ecf20Sopenharmony_ci break; 588c2ecf20Sopenharmony_ci default: 598c2ecf20Sopenharmony_ci rc = -EIO; 608c2ecf20Sopenharmony_ci break; 618c2ecf20Sopenharmony_ci } 628c2ecf20Sopenharmony_ci return rc; 638c2ecf20Sopenharmony_ci} 648c2ecf20Sopenharmony_ci 658c2ecf20Sopenharmony_cistatic int qeth_l2_send_setdelmac_cb(struct qeth_card *card, 668c2ecf20Sopenharmony_ci struct qeth_reply *reply, 678c2ecf20Sopenharmony_ci unsigned long data) 688c2ecf20Sopenharmony_ci{ 698c2ecf20Sopenharmony_ci struct qeth_ipa_cmd *cmd = (struct qeth_ipa_cmd *) data; 708c2ecf20Sopenharmony_ci 718c2ecf20Sopenharmony_ci return qeth_l2_setdelmac_makerc(card, cmd->hdr.return_code); 728c2ecf20Sopenharmony_ci} 738c2ecf20Sopenharmony_ci 748c2ecf20Sopenharmony_cistatic int qeth_l2_send_setdelmac(struct qeth_card *card, __u8 *mac, 758c2ecf20Sopenharmony_ci enum qeth_ipa_cmds ipacmd) 768c2ecf20Sopenharmony_ci{ 778c2ecf20Sopenharmony_ci struct qeth_ipa_cmd *cmd; 788c2ecf20Sopenharmony_ci struct qeth_cmd_buffer *iob; 798c2ecf20Sopenharmony_ci 808c2ecf20Sopenharmony_ci QETH_CARD_TEXT(card, 2, "L2sdmac"); 818c2ecf20Sopenharmony_ci iob = qeth_ipa_alloc_cmd(card, ipacmd, QETH_PROT_IPV4, 828c2ecf20Sopenharmony_ci IPA_DATA_SIZEOF(setdelmac)); 838c2ecf20Sopenharmony_ci if (!iob) 848c2ecf20Sopenharmony_ci return -ENOMEM; 858c2ecf20Sopenharmony_ci cmd = __ipa_cmd(iob); 868c2ecf20Sopenharmony_ci cmd->data.setdelmac.mac_length = ETH_ALEN; 878c2ecf20Sopenharmony_ci ether_addr_copy(cmd->data.setdelmac.mac, mac); 888c2ecf20Sopenharmony_ci return qeth_send_ipa_cmd(card, iob, qeth_l2_send_setdelmac_cb, NULL); 898c2ecf20Sopenharmony_ci} 908c2ecf20Sopenharmony_ci 918c2ecf20Sopenharmony_cistatic int qeth_l2_send_setmac(struct qeth_card *card, __u8 *mac) 928c2ecf20Sopenharmony_ci{ 938c2ecf20Sopenharmony_ci int rc; 948c2ecf20Sopenharmony_ci 958c2ecf20Sopenharmony_ci QETH_CARD_TEXT(card, 2, "L2Setmac"); 968c2ecf20Sopenharmony_ci rc = qeth_l2_send_setdelmac(card, mac, IPA_CMD_SETVMAC); 978c2ecf20Sopenharmony_ci if (rc == 0) { 988c2ecf20Sopenharmony_ci dev_info(&card->gdev->dev, 998c2ecf20Sopenharmony_ci "MAC address %pM successfully registered\n", mac); 1008c2ecf20Sopenharmony_ci } else { 1018c2ecf20Sopenharmony_ci switch (rc) { 1028c2ecf20Sopenharmony_ci case -EADDRINUSE: 1038c2ecf20Sopenharmony_ci dev_warn(&card->gdev->dev, 1048c2ecf20Sopenharmony_ci "MAC address %pM already exists\n", mac); 1058c2ecf20Sopenharmony_ci break; 1068c2ecf20Sopenharmony_ci case -EADDRNOTAVAIL: 1078c2ecf20Sopenharmony_ci dev_warn(&card->gdev->dev, 1088c2ecf20Sopenharmony_ci "MAC address %pM is not authorized\n", mac); 1098c2ecf20Sopenharmony_ci break; 1108c2ecf20Sopenharmony_ci } 1118c2ecf20Sopenharmony_ci } 1128c2ecf20Sopenharmony_ci return rc; 1138c2ecf20Sopenharmony_ci} 1148c2ecf20Sopenharmony_ci 1158c2ecf20Sopenharmony_cistatic int qeth_l2_write_mac(struct qeth_card *card, u8 *mac) 1168c2ecf20Sopenharmony_ci{ 1178c2ecf20Sopenharmony_ci enum qeth_ipa_cmds cmd = is_multicast_ether_addr(mac) ? 1188c2ecf20Sopenharmony_ci IPA_CMD_SETGMAC : IPA_CMD_SETVMAC; 1198c2ecf20Sopenharmony_ci int rc; 1208c2ecf20Sopenharmony_ci 1218c2ecf20Sopenharmony_ci QETH_CARD_TEXT(card, 2, "L2Wmac"); 1228c2ecf20Sopenharmony_ci rc = qeth_l2_send_setdelmac(card, mac, cmd); 1238c2ecf20Sopenharmony_ci if (rc == -EADDRINUSE) 1248c2ecf20Sopenharmony_ci QETH_DBF_MESSAGE(2, "MAC already registered on device %x\n", 1258c2ecf20Sopenharmony_ci CARD_DEVID(card)); 1268c2ecf20Sopenharmony_ci else if (rc) 1278c2ecf20Sopenharmony_ci QETH_DBF_MESSAGE(2, "Failed to register MAC on device %x: %d\n", 1288c2ecf20Sopenharmony_ci CARD_DEVID(card), rc); 1298c2ecf20Sopenharmony_ci return rc; 1308c2ecf20Sopenharmony_ci} 1318c2ecf20Sopenharmony_ci 1328c2ecf20Sopenharmony_cistatic int qeth_l2_remove_mac(struct qeth_card *card, u8 *mac) 1338c2ecf20Sopenharmony_ci{ 1348c2ecf20Sopenharmony_ci enum qeth_ipa_cmds cmd = is_multicast_ether_addr(mac) ? 1358c2ecf20Sopenharmony_ci IPA_CMD_DELGMAC : IPA_CMD_DELVMAC; 1368c2ecf20Sopenharmony_ci int rc; 1378c2ecf20Sopenharmony_ci 1388c2ecf20Sopenharmony_ci QETH_CARD_TEXT(card, 2, "L2Rmac"); 1398c2ecf20Sopenharmony_ci rc = qeth_l2_send_setdelmac(card, mac, cmd); 1408c2ecf20Sopenharmony_ci if (rc) 1418c2ecf20Sopenharmony_ci QETH_DBF_MESSAGE(2, "Failed to delete MAC on device %u: %d\n", 1428c2ecf20Sopenharmony_ci CARD_DEVID(card), rc); 1438c2ecf20Sopenharmony_ci return rc; 1448c2ecf20Sopenharmony_ci} 1458c2ecf20Sopenharmony_ci 1468c2ecf20Sopenharmony_cistatic void qeth_l2_drain_rx_mode_cache(struct qeth_card *card) 1478c2ecf20Sopenharmony_ci{ 1488c2ecf20Sopenharmony_ci struct qeth_mac *mac; 1498c2ecf20Sopenharmony_ci struct hlist_node *tmp; 1508c2ecf20Sopenharmony_ci int i; 1518c2ecf20Sopenharmony_ci 1528c2ecf20Sopenharmony_ci hash_for_each_safe(card->rx_mode_addrs, i, tmp, mac, hnode) { 1538c2ecf20Sopenharmony_ci hash_del(&mac->hnode); 1548c2ecf20Sopenharmony_ci kfree(mac); 1558c2ecf20Sopenharmony_ci } 1568c2ecf20Sopenharmony_ci} 1578c2ecf20Sopenharmony_ci 1588c2ecf20Sopenharmony_cistatic void qeth_l2_fill_header(struct qeth_qdio_out_q *queue, 1598c2ecf20Sopenharmony_ci struct qeth_hdr *hdr, struct sk_buff *skb, 1608c2ecf20Sopenharmony_ci int ipv, unsigned int data_len) 1618c2ecf20Sopenharmony_ci{ 1628c2ecf20Sopenharmony_ci int cast_type = qeth_get_ether_cast_type(skb); 1638c2ecf20Sopenharmony_ci struct vlan_ethhdr *veth = vlan_eth_hdr(skb); 1648c2ecf20Sopenharmony_ci 1658c2ecf20Sopenharmony_ci hdr->hdr.l2.pkt_length = data_len; 1668c2ecf20Sopenharmony_ci 1678c2ecf20Sopenharmony_ci if (skb_is_gso(skb)) { 1688c2ecf20Sopenharmony_ci hdr->hdr.l2.id = QETH_HEADER_TYPE_L2_TSO; 1698c2ecf20Sopenharmony_ci } else { 1708c2ecf20Sopenharmony_ci hdr->hdr.l2.id = QETH_HEADER_TYPE_LAYER2; 1718c2ecf20Sopenharmony_ci if (skb->ip_summed == CHECKSUM_PARTIAL) 1728c2ecf20Sopenharmony_ci qeth_tx_csum(skb, &hdr->hdr.l2.flags[1], ipv); 1738c2ecf20Sopenharmony_ci } 1748c2ecf20Sopenharmony_ci 1758c2ecf20Sopenharmony_ci /* set byte byte 3 to casting flags */ 1768c2ecf20Sopenharmony_ci if (cast_type == RTN_MULTICAST) 1778c2ecf20Sopenharmony_ci hdr->hdr.l2.flags[2] |= QETH_LAYER2_FLAG_MULTICAST; 1788c2ecf20Sopenharmony_ci else if (cast_type == RTN_BROADCAST) 1798c2ecf20Sopenharmony_ci hdr->hdr.l2.flags[2] |= QETH_LAYER2_FLAG_BROADCAST; 1808c2ecf20Sopenharmony_ci else 1818c2ecf20Sopenharmony_ci hdr->hdr.l2.flags[2] |= QETH_LAYER2_FLAG_UNICAST; 1828c2ecf20Sopenharmony_ci 1838c2ecf20Sopenharmony_ci /* VSWITCH relies on the VLAN 1848c2ecf20Sopenharmony_ci * information to be present in 1858c2ecf20Sopenharmony_ci * the QDIO header */ 1868c2ecf20Sopenharmony_ci if (veth->h_vlan_proto == htons(ETH_P_8021Q)) { 1878c2ecf20Sopenharmony_ci hdr->hdr.l2.flags[2] |= QETH_LAYER2_FLAG_VLAN; 1888c2ecf20Sopenharmony_ci hdr->hdr.l2.vlan_id = ntohs(veth->h_vlan_TCI); 1898c2ecf20Sopenharmony_ci } 1908c2ecf20Sopenharmony_ci} 1918c2ecf20Sopenharmony_ci 1928c2ecf20Sopenharmony_cistatic int qeth_l2_setdelvlan_makerc(struct qeth_card *card, u16 retcode) 1938c2ecf20Sopenharmony_ci{ 1948c2ecf20Sopenharmony_ci if (retcode) 1958c2ecf20Sopenharmony_ci QETH_CARD_TEXT_(card, 2, "err%04x", retcode); 1968c2ecf20Sopenharmony_ci 1978c2ecf20Sopenharmony_ci switch (retcode) { 1988c2ecf20Sopenharmony_ci case IPA_RC_SUCCESS: 1998c2ecf20Sopenharmony_ci return 0; 2008c2ecf20Sopenharmony_ci case IPA_RC_L2_INVALID_VLAN_ID: 2018c2ecf20Sopenharmony_ci return -EINVAL; 2028c2ecf20Sopenharmony_ci case IPA_RC_L2_DUP_VLAN_ID: 2038c2ecf20Sopenharmony_ci return -EEXIST; 2048c2ecf20Sopenharmony_ci case IPA_RC_L2_VLAN_ID_NOT_FOUND: 2058c2ecf20Sopenharmony_ci return -ENOENT; 2068c2ecf20Sopenharmony_ci case IPA_RC_L2_VLAN_ID_NOT_ALLOWED: 2078c2ecf20Sopenharmony_ci return -EPERM; 2088c2ecf20Sopenharmony_ci default: 2098c2ecf20Sopenharmony_ci return -EIO; 2108c2ecf20Sopenharmony_ci } 2118c2ecf20Sopenharmony_ci} 2128c2ecf20Sopenharmony_ci 2138c2ecf20Sopenharmony_cistatic int qeth_l2_send_setdelvlan_cb(struct qeth_card *card, 2148c2ecf20Sopenharmony_ci struct qeth_reply *reply, 2158c2ecf20Sopenharmony_ci unsigned long data) 2168c2ecf20Sopenharmony_ci{ 2178c2ecf20Sopenharmony_ci struct qeth_ipa_cmd *cmd = (struct qeth_ipa_cmd *) data; 2188c2ecf20Sopenharmony_ci 2198c2ecf20Sopenharmony_ci QETH_CARD_TEXT(card, 2, "L2sdvcb"); 2208c2ecf20Sopenharmony_ci if (cmd->hdr.return_code) { 2218c2ecf20Sopenharmony_ci QETH_DBF_MESSAGE(2, "Error in processing VLAN %u on device %x: %#x.\n", 2228c2ecf20Sopenharmony_ci cmd->data.setdelvlan.vlan_id, 2238c2ecf20Sopenharmony_ci CARD_DEVID(card), cmd->hdr.return_code); 2248c2ecf20Sopenharmony_ci QETH_CARD_TEXT_(card, 2, "L2VL%4x", cmd->hdr.command); 2258c2ecf20Sopenharmony_ci } 2268c2ecf20Sopenharmony_ci return qeth_l2_setdelvlan_makerc(card, cmd->hdr.return_code); 2278c2ecf20Sopenharmony_ci} 2288c2ecf20Sopenharmony_ci 2298c2ecf20Sopenharmony_cistatic int qeth_l2_send_setdelvlan(struct qeth_card *card, __u16 i, 2308c2ecf20Sopenharmony_ci enum qeth_ipa_cmds ipacmd) 2318c2ecf20Sopenharmony_ci{ 2328c2ecf20Sopenharmony_ci struct qeth_ipa_cmd *cmd; 2338c2ecf20Sopenharmony_ci struct qeth_cmd_buffer *iob; 2348c2ecf20Sopenharmony_ci 2358c2ecf20Sopenharmony_ci QETH_CARD_TEXT_(card, 4, "L2sdv%x", ipacmd); 2368c2ecf20Sopenharmony_ci iob = qeth_ipa_alloc_cmd(card, ipacmd, QETH_PROT_IPV4, 2378c2ecf20Sopenharmony_ci IPA_DATA_SIZEOF(setdelvlan)); 2388c2ecf20Sopenharmony_ci if (!iob) 2398c2ecf20Sopenharmony_ci return -ENOMEM; 2408c2ecf20Sopenharmony_ci cmd = __ipa_cmd(iob); 2418c2ecf20Sopenharmony_ci cmd->data.setdelvlan.vlan_id = i; 2428c2ecf20Sopenharmony_ci return qeth_send_ipa_cmd(card, iob, qeth_l2_send_setdelvlan_cb, NULL); 2438c2ecf20Sopenharmony_ci} 2448c2ecf20Sopenharmony_ci 2458c2ecf20Sopenharmony_cistatic int qeth_l2_vlan_rx_add_vid(struct net_device *dev, 2468c2ecf20Sopenharmony_ci __be16 proto, u16 vid) 2478c2ecf20Sopenharmony_ci{ 2488c2ecf20Sopenharmony_ci struct qeth_card *card = dev->ml_priv; 2498c2ecf20Sopenharmony_ci 2508c2ecf20Sopenharmony_ci QETH_CARD_TEXT_(card, 4, "aid:%d", vid); 2518c2ecf20Sopenharmony_ci if (!vid) 2528c2ecf20Sopenharmony_ci return 0; 2538c2ecf20Sopenharmony_ci 2548c2ecf20Sopenharmony_ci return qeth_l2_send_setdelvlan(card, vid, IPA_CMD_SETVLAN); 2558c2ecf20Sopenharmony_ci} 2568c2ecf20Sopenharmony_ci 2578c2ecf20Sopenharmony_cistatic int qeth_l2_vlan_rx_kill_vid(struct net_device *dev, 2588c2ecf20Sopenharmony_ci __be16 proto, u16 vid) 2598c2ecf20Sopenharmony_ci{ 2608c2ecf20Sopenharmony_ci struct qeth_card *card = dev->ml_priv; 2618c2ecf20Sopenharmony_ci 2628c2ecf20Sopenharmony_ci QETH_CARD_TEXT_(card, 4, "kid:%d", vid); 2638c2ecf20Sopenharmony_ci if (!vid) 2648c2ecf20Sopenharmony_ci return 0; 2658c2ecf20Sopenharmony_ci 2668c2ecf20Sopenharmony_ci return qeth_l2_send_setdelvlan(card, vid, IPA_CMD_DELVLAN); 2678c2ecf20Sopenharmony_ci} 2688c2ecf20Sopenharmony_ci 2698c2ecf20Sopenharmony_cistatic void qeth_l2_set_pnso_mode(struct qeth_card *card, 2708c2ecf20Sopenharmony_ci enum qeth_pnso_mode mode) 2718c2ecf20Sopenharmony_ci{ 2728c2ecf20Sopenharmony_ci spin_lock_irq(get_ccwdev_lock(CARD_RDEV(card))); 2738c2ecf20Sopenharmony_ci WRITE_ONCE(card->info.pnso_mode, mode); 2748c2ecf20Sopenharmony_ci spin_unlock_irq(get_ccwdev_lock(CARD_RDEV(card))); 2758c2ecf20Sopenharmony_ci 2768c2ecf20Sopenharmony_ci if (mode == QETH_PNSO_NONE) 2778c2ecf20Sopenharmony_ci drain_workqueue(card->event_wq); 2788c2ecf20Sopenharmony_ci} 2798c2ecf20Sopenharmony_ci 2808c2ecf20Sopenharmony_cistatic void qeth_l2_dev2br_fdb_flush(struct qeth_card *card) 2818c2ecf20Sopenharmony_ci{ 2828c2ecf20Sopenharmony_ci struct switchdev_notifier_fdb_info info; 2838c2ecf20Sopenharmony_ci 2848c2ecf20Sopenharmony_ci QETH_CARD_TEXT(card, 2, "fdbflush"); 2858c2ecf20Sopenharmony_ci 2868c2ecf20Sopenharmony_ci info.addr = NULL; 2878c2ecf20Sopenharmony_ci /* flush all VLANs: */ 2888c2ecf20Sopenharmony_ci info.vid = 0; 2898c2ecf20Sopenharmony_ci info.added_by_user = false; 2908c2ecf20Sopenharmony_ci info.offloaded = true; 2918c2ecf20Sopenharmony_ci 2928c2ecf20Sopenharmony_ci call_switchdev_notifiers(SWITCHDEV_FDB_FLUSH_TO_BRIDGE, 2938c2ecf20Sopenharmony_ci card->dev, &info.info, NULL); 2948c2ecf20Sopenharmony_ci} 2958c2ecf20Sopenharmony_ci 2968c2ecf20Sopenharmony_cistatic int qeth_l2_request_initial_mac(struct qeth_card *card) 2978c2ecf20Sopenharmony_ci{ 2988c2ecf20Sopenharmony_ci int rc = 0; 2998c2ecf20Sopenharmony_ci 3008c2ecf20Sopenharmony_ci QETH_CARD_TEXT(card, 2, "l2reqmac"); 3018c2ecf20Sopenharmony_ci 3028c2ecf20Sopenharmony_ci if (MACHINE_IS_VM) { 3038c2ecf20Sopenharmony_ci rc = qeth_vm_request_mac(card); 3048c2ecf20Sopenharmony_ci if (!rc) 3058c2ecf20Sopenharmony_ci goto out; 3068c2ecf20Sopenharmony_ci QETH_DBF_MESSAGE(2, "z/VM MAC Service failed on device %x: %#x\n", 3078c2ecf20Sopenharmony_ci CARD_DEVID(card), rc); 3088c2ecf20Sopenharmony_ci QETH_CARD_TEXT_(card, 2, "err%04x", rc); 3098c2ecf20Sopenharmony_ci /* fall back to alternative mechanism: */ 3108c2ecf20Sopenharmony_ci } 3118c2ecf20Sopenharmony_ci 3128c2ecf20Sopenharmony_ci if (!IS_OSN(card)) { 3138c2ecf20Sopenharmony_ci rc = qeth_setadpparms_change_macaddr(card); 3148c2ecf20Sopenharmony_ci if (!rc) 3158c2ecf20Sopenharmony_ci goto out; 3168c2ecf20Sopenharmony_ci QETH_DBF_MESSAGE(2, "READ_MAC Assist failed on device %x: %#x\n", 3178c2ecf20Sopenharmony_ci CARD_DEVID(card), rc); 3188c2ecf20Sopenharmony_ci QETH_CARD_TEXT_(card, 2, "1err%04x", rc); 3198c2ecf20Sopenharmony_ci /* fall back once more: */ 3208c2ecf20Sopenharmony_ci } 3218c2ecf20Sopenharmony_ci 3228c2ecf20Sopenharmony_ci /* some devices don't support a custom MAC address: */ 3238c2ecf20Sopenharmony_ci if (IS_OSM(card) || IS_OSX(card)) 3248c2ecf20Sopenharmony_ci return (rc) ? rc : -EADDRNOTAVAIL; 3258c2ecf20Sopenharmony_ci eth_hw_addr_random(card->dev); 3268c2ecf20Sopenharmony_ci 3278c2ecf20Sopenharmony_ciout: 3288c2ecf20Sopenharmony_ci QETH_CARD_HEX(card, 2, card->dev->dev_addr, card->dev->addr_len); 3298c2ecf20Sopenharmony_ci return 0; 3308c2ecf20Sopenharmony_ci} 3318c2ecf20Sopenharmony_ci 3328c2ecf20Sopenharmony_cistatic void qeth_l2_register_dev_addr(struct qeth_card *card) 3338c2ecf20Sopenharmony_ci{ 3348c2ecf20Sopenharmony_ci if (!is_valid_ether_addr(card->dev->dev_addr)) 3358c2ecf20Sopenharmony_ci qeth_l2_request_initial_mac(card); 3368c2ecf20Sopenharmony_ci 3378c2ecf20Sopenharmony_ci if (!IS_OSN(card) && !qeth_l2_send_setmac(card, card->dev->dev_addr)) 3388c2ecf20Sopenharmony_ci card->info.dev_addr_is_registered = 1; 3398c2ecf20Sopenharmony_ci else 3408c2ecf20Sopenharmony_ci card->info.dev_addr_is_registered = 0; 3418c2ecf20Sopenharmony_ci} 3428c2ecf20Sopenharmony_ci 3438c2ecf20Sopenharmony_cistatic int qeth_l2_validate_addr(struct net_device *dev) 3448c2ecf20Sopenharmony_ci{ 3458c2ecf20Sopenharmony_ci struct qeth_card *card = dev->ml_priv; 3468c2ecf20Sopenharmony_ci 3478c2ecf20Sopenharmony_ci if (card->info.dev_addr_is_registered) 3488c2ecf20Sopenharmony_ci return eth_validate_addr(dev); 3498c2ecf20Sopenharmony_ci 3508c2ecf20Sopenharmony_ci QETH_CARD_TEXT(card, 4, "nomacadr"); 3518c2ecf20Sopenharmony_ci return -EPERM; 3528c2ecf20Sopenharmony_ci} 3538c2ecf20Sopenharmony_ci 3548c2ecf20Sopenharmony_cistatic int qeth_l2_set_mac_address(struct net_device *dev, void *p) 3558c2ecf20Sopenharmony_ci{ 3568c2ecf20Sopenharmony_ci struct sockaddr *addr = p; 3578c2ecf20Sopenharmony_ci struct qeth_card *card = dev->ml_priv; 3588c2ecf20Sopenharmony_ci u8 old_addr[ETH_ALEN]; 3598c2ecf20Sopenharmony_ci int rc = 0; 3608c2ecf20Sopenharmony_ci 3618c2ecf20Sopenharmony_ci QETH_CARD_TEXT(card, 3, "setmac"); 3628c2ecf20Sopenharmony_ci 3638c2ecf20Sopenharmony_ci if (IS_OSM(card) || IS_OSX(card)) { 3648c2ecf20Sopenharmony_ci QETH_CARD_TEXT(card, 3, "setmcTYP"); 3658c2ecf20Sopenharmony_ci return -EOPNOTSUPP; 3668c2ecf20Sopenharmony_ci } 3678c2ecf20Sopenharmony_ci QETH_CARD_HEX(card, 3, addr->sa_data, ETH_ALEN); 3688c2ecf20Sopenharmony_ci if (!is_valid_ether_addr(addr->sa_data)) 3698c2ecf20Sopenharmony_ci return -EADDRNOTAVAIL; 3708c2ecf20Sopenharmony_ci 3718c2ecf20Sopenharmony_ci /* don't register the same address twice */ 3728c2ecf20Sopenharmony_ci if (ether_addr_equal_64bits(dev->dev_addr, addr->sa_data) && 3738c2ecf20Sopenharmony_ci card->info.dev_addr_is_registered) 3748c2ecf20Sopenharmony_ci return 0; 3758c2ecf20Sopenharmony_ci 3768c2ecf20Sopenharmony_ci /* add the new address, switch over, drop the old */ 3778c2ecf20Sopenharmony_ci rc = qeth_l2_send_setmac(card, addr->sa_data); 3788c2ecf20Sopenharmony_ci if (rc) 3798c2ecf20Sopenharmony_ci return rc; 3808c2ecf20Sopenharmony_ci ether_addr_copy(old_addr, dev->dev_addr); 3818c2ecf20Sopenharmony_ci ether_addr_copy(dev->dev_addr, addr->sa_data); 3828c2ecf20Sopenharmony_ci 3838c2ecf20Sopenharmony_ci if (card->info.dev_addr_is_registered) 3848c2ecf20Sopenharmony_ci qeth_l2_remove_mac(card, old_addr); 3858c2ecf20Sopenharmony_ci card->info.dev_addr_is_registered = 1; 3868c2ecf20Sopenharmony_ci return 0; 3878c2ecf20Sopenharmony_ci} 3888c2ecf20Sopenharmony_ci 3898c2ecf20Sopenharmony_cistatic void qeth_l2_promisc_to_bridge(struct qeth_card *card, bool enable) 3908c2ecf20Sopenharmony_ci{ 3918c2ecf20Sopenharmony_ci int role; 3928c2ecf20Sopenharmony_ci int rc; 3938c2ecf20Sopenharmony_ci 3948c2ecf20Sopenharmony_ci QETH_CARD_TEXT(card, 3, "pmisc2br"); 3958c2ecf20Sopenharmony_ci 3968c2ecf20Sopenharmony_ci if (enable) { 3978c2ecf20Sopenharmony_ci if (card->options.sbp.reflect_promisc_primary) 3988c2ecf20Sopenharmony_ci role = QETH_SBP_ROLE_PRIMARY; 3998c2ecf20Sopenharmony_ci else 4008c2ecf20Sopenharmony_ci role = QETH_SBP_ROLE_SECONDARY; 4018c2ecf20Sopenharmony_ci } else 4028c2ecf20Sopenharmony_ci role = QETH_SBP_ROLE_NONE; 4038c2ecf20Sopenharmony_ci 4048c2ecf20Sopenharmony_ci rc = qeth_bridgeport_setrole(card, role); 4058c2ecf20Sopenharmony_ci QETH_CARD_TEXT_(card, 2, "bpm%c%04x", enable ? '+' : '-', rc); 4068c2ecf20Sopenharmony_ci if (!rc) { 4078c2ecf20Sopenharmony_ci card->options.sbp.role = role; 4088c2ecf20Sopenharmony_ci card->info.promisc_mode = enable; 4098c2ecf20Sopenharmony_ci } 4108c2ecf20Sopenharmony_ci} 4118c2ecf20Sopenharmony_ci 4128c2ecf20Sopenharmony_cistatic void qeth_l2_set_promisc_mode(struct qeth_card *card) 4138c2ecf20Sopenharmony_ci{ 4148c2ecf20Sopenharmony_ci bool enable = card->dev->flags & IFF_PROMISC; 4158c2ecf20Sopenharmony_ci 4168c2ecf20Sopenharmony_ci if (card->info.promisc_mode == enable) 4178c2ecf20Sopenharmony_ci return; 4188c2ecf20Sopenharmony_ci 4198c2ecf20Sopenharmony_ci if (qeth_adp_supported(card, IPA_SETADP_SET_PROMISC_MODE)) { 4208c2ecf20Sopenharmony_ci qeth_setadp_promisc_mode(card, enable); 4218c2ecf20Sopenharmony_ci } else { 4228c2ecf20Sopenharmony_ci mutex_lock(&card->sbp_lock); 4238c2ecf20Sopenharmony_ci if (card->options.sbp.reflect_promisc) 4248c2ecf20Sopenharmony_ci qeth_l2_promisc_to_bridge(card, enable); 4258c2ecf20Sopenharmony_ci mutex_unlock(&card->sbp_lock); 4268c2ecf20Sopenharmony_ci } 4278c2ecf20Sopenharmony_ci} 4288c2ecf20Sopenharmony_ci 4298c2ecf20Sopenharmony_ci/* New MAC address is added to the hash table and marked to be written on card 4308c2ecf20Sopenharmony_ci * only if there is not in the hash table storage already 4318c2ecf20Sopenharmony_ci * 4328c2ecf20Sopenharmony_ci*/ 4338c2ecf20Sopenharmony_cistatic void qeth_l2_add_mac(struct qeth_card *card, struct netdev_hw_addr *ha) 4348c2ecf20Sopenharmony_ci{ 4358c2ecf20Sopenharmony_ci u32 mac_hash = get_unaligned((u32 *)(&ha->addr[2])); 4368c2ecf20Sopenharmony_ci struct qeth_mac *mac; 4378c2ecf20Sopenharmony_ci 4388c2ecf20Sopenharmony_ci hash_for_each_possible(card->rx_mode_addrs, mac, hnode, mac_hash) { 4398c2ecf20Sopenharmony_ci if (ether_addr_equal_64bits(ha->addr, mac->mac_addr)) { 4408c2ecf20Sopenharmony_ci mac->disp_flag = QETH_DISP_ADDR_DO_NOTHING; 4418c2ecf20Sopenharmony_ci return; 4428c2ecf20Sopenharmony_ci } 4438c2ecf20Sopenharmony_ci } 4448c2ecf20Sopenharmony_ci 4458c2ecf20Sopenharmony_ci mac = kzalloc(sizeof(struct qeth_mac), GFP_ATOMIC); 4468c2ecf20Sopenharmony_ci if (!mac) 4478c2ecf20Sopenharmony_ci return; 4488c2ecf20Sopenharmony_ci 4498c2ecf20Sopenharmony_ci ether_addr_copy(mac->mac_addr, ha->addr); 4508c2ecf20Sopenharmony_ci mac->disp_flag = QETH_DISP_ADDR_ADD; 4518c2ecf20Sopenharmony_ci 4528c2ecf20Sopenharmony_ci hash_add(card->rx_mode_addrs, &mac->hnode, mac_hash); 4538c2ecf20Sopenharmony_ci} 4548c2ecf20Sopenharmony_ci 4558c2ecf20Sopenharmony_cistatic void qeth_l2_rx_mode_work(struct work_struct *work) 4568c2ecf20Sopenharmony_ci{ 4578c2ecf20Sopenharmony_ci struct qeth_card *card = container_of(work, struct qeth_card, 4588c2ecf20Sopenharmony_ci rx_mode_work); 4598c2ecf20Sopenharmony_ci struct net_device *dev = card->dev; 4608c2ecf20Sopenharmony_ci struct netdev_hw_addr *ha; 4618c2ecf20Sopenharmony_ci struct qeth_mac *mac; 4628c2ecf20Sopenharmony_ci struct hlist_node *tmp; 4638c2ecf20Sopenharmony_ci int i; 4648c2ecf20Sopenharmony_ci int rc; 4658c2ecf20Sopenharmony_ci 4668c2ecf20Sopenharmony_ci QETH_CARD_TEXT(card, 3, "setmulti"); 4678c2ecf20Sopenharmony_ci 4688c2ecf20Sopenharmony_ci netif_addr_lock_bh(dev); 4698c2ecf20Sopenharmony_ci netdev_for_each_mc_addr(ha, dev) 4708c2ecf20Sopenharmony_ci qeth_l2_add_mac(card, ha); 4718c2ecf20Sopenharmony_ci netdev_for_each_uc_addr(ha, dev) 4728c2ecf20Sopenharmony_ci qeth_l2_add_mac(card, ha); 4738c2ecf20Sopenharmony_ci netif_addr_unlock_bh(dev); 4748c2ecf20Sopenharmony_ci 4758c2ecf20Sopenharmony_ci hash_for_each_safe(card->rx_mode_addrs, i, tmp, mac, hnode) { 4768c2ecf20Sopenharmony_ci switch (mac->disp_flag) { 4778c2ecf20Sopenharmony_ci case QETH_DISP_ADDR_DELETE: 4788c2ecf20Sopenharmony_ci qeth_l2_remove_mac(card, mac->mac_addr); 4798c2ecf20Sopenharmony_ci hash_del(&mac->hnode); 4808c2ecf20Sopenharmony_ci kfree(mac); 4818c2ecf20Sopenharmony_ci break; 4828c2ecf20Sopenharmony_ci case QETH_DISP_ADDR_ADD: 4838c2ecf20Sopenharmony_ci rc = qeth_l2_write_mac(card, mac->mac_addr); 4848c2ecf20Sopenharmony_ci if (rc) { 4858c2ecf20Sopenharmony_ci hash_del(&mac->hnode); 4868c2ecf20Sopenharmony_ci kfree(mac); 4878c2ecf20Sopenharmony_ci break; 4888c2ecf20Sopenharmony_ci } 4898c2ecf20Sopenharmony_ci fallthrough; 4908c2ecf20Sopenharmony_ci default: 4918c2ecf20Sopenharmony_ci /* for next call to set_rx_mode(): */ 4928c2ecf20Sopenharmony_ci mac->disp_flag = QETH_DISP_ADDR_DELETE; 4938c2ecf20Sopenharmony_ci } 4948c2ecf20Sopenharmony_ci } 4958c2ecf20Sopenharmony_ci 4968c2ecf20Sopenharmony_ci qeth_l2_set_promisc_mode(card); 4978c2ecf20Sopenharmony_ci} 4988c2ecf20Sopenharmony_ci 4998c2ecf20Sopenharmony_cistatic int qeth_l2_xmit_osn(struct qeth_card *card, struct sk_buff *skb, 5008c2ecf20Sopenharmony_ci struct qeth_qdio_out_q *queue) 5018c2ecf20Sopenharmony_ci{ 5028c2ecf20Sopenharmony_ci gfp_t gfp = GFP_ATOMIC | (skb_pfmemalloc(skb) ? __GFP_MEMALLOC : 0); 5038c2ecf20Sopenharmony_ci struct qeth_hdr *hdr = (struct qeth_hdr *)skb->data; 5048c2ecf20Sopenharmony_ci addr_t end = (addr_t)(skb->data + sizeof(*hdr)); 5058c2ecf20Sopenharmony_ci addr_t start = (addr_t)skb->data; 5068c2ecf20Sopenharmony_ci unsigned int elements = 0; 5078c2ecf20Sopenharmony_ci unsigned int hd_len = 0; 5088c2ecf20Sopenharmony_ci int rc; 5098c2ecf20Sopenharmony_ci 5108c2ecf20Sopenharmony_ci if (skb->protocol == htons(ETH_P_IPV6)) 5118c2ecf20Sopenharmony_ci return -EPROTONOSUPPORT; 5128c2ecf20Sopenharmony_ci 5138c2ecf20Sopenharmony_ci if (qeth_get_elements_for_range(start, end) > 1) { 5148c2ecf20Sopenharmony_ci /* Misaligned HW header, move it to its own buffer element. */ 5158c2ecf20Sopenharmony_ci hdr = kmem_cache_alloc(qeth_core_header_cache, gfp); 5168c2ecf20Sopenharmony_ci if (!hdr) 5178c2ecf20Sopenharmony_ci return -ENOMEM; 5188c2ecf20Sopenharmony_ci hd_len = sizeof(*hdr); 5198c2ecf20Sopenharmony_ci skb_copy_from_linear_data(skb, (char *)hdr, hd_len); 5208c2ecf20Sopenharmony_ci elements++; 5218c2ecf20Sopenharmony_ci } 5228c2ecf20Sopenharmony_ci 5238c2ecf20Sopenharmony_ci elements += qeth_count_elements(skb, hd_len); 5248c2ecf20Sopenharmony_ci if (elements > queue->max_elements) { 5258c2ecf20Sopenharmony_ci rc = -E2BIG; 5268c2ecf20Sopenharmony_ci goto out; 5278c2ecf20Sopenharmony_ci } 5288c2ecf20Sopenharmony_ci 5298c2ecf20Sopenharmony_ci rc = qeth_do_send_packet(card, queue, skb, hdr, hd_len, hd_len, 5308c2ecf20Sopenharmony_ci elements); 5318c2ecf20Sopenharmony_ciout: 5328c2ecf20Sopenharmony_ci if (rc && hd_len) 5338c2ecf20Sopenharmony_ci kmem_cache_free(qeth_core_header_cache, hdr); 5348c2ecf20Sopenharmony_ci return rc; 5358c2ecf20Sopenharmony_ci} 5368c2ecf20Sopenharmony_ci 5378c2ecf20Sopenharmony_cistatic netdev_tx_t qeth_l2_hard_start_xmit(struct sk_buff *skb, 5388c2ecf20Sopenharmony_ci struct net_device *dev) 5398c2ecf20Sopenharmony_ci{ 5408c2ecf20Sopenharmony_ci struct qeth_card *card = dev->ml_priv; 5418c2ecf20Sopenharmony_ci u16 txq = skb_get_queue_mapping(skb); 5428c2ecf20Sopenharmony_ci struct qeth_qdio_out_q *queue; 5438c2ecf20Sopenharmony_ci int rc; 5448c2ecf20Sopenharmony_ci 5458c2ecf20Sopenharmony_ci if (!skb_is_gso(skb)) 5468c2ecf20Sopenharmony_ci qdisc_skb_cb(skb)->pkt_len = skb->len; 5478c2ecf20Sopenharmony_ci if (IS_IQD(card)) 5488c2ecf20Sopenharmony_ci txq = qeth_iqd_translate_txq(dev, txq); 5498c2ecf20Sopenharmony_ci queue = card->qdio.out_qs[txq]; 5508c2ecf20Sopenharmony_ci 5518c2ecf20Sopenharmony_ci if (IS_OSN(card)) 5528c2ecf20Sopenharmony_ci rc = qeth_l2_xmit_osn(card, skb, queue); 5538c2ecf20Sopenharmony_ci else 5548c2ecf20Sopenharmony_ci rc = qeth_xmit(card, skb, queue, qeth_get_ip_version(skb), 5558c2ecf20Sopenharmony_ci qeth_l2_fill_header); 5568c2ecf20Sopenharmony_ci 5578c2ecf20Sopenharmony_ci if (!rc) 5588c2ecf20Sopenharmony_ci return NETDEV_TX_OK; 5598c2ecf20Sopenharmony_ci 5608c2ecf20Sopenharmony_ci QETH_TXQ_STAT_INC(queue, tx_dropped); 5618c2ecf20Sopenharmony_ci kfree_skb(skb); 5628c2ecf20Sopenharmony_ci return NETDEV_TX_OK; 5638c2ecf20Sopenharmony_ci} 5648c2ecf20Sopenharmony_ci 5658c2ecf20Sopenharmony_cistatic u16 qeth_l2_select_queue(struct net_device *dev, struct sk_buff *skb, 5668c2ecf20Sopenharmony_ci struct net_device *sb_dev) 5678c2ecf20Sopenharmony_ci{ 5688c2ecf20Sopenharmony_ci struct qeth_card *card = dev->ml_priv; 5698c2ecf20Sopenharmony_ci 5708c2ecf20Sopenharmony_ci if (IS_IQD(card)) 5718c2ecf20Sopenharmony_ci return qeth_iqd_select_queue(dev, skb, 5728c2ecf20Sopenharmony_ci qeth_get_ether_cast_type(skb), 5738c2ecf20Sopenharmony_ci sb_dev); 5748c2ecf20Sopenharmony_ci if (qeth_uses_tx_prio_queueing(card)) 5758c2ecf20Sopenharmony_ci return qeth_get_priority_queue(card, skb); 5768c2ecf20Sopenharmony_ci 5778c2ecf20Sopenharmony_ci return netdev_pick_tx(dev, skb, sb_dev); 5788c2ecf20Sopenharmony_ci} 5798c2ecf20Sopenharmony_ci 5808c2ecf20Sopenharmony_cistatic void qeth_l2_set_rx_mode(struct net_device *dev) 5818c2ecf20Sopenharmony_ci{ 5828c2ecf20Sopenharmony_ci struct qeth_card *card = dev->ml_priv; 5838c2ecf20Sopenharmony_ci 5848c2ecf20Sopenharmony_ci schedule_work(&card->rx_mode_work); 5858c2ecf20Sopenharmony_ci} 5868c2ecf20Sopenharmony_ci 5878c2ecf20Sopenharmony_ci/** 5888c2ecf20Sopenharmony_ci * qeth_l2_pnso() - perform network subchannel operation 5898c2ecf20Sopenharmony_ci * @card: qeth_card structure pointer 5908c2ecf20Sopenharmony_ci * @oc: Operation Code 5918c2ecf20Sopenharmony_ci * @cnc: Boolean Change-Notification Control 5928c2ecf20Sopenharmony_ci * @cb: Callback function will be executed for each element 5938c2ecf20Sopenharmony_ci * of the address list 5948c2ecf20Sopenharmony_ci * @priv: Pointer to pass to the callback function. 5958c2ecf20Sopenharmony_ci * 5968c2ecf20Sopenharmony_ci * Collects network information in a network address list and calls the 5978c2ecf20Sopenharmony_ci * callback function for every entry in the list. If "change-notification- 5988c2ecf20Sopenharmony_ci * control" is set, further changes in the address list will be reported 5998c2ecf20Sopenharmony_ci * via the IPA command. 6008c2ecf20Sopenharmony_ci */ 6018c2ecf20Sopenharmony_cistatic int qeth_l2_pnso(struct qeth_card *card, u8 oc, int cnc, 6028c2ecf20Sopenharmony_ci void (*cb)(void *priv, struct chsc_pnso_naid_l2 *entry), 6038c2ecf20Sopenharmony_ci void *priv) 6048c2ecf20Sopenharmony_ci{ 6058c2ecf20Sopenharmony_ci struct ccw_device *ddev = CARD_DDEV(card); 6068c2ecf20Sopenharmony_ci struct chsc_pnso_area *rr; 6078c2ecf20Sopenharmony_ci u32 prev_instance = 0; 6088c2ecf20Sopenharmony_ci int isfirstblock = 1; 6098c2ecf20Sopenharmony_ci int i, size, elems; 6108c2ecf20Sopenharmony_ci int rc; 6118c2ecf20Sopenharmony_ci 6128c2ecf20Sopenharmony_ci rr = (struct chsc_pnso_area *)get_zeroed_page(GFP_KERNEL); 6138c2ecf20Sopenharmony_ci if (rr == NULL) 6148c2ecf20Sopenharmony_ci return -ENOMEM; 6158c2ecf20Sopenharmony_ci do { 6168c2ecf20Sopenharmony_ci QETH_CARD_TEXT(card, 2, "PNSO"); 6178c2ecf20Sopenharmony_ci /* on the first iteration, naihdr.resume_token will be zero */ 6188c2ecf20Sopenharmony_ci rc = ccw_device_pnso(ddev, rr, oc, rr->naihdr.resume_token, 6198c2ecf20Sopenharmony_ci cnc); 6208c2ecf20Sopenharmony_ci if (rc) 6218c2ecf20Sopenharmony_ci continue; 6228c2ecf20Sopenharmony_ci if (cb == NULL) 6238c2ecf20Sopenharmony_ci continue; 6248c2ecf20Sopenharmony_ci 6258c2ecf20Sopenharmony_ci size = rr->naihdr.naids; 6268c2ecf20Sopenharmony_ci if (size != sizeof(struct chsc_pnso_naid_l2)) { 6278c2ecf20Sopenharmony_ci WARN_ON_ONCE(1); 6288c2ecf20Sopenharmony_ci continue; 6298c2ecf20Sopenharmony_ci } 6308c2ecf20Sopenharmony_ci 6318c2ecf20Sopenharmony_ci elems = (rr->response.length - sizeof(struct chsc_header) - 6328c2ecf20Sopenharmony_ci sizeof(struct chsc_pnso_naihdr)) / size; 6338c2ecf20Sopenharmony_ci 6348c2ecf20Sopenharmony_ci if (!isfirstblock && (rr->naihdr.instance != prev_instance)) { 6358c2ecf20Sopenharmony_ci /* Inform the caller that they need to scrap */ 6368c2ecf20Sopenharmony_ci /* the data that was already reported via cb */ 6378c2ecf20Sopenharmony_ci rc = -EAGAIN; 6388c2ecf20Sopenharmony_ci break; 6398c2ecf20Sopenharmony_ci } 6408c2ecf20Sopenharmony_ci isfirstblock = 0; 6418c2ecf20Sopenharmony_ci prev_instance = rr->naihdr.instance; 6428c2ecf20Sopenharmony_ci for (i = 0; i < elems; i++) 6438c2ecf20Sopenharmony_ci (*cb)(priv, &rr->entries[i]); 6448c2ecf20Sopenharmony_ci } while ((rc == -EBUSY) || (!rc && /* list stored */ 6458c2ecf20Sopenharmony_ci /* resume token is non-zero => list incomplete */ 6468c2ecf20Sopenharmony_ci (rr->naihdr.resume_token.t1 || rr->naihdr.resume_token.t2))); 6478c2ecf20Sopenharmony_ci 6488c2ecf20Sopenharmony_ci if (rc) 6498c2ecf20Sopenharmony_ci QETH_CARD_TEXT_(card, 2, "PNrp%04x", rr->response.code); 6508c2ecf20Sopenharmony_ci 6518c2ecf20Sopenharmony_ci free_page((unsigned long)rr); 6528c2ecf20Sopenharmony_ci return rc; 6538c2ecf20Sopenharmony_ci} 6548c2ecf20Sopenharmony_ci 6558c2ecf20Sopenharmony_cistatic bool qeth_is_my_net_if_token(struct qeth_card *card, 6568c2ecf20Sopenharmony_ci struct net_if_token *token) 6578c2ecf20Sopenharmony_ci{ 6588c2ecf20Sopenharmony_ci return ((card->info.ddev_devno == token->devnum) && 6598c2ecf20Sopenharmony_ci (card->info.cssid == token->cssid) && 6608c2ecf20Sopenharmony_ci (card->info.iid == token->iid) && 6618c2ecf20Sopenharmony_ci (card->info.ssid == token->ssid) && 6628c2ecf20Sopenharmony_ci (card->info.chpid == token->chpid) && 6638c2ecf20Sopenharmony_ci (card->info.chid == token->chid)); 6648c2ecf20Sopenharmony_ci} 6658c2ecf20Sopenharmony_ci 6668c2ecf20Sopenharmony_ci/** 6678c2ecf20Sopenharmony_ci * qeth_l2_dev2br_fdb_notify() - update fdb of master bridge 6688c2ecf20Sopenharmony_ci * @card: qeth_card structure pointer 6698c2ecf20Sopenharmony_ci * @code: event bitmask: high order bit 0x80 set to 6708c2ecf20Sopenharmony_ci * 1 - removal of an object 6718c2ecf20Sopenharmony_ci * 0 - addition of an object 6728c2ecf20Sopenharmony_ci * Object type(s): 6738c2ecf20Sopenharmony_ci * 0x01 - VLAN, 0x02 - MAC, 0x03 - VLAN and MAC 6748c2ecf20Sopenharmony_ci * @token: "network token" structure identifying 'physical' location 6758c2ecf20Sopenharmony_ci * of the target 6768c2ecf20Sopenharmony_ci * @addr_lnid: structure with MAC address and VLAN ID of the target 6778c2ecf20Sopenharmony_ci */ 6788c2ecf20Sopenharmony_cistatic void qeth_l2_dev2br_fdb_notify(struct qeth_card *card, u8 code, 6798c2ecf20Sopenharmony_ci struct net_if_token *token, 6808c2ecf20Sopenharmony_ci struct mac_addr_lnid *addr_lnid) 6818c2ecf20Sopenharmony_ci{ 6828c2ecf20Sopenharmony_ci struct switchdev_notifier_fdb_info info; 6838c2ecf20Sopenharmony_ci u8 ntfy_mac[ETH_ALEN]; 6848c2ecf20Sopenharmony_ci 6858c2ecf20Sopenharmony_ci ether_addr_copy(ntfy_mac, addr_lnid->mac); 6868c2ecf20Sopenharmony_ci /* Ignore VLAN only changes */ 6878c2ecf20Sopenharmony_ci if (!(code & IPA_ADDR_CHANGE_CODE_MACADDR)) 6888c2ecf20Sopenharmony_ci return; 6898c2ecf20Sopenharmony_ci /* Ignore mcast entries */ 6908c2ecf20Sopenharmony_ci if (is_multicast_ether_addr(ntfy_mac)) 6918c2ecf20Sopenharmony_ci return; 6928c2ecf20Sopenharmony_ci /* Ignore my own addresses */ 6938c2ecf20Sopenharmony_ci if (qeth_is_my_net_if_token(card, token)) 6948c2ecf20Sopenharmony_ci return; 6958c2ecf20Sopenharmony_ci 6968c2ecf20Sopenharmony_ci info.addr = ntfy_mac; 6978c2ecf20Sopenharmony_ci /* don't report VLAN IDs */ 6988c2ecf20Sopenharmony_ci info.vid = 0; 6998c2ecf20Sopenharmony_ci info.added_by_user = false; 7008c2ecf20Sopenharmony_ci info.offloaded = true; 7018c2ecf20Sopenharmony_ci 7028c2ecf20Sopenharmony_ci if (code & IPA_ADDR_CHANGE_CODE_REMOVAL) { 7038c2ecf20Sopenharmony_ci call_switchdev_notifiers(SWITCHDEV_FDB_DEL_TO_BRIDGE, 7048c2ecf20Sopenharmony_ci card->dev, &info.info, NULL); 7058c2ecf20Sopenharmony_ci QETH_CARD_TEXT(card, 4, "andelmac"); 7068c2ecf20Sopenharmony_ci QETH_CARD_TEXT_(card, 4, 7078c2ecf20Sopenharmony_ci "mc%012lx", ether_addr_to_u64(ntfy_mac)); 7088c2ecf20Sopenharmony_ci } else { 7098c2ecf20Sopenharmony_ci call_switchdev_notifiers(SWITCHDEV_FDB_ADD_TO_BRIDGE, 7108c2ecf20Sopenharmony_ci card->dev, &info.info, NULL); 7118c2ecf20Sopenharmony_ci QETH_CARD_TEXT(card, 4, "anaddmac"); 7128c2ecf20Sopenharmony_ci QETH_CARD_TEXT_(card, 4, 7138c2ecf20Sopenharmony_ci "mc%012lx", ether_addr_to_u64(ntfy_mac)); 7148c2ecf20Sopenharmony_ci } 7158c2ecf20Sopenharmony_ci} 7168c2ecf20Sopenharmony_ci 7178c2ecf20Sopenharmony_cistatic void qeth_l2_dev2br_an_set_cb(void *priv, 7188c2ecf20Sopenharmony_ci struct chsc_pnso_naid_l2 *entry) 7198c2ecf20Sopenharmony_ci{ 7208c2ecf20Sopenharmony_ci u8 code = IPA_ADDR_CHANGE_CODE_MACADDR; 7218c2ecf20Sopenharmony_ci struct qeth_card *card = priv; 7228c2ecf20Sopenharmony_ci 7238c2ecf20Sopenharmony_ci if (entry->addr_lnid.lnid < VLAN_N_VID) 7248c2ecf20Sopenharmony_ci code |= IPA_ADDR_CHANGE_CODE_VLANID; 7258c2ecf20Sopenharmony_ci qeth_l2_dev2br_fdb_notify(card, code, 7268c2ecf20Sopenharmony_ci (struct net_if_token *)&entry->nit, 7278c2ecf20Sopenharmony_ci (struct mac_addr_lnid *)&entry->addr_lnid); 7288c2ecf20Sopenharmony_ci} 7298c2ecf20Sopenharmony_ci 7308c2ecf20Sopenharmony_ci/** 7318c2ecf20Sopenharmony_ci * qeth_l2_dev2br_an_set() - 7328c2ecf20Sopenharmony_ci * Enable or disable 'dev to bridge network address notification' 7338c2ecf20Sopenharmony_ci * @card: qeth_card structure pointer 7348c2ecf20Sopenharmony_ci * @enable: Enable or disable 'dev to bridge network address notification' 7358c2ecf20Sopenharmony_ci * 7368c2ecf20Sopenharmony_ci * Returns negative errno-compatible error indication or 0 on success. 7378c2ecf20Sopenharmony_ci * 7388c2ecf20Sopenharmony_ci * On enable, emits a series of address notifications for all 7398c2ecf20Sopenharmony_ci * currently registered hosts. 7408c2ecf20Sopenharmony_ci * 7418c2ecf20Sopenharmony_ci * Must be called under rtnl_lock 7428c2ecf20Sopenharmony_ci */ 7438c2ecf20Sopenharmony_cistatic int qeth_l2_dev2br_an_set(struct qeth_card *card, bool enable) 7448c2ecf20Sopenharmony_ci{ 7458c2ecf20Sopenharmony_ci int rc; 7468c2ecf20Sopenharmony_ci 7478c2ecf20Sopenharmony_ci if (enable) { 7488c2ecf20Sopenharmony_ci QETH_CARD_TEXT(card, 2, "anseton"); 7498c2ecf20Sopenharmony_ci rc = qeth_l2_pnso(card, PNSO_OC_NET_ADDR_INFO, 1, 7508c2ecf20Sopenharmony_ci qeth_l2_dev2br_an_set_cb, card); 7518c2ecf20Sopenharmony_ci if (rc == -EAGAIN) 7528c2ecf20Sopenharmony_ci /* address notification enabled, but inconsistent 7538c2ecf20Sopenharmony_ci * addresses reported -> disable address notification 7548c2ecf20Sopenharmony_ci */ 7558c2ecf20Sopenharmony_ci qeth_l2_pnso(card, PNSO_OC_NET_ADDR_INFO, 0, 7568c2ecf20Sopenharmony_ci NULL, NULL); 7578c2ecf20Sopenharmony_ci } else { 7588c2ecf20Sopenharmony_ci QETH_CARD_TEXT(card, 2, "ansetoff"); 7598c2ecf20Sopenharmony_ci rc = qeth_l2_pnso(card, PNSO_OC_NET_ADDR_INFO, 0, NULL, NULL); 7608c2ecf20Sopenharmony_ci } 7618c2ecf20Sopenharmony_ci 7628c2ecf20Sopenharmony_ci return rc; 7638c2ecf20Sopenharmony_ci} 7648c2ecf20Sopenharmony_ci 7658c2ecf20Sopenharmony_cistatic int qeth_l2_bridge_getlink(struct sk_buff *skb, u32 pid, u32 seq, 7668c2ecf20Sopenharmony_ci struct net_device *dev, u32 filter_mask, 7678c2ecf20Sopenharmony_ci int nlflags) 7688c2ecf20Sopenharmony_ci{ 7698c2ecf20Sopenharmony_ci struct qeth_priv *priv = netdev_priv(dev); 7708c2ecf20Sopenharmony_ci struct qeth_card *card = dev->ml_priv; 7718c2ecf20Sopenharmony_ci u16 mode = BRIDGE_MODE_UNDEF; 7728c2ecf20Sopenharmony_ci 7738c2ecf20Sopenharmony_ci /* Do not even show qeth devs that cannot do bridge_setlink */ 7748c2ecf20Sopenharmony_ci if (!priv->brport_hw_features || !netif_device_present(dev) || 7758c2ecf20Sopenharmony_ci qeth_bridgeport_is_in_use(card)) 7768c2ecf20Sopenharmony_ci return -EOPNOTSUPP; 7778c2ecf20Sopenharmony_ci 7788c2ecf20Sopenharmony_ci return ndo_dflt_bridge_getlink(skb, pid, seq, dev, 7798c2ecf20Sopenharmony_ci mode, priv->brport_features, 7808c2ecf20Sopenharmony_ci priv->brport_hw_features, 7818c2ecf20Sopenharmony_ci nlflags, filter_mask, NULL); 7828c2ecf20Sopenharmony_ci} 7838c2ecf20Sopenharmony_ci 7848c2ecf20Sopenharmony_cistatic const struct nla_policy qeth_brport_policy[IFLA_BRPORT_MAX + 1] = { 7858c2ecf20Sopenharmony_ci [IFLA_BRPORT_LEARNING_SYNC] = { .type = NLA_U8 }, 7868c2ecf20Sopenharmony_ci}; 7878c2ecf20Sopenharmony_ci 7888c2ecf20Sopenharmony_ci/** 7898c2ecf20Sopenharmony_ci * qeth_l2_bridge_setlink() - set bridgeport attributes 7908c2ecf20Sopenharmony_ci * @dev: netdevice 7918c2ecf20Sopenharmony_ci * @nlh: netlink message header 7928c2ecf20Sopenharmony_ci * @flags: bridge flags (here: BRIDGE_FLAGS_SELF) 7938c2ecf20Sopenharmony_ci * @extack: extended ACK report struct 7948c2ecf20Sopenharmony_ci * 7958c2ecf20Sopenharmony_ci * Called under rtnl_lock 7968c2ecf20Sopenharmony_ci */ 7978c2ecf20Sopenharmony_cistatic int qeth_l2_bridge_setlink(struct net_device *dev, struct nlmsghdr *nlh, 7988c2ecf20Sopenharmony_ci u16 flags, struct netlink_ext_ack *extack) 7998c2ecf20Sopenharmony_ci{ 8008c2ecf20Sopenharmony_ci struct qeth_priv *priv = netdev_priv(dev); 8018c2ecf20Sopenharmony_ci struct nlattr *bp_tb[IFLA_BRPORT_MAX + 1]; 8028c2ecf20Sopenharmony_ci struct qeth_card *card = dev->ml_priv; 8038c2ecf20Sopenharmony_ci struct nlattr *attr, *nested_attr; 8048c2ecf20Sopenharmony_ci bool enable, has_protinfo = false; 8058c2ecf20Sopenharmony_ci int rem1, rem2; 8068c2ecf20Sopenharmony_ci int rc; 8078c2ecf20Sopenharmony_ci 8088c2ecf20Sopenharmony_ci if (!netif_device_present(dev)) 8098c2ecf20Sopenharmony_ci return -ENODEV; 8108c2ecf20Sopenharmony_ci if (!(priv->brport_hw_features)) 8118c2ecf20Sopenharmony_ci return -EOPNOTSUPP; 8128c2ecf20Sopenharmony_ci 8138c2ecf20Sopenharmony_ci nlmsg_for_each_attr(attr, nlh, sizeof(struct ifinfomsg), rem1) { 8148c2ecf20Sopenharmony_ci if (nla_type(attr) == IFLA_PROTINFO) { 8158c2ecf20Sopenharmony_ci rc = nla_parse_nested(bp_tb, IFLA_BRPORT_MAX, attr, 8168c2ecf20Sopenharmony_ci qeth_brport_policy, extack); 8178c2ecf20Sopenharmony_ci if (rc) 8188c2ecf20Sopenharmony_ci return rc; 8198c2ecf20Sopenharmony_ci has_protinfo = true; 8208c2ecf20Sopenharmony_ci } else if (nla_type(attr) == IFLA_AF_SPEC) { 8218c2ecf20Sopenharmony_ci nla_for_each_nested(nested_attr, attr, rem2) { 8228c2ecf20Sopenharmony_ci if (nla_type(nested_attr) == IFLA_BRIDGE_FLAGS) 8238c2ecf20Sopenharmony_ci continue; 8248c2ecf20Sopenharmony_ci NL_SET_ERR_MSG_ATTR(extack, nested_attr, 8258c2ecf20Sopenharmony_ci "Unsupported attribute"); 8268c2ecf20Sopenharmony_ci return -EINVAL; 8278c2ecf20Sopenharmony_ci } 8288c2ecf20Sopenharmony_ci } else { 8298c2ecf20Sopenharmony_ci NL_SET_ERR_MSG_ATTR(extack, attr, "Unsupported attribute"); 8308c2ecf20Sopenharmony_ci return -EINVAL; 8318c2ecf20Sopenharmony_ci } 8328c2ecf20Sopenharmony_ci } 8338c2ecf20Sopenharmony_ci if (!has_protinfo) 8348c2ecf20Sopenharmony_ci return 0; 8358c2ecf20Sopenharmony_ci if (!bp_tb[IFLA_BRPORT_LEARNING_SYNC]) 8368c2ecf20Sopenharmony_ci return -EINVAL; 8378c2ecf20Sopenharmony_ci enable = !!nla_get_u8(bp_tb[IFLA_BRPORT_LEARNING_SYNC]); 8388c2ecf20Sopenharmony_ci 8398c2ecf20Sopenharmony_ci if (enable == !!(priv->brport_features & BR_LEARNING_SYNC)) 8408c2ecf20Sopenharmony_ci return 0; 8418c2ecf20Sopenharmony_ci 8428c2ecf20Sopenharmony_ci mutex_lock(&card->sbp_lock); 8438c2ecf20Sopenharmony_ci /* do not change anything if BridgePort is enabled */ 8448c2ecf20Sopenharmony_ci if (qeth_bridgeport_is_in_use(card)) { 8458c2ecf20Sopenharmony_ci NL_SET_ERR_MSG(extack, "n/a (BridgePort)"); 8468c2ecf20Sopenharmony_ci rc = -EBUSY; 8478c2ecf20Sopenharmony_ci } else if (enable) { 8488c2ecf20Sopenharmony_ci qeth_l2_set_pnso_mode(card, QETH_PNSO_ADDR_INFO); 8498c2ecf20Sopenharmony_ci rc = qeth_l2_dev2br_an_set(card, true); 8508c2ecf20Sopenharmony_ci if (rc) 8518c2ecf20Sopenharmony_ci qeth_l2_set_pnso_mode(card, QETH_PNSO_NONE); 8528c2ecf20Sopenharmony_ci else 8538c2ecf20Sopenharmony_ci priv->brport_features |= BR_LEARNING_SYNC; 8548c2ecf20Sopenharmony_ci } else { 8558c2ecf20Sopenharmony_ci rc = qeth_l2_dev2br_an_set(card, false); 8568c2ecf20Sopenharmony_ci if (!rc) { 8578c2ecf20Sopenharmony_ci qeth_l2_set_pnso_mode(card, QETH_PNSO_NONE); 8588c2ecf20Sopenharmony_ci priv->brport_features ^= BR_LEARNING_SYNC; 8598c2ecf20Sopenharmony_ci qeth_l2_dev2br_fdb_flush(card); 8608c2ecf20Sopenharmony_ci } 8618c2ecf20Sopenharmony_ci } 8628c2ecf20Sopenharmony_ci mutex_unlock(&card->sbp_lock); 8638c2ecf20Sopenharmony_ci 8648c2ecf20Sopenharmony_ci return rc; 8658c2ecf20Sopenharmony_ci} 8668c2ecf20Sopenharmony_ci 8678c2ecf20Sopenharmony_cistatic const struct net_device_ops qeth_l2_netdev_ops = { 8688c2ecf20Sopenharmony_ci .ndo_open = qeth_open, 8698c2ecf20Sopenharmony_ci .ndo_stop = qeth_stop, 8708c2ecf20Sopenharmony_ci .ndo_get_stats64 = qeth_get_stats64, 8718c2ecf20Sopenharmony_ci .ndo_start_xmit = qeth_l2_hard_start_xmit, 8728c2ecf20Sopenharmony_ci .ndo_features_check = qeth_features_check, 8738c2ecf20Sopenharmony_ci .ndo_select_queue = qeth_l2_select_queue, 8748c2ecf20Sopenharmony_ci .ndo_validate_addr = qeth_l2_validate_addr, 8758c2ecf20Sopenharmony_ci .ndo_set_rx_mode = qeth_l2_set_rx_mode, 8768c2ecf20Sopenharmony_ci .ndo_do_ioctl = qeth_do_ioctl, 8778c2ecf20Sopenharmony_ci .ndo_set_mac_address = qeth_l2_set_mac_address, 8788c2ecf20Sopenharmony_ci .ndo_vlan_rx_add_vid = qeth_l2_vlan_rx_add_vid, 8798c2ecf20Sopenharmony_ci .ndo_vlan_rx_kill_vid = qeth_l2_vlan_rx_kill_vid, 8808c2ecf20Sopenharmony_ci .ndo_tx_timeout = qeth_tx_timeout, 8818c2ecf20Sopenharmony_ci .ndo_fix_features = qeth_fix_features, 8828c2ecf20Sopenharmony_ci .ndo_set_features = qeth_set_features, 8838c2ecf20Sopenharmony_ci .ndo_bridge_getlink = qeth_l2_bridge_getlink, 8848c2ecf20Sopenharmony_ci .ndo_bridge_setlink = qeth_l2_bridge_setlink, 8858c2ecf20Sopenharmony_ci}; 8868c2ecf20Sopenharmony_ci 8878c2ecf20Sopenharmony_cistatic const struct net_device_ops qeth_osn_netdev_ops = { 8888c2ecf20Sopenharmony_ci .ndo_open = qeth_open, 8898c2ecf20Sopenharmony_ci .ndo_stop = qeth_stop, 8908c2ecf20Sopenharmony_ci .ndo_get_stats64 = qeth_get_stats64, 8918c2ecf20Sopenharmony_ci .ndo_start_xmit = qeth_l2_hard_start_xmit, 8928c2ecf20Sopenharmony_ci .ndo_validate_addr = eth_validate_addr, 8938c2ecf20Sopenharmony_ci .ndo_tx_timeout = qeth_tx_timeout, 8948c2ecf20Sopenharmony_ci}; 8958c2ecf20Sopenharmony_ci 8968c2ecf20Sopenharmony_cistatic int qeth_l2_setup_netdev(struct qeth_card *card) 8978c2ecf20Sopenharmony_ci{ 8988c2ecf20Sopenharmony_ci if (IS_OSN(card)) { 8998c2ecf20Sopenharmony_ci card->dev->netdev_ops = &qeth_osn_netdev_ops; 9008c2ecf20Sopenharmony_ci card->dev->flags |= IFF_NOARP; 9018c2ecf20Sopenharmony_ci goto add_napi; 9028c2ecf20Sopenharmony_ci } 9038c2ecf20Sopenharmony_ci 9048c2ecf20Sopenharmony_ci card->dev->needed_headroom = sizeof(struct qeth_hdr); 9058c2ecf20Sopenharmony_ci card->dev->netdev_ops = &qeth_l2_netdev_ops; 9068c2ecf20Sopenharmony_ci card->dev->priv_flags |= IFF_UNICAST_FLT; 9078c2ecf20Sopenharmony_ci 9088c2ecf20Sopenharmony_ci if (IS_OSM(card)) { 9098c2ecf20Sopenharmony_ci card->dev->features |= NETIF_F_VLAN_CHALLENGED; 9108c2ecf20Sopenharmony_ci } else { 9118c2ecf20Sopenharmony_ci if (!IS_VM_NIC(card)) 9128c2ecf20Sopenharmony_ci card->dev->hw_features |= NETIF_F_HW_VLAN_CTAG_FILTER; 9138c2ecf20Sopenharmony_ci card->dev->features |= NETIF_F_HW_VLAN_CTAG_FILTER; 9148c2ecf20Sopenharmony_ci } 9158c2ecf20Sopenharmony_ci 9168c2ecf20Sopenharmony_ci if (IS_OSD(card) && !IS_VM_NIC(card)) { 9178c2ecf20Sopenharmony_ci card->dev->features |= NETIF_F_SG; 9188c2ecf20Sopenharmony_ci /* OSA 3S and earlier has no RX/TX support */ 9198c2ecf20Sopenharmony_ci if (qeth_is_supported(card, IPA_OUTBOUND_CHECKSUM)) { 9208c2ecf20Sopenharmony_ci card->dev->hw_features |= NETIF_F_IP_CSUM; 9218c2ecf20Sopenharmony_ci card->dev->vlan_features |= NETIF_F_IP_CSUM; 9228c2ecf20Sopenharmony_ci } 9238c2ecf20Sopenharmony_ci } 9248c2ecf20Sopenharmony_ci if (qeth_is_supported6(card, IPA_OUTBOUND_CHECKSUM_V6)) { 9258c2ecf20Sopenharmony_ci card->dev->hw_features |= NETIF_F_IPV6_CSUM; 9268c2ecf20Sopenharmony_ci card->dev->vlan_features |= NETIF_F_IPV6_CSUM; 9278c2ecf20Sopenharmony_ci } 9288c2ecf20Sopenharmony_ci if (qeth_is_supported(card, IPA_INBOUND_CHECKSUM) || 9298c2ecf20Sopenharmony_ci qeth_is_supported6(card, IPA_INBOUND_CHECKSUM_V6)) { 9308c2ecf20Sopenharmony_ci card->dev->hw_features |= NETIF_F_RXCSUM; 9318c2ecf20Sopenharmony_ci card->dev->vlan_features |= NETIF_F_RXCSUM; 9328c2ecf20Sopenharmony_ci } 9338c2ecf20Sopenharmony_ci if (qeth_is_supported(card, IPA_OUTBOUND_TSO)) { 9348c2ecf20Sopenharmony_ci card->dev->hw_features |= NETIF_F_TSO; 9358c2ecf20Sopenharmony_ci card->dev->vlan_features |= NETIF_F_TSO; 9368c2ecf20Sopenharmony_ci } 9378c2ecf20Sopenharmony_ci if (qeth_is_supported6(card, IPA_OUTBOUND_TSO)) { 9388c2ecf20Sopenharmony_ci card->dev->hw_features |= NETIF_F_TSO6; 9398c2ecf20Sopenharmony_ci card->dev->vlan_features |= NETIF_F_TSO6; 9408c2ecf20Sopenharmony_ci } 9418c2ecf20Sopenharmony_ci 9428c2ecf20Sopenharmony_ci if (card->dev->hw_features & (NETIF_F_TSO | NETIF_F_TSO6)) { 9438c2ecf20Sopenharmony_ci card->dev->needed_headroom = sizeof(struct qeth_hdr_tso); 9448c2ecf20Sopenharmony_ci netif_keep_dst(card->dev); 9458c2ecf20Sopenharmony_ci netif_set_gso_max_size(card->dev, 9468c2ecf20Sopenharmony_ci PAGE_SIZE * (QDIO_MAX_ELEMENTS_PER_BUFFER - 1)); 9478c2ecf20Sopenharmony_ci } 9488c2ecf20Sopenharmony_ci 9498c2ecf20Sopenharmony_ciadd_napi: 9508c2ecf20Sopenharmony_ci netif_napi_add(card->dev, &card->napi, qeth_poll, QETH_NAPI_WEIGHT); 9518c2ecf20Sopenharmony_ci return register_netdev(card->dev); 9528c2ecf20Sopenharmony_ci} 9538c2ecf20Sopenharmony_ci 9548c2ecf20Sopenharmony_cistatic void qeth_l2_trace_features(struct qeth_card *card) 9558c2ecf20Sopenharmony_ci{ 9568c2ecf20Sopenharmony_ci /* Set BridgePort features */ 9578c2ecf20Sopenharmony_ci QETH_CARD_TEXT(card, 2, "featuSBP"); 9588c2ecf20Sopenharmony_ci QETH_CARD_HEX(card, 2, &card->options.sbp.supported_funcs, 9598c2ecf20Sopenharmony_ci sizeof(card->options.sbp.supported_funcs)); 9608c2ecf20Sopenharmony_ci /* VNIC Characteristics features */ 9618c2ecf20Sopenharmony_ci QETH_CARD_TEXT(card, 2, "feaVNICC"); 9628c2ecf20Sopenharmony_ci QETH_CARD_HEX(card, 2, &card->options.vnicc.sup_chars, 9638c2ecf20Sopenharmony_ci sizeof(card->options.vnicc.sup_chars)); 9648c2ecf20Sopenharmony_ci} 9658c2ecf20Sopenharmony_ci 9668c2ecf20Sopenharmony_cistatic void qeth_l2_setup_bridgeport_attrs(struct qeth_card *card) 9678c2ecf20Sopenharmony_ci{ 9688c2ecf20Sopenharmony_ci if (!card->options.sbp.reflect_promisc && 9698c2ecf20Sopenharmony_ci card->options.sbp.role != QETH_SBP_ROLE_NONE) { 9708c2ecf20Sopenharmony_ci /* Conditional to avoid spurious error messages */ 9718c2ecf20Sopenharmony_ci qeth_bridgeport_setrole(card, card->options.sbp.role); 9728c2ecf20Sopenharmony_ci /* Let the callback function refresh the stored role value. */ 9738c2ecf20Sopenharmony_ci qeth_bridgeport_query_ports(card, &card->options.sbp.role, 9748c2ecf20Sopenharmony_ci NULL); 9758c2ecf20Sopenharmony_ci } 9768c2ecf20Sopenharmony_ci if (card->options.sbp.hostnotification) { 9778c2ecf20Sopenharmony_ci if (qeth_bridgeport_an_set(card, 1)) 9788c2ecf20Sopenharmony_ci card->options.sbp.hostnotification = 0; 9798c2ecf20Sopenharmony_ci } 9808c2ecf20Sopenharmony_ci} 9818c2ecf20Sopenharmony_ci 9828c2ecf20Sopenharmony_ci/** 9838c2ecf20Sopenharmony_ci * qeth_l2_detect_dev2br_support() - 9848c2ecf20Sopenharmony_ci * Detect whether this card supports 'dev to bridge fdb network address 9858c2ecf20Sopenharmony_ci * change notification' and thus can support the learning_sync bridgeport 9868c2ecf20Sopenharmony_ci * attribute 9878c2ecf20Sopenharmony_ci * @card: qeth_card structure pointer 9888c2ecf20Sopenharmony_ci */ 9898c2ecf20Sopenharmony_cistatic void qeth_l2_detect_dev2br_support(struct qeth_card *card) 9908c2ecf20Sopenharmony_ci{ 9918c2ecf20Sopenharmony_ci struct qeth_priv *priv = netdev_priv(card->dev); 9928c2ecf20Sopenharmony_ci bool dev2br_supported; 9938c2ecf20Sopenharmony_ci 9948c2ecf20Sopenharmony_ci QETH_CARD_TEXT(card, 2, "d2brsup"); 9958c2ecf20Sopenharmony_ci if (!IS_IQD(card)) 9968c2ecf20Sopenharmony_ci return; 9978c2ecf20Sopenharmony_ci 9988c2ecf20Sopenharmony_ci /* dev2br requires valid cssid,iid,chid */ 9998c2ecf20Sopenharmony_ci dev2br_supported = card->info.ids_valid && 10008c2ecf20Sopenharmony_ci css_general_characteristics.enarf; 10018c2ecf20Sopenharmony_ci QETH_CARD_TEXT_(card, 2, "D2Bsup%02x", dev2br_supported); 10028c2ecf20Sopenharmony_ci 10038c2ecf20Sopenharmony_ci if (dev2br_supported) 10048c2ecf20Sopenharmony_ci priv->brport_hw_features |= BR_LEARNING_SYNC; 10058c2ecf20Sopenharmony_ci else 10068c2ecf20Sopenharmony_ci priv->brport_hw_features &= ~BR_LEARNING_SYNC; 10078c2ecf20Sopenharmony_ci} 10088c2ecf20Sopenharmony_ci 10098c2ecf20Sopenharmony_cistatic void qeth_l2_enable_brport_features(struct qeth_card *card) 10108c2ecf20Sopenharmony_ci{ 10118c2ecf20Sopenharmony_ci struct qeth_priv *priv = netdev_priv(card->dev); 10128c2ecf20Sopenharmony_ci int rc; 10138c2ecf20Sopenharmony_ci 10148c2ecf20Sopenharmony_ci if (priv->brport_features & BR_LEARNING_SYNC) { 10158c2ecf20Sopenharmony_ci if (priv->brport_hw_features & BR_LEARNING_SYNC) { 10168c2ecf20Sopenharmony_ci qeth_l2_set_pnso_mode(card, QETH_PNSO_ADDR_INFO); 10178c2ecf20Sopenharmony_ci rc = qeth_l2_dev2br_an_set(card, true); 10188c2ecf20Sopenharmony_ci if (rc == -EAGAIN) { 10198c2ecf20Sopenharmony_ci /* Recoverable error, retry once */ 10208c2ecf20Sopenharmony_ci qeth_l2_set_pnso_mode(card, QETH_PNSO_NONE); 10218c2ecf20Sopenharmony_ci qeth_l2_dev2br_fdb_flush(card); 10228c2ecf20Sopenharmony_ci qeth_l2_set_pnso_mode(card, QETH_PNSO_ADDR_INFO); 10238c2ecf20Sopenharmony_ci rc = qeth_l2_dev2br_an_set(card, true); 10248c2ecf20Sopenharmony_ci } 10258c2ecf20Sopenharmony_ci if (rc) { 10268c2ecf20Sopenharmony_ci netdev_err(card->dev, 10278c2ecf20Sopenharmony_ci "failed to enable bridge learning_sync: %d\n", 10288c2ecf20Sopenharmony_ci rc); 10298c2ecf20Sopenharmony_ci qeth_l2_set_pnso_mode(card, QETH_PNSO_NONE); 10308c2ecf20Sopenharmony_ci qeth_l2_dev2br_fdb_flush(card); 10318c2ecf20Sopenharmony_ci priv->brport_features ^= BR_LEARNING_SYNC; 10328c2ecf20Sopenharmony_ci } 10338c2ecf20Sopenharmony_ci } else { 10348c2ecf20Sopenharmony_ci dev_warn(&card->gdev->dev, 10358c2ecf20Sopenharmony_ci "bridge learning_sync not supported\n"); 10368c2ecf20Sopenharmony_ci priv->brport_features ^= BR_LEARNING_SYNC; 10378c2ecf20Sopenharmony_ci } 10388c2ecf20Sopenharmony_ci } 10398c2ecf20Sopenharmony_ci} 10408c2ecf20Sopenharmony_ci 10418c2ecf20Sopenharmony_ci#ifdef CONFIG_QETH_OSN 10428c2ecf20Sopenharmony_cistatic void qeth_osn_assist_cb(struct qeth_card *card, 10438c2ecf20Sopenharmony_ci struct qeth_cmd_buffer *iob, 10448c2ecf20Sopenharmony_ci unsigned int data_length) 10458c2ecf20Sopenharmony_ci{ 10468c2ecf20Sopenharmony_ci qeth_notify_cmd(iob, 0); 10478c2ecf20Sopenharmony_ci qeth_put_cmd(iob); 10488c2ecf20Sopenharmony_ci} 10498c2ecf20Sopenharmony_ci 10508c2ecf20Sopenharmony_ciint qeth_osn_assist(struct net_device *dev, void *data, int data_len) 10518c2ecf20Sopenharmony_ci{ 10528c2ecf20Sopenharmony_ci struct qeth_cmd_buffer *iob; 10538c2ecf20Sopenharmony_ci struct qeth_card *card; 10548c2ecf20Sopenharmony_ci 10558c2ecf20Sopenharmony_ci if (data_len < 0) 10568c2ecf20Sopenharmony_ci return -EINVAL; 10578c2ecf20Sopenharmony_ci if (!dev) 10588c2ecf20Sopenharmony_ci return -ENODEV; 10598c2ecf20Sopenharmony_ci card = dev->ml_priv; 10608c2ecf20Sopenharmony_ci if (!card) 10618c2ecf20Sopenharmony_ci return -ENODEV; 10628c2ecf20Sopenharmony_ci QETH_CARD_TEXT(card, 2, "osnsdmc"); 10638c2ecf20Sopenharmony_ci if (!qeth_card_hw_is_reachable(card)) 10648c2ecf20Sopenharmony_ci return -ENODEV; 10658c2ecf20Sopenharmony_ci 10668c2ecf20Sopenharmony_ci iob = qeth_alloc_cmd(&card->write, IPA_PDU_HEADER_SIZE + data_len, 1, 10678c2ecf20Sopenharmony_ci QETH_IPA_TIMEOUT); 10688c2ecf20Sopenharmony_ci if (!iob) 10698c2ecf20Sopenharmony_ci return -ENOMEM; 10708c2ecf20Sopenharmony_ci 10718c2ecf20Sopenharmony_ci qeth_prepare_ipa_cmd(card, iob, (u16) data_len, NULL); 10728c2ecf20Sopenharmony_ci 10738c2ecf20Sopenharmony_ci memcpy(__ipa_cmd(iob), data, data_len); 10748c2ecf20Sopenharmony_ci iob->callback = qeth_osn_assist_cb; 10758c2ecf20Sopenharmony_ci return qeth_send_ipa_cmd(card, iob, NULL, NULL); 10768c2ecf20Sopenharmony_ci} 10778c2ecf20Sopenharmony_ciEXPORT_SYMBOL(qeth_osn_assist); 10788c2ecf20Sopenharmony_ci 10798c2ecf20Sopenharmony_ciint qeth_osn_register(unsigned char *read_dev_no, struct net_device **dev, 10808c2ecf20Sopenharmony_ci int (*assist_cb)(struct net_device *, void *), 10818c2ecf20Sopenharmony_ci int (*data_cb)(struct sk_buff *)) 10828c2ecf20Sopenharmony_ci{ 10838c2ecf20Sopenharmony_ci struct qeth_card *card; 10848c2ecf20Sopenharmony_ci char bus_id[16]; 10858c2ecf20Sopenharmony_ci u16 devno; 10868c2ecf20Sopenharmony_ci 10878c2ecf20Sopenharmony_ci memcpy(&devno, read_dev_no, 2); 10888c2ecf20Sopenharmony_ci sprintf(bus_id, "0.0.%04x", devno); 10898c2ecf20Sopenharmony_ci card = qeth_get_card_by_busid(bus_id); 10908c2ecf20Sopenharmony_ci if (!card || !IS_OSN(card)) 10918c2ecf20Sopenharmony_ci return -ENODEV; 10928c2ecf20Sopenharmony_ci *dev = card->dev; 10938c2ecf20Sopenharmony_ci 10948c2ecf20Sopenharmony_ci QETH_CARD_TEXT(card, 2, "osnreg"); 10958c2ecf20Sopenharmony_ci if ((assist_cb == NULL) || (data_cb == NULL)) 10968c2ecf20Sopenharmony_ci return -EINVAL; 10978c2ecf20Sopenharmony_ci card->osn_info.assist_cb = assist_cb; 10988c2ecf20Sopenharmony_ci card->osn_info.data_cb = data_cb; 10998c2ecf20Sopenharmony_ci return 0; 11008c2ecf20Sopenharmony_ci} 11018c2ecf20Sopenharmony_ciEXPORT_SYMBOL(qeth_osn_register); 11028c2ecf20Sopenharmony_ci 11038c2ecf20Sopenharmony_civoid qeth_osn_deregister(struct net_device *dev) 11048c2ecf20Sopenharmony_ci{ 11058c2ecf20Sopenharmony_ci struct qeth_card *card; 11068c2ecf20Sopenharmony_ci 11078c2ecf20Sopenharmony_ci if (!dev) 11088c2ecf20Sopenharmony_ci return; 11098c2ecf20Sopenharmony_ci card = dev->ml_priv; 11108c2ecf20Sopenharmony_ci if (!card) 11118c2ecf20Sopenharmony_ci return; 11128c2ecf20Sopenharmony_ci QETH_CARD_TEXT(card, 2, "osndereg"); 11138c2ecf20Sopenharmony_ci card->osn_info.assist_cb = NULL; 11148c2ecf20Sopenharmony_ci card->osn_info.data_cb = NULL; 11158c2ecf20Sopenharmony_ci} 11168c2ecf20Sopenharmony_ciEXPORT_SYMBOL(qeth_osn_deregister); 11178c2ecf20Sopenharmony_ci#endif 11188c2ecf20Sopenharmony_ci 11198c2ecf20Sopenharmony_ci/* SETBRIDGEPORT support, async notifications */ 11208c2ecf20Sopenharmony_ci 11218c2ecf20Sopenharmony_cienum qeth_an_event_type {anev_reg_unreg, anev_abort, anev_reset}; 11228c2ecf20Sopenharmony_ci 11238c2ecf20Sopenharmony_ci/** 11248c2ecf20Sopenharmony_ci * qeth_bridge_emit_host_event() - bridgeport address change notification 11258c2ecf20Sopenharmony_ci * @card: qeth_card structure pointer, for udev events. 11268c2ecf20Sopenharmony_ci * @evtype: "normal" register/unregister, or abort, or reset. For abort 11278c2ecf20Sopenharmony_ci * and reset token and addr_lnid are unused and may be NULL. 11288c2ecf20Sopenharmony_ci * @code: event bitmask: high order bit 0x80 value 1 means removal of an 11298c2ecf20Sopenharmony_ci * object, 0 - addition of an object. 11308c2ecf20Sopenharmony_ci * 0x01 - VLAN, 0x02 - MAC, 0x03 - VLAN and MAC. 11318c2ecf20Sopenharmony_ci * @token: "network token" structure identifying physical address of the port. 11328c2ecf20Sopenharmony_ci * @addr_lnid: pointer to structure with MAC address and VLAN ID. 11338c2ecf20Sopenharmony_ci * 11348c2ecf20Sopenharmony_ci * This function is called when registrations and deregistrations are 11358c2ecf20Sopenharmony_ci * reported by the hardware, and also when notifications are enabled - 11368c2ecf20Sopenharmony_ci * for all currently registered addresses. 11378c2ecf20Sopenharmony_ci */ 11388c2ecf20Sopenharmony_cistatic void qeth_bridge_emit_host_event(struct qeth_card *card, 11398c2ecf20Sopenharmony_ci enum qeth_an_event_type evtype, 11408c2ecf20Sopenharmony_ci u8 code, 11418c2ecf20Sopenharmony_ci struct net_if_token *token, 11428c2ecf20Sopenharmony_ci struct mac_addr_lnid *addr_lnid) 11438c2ecf20Sopenharmony_ci{ 11448c2ecf20Sopenharmony_ci char str[7][32]; 11458c2ecf20Sopenharmony_ci char *env[8]; 11468c2ecf20Sopenharmony_ci int i = 0; 11478c2ecf20Sopenharmony_ci 11488c2ecf20Sopenharmony_ci switch (evtype) { 11498c2ecf20Sopenharmony_ci case anev_reg_unreg: 11508c2ecf20Sopenharmony_ci snprintf(str[i], sizeof(str[i]), "BRIDGEDHOST=%s", 11518c2ecf20Sopenharmony_ci (code & IPA_ADDR_CHANGE_CODE_REMOVAL) 11528c2ecf20Sopenharmony_ci ? "deregister" : "register"); 11538c2ecf20Sopenharmony_ci env[i] = str[i]; i++; 11548c2ecf20Sopenharmony_ci if (code & IPA_ADDR_CHANGE_CODE_VLANID) { 11558c2ecf20Sopenharmony_ci snprintf(str[i], sizeof(str[i]), "VLAN=%d", 11568c2ecf20Sopenharmony_ci addr_lnid->lnid); 11578c2ecf20Sopenharmony_ci env[i] = str[i]; i++; 11588c2ecf20Sopenharmony_ci } 11598c2ecf20Sopenharmony_ci if (code & IPA_ADDR_CHANGE_CODE_MACADDR) { 11608c2ecf20Sopenharmony_ci snprintf(str[i], sizeof(str[i]), "MAC=%pM", 11618c2ecf20Sopenharmony_ci addr_lnid->mac); 11628c2ecf20Sopenharmony_ci env[i] = str[i]; i++; 11638c2ecf20Sopenharmony_ci } 11648c2ecf20Sopenharmony_ci snprintf(str[i], sizeof(str[i]), "NTOK_BUSID=%x.%x.%04x", 11658c2ecf20Sopenharmony_ci token->cssid, token->ssid, token->devnum); 11668c2ecf20Sopenharmony_ci env[i] = str[i]; i++; 11678c2ecf20Sopenharmony_ci snprintf(str[i], sizeof(str[i]), "NTOK_IID=%02x", token->iid); 11688c2ecf20Sopenharmony_ci env[i] = str[i]; i++; 11698c2ecf20Sopenharmony_ci snprintf(str[i], sizeof(str[i]), "NTOK_CHPID=%02x", 11708c2ecf20Sopenharmony_ci token->chpid); 11718c2ecf20Sopenharmony_ci env[i] = str[i]; i++; 11728c2ecf20Sopenharmony_ci snprintf(str[i], sizeof(str[i]), "NTOK_CHID=%04x", token->chid); 11738c2ecf20Sopenharmony_ci env[i] = str[i]; i++; 11748c2ecf20Sopenharmony_ci break; 11758c2ecf20Sopenharmony_ci case anev_abort: 11768c2ecf20Sopenharmony_ci snprintf(str[i], sizeof(str[i]), "BRIDGEDHOST=abort"); 11778c2ecf20Sopenharmony_ci env[i] = str[i]; i++; 11788c2ecf20Sopenharmony_ci break; 11798c2ecf20Sopenharmony_ci case anev_reset: 11808c2ecf20Sopenharmony_ci snprintf(str[i], sizeof(str[i]), "BRIDGEDHOST=reset"); 11818c2ecf20Sopenharmony_ci env[i] = str[i]; i++; 11828c2ecf20Sopenharmony_ci break; 11838c2ecf20Sopenharmony_ci } 11848c2ecf20Sopenharmony_ci env[i] = NULL; 11858c2ecf20Sopenharmony_ci kobject_uevent_env(&card->gdev->dev.kobj, KOBJ_CHANGE, env); 11868c2ecf20Sopenharmony_ci} 11878c2ecf20Sopenharmony_ci 11888c2ecf20Sopenharmony_cistruct qeth_bridge_state_data { 11898c2ecf20Sopenharmony_ci struct work_struct worker; 11908c2ecf20Sopenharmony_ci struct qeth_card *card; 11918c2ecf20Sopenharmony_ci u8 role; 11928c2ecf20Sopenharmony_ci u8 state; 11938c2ecf20Sopenharmony_ci}; 11948c2ecf20Sopenharmony_ci 11958c2ecf20Sopenharmony_cistatic void qeth_bridge_state_change_worker(struct work_struct *work) 11968c2ecf20Sopenharmony_ci{ 11978c2ecf20Sopenharmony_ci struct qeth_bridge_state_data *data = 11988c2ecf20Sopenharmony_ci container_of(work, struct qeth_bridge_state_data, worker); 11998c2ecf20Sopenharmony_ci char env_locrem[32]; 12008c2ecf20Sopenharmony_ci char env_role[32]; 12018c2ecf20Sopenharmony_ci char env_state[32]; 12028c2ecf20Sopenharmony_ci char *env[] = { 12038c2ecf20Sopenharmony_ci env_locrem, 12048c2ecf20Sopenharmony_ci env_role, 12058c2ecf20Sopenharmony_ci env_state, 12068c2ecf20Sopenharmony_ci NULL 12078c2ecf20Sopenharmony_ci }; 12088c2ecf20Sopenharmony_ci 12098c2ecf20Sopenharmony_ci snprintf(env_locrem, sizeof(env_locrem), "BRIDGEPORT=statechange"); 12108c2ecf20Sopenharmony_ci snprintf(env_role, sizeof(env_role), "ROLE=%s", 12118c2ecf20Sopenharmony_ci (data->role == QETH_SBP_ROLE_NONE) ? "none" : 12128c2ecf20Sopenharmony_ci (data->role == QETH_SBP_ROLE_PRIMARY) ? "primary" : 12138c2ecf20Sopenharmony_ci (data->role == QETH_SBP_ROLE_SECONDARY) ? "secondary" : 12148c2ecf20Sopenharmony_ci "<INVALID>"); 12158c2ecf20Sopenharmony_ci snprintf(env_state, sizeof(env_state), "STATE=%s", 12168c2ecf20Sopenharmony_ci (data->state == QETH_SBP_STATE_INACTIVE) ? "inactive" : 12178c2ecf20Sopenharmony_ci (data->state == QETH_SBP_STATE_STANDBY) ? "standby" : 12188c2ecf20Sopenharmony_ci (data->state == QETH_SBP_STATE_ACTIVE) ? "active" : 12198c2ecf20Sopenharmony_ci "<INVALID>"); 12208c2ecf20Sopenharmony_ci kobject_uevent_env(&data->card->gdev->dev.kobj, 12218c2ecf20Sopenharmony_ci KOBJ_CHANGE, env); 12228c2ecf20Sopenharmony_ci kfree(data); 12238c2ecf20Sopenharmony_ci} 12248c2ecf20Sopenharmony_ci 12258c2ecf20Sopenharmony_cistatic void qeth_bridge_state_change(struct qeth_card *card, 12268c2ecf20Sopenharmony_ci struct qeth_ipa_cmd *cmd) 12278c2ecf20Sopenharmony_ci{ 12288c2ecf20Sopenharmony_ci struct qeth_sbp_port_data *qports = &cmd->data.sbp.data.port_data; 12298c2ecf20Sopenharmony_ci struct qeth_bridge_state_data *data; 12308c2ecf20Sopenharmony_ci 12318c2ecf20Sopenharmony_ci QETH_CARD_TEXT(card, 2, "brstchng"); 12328c2ecf20Sopenharmony_ci if (qports->num_entries == 0) { 12338c2ecf20Sopenharmony_ci QETH_CARD_TEXT(card, 2, "BPempty"); 12348c2ecf20Sopenharmony_ci return; 12358c2ecf20Sopenharmony_ci } 12368c2ecf20Sopenharmony_ci if (qports->entry_length != sizeof(struct qeth_sbp_port_entry)) { 12378c2ecf20Sopenharmony_ci QETH_CARD_TEXT_(card, 2, "BPsz%04x", qports->entry_length); 12388c2ecf20Sopenharmony_ci return; 12398c2ecf20Sopenharmony_ci } 12408c2ecf20Sopenharmony_ci 12418c2ecf20Sopenharmony_ci data = kzalloc(sizeof(*data), GFP_ATOMIC); 12428c2ecf20Sopenharmony_ci if (!data) { 12438c2ecf20Sopenharmony_ci QETH_CARD_TEXT(card, 2, "BPSalloc"); 12448c2ecf20Sopenharmony_ci return; 12458c2ecf20Sopenharmony_ci } 12468c2ecf20Sopenharmony_ci INIT_WORK(&data->worker, qeth_bridge_state_change_worker); 12478c2ecf20Sopenharmony_ci data->card = card; 12488c2ecf20Sopenharmony_ci /* Information for the local port: */ 12498c2ecf20Sopenharmony_ci data->role = qports->entry[0].role; 12508c2ecf20Sopenharmony_ci data->state = qports->entry[0].state; 12518c2ecf20Sopenharmony_ci 12528c2ecf20Sopenharmony_ci queue_work(card->event_wq, &data->worker); 12538c2ecf20Sopenharmony_ci} 12548c2ecf20Sopenharmony_ci 12558c2ecf20Sopenharmony_cistruct qeth_addr_change_data { 12568c2ecf20Sopenharmony_ci struct delayed_work dwork; 12578c2ecf20Sopenharmony_ci struct qeth_card *card; 12588c2ecf20Sopenharmony_ci struct qeth_ipacmd_addr_change ac_event; 12598c2ecf20Sopenharmony_ci}; 12608c2ecf20Sopenharmony_ci 12618c2ecf20Sopenharmony_cistatic void qeth_l2_dev2br_worker(struct work_struct *work) 12628c2ecf20Sopenharmony_ci{ 12638c2ecf20Sopenharmony_ci struct delayed_work *dwork = to_delayed_work(work); 12648c2ecf20Sopenharmony_ci struct qeth_addr_change_data *data; 12658c2ecf20Sopenharmony_ci struct qeth_card *card; 12668c2ecf20Sopenharmony_ci struct qeth_priv *priv; 12678c2ecf20Sopenharmony_ci unsigned int i; 12688c2ecf20Sopenharmony_ci int rc; 12698c2ecf20Sopenharmony_ci 12708c2ecf20Sopenharmony_ci data = container_of(dwork, struct qeth_addr_change_data, dwork); 12718c2ecf20Sopenharmony_ci card = data->card; 12728c2ecf20Sopenharmony_ci priv = netdev_priv(card->dev); 12738c2ecf20Sopenharmony_ci 12748c2ecf20Sopenharmony_ci QETH_CARD_TEXT(card, 4, "dev2brew"); 12758c2ecf20Sopenharmony_ci 12768c2ecf20Sopenharmony_ci if (READ_ONCE(card->info.pnso_mode) == QETH_PNSO_NONE) 12778c2ecf20Sopenharmony_ci goto free; 12788c2ecf20Sopenharmony_ci 12798c2ecf20Sopenharmony_ci /* Potential re-config in progress, try again later: */ 12808c2ecf20Sopenharmony_ci if (!rtnl_trylock()) { 12818c2ecf20Sopenharmony_ci queue_delayed_work(card->event_wq, dwork, 12828c2ecf20Sopenharmony_ci msecs_to_jiffies(100)); 12838c2ecf20Sopenharmony_ci return; 12848c2ecf20Sopenharmony_ci } 12858c2ecf20Sopenharmony_ci if (!netif_device_present(card->dev)) 12868c2ecf20Sopenharmony_ci goto out_unlock; 12878c2ecf20Sopenharmony_ci 12888c2ecf20Sopenharmony_ci if (data->ac_event.lost_event_mask) { 12898c2ecf20Sopenharmony_ci QETH_DBF_MESSAGE(3, 12908c2ecf20Sopenharmony_ci "Address change notification overflow on device %x\n", 12918c2ecf20Sopenharmony_ci CARD_DEVID(card)); 12928c2ecf20Sopenharmony_ci /* Card fdb and bridge fdb are out of sync, card has stopped 12938c2ecf20Sopenharmony_ci * notifications (no need to drain_workqueue). Purge all 12948c2ecf20Sopenharmony_ci * 'extern_learn' entries from the parent bridge and restart 12958c2ecf20Sopenharmony_ci * the notifications. 12968c2ecf20Sopenharmony_ci */ 12978c2ecf20Sopenharmony_ci qeth_l2_dev2br_fdb_flush(card); 12988c2ecf20Sopenharmony_ci rc = qeth_l2_dev2br_an_set(card, true); 12998c2ecf20Sopenharmony_ci if (rc) { 13008c2ecf20Sopenharmony_ci /* TODO: if we want to retry after -EAGAIN, be 13018c2ecf20Sopenharmony_ci * aware there could be stale entries in the 13028c2ecf20Sopenharmony_ci * workqueue now, that need to be drained. 13038c2ecf20Sopenharmony_ci * For now we give up: 13048c2ecf20Sopenharmony_ci */ 13058c2ecf20Sopenharmony_ci netdev_err(card->dev, 13068c2ecf20Sopenharmony_ci "bridge learning_sync failed to recover: %d\n", 13078c2ecf20Sopenharmony_ci rc); 13088c2ecf20Sopenharmony_ci WRITE_ONCE(card->info.pnso_mode, 13098c2ecf20Sopenharmony_ci QETH_PNSO_NONE); 13108c2ecf20Sopenharmony_ci /* To remove fdb entries reported by an_set: */ 13118c2ecf20Sopenharmony_ci qeth_l2_dev2br_fdb_flush(card); 13128c2ecf20Sopenharmony_ci priv->brport_features ^= BR_LEARNING_SYNC; 13138c2ecf20Sopenharmony_ci } else { 13148c2ecf20Sopenharmony_ci QETH_DBF_MESSAGE(3, 13158c2ecf20Sopenharmony_ci "Address Notification resynced on device %x\n", 13168c2ecf20Sopenharmony_ci CARD_DEVID(card)); 13178c2ecf20Sopenharmony_ci } 13188c2ecf20Sopenharmony_ci } else { 13198c2ecf20Sopenharmony_ci for (i = 0; i < data->ac_event.num_entries; i++) { 13208c2ecf20Sopenharmony_ci struct qeth_ipacmd_addr_change_entry *entry = 13218c2ecf20Sopenharmony_ci &data->ac_event.entry[i]; 13228c2ecf20Sopenharmony_ci qeth_l2_dev2br_fdb_notify(card, 13238c2ecf20Sopenharmony_ci entry->change_code, 13248c2ecf20Sopenharmony_ci &entry->token, 13258c2ecf20Sopenharmony_ci &entry->addr_lnid); 13268c2ecf20Sopenharmony_ci } 13278c2ecf20Sopenharmony_ci } 13288c2ecf20Sopenharmony_ci 13298c2ecf20Sopenharmony_ciout_unlock: 13308c2ecf20Sopenharmony_ci rtnl_unlock(); 13318c2ecf20Sopenharmony_ci 13328c2ecf20Sopenharmony_cifree: 13338c2ecf20Sopenharmony_ci kfree(data); 13348c2ecf20Sopenharmony_ci} 13358c2ecf20Sopenharmony_ci 13368c2ecf20Sopenharmony_cistatic void qeth_addr_change_event_worker(struct work_struct *work) 13378c2ecf20Sopenharmony_ci{ 13388c2ecf20Sopenharmony_ci struct delayed_work *dwork = to_delayed_work(work); 13398c2ecf20Sopenharmony_ci struct qeth_addr_change_data *data; 13408c2ecf20Sopenharmony_ci struct qeth_card *card; 13418c2ecf20Sopenharmony_ci int i; 13428c2ecf20Sopenharmony_ci 13438c2ecf20Sopenharmony_ci data = container_of(dwork, struct qeth_addr_change_data, dwork); 13448c2ecf20Sopenharmony_ci card = data->card; 13458c2ecf20Sopenharmony_ci 13468c2ecf20Sopenharmony_ci QETH_CARD_TEXT(data->card, 4, "adrchgew"); 13478c2ecf20Sopenharmony_ci 13488c2ecf20Sopenharmony_ci if (READ_ONCE(card->info.pnso_mode) == QETH_PNSO_NONE) 13498c2ecf20Sopenharmony_ci goto free; 13508c2ecf20Sopenharmony_ci 13518c2ecf20Sopenharmony_ci if (data->ac_event.lost_event_mask) { 13528c2ecf20Sopenharmony_ci /* Potential re-config in progress, try again later: */ 13538c2ecf20Sopenharmony_ci if (!mutex_trylock(&card->sbp_lock)) { 13548c2ecf20Sopenharmony_ci queue_delayed_work(card->event_wq, dwork, 13558c2ecf20Sopenharmony_ci msecs_to_jiffies(100)); 13568c2ecf20Sopenharmony_ci return; 13578c2ecf20Sopenharmony_ci } 13588c2ecf20Sopenharmony_ci 13598c2ecf20Sopenharmony_ci dev_info(&data->card->gdev->dev, 13608c2ecf20Sopenharmony_ci "Address change notification stopped on %s (%s)\n", 13618c2ecf20Sopenharmony_ci netdev_name(card->dev), 13628c2ecf20Sopenharmony_ci (data->ac_event.lost_event_mask == 0x01) 13638c2ecf20Sopenharmony_ci ? "Overflow" 13648c2ecf20Sopenharmony_ci : (data->ac_event.lost_event_mask == 0x02) 13658c2ecf20Sopenharmony_ci ? "Bridge port state change" 13668c2ecf20Sopenharmony_ci : "Unknown reason"); 13678c2ecf20Sopenharmony_ci 13688c2ecf20Sopenharmony_ci data->card->options.sbp.hostnotification = 0; 13698c2ecf20Sopenharmony_ci card->info.pnso_mode = QETH_PNSO_NONE; 13708c2ecf20Sopenharmony_ci mutex_unlock(&data->card->sbp_lock); 13718c2ecf20Sopenharmony_ci qeth_bridge_emit_host_event(data->card, anev_abort, 13728c2ecf20Sopenharmony_ci 0, NULL, NULL); 13738c2ecf20Sopenharmony_ci } else 13748c2ecf20Sopenharmony_ci for (i = 0; i < data->ac_event.num_entries; i++) { 13758c2ecf20Sopenharmony_ci struct qeth_ipacmd_addr_change_entry *entry = 13768c2ecf20Sopenharmony_ci &data->ac_event.entry[i]; 13778c2ecf20Sopenharmony_ci qeth_bridge_emit_host_event(data->card, 13788c2ecf20Sopenharmony_ci anev_reg_unreg, 13798c2ecf20Sopenharmony_ci entry->change_code, 13808c2ecf20Sopenharmony_ci &entry->token, 13818c2ecf20Sopenharmony_ci &entry->addr_lnid); 13828c2ecf20Sopenharmony_ci } 13838c2ecf20Sopenharmony_ci 13848c2ecf20Sopenharmony_cifree: 13858c2ecf20Sopenharmony_ci kfree(data); 13868c2ecf20Sopenharmony_ci} 13878c2ecf20Sopenharmony_ci 13888c2ecf20Sopenharmony_cistatic void qeth_addr_change_event(struct qeth_card *card, 13898c2ecf20Sopenharmony_ci struct qeth_ipa_cmd *cmd) 13908c2ecf20Sopenharmony_ci{ 13918c2ecf20Sopenharmony_ci struct qeth_ipacmd_addr_change *hostevs = 13928c2ecf20Sopenharmony_ci &cmd->data.addrchange; 13938c2ecf20Sopenharmony_ci struct qeth_addr_change_data *data; 13948c2ecf20Sopenharmony_ci int extrasize; 13958c2ecf20Sopenharmony_ci 13968c2ecf20Sopenharmony_ci if (card->info.pnso_mode == QETH_PNSO_NONE) 13978c2ecf20Sopenharmony_ci return; 13988c2ecf20Sopenharmony_ci 13998c2ecf20Sopenharmony_ci QETH_CARD_TEXT(card, 4, "adrchgev"); 14008c2ecf20Sopenharmony_ci if (cmd->hdr.return_code != 0x0000) { 14018c2ecf20Sopenharmony_ci if (cmd->hdr.return_code == 0x0010) { 14028c2ecf20Sopenharmony_ci if (hostevs->lost_event_mask == 0x00) 14038c2ecf20Sopenharmony_ci hostevs->lost_event_mask = 0xff; 14048c2ecf20Sopenharmony_ci } else { 14058c2ecf20Sopenharmony_ci QETH_CARD_TEXT_(card, 2, "ACHN%04x", 14068c2ecf20Sopenharmony_ci cmd->hdr.return_code); 14078c2ecf20Sopenharmony_ci return; 14088c2ecf20Sopenharmony_ci } 14098c2ecf20Sopenharmony_ci } 14108c2ecf20Sopenharmony_ci extrasize = sizeof(struct qeth_ipacmd_addr_change_entry) * 14118c2ecf20Sopenharmony_ci hostevs->num_entries; 14128c2ecf20Sopenharmony_ci data = kzalloc(sizeof(struct qeth_addr_change_data) + extrasize, 14138c2ecf20Sopenharmony_ci GFP_ATOMIC); 14148c2ecf20Sopenharmony_ci if (!data) { 14158c2ecf20Sopenharmony_ci QETH_CARD_TEXT(card, 2, "ACNalloc"); 14168c2ecf20Sopenharmony_ci return; 14178c2ecf20Sopenharmony_ci } 14188c2ecf20Sopenharmony_ci if (card->info.pnso_mode == QETH_PNSO_BRIDGEPORT) 14198c2ecf20Sopenharmony_ci INIT_DELAYED_WORK(&data->dwork, qeth_addr_change_event_worker); 14208c2ecf20Sopenharmony_ci else 14218c2ecf20Sopenharmony_ci INIT_DELAYED_WORK(&data->dwork, qeth_l2_dev2br_worker); 14228c2ecf20Sopenharmony_ci data->card = card; 14238c2ecf20Sopenharmony_ci memcpy(&data->ac_event, hostevs, 14248c2ecf20Sopenharmony_ci sizeof(struct qeth_ipacmd_addr_change) + extrasize); 14258c2ecf20Sopenharmony_ci queue_delayed_work(card->event_wq, &data->dwork, 0); 14268c2ecf20Sopenharmony_ci} 14278c2ecf20Sopenharmony_ci 14288c2ecf20Sopenharmony_ci/* SETBRIDGEPORT support; sending commands */ 14298c2ecf20Sopenharmony_ci 14308c2ecf20Sopenharmony_cistruct _qeth_sbp_cbctl { 14318c2ecf20Sopenharmony_ci union { 14328c2ecf20Sopenharmony_ci u32 supported; 14338c2ecf20Sopenharmony_ci struct { 14348c2ecf20Sopenharmony_ci enum qeth_sbp_roles *role; 14358c2ecf20Sopenharmony_ci enum qeth_sbp_states *state; 14368c2ecf20Sopenharmony_ci } qports; 14378c2ecf20Sopenharmony_ci } data; 14388c2ecf20Sopenharmony_ci}; 14398c2ecf20Sopenharmony_ci 14408c2ecf20Sopenharmony_cistatic int qeth_bridgeport_makerc(struct qeth_card *card, 14418c2ecf20Sopenharmony_ci struct qeth_ipa_cmd *cmd) 14428c2ecf20Sopenharmony_ci{ 14438c2ecf20Sopenharmony_ci struct qeth_ipacmd_setbridgeport *sbp = &cmd->data.sbp; 14448c2ecf20Sopenharmony_ci enum qeth_ipa_sbp_cmd setcmd = sbp->hdr.command_code; 14458c2ecf20Sopenharmony_ci u16 ipa_rc = cmd->hdr.return_code; 14468c2ecf20Sopenharmony_ci u16 sbp_rc = sbp->hdr.return_code; 14478c2ecf20Sopenharmony_ci int rc; 14488c2ecf20Sopenharmony_ci 14498c2ecf20Sopenharmony_ci if (ipa_rc == IPA_RC_SUCCESS && sbp_rc == IPA_RC_SUCCESS) 14508c2ecf20Sopenharmony_ci return 0; 14518c2ecf20Sopenharmony_ci 14528c2ecf20Sopenharmony_ci if ((IS_IQD(card) && ipa_rc == IPA_RC_SUCCESS) || 14538c2ecf20Sopenharmony_ci (!IS_IQD(card) && ipa_rc == sbp_rc)) { 14548c2ecf20Sopenharmony_ci switch (sbp_rc) { 14558c2ecf20Sopenharmony_ci case IPA_RC_SUCCESS: 14568c2ecf20Sopenharmony_ci rc = 0; 14578c2ecf20Sopenharmony_ci break; 14588c2ecf20Sopenharmony_ci case IPA_RC_L2_UNSUPPORTED_CMD: 14598c2ecf20Sopenharmony_ci case IPA_RC_UNSUPPORTED_COMMAND: 14608c2ecf20Sopenharmony_ci rc = -EOPNOTSUPP; 14618c2ecf20Sopenharmony_ci break; 14628c2ecf20Sopenharmony_ci case IPA_RC_SBP_OSA_NOT_CONFIGURED: 14638c2ecf20Sopenharmony_ci case IPA_RC_SBP_IQD_NOT_CONFIGURED: 14648c2ecf20Sopenharmony_ci rc = -ENODEV; /* maybe not the best code here? */ 14658c2ecf20Sopenharmony_ci dev_err(&card->gdev->dev, 14668c2ecf20Sopenharmony_ci "The device is not configured as a Bridge Port\n"); 14678c2ecf20Sopenharmony_ci break; 14688c2ecf20Sopenharmony_ci case IPA_RC_SBP_OSA_OS_MISMATCH: 14698c2ecf20Sopenharmony_ci case IPA_RC_SBP_IQD_OS_MISMATCH: 14708c2ecf20Sopenharmony_ci rc = -EPERM; 14718c2ecf20Sopenharmony_ci dev_err(&card->gdev->dev, 14728c2ecf20Sopenharmony_ci "A Bridge Port is already configured by a different operating system\n"); 14738c2ecf20Sopenharmony_ci break; 14748c2ecf20Sopenharmony_ci case IPA_RC_SBP_OSA_ANO_DEV_PRIMARY: 14758c2ecf20Sopenharmony_ci case IPA_RC_SBP_IQD_ANO_DEV_PRIMARY: 14768c2ecf20Sopenharmony_ci switch (setcmd) { 14778c2ecf20Sopenharmony_ci case IPA_SBP_SET_PRIMARY_BRIDGE_PORT: 14788c2ecf20Sopenharmony_ci rc = -EEXIST; 14798c2ecf20Sopenharmony_ci dev_err(&card->gdev->dev, 14808c2ecf20Sopenharmony_ci "The LAN already has a primary Bridge Port\n"); 14818c2ecf20Sopenharmony_ci break; 14828c2ecf20Sopenharmony_ci case IPA_SBP_SET_SECONDARY_BRIDGE_PORT: 14838c2ecf20Sopenharmony_ci rc = -EBUSY; 14848c2ecf20Sopenharmony_ci dev_err(&card->gdev->dev, 14858c2ecf20Sopenharmony_ci "The device is already a primary Bridge Port\n"); 14868c2ecf20Sopenharmony_ci break; 14878c2ecf20Sopenharmony_ci default: 14888c2ecf20Sopenharmony_ci rc = -EIO; 14898c2ecf20Sopenharmony_ci } 14908c2ecf20Sopenharmony_ci break; 14918c2ecf20Sopenharmony_ci case IPA_RC_SBP_OSA_CURRENT_SECOND: 14928c2ecf20Sopenharmony_ci case IPA_RC_SBP_IQD_CURRENT_SECOND: 14938c2ecf20Sopenharmony_ci rc = -EBUSY; 14948c2ecf20Sopenharmony_ci dev_err(&card->gdev->dev, 14958c2ecf20Sopenharmony_ci "The device is already a secondary Bridge Port\n"); 14968c2ecf20Sopenharmony_ci break; 14978c2ecf20Sopenharmony_ci case IPA_RC_SBP_OSA_LIMIT_SECOND: 14988c2ecf20Sopenharmony_ci case IPA_RC_SBP_IQD_LIMIT_SECOND: 14998c2ecf20Sopenharmony_ci rc = -EEXIST; 15008c2ecf20Sopenharmony_ci dev_err(&card->gdev->dev, 15018c2ecf20Sopenharmony_ci "The LAN cannot have more secondary Bridge Ports\n"); 15028c2ecf20Sopenharmony_ci break; 15038c2ecf20Sopenharmony_ci case IPA_RC_SBP_OSA_CURRENT_PRIMARY: 15048c2ecf20Sopenharmony_ci case IPA_RC_SBP_IQD_CURRENT_PRIMARY: 15058c2ecf20Sopenharmony_ci rc = -EBUSY; 15068c2ecf20Sopenharmony_ci dev_err(&card->gdev->dev, 15078c2ecf20Sopenharmony_ci "The device is already a primary Bridge Port\n"); 15088c2ecf20Sopenharmony_ci break; 15098c2ecf20Sopenharmony_ci case IPA_RC_SBP_OSA_NOT_AUTHD_BY_ZMAN: 15108c2ecf20Sopenharmony_ci case IPA_RC_SBP_IQD_NOT_AUTHD_BY_ZMAN: 15118c2ecf20Sopenharmony_ci rc = -EACCES; 15128c2ecf20Sopenharmony_ci dev_err(&card->gdev->dev, 15138c2ecf20Sopenharmony_ci "The device is not authorized to be a Bridge Port\n"); 15148c2ecf20Sopenharmony_ci break; 15158c2ecf20Sopenharmony_ci default: 15168c2ecf20Sopenharmony_ci rc = -EIO; 15178c2ecf20Sopenharmony_ci } 15188c2ecf20Sopenharmony_ci } else { 15198c2ecf20Sopenharmony_ci switch (ipa_rc) { 15208c2ecf20Sopenharmony_ci case IPA_RC_NOTSUPP: 15218c2ecf20Sopenharmony_ci rc = -EOPNOTSUPP; 15228c2ecf20Sopenharmony_ci break; 15238c2ecf20Sopenharmony_ci case IPA_RC_UNSUPPORTED_COMMAND: 15248c2ecf20Sopenharmony_ci rc = -EOPNOTSUPP; 15258c2ecf20Sopenharmony_ci break; 15268c2ecf20Sopenharmony_ci default: 15278c2ecf20Sopenharmony_ci rc = -EIO; 15288c2ecf20Sopenharmony_ci } 15298c2ecf20Sopenharmony_ci } 15308c2ecf20Sopenharmony_ci 15318c2ecf20Sopenharmony_ci if (rc) { 15328c2ecf20Sopenharmony_ci QETH_CARD_TEXT_(card, 2, "SBPi%04x", ipa_rc); 15338c2ecf20Sopenharmony_ci QETH_CARD_TEXT_(card, 2, "SBPc%04x", sbp_rc); 15348c2ecf20Sopenharmony_ci } 15358c2ecf20Sopenharmony_ci return rc; 15368c2ecf20Sopenharmony_ci} 15378c2ecf20Sopenharmony_ci 15388c2ecf20Sopenharmony_cistatic struct qeth_cmd_buffer *qeth_sbp_build_cmd(struct qeth_card *card, 15398c2ecf20Sopenharmony_ci enum qeth_ipa_sbp_cmd sbp_cmd, 15408c2ecf20Sopenharmony_ci unsigned int data_length) 15418c2ecf20Sopenharmony_ci{ 15428c2ecf20Sopenharmony_ci enum qeth_ipa_cmds ipa_cmd = IS_IQD(card) ? IPA_CMD_SETBRIDGEPORT_IQD : 15438c2ecf20Sopenharmony_ci IPA_CMD_SETBRIDGEPORT_OSA; 15448c2ecf20Sopenharmony_ci struct qeth_ipacmd_sbp_hdr *hdr; 15458c2ecf20Sopenharmony_ci struct qeth_cmd_buffer *iob; 15468c2ecf20Sopenharmony_ci 15478c2ecf20Sopenharmony_ci iob = qeth_ipa_alloc_cmd(card, ipa_cmd, QETH_PROT_NONE, 15488c2ecf20Sopenharmony_ci data_length + 15498c2ecf20Sopenharmony_ci offsetof(struct qeth_ipacmd_setbridgeport, 15508c2ecf20Sopenharmony_ci data)); 15518c2ecf20Sopenharmony_ci if (!iob) 15528c2ecf20Sopenharmony_ci return iob; 15538c2ecf20Sopenharmony_ci 15548c2ecf20Sopenharmony_ci hdr = &__ipa_cmd(iob)->data.sbp.hdr; 15558c2ecf20Sopenharmony_ci hdr->cmdlength = sizeof(*hdr) + data_length; 15568c2ecf20Sopenharmony_ci hdr->command_code = sbp_cmd; 15578c2ecf20Sopenharmony_ci hdr->used_total = 1; 15588c2ecf20Sopenharmony_ci hdr->seq_no = 1; 15598c2ecf20Sopenharmony_ci return iob; 15608c2ecf20Sopenharmony_ci} 15618c2ecf20Sopenharmony_ci 15628c2ecf20Sopenharmony_cistatic int qeth_bridgeport_query_support_cb(struct qeth_card *card, 15638c2ecf20Sopenharmony_ci struct qeth_reply *reply, unsigned long data) 15648c2ecf20Sopenharmony_ci{ 15658c2ecf20Sopenharmony_ci struct qeth_ipa_cmd *cmd = (struct qeth_ipa_cmd *) data; 15668c2ecf20Sopenharmony_ci struct _qeth_sbp_cbctl *cbctl = (struct _qeth_sbp_cbctl *)reply->param; 15678c2ecf20Sopenharmony_ci int rc; 15688c2ecf20Sopenharmony_ci 15698c2ecf20Sopenharmony_ci QETH_CARD_TEXT(card, 2, "brqsupcb"); 15708c2ecf20Sopenharmony_ci rc = qeth_bridgeport_makerc(card, cmd); 15718c2ecf20Sopenharmony_ci if (rc) 15728c2ecf20Sopenharmony_ci return rc; 15738c2ecf20Sopenharmony_ci 15748c2ecf20Sopenharmony_ci cbctl->data.supported = 15758c2ecf20Sopenharmony_ci cmd->data.sbp.data.query_cmds_supp.supported_cmds; 15768c2ecf20Sopenharmony_ci return 0; 15778c2ecf20Sopenharmony_ci} 15788c2ecf20Sopenharmony_ci 15798c2ecf20Sopenharmony_ci/** 15808c2ecf20Sopenharmony_ci * qeth_bridgeport_query_support() - store bitmask of supported subfunctions. 15818c2ecf20Sopenharmony_ci * @card: qeth_card structure pointer. 15828c2ecf20Sopenharmony_ci * 15838c2ecf20Sopenharmony_ci * Sets bitmask of supported setbridgeport subfunctions in the qeth_card 15848c2ecf20Sopenharmony_ci * strucutre: card->options.sbp.supported_funcs. 15858c2ecf20Sopenharmony_ci */ 15868c2ecf20Sopenharmony_cistatic void qeth_bridgeport_query_support(struct qeth_card *card) 15878c2ecf20Sopenharmony_ci{ 15888c2ecf20Sopenharmony_ci struct qeth_cmd_buffer *iob; 15898c2ecf20Sopenharmony_ci struct _qeth_sbp_cbctl cbctl; 15908c2ecf20Sopenharmony_ci 15918c2ecf20Sopenharmony_ci QETH_CARD_TEXT(card, 2, "brqsuppo"); 15928c2ecf20Sopenharmony_ci iob = qeth_sbp_build_cmd(card, IPA_SBP_QUERY_COMMANDS_SUPPORTED, 15938c2ecf20Sopenharmony_ci SBP_DATA_SIZEOF(query_cmds_supp)); 15948c2ecf20Sopenharmony_ci if (!iob) 15958c2ecf20Sopenharmony_ci return; 15968c2ecf20Sopenharmony_ci 15978c2ecf20Sopenharmony_ci if (qeth_send_ipa_cmd(card, iob, qeth_bridgeport_query_support_cb, 15988c2ecf20Sopenharmony_ci &cbctl)) { 15998c2ecf20Sopenharmony_ci card->options.sbp.role = QETH_SBP_ROLE_NONE; 16008c2ecf20Sopenharmony_ci card->options.sbp.supported_funcs = 0; 16018c2ecf20Sopenharmony_ci return; 16028c2ecf20Sopenharmony_ci } 16038c2ecf20Sopenharmony_ci card->options.sbp.supported_funcs = cbctl.data.supported; 16048c2ecf20Sopenharmony_ci} 16058c2ecf20Sopenharmony_ci 16068c2ecf20Sopenharmony_cistatic int qeth_bridgeport_query_ports_cb(struct qeth_card *card, 16078c2ecf20Sopenharmony_ci struct qeth_reply *reply, unsigned long data) 16088c2ecf20Sopenharmony_ci{ 16098c2ecf20Sopenharmony_ci struct qeth_ipa_cmd *cmd = (struct qeth_ipa_cmd *) data; 16108c2ecf20Sopenharmony_ci struct _qeth_sbp_cbctl *cbctl = (struct _qeth_sbp_cbctl *)reply->param; 16118c2ecf20Sopenharmony_ci struct qeth_sbp_port_data *qports; 16128c2ecf20Sopenharmony_ci int rc; 16138c2ecf20Sopenharmony_ci 16148c2ecf20Sopenharmony_ci QETH_CARD_TEXT(card, 2, "brqprtcb"); 16158c2ecf20Sopenharmony_ci rc = qeth_bridgeport_makerc(card, cmd); 16168c2ecf20Sopenharmony_ci if (rc) 16178c2ecf20Sopenharmony_ci return rc; 16188c2ecf20Sopenharmony_ci 16198c2ecf20Sopenharmony_ci qports = &cmd->data.sbp.data.port_data; 16208c2ecf20Sopenharmony_ci if (qports->entry_length != sizeof(struct qeth_sbp_port_entry)) { 16218c2ecf20Sopenharmony_ci QETH_CARD_TEXT_(card, 2, "SBPs%04x", qports->entry_length); 16228c2ecf20Sopenharmony_ci return -EINVAL; 16238c2ecf20Sopenharmony_ci } 16248c2ecf20Sopenharmony_ci /* first entry contains the state of the local port */ 16258c2ecf20Sopenharmony_ci if (qports->num_entries > 0) { 16268c2ecf20Sopenharmony_ci if (cbctl->data.qports.role) 16278c2ecf20Sopenharmony_ci *cbctl->data.qports.role = qports->entry[0].role; 16288c2ecf20Sopenharmony_ci if (cbctl->data.qports.state) 16298c2ecf20Sopenharmony_ci *cbctl->data.qports.state = qports->entry[0].state; 16308c2ecf20Sopenharmony_ci } 16318c2ecf20Sopenharmony_ci return 0; 16328c2ecf20Sopenharmony_ci} 16338c2ecf20Sopenharmony_ci 16348c2ecf20Sopenharmony_ci/** 16358c2ecf20Sopenharmony_ci * qeth_bridgeport_query_ports() - query local bridgeport status. 16368c2ecf20Sopenharmony_ci * @card: qeth_card structure pointer. 16378c2ecf20Sopenharmony_ci * @role: Role of the port: 0-none, 1-primary, 2-secondary. 16388c2ecf20Sopenharmony_ci * @state: State of the port: 0-inactive, 1-standby, 2-active. 16398c2ecf20Sopenharmony_ci * 16408c2ecf20Sopenharmony_ci * Returns negative errno-compatible error indication or 0 on success. 16418c2ecf20Sopenharmony_ci * 16428c2ecf20Sopenharmony_ci * 'role' and 'state' are not updated in case of hardware operation failure. 16438c2ecf20Sopenharmony_ci */ 16448c2ecf20Sopenharmony_ciint qeth_bridgeport_query_ports(struct qeth_card *card, 16458c2ecf20Sopenharmony_ci enum qeth_sbp_roles *role, enum qeth_sbp_states *state) 16468c2ecf20Sopenharmony_ci{ 16478c2ecf20Sopenharmony_ci struct qeth_cmd_buffer *iob; 16488c2ecf20Sopenharmony_ci struct _qeth_sbp_cbctl cbctl = { 16498c2ecf20Sopenharmony_ci .data = { 16508c2ecf20Sopenharmony_ci .qports = { 16518c2ecf20Sopenharmony_ci .role = role, 16528c2ecf20Sopenharmony_ci .state = state, 16538c2ecf20Sopenharmony_ci }, 16548c2ecf20Sopenharmony_ci }, 16558c2ecf20Sopenharmony_ci }; 16568c2ecf20Sopenharmony_ci 16578c2ecf20Sopenharmony_ci QETH_CARD_TEXT(card, 2, "brqports"); 16588c2ecf20Sopenharmony_ci if (!(card->options.sbp.supported_funcs & IPA_SBP_QUERY_BRIDGE_PORTS)) 16598c2ecf20Sopenharmony_ci return -EOPNOTSUPP; 16608c2ecf20Sopenharmony_ci iob = qeth_sbp_build_cmd(card, IPA_SBP_QUERY_BRIDGE_PORTS, 0); 16618c2ecf20Sopenharmony_ci if (!iob) 16628c2ecf20Sopenharmony_ci return -ENOMEM; 16638c2ecf20Sopenharmony_ci 16648c2ecf20Sopenharmony_ci return qeth_send_ipa_cmd(card, iob, qeth_bridgeport_query_ports_cb, 16658c2ecf20Sopenharmony_ci &cbctl); 16668c2ecf20Sopenharmony_ci} 16678c2ecf20Sopenharmony_ci 16688c2ecf20Sopenharmony_cistatic int qeth_bridgeport_set_cb(struct qeth_card *card, 16698c2ecf20Sopenharmony_ci struct qeth_reply *reply, unsigned long data) 16708c2ecf20Sopenharmony_ci{ 16718c2ecf20Sopenharmony_ci struct qeth_ipa_cmd *cmd = (struct qeth_ipa_cmd *)data; 16728c2ecf20Sopenharmony_ci 16738c2ecf20Sopenharmony_ci QETH_CARD_TEXT(card, 2, "brsetrcb"); 16748c2ecf20Sopenharmony_ci return qeth_bridgeport_makerc(card, cmd); 16758c2ecf20Sopenharmony_ci} 16768c2ecf20Sopenharmony_ci 16778c2ecf20Sopenharmony_ci/** 16788c2ecf20Sopenharmony_ci * qeth_bridgeport_setrole() - Assign primary role to the port. 16798c2ecf20Sopenharmony_ci * @card: qeth_card structure pointer. 16808c2ecf20Sopenharmony_ci * @role: Role to assign. 16818c2ecf20Sopenharmony_ci * 16828c2ecf20Sopenharmony_ci * Returns negative errno-compatible error indication or 0 on success. 16838c2ecf20Sopenharmony_ci */ 16848c2ecf20Sopenharmony_ciint qeth_bridgeport_setrole(struct qeth_card *card, enum qeth_sbp_roles role) 16858c2ecf20Sopenharmony_ci{ 16868c2ecf20Sopenharmony_ci struct qeth_cmd_buffer *iob; 16878c2ecf20Sopenharmony_ci enum qeth_ipa_sbp_cmd setcmd; 16888c2ecf20Sopenharmony_ci unsigned int cmdlength = 0; 16898c2ecf20Sopenharmony_ci 16908c2ecf20Sopenharmony_ci QETH_CARD_TEXT(card, 2, "brsetrol"); 16918c2ecf20Sopenharmony_ci switch (role) { 16928c2ecf20Sopenharmony_ci case QETH_SBP_ROLE_NONE: 16938c2ecf20Sopenharmony_ci setcmd = IPA_SBP_RESET_BRIDGE_PORT_ROLE; 16948c2ecf20Sopenharmony_ci break; 16958c2ecf20Sopenharmony_ci case QETH_SBP_ROLE_PRIMARY: 16968c2ecf20Sopenharmony_ci setcmd = IPA_SBP_SET_PRIMARY_BRIDGE_PORT; 16978c2ecf20Sopenharmony_ci cmdlength = SBP_DATA_SIZEOF(set_primary); 16988c2ecf20Sopenharmony_ci break; 16998c2ecf20Sopenharmony_ci case QETH_SBP_ROLE_SECONDARY: 17008c2ecf20Sopenharmony_ci setcmd = IPA_SBP_SET_SECONDARY_BRIDGE_PORT; 17018c2ecf20Sopenharmony_ci break; 17028c2ecf20Sopenharmony_ci default: 17038c2ecf20Sopenharmony_ci return -EINVAL; 17048c2ecf20Sopenharmony_ci } 17058c2ecf20Sopenharmony_ci if (!(card->options.sbp.supported_funcs & setcmd)) 17068c2ecf20Sopenharmony_ci return -EOPNOTSUPP; 17078c2ecf20Sopenharmony_ci iob = qeth_sbp_build_cmd(card, setcmd, cmdlength); 17088c2ecf20Sopenharmony_ci if (!iob) 17098c2ecf20Sopenharmony_ci return -ENOMEM; 17108c2ecf20Sopenharmony_ci 17118c2ecf20Sopenharmony_ci return qeth_send_ipa_cmd(card, iob, qeth_bridgeport_set_cb, NULL); 17128c2ecf20Sopenharmony_ci} 17138c2ecf20Sopenharmony_ci 17148c2ecf20Sopenharmony_cistatic void qeth_bridgeport_an_set_cb(void *priv, 17158c2ecf20Sopenharmony_ci struct chsc_pnso_naid_l2 *entry) 17168c2ecf20Sopenharmony_ci{ 17178c2ecf20Sopenharmony_ci struct qeth_card *card = (struct qeth_card *)priv; 17188c2ecf20Sopenharmony_ci u8 code; 17198c2ecf20Sopenharmony_ci 17208c2ecf20Sopenharmony_ci code = IPA_ADDR_CHANGE_CODE_MACADDR; 17218c2ecf20Sopenharmony_ci if (entry->addr_lnid.lnid < VLAN_N_VID) 17228c2ecf20Sopenharmony_ci code |= IPA_ADDR_CHANGE_CODE_VLANID; 17238c2ecf20Sopenharmony_ci qeth_bridge_emit_host_event(card, anev_reg_unreg, code, 17248c2ecf20Sopenharmony_ci (struct net_if_token *)&entry->nit, 17258c2ecf20Sopenharmony_ci (struct mac_addr_lnid *)&entry->addr_lnid); 17268c2ecf20Sopenharmony_ci} 17278c2ecf20Sopenharmony_ci 17288c2ecf20Sopenharmony_ci/** 17298c2ecf20Sopenharmony_ci * qeth_bridgeport_an_set() - Enable or disable bridgeport address notification 17308c2ecf20Sopenharmony_ci * @card: qeth_card structure pointer. 17318c2ecf20Sopenharmony_ci * @enable: 0 - disable, non-zero - enable notifications 17328c2ecf20Sopenharmony_ci * 17338c2ecf20Sopenharmony_ci * Returns negative errno-compatible error indication or 0 on success. 17348c2ecf20Sopenharmony_ci * 17358c2ecf20Sopenharmony_ci * On enable, emits a series of address notifications udev events for all 17368c2ecf20Sopenharmony_ci * currently registered hosts. 17378c2ecf20Sopenharmony_ci */ 17388c2ecf20Sopenharmony_ciint qeth_bridgeport_an_set(struct qeth_card *card, int enable) 17398c2ecf20Sopenharmony_ci{ 17408c2ecf20Sopenharmony_ci int rc; 17418c2ecf20Sopenharmony_ci 17428c2ecf20Sopenharmony_ci if (!card->options.sbp.supported_funcs) 17438c2ecf20Sopenharmony_ci return -EOPNOTSUPP; 17448c2ecf20Sopenharmony_ci 17458c2ecf20Sopenharmony_ci if (enable) { 17468c2ecf20Sopenharmony_ci qeth_bridge_emit_host_event(card, anev_reset, 0, NULL, NULL); 17478c2ecf20Sopenharmony_ci qeth_l2_set_pnso_mode(card, QETH_PNSO_BRIDGEPORT); 17488c2ecf20Sopenharmony_ci rc = qeth_l2_pnso(card, PNSO_OC_NET_BRIDGE_INFO, 1, 17498c2ecf20Sopenharmony_ci qeth_bridgeport_an_set_cb, card); 17508c2ecf20Sopenharmony_ci if (rc) 17518c2ecf20Sopenharmony_ci qeth_l2_set_pnso_mode(card, QETH_PNSO_NONE); 17528c2ecf20Sopenharmony_ci } else { 17538c2ecf20Sopenharmony_ci rc = qeth_l2_pnso(card, PNSO_OC_NET_BRIDGE_INFO, 0, NULL, NULL); 17548c2ecf20Sopenharmony_ci qeth_l2_set_pnso_mode(card, QETH_PNSO_NONE); 17558c2ecf20Sopenharmony_ci } 17568c2ecf20Sopenharmony_ci return rc; 17578c2ecf20Sopenharmony_ci} 17588c2ecf20Sopenharmony_ci 17598c2ecf20Sopenharmony_ci/* VNIC Characteristics support */ 17608c2ecf20Sopenharmony_ci 17618c2ecf20Sopenharmony_ci/* handle VNICC IPA command return codes; convert to error codes */ 17628c2ecf20Sopenharmony_cistatic int qeth_l2_vnicc_makerc(struct qeth_card *card, u16 ipa_rc) 17638c2ecf20Sopenharmony_ci{ 17648c2ecf20Sopenharmony_ci int rc; 17658c2ecf20Sopenharmony_ci 17668c2ecf20Sopenharmony_ci switch (ipa_rc) { 17678c2ecf20Sopenharmony_ci case IPA_RC_SUCCESS: 17688c2ecf20Sopenharmony_ci return ipa_rc; 17698c2ecf20Sopenharmony_ci case IPA_RC_L2_UNSUPPORTED_CMD: 17708c2ecf20Sopenharmony_ci case IPA_RC_NOTSUPP: 17718c2ecf20Sopenharmony_ci rc = -EOPNOTSUPP; 17728c2ecf20Sopenharmony_ci break; 17738c2ecf20Sopenharmony_ci case IPA_RC_VNICC_OOSEQ: 17748c2ecf20Sopenharmony_ci rc = -EALREADY; 17758c2ecf20Sopenharmony_ci break; 17768c2ecf20Sopenharmony_ci case IPA_RC_VNICC_VNICBP: 17778c2ecf20Sopenharmony_ci rc = -EBUSY; 17788c2ecf20Sopenharmony_ci break; 17798c2ecf20Sopenharmony_ci case IPA_RC_L2_ADDR_TABLE_FULL: 17808c2ecf20Sopenharmony_ci rc = -ENOSPC; 17818c2ecf20Sopenharmony_ci break; 17828c2ecf20Sopenharmony_ci case IPA_RC_L2_MAC_NOT_AUTH_BY_ADP: 17838c2ecf20Sopenharmony_ci rc = -EACCES; 17848c2ecf20Sopenharmony_ci break; 17858c2ecf20Sopenharmony_ci default: 17868c2ecf20Sopenharmony_ci rc = -EIO; 17878c2ecf20Sopenharmony_ci } 17888c2ecf20Sopenharmony_ci 17898c2ecf20Sopenharmony_ci QETH_CARD_TEXT_(card, 2, "err%04x", ipa_rc); 17908c2ecf20Sopenharmony_ci return rc; 17918c2ecf20Sopenharmony_ci} 17928c2ecf20Sopenharmony_ci 17938c2ecf20Sopenharmony_ci/* generic VNICC request call back */ 17948c2ecf20Sopenharmony_cistatic int qeth_l2_vnicc_request_cb(struct qeth_card *card, 17958c2ecf20Sopenharmony_ci struct qeth_reply *reply, 17968c2ecf20Sopenharmony_ci unsigned long data) 17978c2ecf20Sopenharmony_ci{ 17988c2ecf20Sopenharmony_ci struct qeth_ipa_cmd *cmd = (struct qeth_ipa_cmd *) data; 17998c2ecf20Sopenharmony_ci struct qeth_ipacmd_vnicc *rep = &cmd->data.vnicc; 18008c2ecf20Sopenharmony_ci u32 sub_cmd = cmd->data.vnicc.hdr.sub_command; 18018c2ecf20Sopenharmony_ci 18028c2ecf20Sopenharmony_ci QETH_CARD_TEXT(card, 2, "vniccrcb"); 18038c2ecf20Sopenharmony_ci if (cmd->hdr.return_code) 18048c2ecf20Sopenharmony_ci return qeth_l2_vnicc_makerc(card, cmd->hdr.return_code); 18058c2ecf20Sopenharmony_ci /* return results to caller */ 18068c2ecf20Sopenharmony_ci card->options.vnicc.sup_chars = rep->vnicc_cmds.supported; 18078c2ecf20Sopenharmony_ci card->options.vnicc.cur_chars = rep->vnicc_cmds.enabled; 18088c2ecf20Sopenharmony_ci 18098c2ecf20Sopenharmony_ci if (sub_cmd == IPA_VNICC_QUERY_CMDS) 18108c2ecf20Sopenharmony_ci *(u32 *)reply->param = rep->data.query_cmds.sup_cmds; 18118c2ecf20Sopenharmony_ci else if (sub_cmd == IPA_VNICC_GET_TIMEOUT) 18128c2ecf20Sopenharmony_ci *(u32 *)reply->param = rep->data.getset_timeout.timeout; 18138c2ecf20Sopenharmony_ci 18148c2ecf20Sopenharmony_ci return 0; 18158c2ecf20Sopenharmony_ci} 18168c2ecf20Sopenharmony_ci 18178c2ecf20Sopenharmony_cistatic struct qeth_cmd_buffer *qeth_l2_vnicc_build_cmd(struct qeth_card *card, 18188c2ecf20Sopenharmony_ci u32 vnicc_cmd, 18198c2ecf20Sopenharmony_ci unsigned int data_length) 18208c2ecf20Sopenharmony_ci{ 18218c2ecf20Sopenharmony_ci struct qeth_ipacmd_vnicc_hdr *hdr; 18228c2ecf20Sopenharmony_ci struct qeth_cmd_buffer *iob; 18238c2ecf20Sopenharmony_ci 18248c2ecf20Sopenharmony_ci iob = qeth_ipa_alloc_cmd(card, IPA_CMD_VNICC, QETH_PROT_NONE, 18258c2ecf20Sopenharmony_ci data_length + 18268c2ecf20Sopenharmony_ci offsetof(struct qeth_ipacmd_vnicc, data)); 18278c2ecf20Sopenharmony_ci if (!iob) 18288c2ecf20Sopenharmony_ci return NULL; 18298c2ecf20Sopenharmony_ci 18308c2ecf20Sopenharmony_ci hdr = &__ipa_cmd(iob)->data.vnicc.hdr; 18318c2ecf20Sopenharmony_ci hdr->data_length = sizeof(*hdr) + data_length; 18328c2ecf20Sopenharmony_ci hdr->sub_command = vnicc_cmd; 18338c2ecf20Sopenharmony_ci return iob; 18348c2ecf20Sopenharmony_ci} 18358c2ecf20Sopenharmony_ci 18368c2ecf20Sopenharmony_ci/* VNICC query VNIC characteristics request */ 18378c2ecf20Sopenharmony_cistatic int qeth_l2_vnicc_query_chars(struct qeth_card *card) 18388c2ecf20Sopenharmony_ci{ 18398c2ecf20Sopenharmony_ci struct qeth_cmd_buffer *iob; 18408c2ecf20Sopenharmony_ci 18418c2ecf20Sopenharmony_ci QETH_CARD_TEXT(card, 2, "vniccqch"); 18428c2ecf20Sopenharmony_ci iob = qeth_l2_vnicc_build_cmd(card, IPA_VNICC_QUERY_CHARS, 0); 18438c2ecf20Sopenharmony_ci if (!iob) 18448c2ecf20Sopenharmony_ci return -ENOMEM; 18458c2ecf20Sopenharmony_ci 18468c2ecf20Sopenharmony_ci return qeth_send_ipa_cmd(card, iob, qeth_l2_vnicc_request_cb, NULL); 18478c2ecf20Sopenharmony_ci} 18488c2ecf20Sopenharmony_ci 18498c2ecf20Sopenharmony_ci/* VNICC query sub commands request */ 18508c2ecf20Sopenharmony_cistatic int qeth_l2_vnicc_query_cmds(struct qeth_card *card, u32 vnic_char, 18518c2ecf20Sopenharmony_ci u32 *sup_cmds) 18528c2ecf20Sopenharmony_ci{ 18538c2ecf20Sopenharmony_ci struct qeth_cmd_buffer *iob; 18548c2ecf20Sopenharmony_ci 18558c2ecf20Sopenharmony_ci QETH_CARD_TEXT(card, 2, "vniccqcm"); 18568c2ecf20Sopenharmony_ci iob = qeth_l2_vnicc_build_cmd(card, IPA_VNICC_QUERY_CMDS, 18578c2ecf20Sopenharmony_ci VNICC_DATA_SIZEOF(query_cmds)); 18588c2ecf20Sopenharmony_ci if (!iob) 18598c2ecf20Sopenharmony_ci return -ENOMEM; 18608c2ecf20Sopenharmony_ci 18618c2ecf20Sopenharmony_ci __ipa_cmd(iob)->data.vnicc.data.query_cmds.vnic_char = vnic_char; 18628c2ecf20Sopenharmony_ci 18638c2ecf20Sopenharmony_ci return qeth_send_ipa_cmd(card, iob, qeth_l2_vnicc_request_cb, sup_cmds); 18648c2ecf20Sopenharmony_ci} 18658c2ecf20Sopenharmony_ci 18668c2ecf20Sopenharmony_ci/* VNICC enable/disable characteristic request */ 18678c2ecf20Sopenharmony_cistatic int qeth_l2_vnicc_set_char(struct qeth_card *card, u32 vnic_char, 18688c2ecf20Sopenharmony_ci u32 cmd) 18698c2ecf20Sopenharmony_ci{ 18708c2ecf20Sopenharmony_ci struct qeth_cmd_buffer *iob; 18718c2ecf20Sopenharmony_ci 18728c2ecf20Sopenharmony_ci QETH_CARD_TEXT(card, 2, "vniccedc"); 18738c2ecf20Sopenharmony_ci iob = qeth_l2_vnicc_build_cmd(card, cmd, VNICC_DATA_SIZEOF(set_char)); 18748c2ecf20Sopenharmony_ci if (!iob) 18758c2ecf20Sopenharmony_ci return -ENOMEM; 18768c2ecf20Sopenharmony_ci 18778c2ecf20Sopenharmony_ci __ipa_cmd(iob)->data.vnicc.data.set_char.vnic_char = vnic_char; 18788c2ecf20Sopenharmony_ci 18798c2ecf20Sopenharmony_ci return qeth_send_ipa_cmd(card, iob, qeth_l2_vnicc_request_cb, NULL); 18808c2ecf20Sopenharmony_ci} 18818c2ecf20Sopenharmony_ci 18828c2ecf20Sopenharmony_ci/* VNICC get/set timeout for characteristic request */ 18838c2ecf20Sopenharmony_cistatic int qeth_l2_vnicc_getset_timeout(struct qeth_card *card, u32 vnicc, 18848c2ecf20Sopenharmony_ci u32 cmd, u32 *timeout) 18858c2ecf20Sopenharmony_ci{ 18868c2ecf20Sopenharmony_ci struct qeth_vnicc_getset_timeout *getset_timeout; 18878c2ecf20Sopenharmony_ci struct qeth_cmd_buffer *iob; 18888c2ecf20Sopenharmony_ci 18898c2ecf20Sopenharmony_ci QETH_CARD_TEXT(card, 2, "vniccgst"); 18908c2ecf20Sopenharmony_ci iob = qeth_l2_vnicc_build_cmd(card, cmd, 18918c2ecf20Sopenharmony_ci VNICC_DATA_SIZEOF(getset_timeout)); 18928c2ecf20Sopenharmony_ci if (!iob) 18938c2ecf20Sopenharmony_ci return -ENOMEM; 18948c2ecf20Sopenharmony_ci 18958c2ecf20Sopenharmony_ci getset_timeout = &__ipa_cmd(iob)->data.vnicc.data.getset_timeout; 18968c2ecf20Sopenharmony_ci getset_timeout->vnic_char = vnicc; 18978c2ecf20Sopenharmony_ci 18988c2ecf20Sopenharmony_ci if (cmd == IPA_VNICC_SET_TIMEOUT) 18998c2ecf20Sopenharmony_ci getset_timeout->timeout = *timeout; 19008c2ecf20Sopenharmony_ci 19018c2ecf20Sopenharmony_ci return qeth_send_ipa_cmd(card, iob, qeth_l2_vnicc_request_cb, timeout); 19028c2ecf20Sopenharmony_ci} 19038c2ecf20Sopenharmony_ci 19048c2ecf20Sopenharmony_ci/* recover user timeout setting */ 19058c2ecf20Sopenharmony_cistatic bool qeth_l2_vnicc_recover_timeout(struct qeth_card *card, u32 vnicc, 19068c2ecf20Sopenharmony_ci u32 *timeout) 19078c2ecf20Sopenharmony_ci{ 19088c2ecf20Sopenharmony_ci if (card->options.vnicc.sup_chars & vnicc && 19098c2ecf20Sopenharmony_ci card->options.vnicc.getset_timeout_sup & vnicc && 19108c2ecf20Sopenharmony_ci !qeth_l2_vnicc_getset_timeout(card, vnicc, IPA_VNICC_SET_TIMEOUT, 19118c2ecf20Sopenharmony_ci timeout)) 19128c2ecf20Sopenharmony_ci return false; 19138c2ecf20Sopenharmony_ci *timeout = QETH_VNICC_DEFAULT_TIMEOUT; 19148c2ecf20Sopenharmony_ci return true; 19158c2ecf20Sopenharmony_ci} 19168c2ecf20Sopenharmony_ci 19178c2ecf20Sopenharmony_ci/* set current VNICC flag state; called from sysfs store function */ 19188c2ecf20Sopenharmony_ciint qeth_l2_vnicc_set_state(struct qeth_card *card, u32 vnicc, bool state) 19198c2ecf20Sopenharmony_ci{ 19208c2ecf20Sopenharmony_ci int rc = 0; 19218c2ecf20Sopenharmony_ci u32 cmd; 19228c2ecf20Sopenharmony_ci 19238c2ecf20Sopenharmony_ci QETH_CARD_TEXT(card, 2, "vniccsch"); 19248c2ecf20Sopenharmony_ci 19258c2ecf20Sopenharmony_ci /* check if characteristic and enable/disable are supported */ 19268c2ecf20Sopenharmony_ci if (!(card->options.vnicc.sup_chars & vnicc) || 19278c2ecf20Sopenharmony_ci !(card->options.vnicc.set_char_sup & vnicc)) 19288c2ecf20Sopenharmony_ci return -EOPNOTSUPP; 19298c2ecf20Sopenharmony_ci 19308c2ecf20Sopenharmony_ci if (qeth_bridgeport_is_in_use(card)) 19318c2ecf20Sopenharmony_ci return -EBUSY; 19328c2ecf20Sopenharmony_ci 19338c2ecf20Sopenharmony_ci /* set enable/disable command and store wanted characteristic */ 19348c2ecf20Sopenharmony_ci if (state) { 19358c2ecf20Sopenharmony_ci cmd = IPA_VNICC_ENABLE; 19368c2ecf20Sopenharmony_ci card->options.vnicc.wanted_chars |= vnicc; 19378c2ecf20Sopenharmony_ci } else { 19388c2ecf20Sopenharmony_ci cmd = IPA_VNICC_DISABLE; 19398c2ecf20Sopenharmony_ci card->options.vnicc.wanted_chars &= ~vnicc; 19408c2ecf20Sopenharmony_ci } 19418c2ecf20Sopenharmony_ci 19428c2ecf20Sopenharmony_ci /* do we need to do anything? */ 19438c2ecf20Sopenharmony_ci if (card->options.vnicc.cur_chars == card->options.vnicc.wanted_chars) 19448c2ecf20Sopenharmony_ci return rc; 19458c2ecf20Sopenharmony_ci 19468c2ecf20Sopenharmony_ci /* if card is not ready, simply stop here */ 19478c2ecf20Sopenharmony_ci if (!qeth_card_hw_is_reachable(card)) { 19488c2ecf20Sopenharmony_ci if (state) 19498c2ecf20Sopenharmony_ci card->options.vnicc.cur_chars |= vnicc; 19508c2ecf20Sopenharmony_ci else 19518c2ecf20Sopenharmony_ci card->options.vnicc.cur_chars &= ~vnicc; 19528c2ecf20Sopenharmony_ci return rc; 19538c2ecf20Sopenharmony_ci } 19548c2ecf20Sopenharmony_ci 19558c2ecf20Sopenharmony_ci rc = qeth_l2_vnicc_set_char(card, vnicc, cmd); 19568c2ecf20Sopenharmony_ci if (rc) 19578c2ecf20Sopenharmony_ci card->options.vnicc.wanted_chars = 19588c2ecf20Sopenharmony_ci card->options.vnicc.cur_chars; 19598c2ecf20Sopenharmony_ci else { 19608c2ecf20Sopenharmony_ci /* successful online VNICC change; handle special cases */ 19618c2ecf20Sopenharmony_ci if (state && vnicc == QETH_VNICC_RX_BCAST) 19628c2ecf20Sopenharmony_ci card->options.vnicc.rx_bcast_enabled = true; 19638c2ecf20Sopenharmony_ci if (!state && vnicc == QETH_VNICC_LEARNING) 19648c2ecf20Sopenharmony_ci qeth_l2_vnicc_recover_timeout(card, vnicc, 19658c2ecf20Sopenharmony_ci &card->options.vnicc.learning_timeout); 19668c2ecf20Sopenharmony_ci } 19678c2ecf20Sopenharmony_ci 19688c2ecf20Sopenharmony_ci return rc; 19698c2ecf20Sopenharmony_ci} 19708c2ecf20Sopenharmony_ci 19718c2ecf20Sopenharmony_ci/* get current VNICC flag state; called from sysfs show function */ 19728c2ecf20Sopenharmony_ciint qeth_l2_vnicc_get_state(struct qeth_card *card, u32 vnicc, bool *state) 19738c2ecf20Sopenharmony_ci{ 19748c2ecf20Sopenharmony_ci int rc = 0; 19758c2ecf20Sopenharmony_ci 19768c2ecf20Sopenharmony_ci QETH_CARD_TEXT(card, 2, "vniccgch"); 19778c2ecf20Sopenharmony_ci 19788c2ecf20Sopenharmony_ci /* check if characteristic is supported */ 19798c2ecf20Sopenharmony_ci if (!(card->options.vnicc.sup_chars & vnicc)) 19808c2ecf20Sopenharmony_ci return -EOPNOTSUPP; 19818c2ecf20Sopenharmony_ci 19828c2ecf20Sopenharmony_ci if (qeth_bridgeport_is_in_use(card)) 19838c2ecf20Sopenharmony_ci return -EBUSY; 19848c2ecf20Sopenharmony_ci 19858c2ecf20Sopenharmony_ci /* if card is ready, query current VNICC state */ 19868c2ecf20Sopenharmony_ci if (qeth_card_hw_is_reachable(card)) 19878c2ecf20Sopenharmony_ci rc = qeth_l2_vnicc_query_chars(card); 19888c2ecf20Sopenharmony_ci 19898c2ecf20Sopenharmony_ci *state = (card->options.vnicc.cur_chars & vnicc) ? true : false; 19908c2ecf20Sopenharmony_ci return rc; 19918c2ecf20Sopenharmony_ci} 19928c2ecf20Sopenharmony_ci 19938c2ecf20Sopenharmony_ci/* set VNICC timeout; called from sysfs store function. Currently, only learning 19948c2ecf20Sopenharmony_ci * supports timeout 19958c2ecf20Sopenharmony_ci */ 19968c2ecf20Sopenharmony_ciint qeth_l2_vnicc_set_timeout(struct qeth_card *card, u32 timeout) 19978c2ecf20Sopenharmony_ci{ 19988c2ecf20Sopenharmony_ci int rc = 0; 19998c2ecf20Sopenharmony_ci 20008c2ecf20Sopenharmony_ci QETH_CARD_TEXT(card, 2, "vniccsto"); 20018c2ecf20Sopenharmony_ci 20028c2ecf20Sopenharmony_ci /* check if characteristic and set_timeout are supported */ 20038c2ecf20Sopenharmony_ci if (!(card->options.vnicc.sup_chars & QETH_VNICC_LEARNING) || 20048c2ecf20Sopenharmony_ci !(card->options.vnicc.getset_timeout_sup & QETH_VNICC_LEARNING)) 20058c2ecf20Sopenharmony_ci return -EOPNOTSUPP; 20068c2ecf20Sopenharmony_ci 20078c2ecf20Sopenharmony_ci if (qeth_bridgeport_is_in_use(card)) 20088c2ecf20Sopenharmony_ci return -EBUSY; 20098c2ecf20Sopenharmony_ci 20108c2ecf20Sopenharmony_ci /* do we need to do anything? */ 20118c2ecf20Sopenharmony_ci if (card->options.vnicc.learning_timeout == timeout) 20128c2ecf20Sopenharmony_ci return rc; 20138c2ecf20Sopenharmony_ci 20148c2ecf20Sopenharmony_ci /* if card is not ready, simply store the value internally and return */ 20158c2ecf20Sopenharmony_ci if (!qeth_card_hw_is_reachable(card)) { 20168c2ecf20Sopenharmony_ci card->options.vnicc.learning_timeout = timeout; 20178c2ecf20Sopenharmony_ci return rc; 20188c2ecf20Sopenharmony_ci } 20198c2ecf20Sopenharmony_ci 20208c2ecf20Sopenharmony_ci /* send timeout value to card; if successful, store value internally */ 20218c2ecf20Sopenharmony_ci rc = qeth_l2_vnicc_getset_timeout(card, QETH_VNICC_LEARNING, 20228c2ecf20Sopenharmony_ci IPA_VNICC_SET_TIMEOUT, &timeout); 20238c2ecf20Sopenharmony_ci if (!rc) 20248c2ecf20Sopenharmony_ci card->options.vnicc.learning_timeout = timeout; 20258c2ecf20Sopenharmony_ci 20268c2ecf20Sopenharmony_ci return rc; 20278c2ecf20Sopenharmony_ci} 20288c2ecf20Sopenharmony_ci 20298c2ecf20Sopenharmony_ci/* get current VNICC timeout; called from sysfs show function. Currently, only 20308c2ecf20Sopenharmony_ci * learning supports timeout 20318c2ecf20Sopenharmony_ci */ 20328c2ecf20Sopenharmony_ciint qeth_l2_vnicc_get_timeout(struct qeth_card *card, u32 *timeout) 20338c2ecf20Sopenharmony_ci{ 20348c2ecf20Sopenharmony_ci int rc = 0; 20358c2ecf20Sopenharmony_ci 20368c2ecf20Sopenharmony_ci QETH_CARD_TEXT(card, 2, "vniccgto"); 20378c2ecf20Sopenharmony_ci 20388c2ecf20Sopenharmony_ci /* check if characteristic and get_timeout are supported */ 20398c2ecf20Sopenharmony_ci if (!(card->options.vnicc.sup_chars & QETH_VNICC_LEARNING) || 20408c2ecf20Sopenharmony_ci !(card->options.vnicc.getset_timeout_sup & QETH_VNICC_LEARNING)) 20418c2ecf20Sopenharmony_ci return -EOPNOTSUPP; 20428c2ecf20Sopenharmony_ci 20438c2ecf20Sopenharmony_ci if (qeth_bridgeport_is_in_use(card)) 20448c2ecf20Sopenharmony_ci return -EBUSY; 20458c2ecf20Sopenharmony_ci 20468c2ecf20Sopenharmony_ci /* if card is ready, get timeout. Otherwise, just return stored value */ 20478c2ecf20Sopenharmony_ci *timeout = card->options.vnicc.learning_timeout; 20488c2ecf20Sopenharmony_ci if (qeth_card_hw_is_reachable(card)) 20498c2ecf20Sopenharmony_ci rc = qeth_l2_vnicc_getset_timeout(card, QETH_VNICC_LEARNING, 20508c2ecf20Sopenharmony_ci IPA_VNICC_GET_TIMEOUT, 20518c2ecf20Sopenharmony_ci timeout); 20528c2ecf20Sopenharmony_ci 20538c2ecf20Sopenharmony_ci return rc; 20548c2ecf20Sopenharmony_ci} 20558c2ecf20Sopenharmony_ci 20568c2ecf20Sopenharmony_ci/* check if VNICC is currently enabled */ 20578c2ecf20Sopenharmony_cistatic bool _qeth_l2_vnicc_is_in_use(struct qeth_card *card) 20588c2ecf20Sopenharmony_ci{ 20598c2ecf20Sopenharmony_ci if (!card->options.vnicc.sup_chars) 20608c2ecf20Sopenharmony_ci return false; 20618c2ecf20Sopenharmony_ci /* default values are only OK if rx_bcast was not enabled by user 20628c2ecf20Sopenharmony_ci * or the card is offline. 20638c2ecf20Sopenharmony_ci */ 20648c2ecf20Sopenharmony_ci if (card->options.vnicc.cur_chars == QETH_VNICC_DEFAULT) { 20658c2ecf20Sopenharmony_ci if (!card->options.vnicc.rx_bcast_enabled || 20668c2ecf20Sopenharmony_ci !qeth_card_hw_is_reachable(card)) 20678c2ecf20Sopenharmony_ci return false; 20688c2ecf20Sopenharmony_ci } 20698c2ecf20Sopenharmony_ci return true; 20708c2ecf20Sopenharmony_ci} 20718c2ecf20Sopenharmony_ci 20728c2ecf20Sopenharmony_ci/** 20738c2ecf20Sopenharmony_ci * qeth_bridgeport_allowed - are any qeth_bridgeport functions allowed? 20748c2ecf20Sopenharmony_ci * @card: qeth_card structure pointer 20758c2ecf20Sopenharmony_ci * 20768c2ecf20Sopenharmony_ci * qeth_bridgeport functionality is mutually exclusive with usage of the 20778c2ecf20Sopenharmony_ci * VNIC Characteristics and dev2br address notifications 20788c2ecf20Sopenharmony_ci */ 20798c2ecf20Sopenharmony_cibool qeth_bridgeport_allowed(struct qeth_card *card) 20808c2ecf20Sopenharmony_ci{ 20818c2ecf20Sopenharmony_ci struct qeth_priv *priv = netdev_priv(card->dev); 20828c2ecf20Sopenharmony_ci 20838c2ecf20Sopenharmony_ci return (!_qeth_l2_vnicc_is_in_use(card) && 20848c2ecf20Sopenharmony_ci !(priv->brport_features & BR_LEARNING_SYNC)); 20858c2ecf20Sopenharmony_ci} 20868c2ecf20Sopenharmony_ci 20878c2ecf20Sopenharmony_ci/* recover user characteristic setting */ 20888c2ecf20Sopenharmony_cistatic bool qeth_l2_vnicc_recover_char(struct qeth_card *card, u32 vnicc, 20898c2ecf20Sopenharmony_ci bool enable) 20908c2ecf20Sopenharmony_ci{ 20918c2ecf20Sopenharmony_ci u32 cmd = enable ? IPA_VNICC_ENABLE : IPA_VNICC_DISABLE; 20928c2ecf20Sopenharmony_ci 20938c2ecf20Sopenharmony_ci if (card->options.vnicc.sup_chars & vnicc && 20948c2ecf20Sopenharmony_ci card->options.vnicc.set_char_sup & vnicc && 20958c2ecf20Sopenharmony_ci !qeth_l2_vnicc_set_char(card, vnicc, cmd)) 20968c2ecf20Sopenharmony_ci return false; 20978c2ecf20Sopenharmony_ci card->options.vnicc.wanted_chars &= ~vnicc; 20988c2ecf20Sopenharmony_ci card->options.vnicc.wanted_chars |= QETH_VNICC_DEFAULT & vnicc; 20998c2ecf20Sopenharmony_ci return true; 21008c2ecf20Sopenharmony_ci} 21018c2ecf20Sopenharmony_ci 21028c2ecf20Sopenharmony_ci/* (re-)initialize VNICC */ 21038c2ecf20Sopenharmony_cistatic void qeth_l2_vnicc_init(struct qeth_card *card) 21048c2ecf20Sopenharmony_ci{ 21058c2ecf20Sopenharmony_ci u32 *timeout = &card->options.vnicc.learning_timeout; 21068c2ecf20Sopenharmony_ci bool enable, error = false; 21078c2ecf20Sopenharmony_ci unsigned int chars_len, i; 21088c2ecf20Sopenharmony_ci unsigned long chars_tmp; 21098c2ecf20Sopenharmony_ci u32 sup_cmds, vnicc; 21108c2ecf20Sopenharmony_ci 21118c2ecf20Sopenharmony_ci QETH_CARD_TEXT(card, 2, "vniccini"); 21128c2ecf20Sopenharmony_ci /* reset rx_bcast */ 21138c2ecf20Sopenharmony_ci card->options.vnicc.rx_bcast_enabled = 0; 21148c2ecf20Sopenharmony_ci /* initial query and storage of VNIC characteristics */ 21158c2ecf20Sopenharmony_ci if (qeth_l2_vnicc_query_chars(card)) { 21168c2ecf20Sopenharmony_ci if (card->options.vnicc.wanted_chars != QETH_VNICC_DEFAULT || 21178c2ecf20Sopenharmony_ci *timeout != QETH_VNICC_DEFAULT_TIMEOUT) 21188c2ecf20Sopenharmony_ci dev_err(&card->gdev->dev, "Configuring the VNIC characteristics failed\n"); 21198c2ecf20Sopenharmony_ci /* fail quietly if user didn't change the default config */ 21208c2ecf20Sopenharmony_ci card->options.vnicc.sup_chars = 0; 21218c2ecf20Sopenharmony_ci card->options.vnicc.cur_chars = 0; 21228c2ecf20Sopenharmony_ci card->options.vnicc.wanted_chars = QETH_VNICC_DEFAULT; 21238c2ecf20Sopenharmony_ci return; 21248c2ecf20Sopenharmony_ci } 21258c2ecf20Sopenharmony_ci /* get supported commands for each supported characteristic */ 21268c2ecf20Sopenharmony_ci chars_tmp = card->options.vnicc.sup_chars; 21278c2ecf20Sopenharmony_ci chars_len = sizeof(card->options.vnicc.sup_chars) * BITS_PER_BYTE; 21288c2ecf20Sopenharmony_ci for_each_set_bit(i, &chars_tmp, chars_len) { 21298c2ecf20Sopenharmony_ci vnicc = BIT(i); 21308c2ecf20Sopenharmony_ci if (qeth_l2_vnicc_query_cmds(card, vnicc, &sup_cmds)) { 21318c2ecf20Sopenharmony_ci sup_cmds = 0; 21328c2ecf20Sopenharmony_ci error = true; 21338c2ecf20Sopenharmony_ci } 21348c2ecf20Sopenharmony_ci if ((sup_cmds & IPA_VNICC_SET_TIMEOUT) && 21358c2ecf20Sopenharmony_ci (sup_cmds & IPA_VNICC_GET_TIMEOUT)) 21368c2ecf20Sopenharmony_ci card->options.vnicc.getset_timeout_sup |= vnicc; 21378c2ecf20Sopenharmony_ci else 21388c2ecf20Sopenharmony_ci card->options.vnicc.getset_timeout_sup &= ~vnicc; 21398c2ecf20Sopenharmony_ci if ((sup_cmds & IPA_VNICC_ENABLE) && 21408c2ecf20Sopenharmony_ci (sup_cmds & IPA_VNICC_DISABLE)) 21418c2ecf20Sopenharmony_ci card->options.vnicc.set_char_sup |= vnicc; 21428c2ecf20Sopenharmony_ci else 21438c2ecf20Sopenharmony_ci card->options.vnicc.set_char_sup &= ~vnicc; 21448c2ecf20Sopenharmony_ci } 21458c2ecf20Sopenharmony_ci /* enforce assumed default values and recover settings, if changed */ 21468c2ecf20Sopenharmony_ci error |= qeth_l2_vnicc_recover_timeout(card, QETH_VNICC_LEARNING, 21478c2ecf20Sopenharmony_ci timeout); 21488c2ecf20Sopenharmony_ci /* Change chars, if necessary */ 21498c2ecf20Sopenharmony_ci chars_tmp = card->options.vnicc.wanted_chars ^ 21508c2ecf20Sopenharmony_ci card->options.vnicc.cur_chars; 21518c2ecf20Sopenharmony_ci chars_len = sizeof(card->options.vnicc.wanted_chars) * BITS_PER_BYTE; 21528c2ecf20Sopenharmony_ci for_each_set_bit(i, &chars_tmp, chars_len) { 21538c2ecf20Sopenharmony_ci vnicc = BIT(i); 21548c2ecf20Sopenharmony_ci enable = card->options.vnicc.wanted_chars & vnicc; 21558c2ecf20Sopenharmony_ci error |= qeth_l2_vnicc_recover_char(card, vnicc, enable); 21568c2ecf20Sopenharmony_ci } 21578c2ecf20Sopenharmony_ci if (error) 21588c2ecf20Sopenharmony_ci dev_err(&card->gdev->dev, "Configuring the VNIC characteristics failed\n"); 21598c2ecf20Sopenharmony_ci} 21608c2ecf20Sopenharmony_ci 21618c2ecf20Sopenharmony_ci/* configure default values of VNIC characteristics */ 21628c2ecf20Sopenharmony_cistatic void qeth_l2_vnicc_set_defaults(struct qeth_card *card) 21638c2ecf20Sopenharmony_ci{ 21648c2ecf20Sopenharmony_ci /* characteristics values */ 21658c2ecf20Sopenharmony_ci card->options.vnicc.sup_chars = QETH_VNICC_ALL; 21668c2ecf20Sopenharmony_ci card->options.vnicc.cur_chars = QETH_VNICC_DEFAULT; 21678c2ecf20Sopenharmony_ci card->options.vnicc.learning_timeout = QETH_VNICC_DEFAULT_TIMEOUT; 21688c2ecf20Sopenharmony_ci /* supported commands */ 21698c2ecf20Sopenharmony_ci card->options.vnicc.set_char_sup = QETH_VNICC_ALL; 21708c2ecf20Sopenharmony_ci card->options.vnicc.getset_timeout_sup = QETH_VNICC_LEARNING; 21718c2ecf20Sopenharmony_ci /* settings wanted by users */ 21728c2ecf20Sopenharmony_ci card->options.vnicc.wanted_chars = QETH_VNICC_DEFAULT; 21738c2ecf20Sopenharmony_ci} 21748c2ecf20Sopenharmony_ci 21758c2ecf20Sopenharmony_cistatic const struct device_type qeth_l2_devtype = { 21768c2ecf20Sopenharmony_ci .name = "qeth_layer2", 21778c2ecf20Sopenharmony_ci .groups = qeth_l2_attr_groups, 21788c2ecf20Sopenharmony_ci}; 21798c2ecf20Sopenharmony_ci 21808c2ecf20Sopenharmony_cistatic int qeth_l2_probe_device(struct ccwgroup_device *gdev) 21818c2ecf20Sopenharmony_ci{ 21828c2ecf20Sopenharmony_ci struct qeth_card *card = dev_get_drvdata(&gdev->dev); 21838c2ecf20Sopenharmony_ci int rc; 21848c2ecf20Sopenharmony_ci 21858c2ecf20Sopenharmony_ci if (IS_OSN(card)) 21868c2ecf20Sopenharmony_ci dev_notice(&gdev->dev, "OSN support will be dropped in 2021\n"); 21878c2ecf20Sopenharmony_ci 21888c2ecf20Sopenharmony_ci qeth_l2_vnicc_set_defaults(card); 21898c2ecf20Sopenharmony_ci mutex_init(&card->sbp_lock); 21908c2ecf20Sopenharmony_ci 21918c2ecf20Sopenharmony_ci if (gdev->dev.type == &qeth_generic_devtype) { 21928c2ecf20Sopenharmony_ci rc = qeth_l2_create_device_attributes(&gdev->dev); 21938c2ecf20Sopenharmony_ci if (rc) 21948c2ecf20Sopenharmony_ci return rc; 21958c2ecf20Sopenharmony_ci } 21968c2ecf20Sopenharmony_ci 21978c2ecf20Sopenharmony_ci INIT_WORK(&card->rx_mode_work, qeth_l2_rx_mode_work); 21988c2ecf20Sopenharmony_ci return 0; 21998c2ecf20Sopenharmony_ci} 22008c2ecf20Sopenharmony_ci 22018c2ecf20Sopenharmony_cistatic void qeth_l2_remove_device(struct ccwgroup_device *gdev) 22028c2ecf20Sopenharmony_ci{ 22038c2ecf20Sopenharmony_ci struct qeth_card *card = dev_get_drvdata(&gdev->dev); 22048c2ecf20Sopenharmony_ci 22058c2ecf20Sopenharmony_ci if (gdev->dev.type == &qeth_generic_devtype) 22068c2ecf20Sopenharmony_ci qeth_l2_remove_device_attributes(&gdev->dev); 22078c2ecf20Sopenharmony_ci qeth_set_allowed_threads(card, 0, 1); 22088c2ecf20Sopenharmony_ci wait_event(card->wait_q, qeth_threads_running(card, 0xffffffff) == 0); 22098c2ecf20Sopenharmony_ci 22108c2ecf20Sopenharmony_ci if (gdev->state == CCWGROUP_ONLINE) 22118c2ecf20Sopenharmony_ci qeth_set_offline(card, card->discipline, false); 22128c2ecf20Sopenharmony_ci 22138c2ecf20Sopenharmony_ci cancel_work_sync(&card->close_dev_work); 22148c2ecf20Sopenharmony_ci if (card->dev->reg_state == NETREG_REGISTERED) 22158c2ecf20Sopenharmony_ci unregister_netdev(card->dev); 22168c2ecf20Sopenharmony_ci} 22178c2ecf20Sopenharmony_ci 22188c2ecf20Sopenharmony_cistatic int qeth_l2_set_online(struct qeth_card *card, bool carrier_ok) 22198c2ecf20Sopenharmony_ci{ 22208c2ecf20Sopenharmony_ci struct net_device *dev = card->dev; 22218c2ecf20Sopenharmony_ci int rc = 0; 22228c2ecf20Sopenharmony_ci 22238c2ecf20Sopenharmony_ci qeth_l2_detect_dev2br_support(card); 22248c2ecf20Sopenharmony_ci 22258c2ecf20Sopenharmony_ci mutex_lock(&card->sbp_lock); 22268c2ecf20Sopenharmony_ci qeth_bridgeport_query_support(card); 22278c2ecf20Sopenharmony_ci if (card->options.sbp.supported_funcs) { 22288c2ecf20Sopenharmony_ci qeth_l2_setup_bridgeport_attrs(card); 22298c2ecf20Sopenharmony_ci dev_info(&card->gdev->dev, 22308c2ecf20Sopenharmony_ci "The device represents a Bridge Capable Port\n"); 22318c2ecf20Sopenharmony_ci } 22328c2ecf20Sopenharmony_ci mutex_unlock(&card->sbp_lock); 22338c2ecf20Sopenharmony_ci 22348c2ecf20Sopenharmony_ci qeth_l2_register_dev_addr(card); 22358c2ecf20Sopenharmony_ci 22368c2ecf20Sopenharmony_ci /* for the rx_bcast characteristic, init VNICC after setmac */ 22378c2ecf20Sopenharmony_ci qeth_l2_vnicc_init(card); 22388c2ecf20Sopenharmony_ci 22398c2ecf20Sopenharmony_ci qeth_l2_trace_features(card); 22408c2ecf20Sopenharmony_ci 22418c2ecf20Sopenharmony_ci /* softsetup */ 22428c2ecf20Sopenharmony_ci QETH_CARD_TEXT(card, 2, "softsetp"); 22438c2ecf20Sopenharmony_ci 22448c2ecf20Sopenharmony_ci card->state = CARD_STATE_SOFTSETUP; 22458c2ecf20Sopenharmony_ci 22468c2ecf20Sopenharmony_ci qeth_set_allowed_threads(card, 0xffffffff, 0); 22478c2ecf20Sopenharmony_ci 22488c2ecf20Sopenharmony_ci if (dev->reg_state != NETREG_REGISTERED) { 22498c2ecf20Sopenharmony_ci rc = qeth_l2_setup_netdev(card); 22508c2ecf20Sopenharmony_ci if (rc) 22518c2ecf20Sopenharmony_ci goto err_setup; 22528c2ecf20Sopenharmony_ci 22538c2ecf20Sopenharmony_ci if (carrier_ok) 22548c2ecf20Sopenharmony_ci netif_carrier_on(dev); 22558c2ecf20Sopenharmony_ci } else { 22568c2ecf20Sopenharmony_ci rtnl_lock(); 22578c2ecf20Sopenharmony_ci rc = qeth_set_real_num_tx_queues(card, 22588c2ecf20Sopenharmony_ci qeth_tx_actual_queues(card)); 22598c2ecf20Sopenharmony_ci if (rc) { 22608c2ecf20Sopenharmony_ci rtnl_unlock(); 22618c2ecf20Sopenharmony_ci goto err_set_queues; 22628c2ecf20Sopenharmony_ci } 22638c2ecf20Sopenharmony_ci 22648c2ecf20Sopenharmony_ci if (carrier_ok) 22658c2ecf20Sopenharmony_ci netif_carrier_on(dev); 22668c2ecf20Sopenharmony_ci else 22678c2ecf20Sopenharmony_ci netif_carrier_off(dev); 22688c2ecf20Sopenharmony_ci 22698c2ecf20Sopenharmony_ci netif_device_attach(dev); 22708c2ecf20Sopenharmony_ci qeth_enable_hw_features(dev); 22718c2ecf20Sopenharmony_ci qeth_l2_enable_brport_features(card); 22728c2ecf20Sopenharmony_ci 22738c2ecf20Sopenharmony_ci if (netif_running(dev)) { 22748c2ecf20Sopenharmony_ci local_bh_disable(); 22758c2ecf20Sopenharmony_ci napi_schedule(&card->napi); 22768c2ecf20Sopenharmony_ci /* kick-start the NAPI softirq: */ 22778c2ecf20Sopenharmony_ci local_bh_enable(); 22788c2ecf20Sopenharmony_ci qeth_l2_set_rx_mode(dev); 22798c2ecf20Sopenharmony_ci } 22808c2ecf20Sopenharmony_ci rtnl_unlock(); 22818c2ecf20Sopenharmony_ci } 22828c2ecf20Sopenharmony_ci return 0; 22838c2ecf20Sopenharmony_ci 22848c2ecf20Sopenharmony_cierr_set_queues: 22858c2ecf20Sopenharmony_cierr_setup: 22868c2ecf20Sopenharmony_ci qeth_set_allowed_threads(card, 0, 1); 22878c2ecf20Sopenharmony_ci card->state = CARD_STATE_DOWN; 22888c2ecf20Sopenharmony_ci return rc; 22898c2ecf20Sopenharmony_ci} 22908c2ecf20Sopenharmony_ci 22918c2ecf20Sopenharmony_cistatic void qeth_l2_set_offline(struct qeth_card *card) 22928c2ecf20Sopenharmony_ci{ 22938c2ecf20Sopenharmony_ci struct qeth_priv *priv = netdev_priv(card->dev); 22948c2ecf20Sopenharmony_ci 22958c2ecf20Sopenharmony_ci qeth_set_allowed_threads(card, 0, 1); 22968c2ecf20Sopenharmony_ci qeth_l2_drain_rx_mode_cache(card); 22978c2ecf20Sopenharmony_ci 22988c2ecf20Sopenharmony_ci if (card->state == CARD_STATE_SOFTSETUP) 22998c2ecf20Sopenharmony_ci card->state = CARD_STATE_DOWN; 23008c2ecf20Sopenharmony_ci 23018c2ecf20Sopenharmony_ci qeth_l2_set_pnso_mode(card, QETH_PNSO_NONE); 23028c2ecf20Sopenharmony_ci if (priv->brport_features & BR_LEARNING_SYNC) { 23038c2ecf20Sopenharmony_ci rtnl_lock(); 23048c2ecf20Sopenharmony_ci qeth_l2_dev2br_fdb_flush(card); 23058c2ecf20Sopenharmony_ci rtnl_unlock(); 23068c2ecf20Sopenharmony_ci } 23078c2ecf20Sopenharmony_ci} 23088c2ecf20Sopenharmony_ci 23098c2ecf20Sopenharmony_ci/* Returns zero if the command is successfully "consumed" */ 23108c2ecf20Sopenharmony_cistatic int qeth_l2_control_event(struct qeth_card *card, 23118c2ecf20Sopenharmony_ci struct qeth_ipa_cmd *cmd) 23128c2ecf20Sopenharmony_ci{ 23138c2ecf20Sopenharmony_ci switch (cmd->hdr.command) { 23148c2ecf20Sopenharmony_ci case IPA_CMD_SETBRIDGEPORT_OSA: 23158c2ecf20Sopenharmony_ci case IPA_CMD_SETBRIDGEPORT_IQD: 23168c2ecf20Sopenharmony_ci if (cmd->data.sbp.hdr.command_code == 23178c2ecf20Sopenharmony_ci IPA_SBP_BRIDGE_PORT_STATE_CHANGE) { 23188c2ecf20Sopenharmony_ci qeth_bridge_state_change(card, cmd); 23198c2ecf20Sopenharmony_ci return 0; 23208c2ecf20Sopenharmony_ci } 23218c2ecf20Sopenharmony_ci 23228c2ecf20Sopenharmony_ci return 1; 23238c2ecf20Sopenharmony_ci case IPA_CMD_ADDRESS_CHANGE_NOTIF: 23248c2ecf20Sopenharmony_ci qeth_addr_change_event(card, cmd); 23258c2ecf20Sopenharmony_ci return 0; 23268c2ecf20Sopenharmony_ci default: 23278c2ecf20Sopenharmony_ci return 1; 23288c2ecf20Sopenharmony_ci } 23298c2ecf20Sopenharmony_ci} 23308c2ecf20Sopenharmony_ci 23318c2ecf20Sopenharmony_ciconst struct qeth_discipline qeth_l2_discipline = { 23328c2ecf20Sopenharmony_ci .devtype = &qeth_l2_devtype, 23338c2ecf20Sopenharmony_ci .setup = qeth_l2_probe_device, 23348c2ecf20Sopenharmony_ci .remove = qeth_l2_remove_device, 23358c2ecf20Sopenharmony_ci .set_online = qeth_l2_set_online, 23368c2ecf20Sopenharmony_ci .set_offline = qeth_l2_set_offline, 23378c2ecf20Sopenharmony_ci .do_ioctl = NULL, 23388c2ecf20Sopenharmony_ci .control_event_handler = qeth_l2_control_event, 23398c2ecf20Sopenharmony_ci}; 23408c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(qeth_l2_discipline); 23418c2ecf20Sopenharmony_ci 23428c2ecf20Sopenharmony_cistatic int __init qeth_l2_init(void) 23438c2ecf20Sopenharmony_ci{ 23448c2ecf20Sopenharmony_ci pr_info("register layer 2 discipline\n"); 23458c2ecf20Sopenharmony_ci return 0; 23468c2ecf20Sopenharmony_ci} 23478c2ecf20Sopenharmony_ci 23488c2ecf20Sopenharmony_cistatic void __exit qeth_l2_exit(void) 23498c2ecf20Sopenharmony_ci{ 23508c2ecf20Sopenharmony_ci pr_info("unregister layer 2 discipline\n"); 23518c2ecf20Sopenharmony_ci} 23528c2ecf20Sopenharmony_ci 23538c2ecf20Sopenharmony_cimodule_init(qeth_l2_init); 23548c2ecf20Sopenharmony_cimodule_exit(qeth_l2_exit); 23558c2ecf20Sopenharmony_ciMODULE_AUTHOR("Frank Blaschka <frank.blaschka@de.ibm.com>"); 23568c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("qeth layer 2 discipline"); 23578c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 2358