]> Pileus Git - ~andy/linux/blobdiff - drivers/s390/net/qeth_l3_main.c
qeth: add support for af_iucv HiperSockets transport
[~andy/linux] / drivers / s390 / net / qeth_l3_main.c
index 553b6686dd31d72cf9f0c2f3b3d20a04e3d71e57..e2a927ae002ad8d1b257d082d5c148a33105f6ec 100644 (file)
@@ -29,6 +29,7 @@
 #include <net/ip.h>
 #include <net/arp.h>
 #include <net/ip6_checksum.h>
+#include <net/iucv/af_iucv.h>
 
 #include "qeth_l3.h"
 
@@ -267,7 +268,7 @@ static int __qeth_l3_insert_ip_todo(struct qeth_card *card,
        }
 }
 
-static int qeth_l3_delete_ip(struct qeth_card *card, struct qeth_ipaddr *addr)
+int qeth_l3_delete_ip(struct qeth_card *card, struct qeth_ipaddr *addr)
 {
        unsigned long flags;
        int rc = 0;
@@ -286,7 +287,7 @@ static int qeth_l3_delete_ip(struct qeth_card *card, struct qeth_ipaddr *addr)
        return rc;
 }
 
-static int qeth_l3_add_ip(struct qeth_card *card, struct qeth_ipaddr *addr)
+int qeth_l3_add_ip(struct qeth_card *card, struct qeth_ipaddr *addr)
 {
        unsigned long flags;
        int rc = 0;
@@ -305,7 +306,7 @@ static int qeth_l3_add_ip(struct qeth_card *card, struct qeth_ipaddr *addr)
 }
 
 
-static struct qeth_ipaddr *qeth_l3_get_addr_buffer(
+struct qeth_ipaddr *qeth_l3_get_addr_buffer(
                                enum qeth_prot_versions prot)
 {
        struct qeth_ipaddr *addr;
@@ -421,7 +422,7 @@ again:
        list_splice(&fail_list, &card->ip_list);
 }
 
-static void qeth_l3_set_ip_addr_list(struct qeth_card *card)
+void qeth_l3_set_ip_addr_list(struct qeth_card *card)
 {
        struct list_head *tbd_list;
        struct qeth_ipaddr *todo, *addr;
@@ -438,7 +439,7 @@ static void qeth_l3_set_ip_addr_list(struct qeth_card *card)
 
        spin_lock_irqsave(&card->ip_lock, flags);
        tbd_list = card->ip_tbd_list;
-       card->ip_tbd_list = kmalloc(sizeof(struct list_head), GFP_ATOMIC);
+       card->ip_tbd_list = kzalloc(sizeof(struct list_head), GFP_ATOMIC);
        if (!card->ip_tbd_list) {
                QETH_CARD_TEXT(card, 0, "silnomem");
                card->ip_tbd_list = tbd_list;
@@ -1993,12 +1994,13 @@ static int qeth_l3_process_inbound_buffer(struct qeth_card *card,
        __u16 vlan_tag = 0;
        int is_vlan;
        unsigned int len;
+       __u16 magic;
 
        *done = 0;
        BUG_ON(!budget);
        while (budget) {
                skb = qeth_core_get_next_skb(card,
-                       card->qdio.in_q->bufs[card->rx.b_index].buffer,
+                       &card->qdio.in_q->bufs[card->rx.b_index],
                        &card->rx.b_element, &card->rx.e_offset, &hdr);
                if (!skb) {
                        *done = 1;
@@ -2007,12 +2009,26 @@ static int qeth_l3_process_inbound_buffer(struct qeth_card *card,
                skb->dev = card->dev;
                switch (hdr->hdr.l3.id) {
                case QETH_HEADER_TYPE_LAYER3:
-                       is_vlan = qeth_l3_rebuild_skb(card, skb, hdr,
+                       magic = *(__u16 *)skb->data;
+                       if ((card->info.type == QETH_CARD_TYPE_IQD) &&
+                           (magic == ETH_P_AF_IUCV)) {
+                               skb->protocol = ETH_P_AF_IUCV;
+                               skb->pkt_type = PACKET_HOST;
+                               skb->mac_header = NET_SKB_PAD;
+                               skb->dev = card->dev;
+                               len = skb->len;
+                               card->dev->header_ops->create(skb, card->dev, 0,
+                                       card->dev->dev_addr, "FAKELL",
+                                       card->dev->addr_len);
+                               netif_receive_skb(skb);
+                       } else {
+                               is_vlan = qeth_l3_rebuild_skb(card, skb, hdr,
                                                      &vlan_tag);
-                       len = skb->len;
-                       if (is_vlan && !card->options.sniffer)
-                               __vlan_hwaccel_put_tag(skb, vlan_tag);
-                       napi_gro_receive(&card->napi, skb);
+                               len = skb->len;
+                               if (is_vlan && !card->options.sniffer)
+                                       __vlan_hwaccel_put_tag(skb, vlan_tag);
+                               napi_gro_receive(&card->napi, skb);
+                       }
                        break;
                case QETH_HEADER_TYPE_LAYER2: /* for HiperSockets sniffer */
                        skb->pkt_type = PACKET_HOST;
@@ -2784,6 +2800,30 @@ int inline qeth_l3_get_cast_type(struct qeth_card *card, struct sk_buff *skb)
        return cast_type;
 }
 
+static void qeth_l3_fill_af_iucv_hdr(struct qeth_card *card,
+               struct qeth_hdr *hdr, struct sk_buff *skb)
+{
+       char daddr[16];
+       struct af_iucv_trans_hdr *iucv_hdr;
+
+       skb_pull(skb, 14);
+       card->dev->header_ops->create(skb, card->dev, 0,
+                                     card->dev->dev_addr, card->dev->dev_addr,
+                                     card->dev->addr_len);
+       skb_pull(skb, 14);
+       iucv_hdr = (struct af_iucv_trans_hdr *)skb->data;
+       memset(hdr, 0, sizeof(struct qeth_hdr));
+       hdr->hdr.l3.id = QETH_HEADER_TYPE_LAYER3;
+       hdr->hdr.l3.ext_flags = 0;
+       hdr->hdr.l3.length = skb->len;
+       hdr->hdr.l3.flags = QETH_HDR_IPV6 | QETH_CAST_UNICAST;
+       memset(daddr, 0, sizeof(daddr));
+       daddr[0] = 0xfe;
+       daddr[1] = 0x80;
+       memcpy(&daddr[8], iucv_hdr->destUserID, 8);
+       memcpy(hdr->hdr.l3.dest_addr, daddr, 16);
+}
+
 static void qeth_l3_fill_header(struct qeth_card *card, struct qeth_hdr *hdr,
                struct sk_buff *skb, int ipv, int cast_type)
 {
@@ -2936,8 +2976,11 @@ static int qeth_l3_hard_start_xmit(struct sk_buff *skb, struct net_device *dev)
        int data_offset = -1;
        int nr_frags;
 
-       if (((card->info.type == QETH_CARD_TYPE_IQD) && (!ipv)) ||
-            card->options.sniffer)
+       if (((card->info.type == QETH_CARD_TYPE_IQD) &&
+            (((card->options.cq != QETH_CQ_ENABLED) && !ipv) ||
+             ((card->options.cq == QETH_CQ_ENABLED) &&
+              (skb->protocol != ETH_P_AF_IUCV)))) ||
+           card->options.sniffer)
                        goto tx_drop;
 
        if ((card->state != CARD_STATE_UP) || !card->lan_online) {
@@ -2959,7 +3002,10 @@ static int qeth_l3_hard_start_xmit(struct sk_buff *skb, struct net_device *dev)
        if ((card->info.type == QETH_CARD_TYPE_IQD) && (!large_send) &&
            (skb_shinfo(skb)->nr_frags == 0)) {
                new_skb = skb;
-               data_offset = ETH_HLEN;
+               if (new_skb->protocol == ETH_P_AF_IUCV)
+                       data_offset = 0;
+               else
+                       data_offset = ETH_HLEN;
                hdr = kmem_cache_alloc(qeth_core_header_cache, GFP_ATOMIC);
                if (!hdr)
                        goto tx_drop;
@@ -3024,9 +3070,13 @@ static int qeth_l3_hard_start_xmit(struct sk_buff *skb, struct net_device *dev)
                        qeth_l3_fill_header(card, hdr, new_skb, ipv,
                                                cast_type);
                } else {
-                       qeth_l3_fill_header(card, hdr, new_skb, ipv,
-                                               cast_type);
-                       hdr->hdr.l3.length = new_skb->len - data_offset;
+                       if (new_skb->protocol == ETH_P_AF_IUCV)
+                               qeth_l3_fill_af_iucv_hdr(card, hdr, new_skb);
+                       else {
+                               qeth_l3_fill_header(card, hdr, new_skb, ipv,
+                                                       cast_type);
+                               hdr->hdr.l3.length = new_skb->len - data_offset;
+                       }
                }
 
                if (skb->ip_summed == CHECKSUM_PARTIAL)
@@ -3289,6 +3339,8 @@ static int qeth_l3_setup_netdev(struct qeth_card *card)
                card->dev->flags |= IFF_NOARP;
                card->dev->netdev_ops = &qeth_l3_netdev_ops;
                qeth_l3_iqd_read_initial_mac(card);
+               if (card->options.hsuid[0])
+                       memcpy(card->dev->perm_addr, card->options.hsuid, 9);
        } else
                return -ENODEV;
 
@@ -3659,7 +3711,6 @@ static int qeth_l3_ip6_event(struct notifier_block *this,
        struct qeth_ipaddr *addr;
        struct qeth_card *card;
 
-
        card = qeth_l3_get_card_from_dev(dev);
        if (!card)
                return NOTIFY_DONE;