These changes are the raw update to linux-4.4.6-rt14. Kernel sources
[kvmfornfv.git] / kernel / drivers / s390 / net / qeth_l2_main.c
index 0ea0869..8f1b091 100644 (file)
@@ -19,7 +19,9 @@
 #include <linux/mii.h>
 #include <linux/ip.h>
 #include <linux/list.h>
-
+#include <linux/hash.h>
+#include <linux/hashtable.h>
+#include <linux/string.h>
 #include "qeth_core.h"
 #include "qeth_l2.h"
 
@@ -28,7 +30,7 @@ static int qeth_l2_stop(struct net_device *);
 static int qeth_l2_send_delmac(struct qeth_card *, __u8 *);
 static int qeth_l2_send_setdelmac(struct qeth_card *, __u8 *,
                           enum qeth_ipa_cmds);
-static void qeth_l2_set_multicast_list(struct net_device *);
+static void qeth_l2_set_rx_mode(struct net_device *);
 static int qeth_l2_recover(void *);
 static void qeth_bridgeport_query_support(struct qeth_card *card);
 static void qeth_bridge_state_change(struct qeth_card *card,
@@ -137,7 +139,7 @@ static int qeth_setdel_makerc(struct qeth_card *card, int retcode)
                rc = 0;
                break;
        case IPA_RC_L2_UNSUPPORTED_CMD:
-               rc = -ENOSYS;
+               rc = -EOPNOTSUPP;
                break;
        case IPA_RC_L2_ADDR_TABLE_FULL:
                rc = -ENOSPC;
@@ -193,49 +195,44 @@ static int qeth_l2_send_delgroupmac(struct qeth_card *card, __u8 *mac)
        return rc;
 }
 
-static void qeth_l2_add_mc(struct qeth_card *card, __u8 *mac, int vmac)
+static inline u32 qeth_l2_mac_hash(const u8 *addr)
 {
-       struct qeth_mc_mac *mc;
-       int rc;
-
-       mc = kmalloc(sizeof(struct qeth_mc_mac), GFP_ATOMIC);
+       return get_unaligned((u32 *)(&addr[2]));
+}
 
-       if (!mc)
-               return;
+static int qeth_l2_write_mac(struct qeth_card *card, struct qeth_mac *mac)
+{
 
-       memcpy(mc->mc_addr, mac, OSA_ADDR_LEN);
-       mc->mc_addrlen = OSA_ADDR_LEN;
-       mc->is_vmac = vmac;
+       int rc;
 
-       if (vmac) {
+       if (mac->is_uc) {
                rc = qeth_setdel_makerc(card,
-                       qeth_l2_send_setdelmac(card, mac, IPA_CMD_SETVMAC));
+                               qeth_l2_send_setdelmac(card, mac->mac_addr,
+                                               IPA_CMD_SETVMAC));
        } else {
                rc = qeth_setdel_makerc(card,
-                       qeth_l2_send_setgroupmac(card, mac));
+                               qeth_l2_send_setgroupmac(card, mac->mac_addr));
        }
-
-       if (!rc)
-               list_add_tail(&mc->list, &card->mc_list);
-       else
-               kfree(mc);
+       return rc;
 }
 
-static void qeth_l2_del_all_mc(struct qeth_card *card, int del)
+static void qeth_l2_del_all_macs(struct qeth_card *card, int del)
 {
-       struct qeth_mc_mac *mc, *tmp;
+       struct qeth_mac *mac;
+       struct hlist_node *tmp;
+       int i;
 
        spin_lock_bh(&card->mclock);
-       list_for_each_entry_safe(mc, tmp, &card->mc_list, list) {
+       hash_for_each_safe(card->mac_htable, i, tmp, mac, hnode) {
                if (del) {
-                       if (mc->is_vmac)
-                               qeth_l2_send_setdelmac(card, mc->mc_addr,
-                                       IPA_CMD_DELVMAC);
+                       if (mac->is_uc)
+                               qeth_l2_send_setdelmac(card, mac->mac_addr,
+                                               IPA_CMD_DELVMAC);
                        else
-                               qeth_l2_send_delgroupmac(card, mc->mc_addr);
+                               qeth_l2_send_delgroupmac(card, mac->mac_addr);
                }
-               list_del(&mc->list);
-               kfree(mc);
+               hash_del(&mac->hnode);
+               kfree(mac);
        }
        spin_unlock_bh(&card->mclock);
 }
@@ -252,6 +249,23 @@ static inline int qeth_l2_get_cast_type(struct qeth_card *card,
        return RTN_UNSPEC;
 }
 
+static inline void qeth_l2_hdr_csum(struct qeth_card *card,
+                                   struct qeth_hdr *hdr, struct sk_buff *skb)
+{
+       struct iphdr *iph = ip_hdr(skb);
+
+       /* tcph->check contains already the pseudo hdr checksum
+        * so just set the header flags
+        */
+       if (iph->protocol == IPPROTO_UDP)
+               hdr->hdr.l2.flags[1] |= QETH_HDR_EXT_UDP;
+       hdr->hdr.l2.flags[1] |= QETH_HDR_EXT_CSUM_TRANSP_REQ |
+               QETH_HDR_EXT_CSUM_HDR_REQ;
+       iph->check = 0;
+       if (card->options.performance_stats)
+               card->perf_stats.tx_csum++;
+}
+
 static void qeth_l2_fill_header(struct qeth_card *card, struct qeth_hdr *hdr,
                        struct sk_buff *skb, int cast_type)
 {
@@ -386,14 +400,44 @@ static int qeth_l2_vlan_rx_kill_vid(struct net_device *dev,
                rc = qeth_l2_send_setdelvlan(card, vid, IPA_CMD_DELVLAN);
                kfree(tmpid);
        }
-       qeth_l2_set_multicast_list(card->dev);
+       qeth_l2_set_rx_mode(card->dev);
        return rc;
 }
 
-static int qeth_l2_stop_card(struct qeth_card *card, int recovery_mode)
+static netdev_features_t qeth_l2_fix_features(struct net_device *dev,
+                                             netdev_features_t features)
 {
-       int rc = 0;
+       struct qeth_card *card = dev->ml_priv;
+
+       QETH_DBF_TEXT(SETUP, 2, "fixfeat");
+       if (!qeth_is_supported(card, IPA_OUTBOUND_CHECKSUM))
+               features &= ~NETIF_F_IP_CSUM;
+       if (!qeth_is_supported(card, IPA_INBOUND_CHECKSUM))
+               features &= ~NETIF_F_RXCSUM;
+       QETH_DBF_HEX(SETUP, 2, &features, sizeof(features));
+       return features;
+}
+
+static int qeth_l2_set_features(struct net_device *dev,
+                               netdev_features_t features)
+{
+       struct qeth_card *card = dev->ml_priv;
+       netdev_features_t changed = dev->features ^ features;
+
+       QETH_DBF_TEXT(SETUP, 2, "setfeat");
+       QETH_DBF_HEX(SETUP, 2, &features, sizeof(features));
 
+       if (card->state == CARD_STATE_DOWN ||
+           card->state == CARD_STATE_RECOVER)
+               return 0;
+
+       if (!(changed & NETIF_F_RXCSUM))
+               return 0;
+       return qeth_set_rx_csum(card, features & NETIF_F_RXCSUM ? 1 : 0);
+}
+
+static void qeth_l2_stop_card(struct qeth_card *card, int recovery_mode)
+{
        QETH_DBF_TEXT(SETUP , 2, "stopcard");
        QETH_DBF_HEX(SETUP, 2, &card, sizeof(void *));
 
@@ -413,7 +457,7 @@ static int qeth_l2_stop_card(struct qeth_card *card, int recovery_mode)
                card->state = CARD_STATE_SOFTSETUP;
        }
        if (card->state == CARD_STATE_SOFTSETUP) {
-               qeth_l2_del_all_mc(card, 0);
+               qeth_l2_del_all_macs(card, 0);
                qeth_clear_ipacmd_list(card);
                card->state = CARD_STATE_HARDSETUP;
        }
@@ -427,7 +471,6 @@ static int qeth_l2_stop_card(struct qeth_card *card, int recovery_mode)
                qeth_clear_cmd_buffers(&card->read);
                qeth_clear_cmd_buffers(&card->write);
        }
-       return rc;
 }
 
 static int qeth_l2_process_inbound_buffer(struct qeth_card *card,
@@ -453,11 +496,19 @@ static int qeth_l2_process_inbound_buffer(struct qeth_card *card,
                case QETH_HEADER_TYPE_LAYER2:
                        skb->pkt_type = PACKET_HOST;
                        skb->protocol = eth_type_trans(skb, skb->dev);
-                       skb->ip_summed = CHECKSUM_NONE;
+                       if ((card->dev->features & NETIF_F_RXCSUM)
+                          && ((hdr->hdr.l2.flags[1] &
+                               (QETH_HDR_EXT_CSUM_HDR_REQ |
+                                  QETH_HDR_EXT_CSUM_TRANSP_REQ)) ==
+                               (QETH_HDR_EXT_CSUM_HDR_REQ |
+                                  QETH_HDR_EXT_CSUM_TRANSP_REQ)))
+                               skb->ip_summed = CHECKSUM_UNNECESSARY;
+                       else
+                               skb->ip_summed = CHECKSUM_NONE;
                        if (skb->protocol == htons(ETH_P_802_2))
                                *((__u32 *)skb->cb) = ++card->seqno.pkt_seqno;
                        len = skb->len;
-                       netif_receive_skb(skb);
+                       napi_gro_receive(&card->napi, skb);
                        break;
                case QETH_HEADER_TYPE_OSN:
                        if (card->info.type == QETH_CARD_TYPE_OSN) {
@@ -683,30 +734,126 @@ static int qeth_l2_set_mac_address(struct net_device *dev, void *p)
        return rc ? -EINVAL : 0;
 }
 
-static void qeth_l2_set_multicast_list(struct net_device *dev)
+static void qeth_promisc_to_bridge(struct qeth_card *card)
+{
+       struct net_device *dev = card->dev;
+       enum qeth_ipa_promisc_modes promisc_mode;
+       int role;
+       int rc;
+
+       QETH_CARD_TEXT(card, 3, "pmisc2br");
+
+       if (!card->options.sbp.reflect_promisc)
+               return;
+       promisc_mode = (dev->flags & IFF_PROMISC) ? SET_PROMISC_MODE_ON
+                                               : SET_PROMISC_MODE_OFF;
+       if (promisc_mode == card->info.promisc_mode)
+               return;
+
+       if (promisc_mode == SET_PROMISC_MODE_ON) {
+               if (card->options.sbp.reflect_promisc_primary)
+                       role = QETH_SBP_ROLE_PRIMARY;
+               else
+                       role = QETH_SBP_ROLE_SECONDARY;
+       } else
+               role = QETH_SBP_ROLE_NONE;
+
+       rc = qeth_bridgeport_setrole(card, role);
+       QETH_DBF_TEXT_(SETUP, 2, "bpm%c%04x",
+                       (promisc_mode == SET_PROMISC_MODE_ON) ? '+' : '-', rc);
+       if (!rc) {
+               card->options.sbp.role = role;
+               card->info.promisc_mode = promisc_mode;
+       }
+
+}
+/* New MAC address is added to the hash table and marked to be written on card
+ * only if there is not in the hash table storage already
+ *
+*/
+static void
+qeth_l2_add_mac(struct qeth_card *card, struct netdev_hw_addr *ha, u8 is_uc)
+{
+       struct qeth_mac *mac;
+
+       hash_for_each_possible(card->mac_htable, mac, hnode,
+                       qeth_l2_mac_hash(ha->addr)) {
+               if (is_uc == mac->is_uc &&
+                   !memcmp(ha->addr, mac->mac_addr, OSA_ADDR_LEN)) {
+                       mac->disp_flag = QETH_DISP_MAC_DO_NOTHING;
+                       return;
+               }
+       }
+
+       mac = kzalloc(sizeof(struct qeth_mac), GFP_ATOMIC);
+
+       if (!mac)
+               return;
+
+       memcpy(mac->mac_addr, ha->addr, OSA_ADDR_LEN);
+       mac->is_uc = is_uc;
+       mac->disp_flag = QETH_DISP_MAC_ADD;
+
+       hash_add(card->mac_htable, &mac->hnode,
+                       qeth_l2_mac_hash(mac->mac_addr));
+
+}
+
+static void qeth_l2_set_rx_mode(struct net_device *dev)
 {
        struct qeth_card *card = dev->ml_priv;
        struct netdev_hw_addr *ha;
+       struct qeth_mac *mac;
+       struct hlist_node *tmp;
+       int i;
+       int rc;
 
        if (card->info.type == QETH_CARD_TYPE_OSN)
-               return ;
+               return;
 
        QETH_CARD_TEXT(card, 3, "setmulti");
        if (qeth_threads_running(card, QETH_RECOVER_THREAD) &&
            (card->state != CARD_STATE_UP))
                return;
-       qeth_l2_del_all_mc(card, 1);
+
        spin_lock_bh(&card->mclock);
+
        netdev_for_each_mc_addr(ha, dev)
-               qeth_l2_add_mc(card, ha->addr, 0);
+               qeth_l2_add_mac(card, ha, 0);
 
        netdev_for_each_uc_addr(ha, dev)
-               qeth_l2_add_mc(card, ha->addr, 1);
+               qeth_l2_add_mac(card, ha, 1);
+
+       hash_for_each_safe(card->mac_htable, i, tmp, mac, hnode) {
+               if (mac->disp_flag == QETH_DISP_MAC_DELETE) {
+                       if (!mac->is_uc)
+                               rc = qeth_l2_send_delgroupmac(card,
+                                               mac->mac_addr);
+                       else {
+                               rc = qeth_l2_send_setdelmac(card, mac->mac_addr,
+                                               IPA_CMD_DELVMAC);
+                       }
+
+                       hash_del(&mac->hnode);
+                       kfree(mac);
+
+               } else if (mac->disp_flag == QETH_DISP_MAC_ADD) {
+                       rc = qeth_l2_write_mac(card, mac);
+                       if (rc) {
+                               hash_del(&mac->hnode);
+                               kfree(mac);
+                       } else
+                               mac->disp_flag = QETH_DISP_MAC_DELETE;
+               } else
+                       mac->disp_flag = QETH_DISP_MAC_DELETE;
+       }
 
        spin_unlock_bh(&card->mclock);
-       if (!qeth_adp_supported(card, IPA_SETADP_SET_PROMISC_MODE))
-               return;
-       qeth_setadp_promisc_mode(card);
+
+       if (qeth_adp_supported(card, IPA_SETADP_SET_PROMISC_MODE))
+               qeth_setadp_promisc_mode(card);
+       else
+               qeth_promisc_to_bridge(card);
 }
 
 static int qeth_l2_hard_start_xmit(struct sk_buff *skb, struct net_device *dev)
@@ -772,6 +919,8 @@ static int qeth_l2_hard_start_xmit(struct sk_buff *skb, struct net_device *dev)
                                                sizeof(struct qeth_hdr));
                        skb_set_mac_header(new_skb, sizeof(struct qeth_hdr));
                        qeth_l2_fill_header(card, hdr, new_skb, cast_type);
+                       if (new_skb->ip_summed == CHECKSUM_PARTIAL)
+                               qeth_l2_hdr_csum(card, hdr, new_skb);
                }
        }
 
@@ -884,7 +1033,7 @@ static int qeth_l2_probe_device(struct ccwgroup_device *gdev)
 
        qeth_l2_create_device_attributes(&gdev->dev);
        INIT_LIST_HEAD(&card->vid_list);
-       INIT_LIST_HEAD(&card->mc_list);
+       hash_init(card->mac_htable);
        card->options.layer2 = 1;
        card->info.hwtrap = 0;
        return 0;
@@ -930,13 +1079,15 @@ static const struct net_device_ops qeth_l2_netdev_ops = {
        .ndo_get_stats          = qeth_get_stats,
        .ndo_start_xmit         = qeth_l2_hard_start_xmit,
        .ndo_validate_addr      = eth_validate_addr,
-       .ndo_set_rx_mode        = qeth_l2_set_multicast_list,
+       .ndo_set_rx_mode        = qeth_l2_set_rx_mode,
        .ndo_do_ioctl           = qeth_l2_do_ioctl,
        .ndo_set_mac_address    = qeth_l2_set_mac_address,
        .ndo_change_mtu         = qeth_change_mtu,
        .ndo_vlan_rx_add_vid    = qeth_l2_vlan_rx_add_vid,
        .ndo_vlan_rx_kill_vid   = qeth_l2_vlan_rx_kill_vid,
        .ndo_tx_timeout         = qeth_tx_timeout,
+       .ndo_fix_features       = qeth_l2_fix_features,
+       .ndo_set_features       = qeth_l2_set_features
 };
 
 static int qeth_l2_setup_netdev(struct qeth_card *card)
@@ -966,6 +1117,11 @@ static int qeth_l2_setup_netdev(struct qeth_card *card)
                (card->info.type != QETH_CARD_TYPE_OSN) ?
                &qeth_l2_ethtool_ops : &qeth_l2_osn_ops;
        card->dev->features |= NETIF_F_HW_VLAN_CTAG_FILTER;
+       if (card->info.type == QETH_CARD_TYPE_OSD && !card->info.guestlan) {
+               card->dev->hw_features = NETIF_F_IP_CSUM | NETIF_F_RXCSUM;
+               /* Turn on RX offloading per default */
+               card->dev->features |= NETIF_F_RXCSUM;
+       }
        card->info.broadcast_capable = 1;
        qeth_l2_request_initial_mac(card);
        SET_NETDEV_DEV(card->dev, &card->gdev->dev);
@@ -973,6 +1129,17 @@ static int qeth_l2_setup_netdev(struct qeth_card *card)
        return register_netdev(card->dev);
 }
 
+static int qeth_l2_start_ipassists(struct qeth_card *card)
+{
+       /* configure isolation level */
+       if (qeth_set_access_ctrl_online(card, 0))
+               return -ENODEV;
+       if (qeth_is_supported(card, IPA_INBOUND_CHECKSUM))
+               qeth_set_rx_csum(card, 1);
+       qeth_start_ipa_tx_checksum(card);
+       return 0;
+}
+
 static int __qeth_l2_set_online(struct ccwgroup_device *gdev, int recovery_mode)
 {
        struct qeth_card *card = dev_get_drvdata(&gdev->dev);
@@ -994,7 +1161,7 @@ static int __qeth_l2_set_online(struct ccwgroup_device *gdev, int recovery_mode)
        qeth_bridgeport_query_support(card);
        if (card->options.sbp.supported_funcs)
                dev_info(&card->gdev->dev,
-               "The device represents a HiperSockets Bridge Capable Port\n");
+               "The device represents a Bridge Capable Port\n");
        qeth_trace_features(card);
 
        if (!card->dev && qeth_l2_setup_netdev(card)) {
@@ -1038,12 +1205,8 @@ static int __qeth_l2_set_online(struct ccwgroup_device *gdev, int recovery_mode)
 contin:
        if ((card->info.type == QETH_CARD_TYPE_OSD) ||
            (card->info.type == QETH_CARD_TYPE_OSX)) {
-               /* configure isolation level */
-               rc = qeth_set_access_ctrl_online(card, 0);
-               if (rc) {
-                       rc = -ENODEV;
+               if (qeth_l2_start_ipassists(card))
                        goto out_remove;
-               }
        }
 
        if (card->info.type != QETH_CARD_TYPE_OSN &&
@@ -1075,7 +1238,7 @@ contin:
                        rtnl_unlock();
                }
                /* this also sets saved unicast addresses */
-               qeth_l2_set_multicast_list(card->dev);
+               qeth_l2_set_rx_mode(card->dev);
        }
        /* let user_space know that device is online */
        kobject_uevent(&gdev->dev.kobj, KOBJ_CHANGE);
@@ -1247,7 +1410,8 @@ static int qeth_l2_control_event(struct qeth_card *card,
                                        struct qeth_ipa_cmd *cmd)
 {
        switch (cmd->hdr.command) {
-       case IPA_CMD_SETBRIDGEPORT:
+       case IPA_CMD_SETBRIDGEPORT_OSA:
+       case IPA_CMD_SETBRIDGEPORT_IQD:
                if (cmd->data.sbp.hdr.command_code ==
                                IPA_SBP_BRIDGE_PORT_STATE_CHANGE) {
                        qeth_bridge_state_change(card, cmd);
@@ -1420,8 +1584,8 @@ static void qeth_bridge_emit_host_event(struct qeth_card *card,
                        env[i] = str[i]; i++;
                }
                if (code & IPA_ADDR_CHANGE_CODE_MACADDR) {
-                       snprintf(str[i], sizeof(str[i]), "MAC=%pM6",
-                               &addr_lnid->mac);
+                       snprintf(str[i], sizeof(str[i]), "MAC=%pM",
+                               addr_lnid->mac);
                        env[i] = str[i]; i++;
                }
                snprintf(str[i], sizeof(str[i]), "NTOK_BUSID=%x.%x.%04x",
@@ -1533,7 +1697,7 @@ static void qeth_bridge_host_event_worker(struct work_struct *work)
 
        if (data->hostevs.lost_event_mask) {
                dev_info(&data->card->gdev->dev,
-"Address notification from the HiperSockets Bridge Port stopped %s (%s)\n",
+"Address notification from the Bridge Port stopped %s (%s)\n",
                        data->card->dev->name,
                        (data->hostevs.lost_event_mask == 0x01)
                        ? "Overflow"
@@ -1617,70 +1781,80 @@ static int qeth_bridgeport_makerc(struct qeth_card *card,
        struct _qeth_sbp_cbctl *cbctl, enum qeth_ipa_sbp_cmd setcmd)
 {
        int rc;
+       int is_iqd = (card->info.type == QETH_CARD_TYPE_IQD);
 
-       switch (cbctl->ipa_rc) {
-       case IPA_RC_SUCCESS:
+       if ((is_iqd && (cbctl->ipa_rc == IPA_RC_SUCCESS)) ||
+           (!is_iqd && (cbctl->ipa_rc == cbctl->cmd_rc)))
                switch (cbctl->cmd_rc) {
                case 0x0000:
                        rc = 0;
                        break;
+               case 0x2B04:
                case 0x0004:
-                       rc = -ENOSYS;
+                       rc = -EOPNOTSUPP;
                        break;
+               case 0x2B0C:
                case 0x000C: /* Not configured as bridge Port */
                        rc = -ENODEV; /* maybe not the best code here? */
                        dev_err(&card->gdev->dev,
-       "The HiperSockets device is not configured as a Bridge Port\n");
+       "The device is not configured as a Bridge Port\n");
                        break;
+               case 0x2B14:
                case 0x0014: /* Another device is Primary */
                        switch (setcmd) {
                        case IPA_SBP_SET_PRIMARY_BRIDGE_PORT:
                                rc = -EEXIST;
                                dev_err(&card->gdev->dev,
-       "The HiperSockets LAN already has a primary Bridge Port\n");
+       "The LAN already has a primary Bridge Port\n");
                                break;
                        case IPA_SBP_SET_SECONDARY_BRIDGE_PORT:
                                rc = -EBUSY;
                                dev_err(&card->gdev->dev,
-       "The HiperSockets device is already a primary Bridge Port\n");
+       "The device is already a primary Bridge Port\n");
                                break;
                        default:
                                rc = -EIO;
                        }
                        break;
+               case 0x2B18:
                case 0x0018: /* This device is currently Secondary */
                        rc = -EBUSY;
                        dev_err(&card->gdev->dev,
-       "The HiperSockets device is already a secondary Bridge Port\n");
+       "The device is already a secondary Bridge Port\n");
                        break;
+               case 0x2B1C:
                case 0x001C: /* Limit for Secondary devices reached */
                        rc = -EEXIST;
                        dev_err(&card->gdev->dev,
-       "The HiperSockets LAN cannot have more secondary Bridge Ports\n");
+       "The LAN cannot have more secondary Bridge Ports\n");
                        break;
+               case 0x2B24:
                case 0x0024: /* This device is currently Primary */
                        rc = -EBUSY;
                        dev_err(&card->gdev->dev,
-       "The HiperSockets device is already a primary Bridge Port\n");
+       "The device is already a primary Bridge Port\n");
                        break;
+               case 0x2B20:
                case 0x0020: /* Not authorized by zManager */
                        rc = -EACCES;
                        dev_err(&card->gdev->dev,
-       "The HiperSockets device is not authorized to be a Bridge Port\n");
+       "The device is not authorized to be a Bridge Port\n");
                        break;
                default:
                        rc = -EIO;
                }
-               break;
-       case IPA_RC_NOTSUPP:
-               rc = -ENOSYS;
-               break;
-       case IPA_RC_UNSUPPORTED_COMMAND:
-               rc = -ENOSYS;
-               break;
-       default:
-               rc = -EIO;
-       }
+       else
+               switch (cbctl->ipa_rc) {
+               case IPA_RC_NOTSUPP:
+                       rc = -EOPNOTSUPP;
+                       break;
+               case IPA_RC_UNSUPPORTED_COMMAND:
+                       rc = -EOPNOTSUPP;
+                       break;
+               default:
+                       rc = -EIO;
+               }
+
        if (rc) {
                QETH_CARD_TEXT_(card, 2, "SBPi%04x", cbctl->ipa_rc);
                QETH_CARD_TEXT_(card, 2, "SBPc%04x", cbctl->cmd_rc);
@@ -1688,6 +1862,13 @@ static int qeth_bridgeport_makerc(struct qeth_card *card,
        return rc;
 }
 
+static inline int ipa_cmd_sbp(struct qeth_card *card)
+{
+       return (card->info.type == QETH_CARD_TYPE_IQD) ?
+               IPA_CMD_SETBRIDGEPORT_IQD :
+               IPA_CMD_SETBRIDGEPORT_OSA;
+}
+
 static int qeth_bridgeport_query_support_cb(struct qeth_card *card,
        struct qeth_reply *reply, unsigned long data)
 {
@@ -1719,7 +1900,7 @@ static void qeth_bridgeport_query_support(struct qeth_card *card)
        struct _qeth_sbp_cbctl cbctl;
 
        QETH_CARD_TEXT(card, 2, "brqsuppo");
-       iob = qeth_get_ipacmd_buffer(card, IPA_CMD_SETBRIDGEPORT, 0);
+       iob = qeth_get_ipacmd_buffer(card, ipa_cmd_sbp(card), 0);
        if (!iob)
                return;
        cmd = (struct qeth_ipa_cmd *)(iob->data+IPA_PDU_HEADER_SIZE);
@@ -1796,7 +1977,7 @@ int qeth_bridgeport_query_ports(struct qeth_card *card,
        QETH_CARD_TEXT(card, 2, "brqports");
        if (!(card->options.sbp.supported_funcs & IPA_SBP_QUERY_BRIDGE_PORTS))
                return -EOPNOTSUPP;
-       iob = qeth_get_ipacmd_buffer(card, IPA_CMD_SETBRIDGEPORT, 0);
+       iob = qeth_get_ipacmd_buffer(card, ipa_cmd_sbp(card), 0);
        if (!iob)
                return -ENOMEM;
        cmd = (struct qeth_ipa_cmd *)(iob->data+IPA_PDU_HEADER_SIZE);
@@ -1808,10 +1989,9 @@ int qeth_bridgeport_query_ports(struct qeth_card *card,
        cmd->data.sbp.hdr.seq_no = 1;
        rc = qeth_send_ipa_cmd(card, iob, qeth_bridgeport_query_ports_cb,
                                (void *)&cbctl);
-       if (rc)
+       if (rc < 0)
                return rc;
-       rc = qeth_bridgeport_makerc(card, &cbctl, IPA_SBP_QUERY_BRIDGE_PORTS);
-       return rc;
+       return qeth_bridgeport_makerc(card, &cbctl, IPA_SBP_QUERY_BRIDGE_PORTS);
 }
 EXPORT_SYMBOL_GPL(qeth_bridgeport_query_ports);
 
@@ -1864,7 +2044,7 @@ int qeth_bridgeport_setrole(struct qeth_card *card, enum qeth_sbp_roles role)
        }
        if (!(card->options.sbp.supported_funcs & setcmd))
                return -EOPNOTSUPP;
-       iob = qeth_get_ipacmd_buffer(card, IPA_CMD_SETBRIDGEPORT, 0);
+       iob = qeth_get_ipacmd_buffer(card, ipa_cmd_sbp(card), 0);
        if (!iob)
                return -ENOMEM;
        cmd = (struct qeth_ipa_cmd *)(iob->data+IPA_PDU_HEADER_SIZE);
@@ -1874,10 +2054,9 @@ int qeth_bridgeport_setrole(struct qeth_card *card, enum qeth_sbp_roles role)
        cmd->data.sbp.hdr.seq_no = 1;
        rc = qeth_send_ipa_cmd(card, iob, qeth_bridgeport_set_cb,
                                (void *)&cbctl);
-       if (rc)
+       if (rc < 0)
                return rc;
-       rc = qeth_bridgeport_makerc(card, &cbctl, setcmd);
-       return rc;
+       return qeth_bridgeport_makerc(card, &cbctl, setcmd);
 }
 
 /**
@@ -1898,7 +2077,7 @@ static int qeth_anset_makerc(struct qeth_card *card, int pnso_rc, u16 response)
                case 0x0004:
                case 0x0100:
                case 0x0106:
-                       rc = -ENOSYS;
+                       rc = -EOPNOTSUPP;
                        dev_err(&card->gdev->dev,
                                "Setting address notification failed\n");
                        break;