/*
*
- * linux/drivers/s390/net/qeth_main.c ($Revision: 1.145 $)
+ * linux/drivers/s390/net/qeth_main.c ($Revision: 1.191 $)
*
* Linux on zSeries OSA Express and HiperSockets support
*
* Frank Pavlic (pavlic@de.ibm.com) and
* Thomas Spatzier <tspat@de.ibm.com>
*
- * $Revision: 1.145 $ $Date: 2004/10/08 15:08:40 $
+ * $Revision: 1.191 $ $Date: 2005/01/31 13:13:57 $
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
-
/***
* eye catcher; just for debugging purposes
*/
#include <linux/config.h>
#include <linux/module.h>
#include <linux/moduleparam.h>
-
#include <linux/string.h>
#include <linux/errno.h>
#include <linux/mm.h>
-
-#include <asm/io.h>
-#include <asm/ebcdic.h>
-#include <linux/ctype.h>
-#include <asm/semaphore.h>
-#include <asm/timex.h>
#include <linux/ip.h>
#include <linux/inetdevice.h>
#include <linux/netdevice.h>
#include <linux/tcp.h>
#include <linux/icmp.h>
#include <linux/skbuff.h>
-#include <net/route.h>
-#include <net/arp.h>
#include <linux/in.h>
#include <linux/igmp.h>
-#include <net/ip.h>
-#include <asm/uaccess.h>
#include <linux/init.h>
#include <linux/reboot.h>
-#include <asm/qeth.h>
#include <linux/mii.h>
#include <linux/rcupdate.h>
+#include <net/arp.h>
+#include <net/ip.h>
+#include <net/route.h>
+
+#include <asm/ebcdic.h>
+#include <asm/io.h>
+#include <asm/qeth.h>
+#include <asm/timex.h>
+#include <asm/semaphore.h>
+#include <asm/uaccess.h>
+
#include "qeth.h"
#include "qeth_mpc.h"
#include "qeth_fs.h"
-#define VERSION_QETH_C "$Revision: 1.145 $"
+#define VERSION_QETH_C "$Revision: 1.191 $"
static const char *version = "qeth S/390 OSA-Express driver";
/**
static struct qeth_ipaddr *
qeth_get_addr_buffer(enum qeth_prot_versions);
+static void
+qeth_set_multicast_list(struct net_device *);
+
static void
qeth_notify_processes(void)
{
{
struct qeth_notify_list_struct *n_entry;
- QETH_DBF_TEXT(trace, 2, "notreg");
+
/*check first if entry already exists*/
spin_lock(&qeth_notify_lock);
list_for_each_entry(n_entry, &qeth_notify_list, list) {
free_netdev(card->dev);
qeth_clear_ip_list(card, 0, 0);
qeth_clear_ipato_list(card);
+ kfree(card->ip_tbd_list);
qeth_free_qdio_buffers(card);
kfree(card);
}
qeth_set_offline(struct ccwgroup_device *cgdev)
{
struct qeth_card *card = (struct qeth_card *) cgdev->dev.driver_data;
+ int rc = 0;
enum qeth_card_states recover_flag;
QETH_DBF_TEXT(setup, 3, "setoffl");
CARD_BUS_ID(card));
return -ERESTARTSYS;
}
- ccw_device_set_offline(CARD_DDEV(card));
- ccw_device_set_offline(CARD_WDEV(card));
- ccw_device_set_offline(CARD_RDEV(card));
+ if ((rc = ccw_device_set_offline(CARD_DDEV(card))) ||
+ (rc = ccw_device_set_offline(CARD_WDEV(card))) ||
+ (rc = ccw_device_set_offline(CARD_RDEV(card)))) {
+ QETH_DBF_TEXT_(setup, 2, "1err%d", rc);
+ }
if (recover_flag == CARD_STATE_UP)
card->state = CARD_STATE_RECOVER;
qeth_notify_processes();
return 0;
}
+static int
+qeth_wait_for_threads(struct qeth_card *card, unsigned long threads);
+
+
static void
qeth_remove_device(struct ccwgroup_device *cgdev)
{
if (!card)
return;
+ if (qeth_wait_for_threads(card, 0xffffffff))
+ return;
+
if (cgdev->state == CCWGROUP_ONLINE){
card->use_hard_stop = 1;
qeth_set_offline(cgdev);
if (todo->users > 0){
/* for VIPA and RXIP limit refcount to 1 */
if (todo->type != QETH_IP_TYPE_NORMAL)
- addr->users = 1;
+ todo->users = 1;
return 1;
} else
return 0;
struct qeth_ipaddr *tmp, *t;
int found = 0;
- list_for_each_entry_safe(tmp, t, &card->ip_tbd_list, entry) {
+ list_for_each_entry_safe(tmp, t, card->ip_tbd_list, entry) {
+ if ((addr->type == QETH_IP_TYPE_DEL_ALL_MC) &&
+ (tmp->type == QETH_IP_TYPE_DEL_ALL_MC))
+ return 0;
if ((tmp->proto == QETH_PROT_IPV4) &&
(addr->proto == QETH_PROT_IPV4) &&
(tmp->type == addr->type) &&
}
return 0;
} else {
- if (addr->users == 0)
- addr->users += add? 1:-1;
- if (add && (addr->type == QETH_IP_TYPE_NORMAL) &&
- qeth_is_addr_covered_by_ipato(card, addr)){
- QETH_DBF_TEXT(trace, 2, "tkovaddr");
- addr->set_flags |= QETH_IPA_SETIP_TAKEOVER_FLAG;
+ if (addr->type == QETH_IP_TYPE_DEL_ALL_MC)
+ list_add(&addr->entry, card->ip_tbd_list);
+ else {
+ if (addr->users == 0)
+ addr->users += add? 1:-1;
+ if (add && (addr->type == QETH_IP_TYPE_NORMAL) &&
+ qeth_is_addr_covered_by_ipato(card, addr)){
+ QETH_DBF_TEXT(trace, 2, "tkovaddr");
+ addr->set_flags |= QETH_IPA_SETIP_TAKEOVER_FLAG;
+ }
+ list_add_tail(&addr->entry, card->ip_tbd_list);
}
- list_add_tail(&addr->entry, &card->ip_tbd_list);
return 1;
}
}
if (addr->proto == QETH_PROT_IPV4)
QETH_DBF_HEX(trace,4,&addr->u.a4.addr,4);
else {
- QETH_DBF_HEX(trace,4,&addr->u.a6.addr,4);
- QETH_DBF_HEX(trace,4,((char *)&addr->u.a6.addr)+4,4);
+ QETH_DBF_HEX(trace,4,&addr->u.a6.addr,8);
+ QETH_DBF_HEX(trace,4,((char *)&addr->u.a6.addr)+8,8);
}
spin_lock_irqsave(&card->ip_lock, flags);
rc = __qeth_insert_ip_todo(card, addr, 0);
if (addr->proto == QETH_PROT_IPV4)
QETH_DBF_HEX(trace,4,&addr->u.a4.addr,4);
else {
- QETH_DBF_HEX(trace,4,&addr->u.a6.addr,4);
- QETH_DBF_HEX(trace,4,((char *)&addr->u.a6.addr)+4,4);
+ QETH_DBF_HEX(trace,4,&addr->u.a6.addr,8);
+ QETH_DBF_HEX(trace,4,((char *)&addr->u.a6.addr)+8,8);
}
spin_lock_irqsave(&card->ip_lock, flags);
rc = __qeth_insert_ip_todo(card, addr, 1);
return rc;
}
-static void
-qeth_reinsert_todos(struct qeth_card *card, struct list_head *todos)
+static inline void
+__qeth_delete_all_mc(struct qeth_card *card, unsigned long *flags)
{
- struct qeth_ipaddr *todo, *tmp;
+ struct qeth_ipaddr *addr, *tmp;
+ int rc;
- list_for_each_entry_safe(todo, tmp, todos, entry){
- list_del_init(&todo->entry);
- if (todo->users < 0) {
- if (!qeth_delete_ip(card, todo))
- kfree(todo);
- } else {
- if (!qeth_add_ip(card, todo))
- kfree(todo);
+ list_for_each_entry_safe(addr, tmp, &card->ip_list, entry) {
+ if (addr->is_multicast) {
+ spin_unlock_irqrestore(&card->ip_lock, *flags);
+ rc = qeth_deregister_addr_entry(card, addr);
+ spin_lock_irqsave(&card->ip_lock, *flags);
+ if (!rc) {
+ list_del(&addr->entry);
+ kfree(addr);
+ }
}
}
}
static void
qeth_set_ip_addr_list(struct qeth_card *card)
{
- struct list_head failed_todos;
+ struct list_head *tbd_list;
struct qeth_ipaddr *todo, *addr;
unsigned long flags;
int rc;
QETH_DBF_TEXT(trace, 2, "sdiplist");
QETH_DBF_HEX(trace, 2, &card, sizeof(void *));
- INIT_LIST_HEAD(&failed_todos);
-
spin_lock_irqsave(&card->ip_lock, flags);
- while (!list_empty(&card->ip_tbd_list)) {
- todo = list_entry(card->ip_tbd_list.next,
- struct qeth_ipaddr, entry);
- list_del_init(&todo->entry);
+ tbd_list = card->ip_tbd_list;
+ card->ip_tbd_list = kmalloc(sizeof(struct list_head), GFP_ATOMIC);
+ if (!card->ip_tbd_list) {
+ QETH_DBF_TEXT(trace, 0, "silnomem");
+ card->ip_tbd_list = tbd_list;
+ spin_unlock_irqrestore(&card->ip_lock, flags);
+ return;
+ } else
+ INIT_LIST_HEAD(card->ip_tbd_list);
+
+ while (!list_empty(tbd_list)){
+ todo = list_entry(tbd_list->next, struct qeth_ipaddr, entry);
+ list_del(&todo->entry);
+ if (todo->type == QETH_IP_TYPE_DEL_ALL_MC){
+ __qeth_delete_all_mc(card, &flags);
+ kfree(todo);
+ continue;
+ }
rc = __qeth_ref_ip_on_card(card, todo, &addr);
if (rc == 0) {
/* nothing to be done; only adjusted refcount */
if (!rc)
list_add_tail(&todo->entry, &card->ip_list);
else
- list_add_tail(&todo->entry, &failed_todos);
+ kfree(todo);
} else if (rc == -1) {
/* on-card entry to be removed */
list_del_init(&addr->entry);
spin_unlock_irqrestore(&card->ip_lock, flags);
rc = qeth_deregister_addr_entry(card, addr);
spin_lock_irqsave(&card->ip_lock, flags);
- if (!rc) {
+ if (!rc)
kfree(addr);
- kfree(todo);
- } else {
+ else
list_add_tail(&addr->entry, &card->ip_list);
- list_add_tail(&todo->entry, &failed_todos);
- }
+ kfree(todo);
}
}
spin_unlock_irqrestore(&card->ip_lock, flags);
- qeth_reinsert_todos(card, &failed_todos);
+ kfree(tbd_list);
}
static void qeth_delete_mc_addresses(struct qeth_card *);
}
static int
-qeth_register_mc_addresses(void *ptr)
-{
- struct qeth_card *card;
-
- card = (struct qeth_card *) ptr;
- daemonize("qeth_reg_mcaddrs");
- QETH_DBF_TEXT(trace,4,"regmcth1");
- if (!qeth_do_run_thread(card, QETH_SET_MC_THREAD))
- return 0;
- QETH_DBF_TEXT(trace,4,"regmcth2");
- qeth_delete_mc_addresses(card);
- qeth_add_multicast_ipv4(card);
-#ifdef CONFIG_QETH_IPV6
- qeth_add_multicast_ipv6(card);
-#endif
- qeth_set_ip_addr_list(card);
- qeth_clear_thread_running_bit(card, QETH_SET_MC_THREAD);
- return 0;
-}
-
-static int
-qeth_register_ip_address(void *ptr)
+qeth_register_ip_addresses(void *ptr)
{
struct qeth_card *card;
return;
if (qeth_do_start_thread(card, QETH_SET_IP_THREAD))
- kernel_thread(qeth_register_ip_address, (void *) card, SIGCHLD);
- if (qeth_do_start_thread(card, QETH_SET_MC_THREAD))
- kernel_thread(qeth_register_mc_addresses, (void *)card,SIGCHLD);
+ kernel_thread(qeth_register_ip_addresses, (void *)card,SIGCHLD);
if (qeth_do_start_thread(card, QETH_RECOVER_THREAD))
kernel_thread(qeth_recover, (void *) card, SIGCHLD);
}
card->options.fake_broadcast = 0;
card->options.add_hhlen = DEFAULT_ADD_HHLEN;
card->options.fake_ll = 0;
+ card->options.layer2 = 0;
}
/**
INIT_WORK(&card->kernel_thread_starter,
(void *)qeth_start_kernel_thread,card);
INIT_LIST_HEAD(&card->ip_list);
- INIT_LIST_HEAD(&card->ip_tbd_list);
+ card->ip_tbd_list = kmalloc(sizeof(struct list_head), GFP_KERNEL);
+ if (!card->ip_tbd_list) {
+ QETH_DBF_TEXT(setup, 0, "iptbdnom");
+ return -ENOMEM;
+ }
+ INIT_LIST_HEAD(card->ip_tbd_list);
INIT_LIST_HEAD(&card->cmd_waiter_list);
init_waitqueue_head(&card->wait_q);
/* intial options */
if (channel->state != CH_STATE_UP){
rc = -ETIME;
QETH_DBF_TEXT_(setup, 2, "3err%d", rc);
+ qeth_clear_cmd_buffers(channel);
} else
rc = 0;
return rc;
if (channel->state != CH_STATE_ACTIVATING) {
PRINT_WARN("qeth: IDX activate timed out!\n");
QETH_DBF_TEXT_(setup, 2, "2err%d", -ETIME);
+ qeth_clear_cmd_buffers(channel);
return -ETIME;
}
return qeth_idx_activate_get_answer(channel,idx_reply_cb);
QETH_DBF_TEXT(trace, 2, "rstipadd");
qeth_clear_ip_list(card, 0, 1);
- if ( (qeth_set_thread_start_bit(card, QETH_SET_IP_THREAD) == 0) ||
- (qeth_set_thread_start_bit(card, QETH_SET_MC_THREAD) == 0) )
- schedule_work(&card->kernel_thread_starter);
+ /* this function will also schedule the SET_IP_THREAD */
+ qeth_set_multicast_list(card->dev);
}
static struct qeth_ipa_cmd *
"there is a network problem or "
"someone pulled the cable or "
"disabled the port.\n",
- card->info.if_name,
+ QETH_CARD_IFNAME(card),
card->info.chpid);
card->lan_online = 0;
- if (netif_carrier_ok(card->dev)) {
- netif_carrier_off(card->dev);
- netif_stop_queue(card->dev);
- }
+ netif_carrier_off(card->dev);
return NULL;
case IPA_CMD_STARTLAN:
PRINT_INFO("Link reestablished on %s "
"(CHPID 0x%X). Scheduling "
"IP address reset.\n",
- card->info.if_name,
+ QETH_CARD_IFNAME(card),
card->info.chpid);
card->lan_online = 1;
- if (!netif_carrier_ok(card->dev)) {
- netif_carrier_on(card->dev);
- netif_wake_queue(card->dev);
- }
+ netif_carrier_on(card->dev);
qeth_reset_ip_addresses(card);
return NULL;
case IPA_CMD_REGISTER_LOCAL_ADDR:
case IPA_CMD_UNREGISTER_LOCAL_ADDR:
PRINT_WARN("probably problem on %s: "
"received IPA command 0x%X\n",
- card->info.if_name,
+ QETH_CARD_IFNAME(card),
cmd->hdr.command);
break;
default:
void *reply_param)
{
int rc;
+ char prot_type;
QETH_DBF_TEXT(trace,4,"sendipa");
memcpy(iob->data, IPA_PDU_HEADER, IPA_PDU_HEADER_SIZE);
+
+ if (card->options.layer2)
+ prot_type = QETH_PROT_LAYER2;
+ else
+ prot_type = QETH_PROT_TCPIP;
+
+ memcpy(QETH_IPA_CMD_PROT_TYPE(iob->data),&prot_type,1);
memcpy(QETH_IPA_CMD_DEST_ADDR(iob->data),
&card->token.ulp_connection_r, QETH_MPC_TOKEN_LENGTH);
qeth_ulp_enable(struct qeth_card *card)
{
int rc;
+ char prot_type;
struct qeth_cmd_buffer *iob;
/*FIXME: trace view callbacks*/
*(QETH_ULP_ENABLE_LINKNUM(iob->data)) =
(__u8) card->info.portno;
+ if (card->options.layer2)
+ prot_type = QETH_PROT_LAYER2;
+ else
+ prot_type = QETH_PROT_TCPIP;
+ memcpy(QETH_ULP_ENABLE_PROT_TYPE(iob->data),&prot_type,1);
memcpy(QETH_ULP_ENABLE_DEST_ADDR(iob->data),
&card->token.cm_connection_r, QETH_MPC_TOKEN_LENGTH);
memcpy(QETH_ULP_ENABLE_FILTER_TOKEN(iob->data),
*hdr = element->addr + offset;
offset += sizeof(struct qeth_hdr);
- skb_len = (*hdr)->length;
+ if (card->options.layer2)
+ skb_len = (*hdr)->hdr.l2.pkt_length;
+ else
+ skb_len = (*hdr)->hdr.l3.length;
+
if (!skb_len)
return NULL;
if (card->options.fake_ll){
no_mem:
if (net_ratelimit()){
PRINT_WARN("No memory for packet received on %s.\n",
- card->info.if_name);
+ QETH_CARD_IFNAME(card));
QETH_DBF_TEXT(trace,2,"noskbmem");
QETH_DBF_TEXT_(trace,2,"%s",CARD_BUS_ID(card));
}
static inline unsigned short
qeth_type_trans(struct sk_buff *skb, struct net_device *dev)
{
- struct ethhdr *eth;
struct qeth_card *card;
+ struct ethhdr *eth;
- QETH_DBF_TEXT(trace,5,"typtrans");
+ QETH_DBF_TEXT(trace,6,"typtrans");
card = (struct qeth_card *)dev->priv;
#ifdef CONFIG_TR
(card->info.link_type == QETH_LINK_TYPE_LANE_TR))
return tr_type_trans(skb,dev);
#endif /* CONFIG_TR */
-
skb->mac.raw = skb->data;
- skb_pull(skb, ETH_ALEN * 2 + sizeof (short));
+ skb_pull(skb, ETH_HLEN );
eth = eth_hdr(skb);
if (*eth->h_dest & 1) {
skb->pkt_type = PACKET_BROADCAST;
else
skb->pkt_type = PACKET_MULTICAST;
- } else {
+ } else if (memcmp(eth->h_dest, dev->dev_addr, ETH_ALEN))
skb->pkt_type = PACKET_OTHERHOST;
- }
+
if (ntohs(eth->h_proto) >= 1536)
return eth->h_proto;
if (*(unsigned short *) (skb->data) == 0xFFFF)
memcpy(fake_hdr->h_dest, card->dev->dev_addr, ETH_ALEN);
}
/* the source MAC address */
- if (hdr->ext_flags & QETH_HDR_EXT_SRC_MAC_ADDR)
- memcpy(fake_hdr->h_source, &hdr->dest_addr[2], ETH_ALEN);
+ if (hdr->hdr.l3.ext_flags & QETH_HDR_EXT_SRC_MAC_ADDR)
+ memcpy(fake_hdr->h_source, &hdr->hdr.l3.dest_addr[2], ETH_ALEN);
else
memset(fake_hdr->h_source, 0, ETH_ALEN);
/* the protocol */
#ifdef CONFIG_QETH_VLAN
u16 *vlan_tag;
- if (hdr->ext_flags & QETH_HDR_EXT_VLAN_FRAME) {
+ if (hdr->hdr.l3.ext_flags &
+ (QETH_HDR_EXT_VLAN_FRAME | QETH_HDR_EXT_INCLUDE_VLAN_TAG)) {
vlan_tag = (u16 *) skb_push(skb, VLAN_HLEN);
- *vlan_tag = hdr->vlan_id;
+ *vlan_tag = (hdr->hdr.l3.ext_flags & QETH_HDR_EXT_VLAN_FRAME)?
+ hdr->hdr.l3.vlan_id : *((u16 *)&hdr->hdr.l3.dest_addr[12]);
*(vlan_tag + 1) = skb->protocol;
skb->protocol = __constant_htons(ETH_P_8021Q);
}
#endif /* CONFIG_QETH_VLAN */
}
+static inline __u16
+qeth_layer2_rebuild_skb(struct qeth_card *card, struct sk_buff *skb,
+ struct qeth_hdr *hdr)
+{
+ unsigned short vlan_id = 0;
+
+ skb->pkt_type = PACKET_HOST;
+ if (card->options.checksum_type == NO_CHECKSUMMING)
+ skb->ip_summed = CHECKSUM_UNNECESSARY;
+ else
+ skb->ip_summed = CHECKSUM_NONE;
+#ifdef CONFIG_QETH_VLAN
+ if (hdr->hdr.l2.flags[2] & (QETH_LAYER2_FLAG_VLAN)) {
+ vlan_id = hdr->hdr.l2.vlan_id;
+ skb_pull(skb, VLAN_HLEN);
+ }
+#endif
+ skb->protocol = qeth_type_trans(skb, skb->dev);
+ return vlan_id;
+}
+
static inline void
qeth_rebuild_skb(struct qeth_card *card, struct sk_buff *skb,
struct qeth_hdr *hdr)
{
#ifdef CONFIG_QETH_IPV6
- if (hdr->flags & QETH_HDR_PASSTHRU){
+ if (hdr->hdr.l3.flags & QETH_HDR_PASSTHRU) {
+ skb->pkt_type = PACKET_HOST;
skb->protocol = qeth_type_trans(skb, card->dev);
return;
}
#endif /* CONFIG_QETH_IPV6 */
- skb->protocol = htons((hdr->flags & QETH_HDR_IPV6)? ETH_P_IPV6 :
+ skb->protocol = htons((hdr->hdr.l3.flags & QETH_HDR_IPV6)? ETH_P_IPV6 :
ETH_P_IP);
- switch (hdr->flags & QETH_HDR_CAST_MASK){
+ switch (hdr->hdr.l3.flags & QETH_HDR_CAST_MASK){
case QETH_CAST_UNICAST:
skb->pkt_type = PACKET_HOST;
break;
default:
skb->pkt_type = PACKET_HOST;
}
+ qeth_rebuild_skb_vlan(card, skb, hdr);
if (card->options.fake_ll)
qeth_rebuild_skb_fake_ll(card, skb, hdr);
else
skb->mac.raw = skb->data;
skb->ip_summed = card->options.checksum_type;
if (card->options.checksum_type == HW_CHECKSUMMING){
- if ( (hdr->ext_flags &
+ if ( (hdr->hdr.l3.ext_flags &
(QETH_HDR_EXT_CSUM_HDR_REQ |
QETH_HDR_EXT_CSUM_TRANSP_REQ)) ==
(QETH_HDR_EXT_CSUM_HDR_REQ |
else
skb->ip_summed = SW_CHECKSUMMING;
}
- qeth_rebuild_skb_vlan(card, skb, hdr);
}
static inline void
struct qeth_qdio_buffer *buf, int index)
{
struct qdio_buffer_element *element;
- int offset;
struct sk_buff *skb;
struct qeth_hdr *hdr;
+ int offset;
int rxrc;
+ __u16 vlan_tag = 0;
/* get first element of current buffer */
element = (struct qdio_buffer_element *)&buf->buffer->element[0];
card->perf_stats.bufs_rec++;
#endif
while((skb = qeth_get_next_skb(card, buf->buffer, &element,
- &offset, &hdr))){
- qeth_rebuild_skb(card, skb, hdr);
+ &offset, &hdr))) {
+ skb->dev = card->dev;
+ if (hdr->hdr.l2.id == QETH_HEADER_TYPE_LAYER2)
+ vlan_tag = qeth_layer2_rebuild_skb(card, skb, hdr);
+ else
+ qeth_rebuild_skb(card, skb, hdr);
/* is device UP ? */
if (!(card->dev->flags & IFF_UP)){
dev_kfree_skb_any(skb);
continue;
}
- skb->dev = card->dev;
+#ifdef CONFIG_QETH_VLAN
+ if (vlan_tag)
+ vlan_hwaccel_rx(skb, card->vlangrp, vlan_tag);
+ else
+#endif
rxrc = netif_rx(skb);
card->dev->last_rx = jiffies;
card->stats.rx_packets++;
}
atomic_sub(count, &queue->used_buffers);
/* check if we need to do something on this outbound queue */
- qeth_check_outbound_queue(queue);
+ if (card->info.type != QETH_CARD_TYPE_IQD)
+ qeth_check_outbound_queue(queue);
netif_wake_queue(card->dev);
#ifdef CONFIG_QETH_PERF_STATS
QETH_DBF_TEXT(trace,3,"qdioclr");
if (card->qdio.state == QETH_QDIO_ESTABLISHED){
- qdio_cleanup(CARD_DDEV(card),
+ if ((rc = qdio_cleanup(CARD_DDEV(card),
(card->info.type == QETH_CARD_TYPE_IQD) ?
QDIO_FLAG_CLEANUP_USING_HALT :
- QDIO_FLAG_CLEANUP_USING_CLEAR);
+ QDIO_FLAG_CLEANUP_USING_CLEAR)))
+ QETH_DBF_TEXT_(trace, 3, "1err%d", rc);
card->qdio.state = QETH_QDIO_ALLOCATED;
}
- rc = qeth_clear_halt_card(card, use_halt);
+ if ((rc = qeth_clear_halt_card(card, use_halt)))
+ QETH_DBF_TEXT_(trace, 3, "2err%d", rc);
card->state = CARD_STATE_DOWN;
return rc;
}
return dev;
}
+/*hard_header fake function; used in case fake_ll is set */
+static int
+qeth_fake_header(struct sk_buff *skb, struct net_device *dev,
+ unsigned short type, void *daddr, void *saddr,
+ unsigned len)
+{
+ struct ethhdr *hdr;
+ struct qeth_card *card;
+
+ card = (struct qeth_card *)dev->priv;
+ hdr = (struct ethhdr *)skb_push(skb, QETH_FAKE_LL_LEN);
+ memcpy(hdr->h_source, card->dev->dev_addr, ETH_ALEN);
+ memcpy(hdr->h_dest, "FAKELL", ETH_ALEN);
+ if (type != ETH_P_802_3)
+ hdr->h_proto = htons(type);
+ else
+ hdr->h_proto = htons(len);
+ return QETH_FAKE_LL_LEN;
+}
+
static inline int
qeth_send_packet(struct qeth_card *, struct sk_buff *);
if (skb==NULL) {
card->stats.tx_dropped++;
card->stats.tx_errors++;
- return -EIO;
+ /* return OK; otherwise ksoftirqd goes to 100% */
+ return NETDEV_TX_OK;
}
- if ((card->state != CARD_STATE_UP) || !netif_carrier_ok(dev)) {
+ if ((card->state != CARD_STATE_UP) || !card->lan_online) {
card->stats.tx_dropped++;
card->stats.tx_errors++;
card->stats.tx_carrier_errors++;
- return -EIO;
+ dev_kfree_skb_any(skb);
+ /* return OK; otherwise ksoftirqd goes to 100% */
+ return NETDEV_TX_OK;
}
#ifdef CONFIG_QETH_PERF_STATS
card->perf_stats.outbound_cnt++;
card->perf_stats.outbound_start_time = qeth_get_micros();
#endif
+ if (dev->hard_header == qeth_fake_header) {
+ if ((skb = qeth_pskb_unshare(skb, GFP_ATOMIC)) == NULL) {
+ card->stats.tx_dropped++;
+ dev_kfree_skb_irq(skb);
+ return 0;
+ }
+ skb_pull(skb, QETH_FAKE_LL_LEN);
+ }
/*
* We only call netif_stop_queue in case of errors. Since we've
* got our own synchronization on queues we can keep the stack's
* queue running.
*/
- if ((rc = qeth_send_packet(card, skb)))
- netif_stop_queue(dev);
+ if ((rc = qeth_send_packet(card, skb))){
+ if (rc == -EBUSY) {
+ netif_stop_queue(dev);
+ rc = NETDEV_TX_BUSY;
+ } else {
+ card->stats.tx_errors++;
+ card->stats.tx_dropped++;
+ dev_kfree_skb_any(skb);
+ /* set to OK; otherwise ksoftirqd goes to 100% */
+ rc = NETDEV_TX_OK;
+ }
+ }
#ifdef CONFIG_QETH_PERF_STATS
card->perf_stats.outbound_time += qeth_get_micros() -
if (card->state != CARD_STATE_SOFTSETUP)
return -ENODEV;
+ if ( (card->options.layer2) &&
+ (!card->info.layer2_mac_registered)) {
+ QETH_DBF_TEXT(trace,4,"nomacadr");
+ return -EPERM;
+ }
card->dev->flags |= IFF_UP;
netif_start_queue(dev);
card->data.state = CH_STATE_UP;
if (!card->lan_online){
if (netif_carrier_ok(dev))
netif_carrier_off(dev);
- netif_stop_queue(dev);
}
return 0;
}
else if (skb->protocol == ETH_P_IP)
return ((skb->nh.raw[16] & 0xf0) == 0xe0) ? RTN_MULTICAST : 0;
/* ... */
- if (!memcmp(skb->nh.raw, skb->dev->broadcast, 6))
+ if (!memcmp(skb->data, skb->dev->broadcast, 6))
return RTN_BROADCAST;
else {
u16 hdr_mac;
- hdr_mac = *((u16 *)skb->nh.raw);
+ hdr_mac = *((u16 *)skb->data);
/* tr multicast? */
switch (card->info.link_type) {
case QETH_LINK_TYPE_HSTR:
if (!new_skb) {
PRINT_ERR("qeth_prepare_skb: could "
"not realloc headroom for qeth_hdr "
- "on interface %s", card->info.if_name);
+ "on interface %s", QETH_CARD_IFNAME(card));
return -ENOMEM;
}
*skb = new_skb;
}
#ifdef CONFIG_QETH_VLAN
- if (card->vlangrp && vlan_tx_tag_present(*skb) && (ipv == 6)){
+ if (card->vlangrp && vlan_tx_tag_present(*skb) &&
+ ((ipv == 6) || card->options.layer2) ) {
/*
* Move the mac addresses (6 bytes src, 6 bytes dest)
* to the beginning of the new header. We are using three
memcpy((*skb)->data, (*skb)->data + 4, 4);
memcpy((*skb)->data + 4, (*skb)->data + 8, 4);
memcpy((*skb)->data + 8, (*skb)->data + 12, 4);
- tag = (u16 *) (*skb)->data + 12;
+ tag = (u16 *)((*skb)->data + 12);
/*
* first two bytes = ETH_P_8021Q (0x8100)
* second two bytes = VLANID
*/
*tag = __constant_htons(ETH_P_8021Q);
- *(tag + 1) = vlan_tx_tag_get(*skb);
- *(tag + 1) = htons(*(tag + 1));
+ *(tag + 1) = htons(vlan_tx_tag_get(*skb));
}
#endif
*hdr = (struct qeth_hdr *) skb_push(*skb, sizeof(struct qeth_hdr));
QETH_IP_HEADER_SIZE) & (~(PAGE_SIZE - 1)))) {
PRINT_ERR("qeth_prepare_skb: misaligned "
"packet on interface %s. Discarded.",
- card->info.if_name);
+ QETH_CARD_IFNAME(card));
return -EINVAL;
}
return 0;
return ct | QETH_CAST_UNICAST;
}
+static inline void
+qeth_layer2_get_packet_type(struct qeth_card *card, struct qeth_hdr *hdr,
+ struct sk_buff *skb)
+{
+ __u16 hdr_mac;
+
+ if (!memcmp(skb->data+QETH_HEADER_SIZE,
+ skb->dev->broadcast,6)) { /* broadcast? */
+ *(__u32 *)hdr->hdr.l2.flags |=
+ QETH_LAYER2_FLAG_BROADCAST << 8;
+ return;
+ }
+ hdr_mac=*((__u16*)skb->data);
+ /* tr multicast? */
+ switch (card->info.link_type) {
+ case QETH_LINK_TYPE_HSTR:
+ case QETH_LINK_TYPE_LANE_TR:
+ if ((hdr_mac == QETH_TR_MAC_NC) ||
+ (hdr_mac == QETH_TR_MAC_C) )
+ *(__u32 *)hdr->hdr.l2.flags |=
+ QETH_LAYER2_FLAG_MULTICAST << 8;
+ else
+ *(__u32 *)hdr->hdr.l2.flags |=
+ QETH_LAYER2_FLAG_UNICAST << 8;
+ break;
+ /* eth or so multicast? */
+ default:
+ if ( (hdr_mac==QETH_ETH_MAC_V4) ||
+ (hdr_mac==QETH_ETH_MAC_V6) )
+ *(__u32 *)hdr->hdr.l2.flags |=
+ QETH_LAYER2_FLAG_MULTICAST << 8;
+ else
+ *(__u32 *)hdr->hdr.l2.flags |=
+ QETH_LAYER2_FLAG_UNICAST << 8;
+ }
+}
+
+static inline void
+qeth_layer2_fill_header(struct qeth_card *card, struct qeth_hdr *hdr,
+ struct sk_buff *skb, int cast_type)
+{
+ memset(hdr, 0, sizeof(struct qeth_hdr));
+ hdr->hdr.l2.id = QETH_HEADER_TYPE_LAYER2;
+
+ /* set byte 0 to "0x02" and byte 3 to casting flags */
+ if (cast_type==RTN_MULTICAST)
+ *(__u32 *)hdr->hdr.l2.flags |= QETH_LAYER2_FLAG_MULTICAST << 8;
+ else if (cast_type==RTN_BROADCAST)
+ *(__u32 *)hdr->hdr.l2.flags |= QETH_LAYER2_FLAG_BROADCAST << 8;
+ else
+ qeth_layer2_get_packet_type(card, hdr, skb);
+
+ hdr->hdr.l2.pkt_length = skb->len-QETH_HEADER_SIZE;
+#ifdef CONFIG_QETH_VLAN
+ /* VSWITCH relies on the VLAN
+ * information to be present in
+ * the QDIO header */
+ if ((card->vlangrp != NULL) &&
+ vlan_tx_tag_present(skb)) {
+ *(__u32 *)hdr->hdr.l2.flags |= QETH_LAYER2_FLAG_VLAN << 8;
+ hdr->hdr.l2.vlan_id = vlan_tx_tag_get(skb);
+ }
+#endif
+}
+
static inline void
qeth_fill_header(struct qeth_card *card, struct qeth_hdr *hdr,
struct sk_buff *skb, int ipv, int cast_type)
{
- hdr->id = 1;
- hdr->ext_flags = 0;
-
QETH_DBF_TEXT(trace, 6, "fillhdr");
+
+ if (card->options.layer2) {
+ qeth_layer2_fill_header(card, hdr, skb, cast_type);
+ return;
+ }
+ hdr->hdr.l3.id = QETH_HEADER_TYPE_LAYER3;
+ hdr->hdr.l3.ext_flags = 0;
#ifdef CONFIG_QETH_VLAN
/*
* before we're going to overwrite this location with next hop ip.
* v6 uses passthrough, v4 sets the tag in the QDIO header.
*/
if (card->vlangrp && vlan_tx_tag_present(skb)) {
- hdr->ext_flags = (ipv == 4)? QETH_EXT_HDR_VLAN_FRAME :
- QETH_EXT_HDR_INCLUDE_VLAN_TAG;
- hdr->vlan_id = vlan_tx_tag_get(skb);
+ hdr->hdr.l3.ext_flags = (ipv == 4) ?
+ QETH_HDR_EXT_VLAN_FRAME :
+ QETH_HDR_EXT_INCLUDE_VLAN_TAG;
+ hdr->hdr.l3.vlan_id = vlan_tx_tag_get(skb);
}
#endif /* CONFIG_QETH_VLAN */
- hdr->length = skb->len - sizeof(struct qeth_hdr);
+ hdr->hdr.l3.length = skb->len - sizeof(struct qeth_hdr);
if (ipv == 4) { /* IPv4 */
- hdr->flags = qeth_get_qeth_hdr_flags4(cast_type);
- memset(hdr->dest_addr, 0, 12);
+ hdr->hdr.l3.flags = qeth_get_qeth_hdr_flags4(cast_type);
+ memset(hdr->hdr.l3.dest_addr, 0, 12);
if ((skb->dst) && (skb->dst->neighbour)) {
- *((u32 *) (&hdr->dest_addr[12])) =
+ *((u32 *) (&hdr->hdr.l3.dest_addr[12])) =
*((u32 *) skb->dst->neighbour->primary_key);
} else {
/* fill in destination address used in ip header */
- *((u32 *) (&hdr->dest_addr[12])) = skb->nh.iph->daddr;
+ *((u32 *) (&hdr->hdr.l3.dest_addr[12])) = skb->nh.iph->daddr;
}
} else if (ipv == 6) { /* IPv6 or passthru */
- hdr->flags = qeth_get_qeth_hdr_flags6(cast_type);
+ hdr->hdr.l3.flags = qeth_get_qeth_hdr_flags6(cast_type);
if ((skb->dst) && (skb->dst->neighbour)) {
- memcpy(hdr->dest_addr,
+ memcpy(hdr->hdr.l3.dest_addr,
skb->dst->neighbour->primary_key, 16);
} else {
/* fill in destination address used in ip header */
- memcpy(hdr->dest_addr, &skb->nh.ipv6h->daddr, 16);
+ memcpy(hdr->hdr.l3.dest_addr, &skb->nh.ipv6h->daddr, 16);
}
} else { /* passthrough */
if (!memcmp(skb->data + sizeof(struct qeth_hdr),
skb->dev->broadcast, 6)) { /* broadcast? */
- hdr->flags = QETH_CAST_BROADCAST | QETH_HDR_PASSTHRU;
+ hdr->hdr.l3.flags = QETH_CAST_BROADCAST | QETH_HDR_PASSTHRU;
} else {
- hdr->flags = (cast_type == RTN_MULTICAST) ?
+ hdr->hdr.l3.flags = (cast_type == RTN_MULTICAST) ?
QETH_CAST_MULTICAST | QETH_HDR_PASSTHRU :
QETH_CAST_UNICAST | QETH_HDR_PASSTHRU;
}
static inline int
qeth_send_packet(struct qeth_card *card, struct sk_buff *skb)
{
- int ipv;
+ int ipv = 0;
int cast_type;
struct qeth_qdio_out_q *queue;
struct qeth_hdr *hdr;
QETH_DBF_TEXT(trace, 6, "sendpkt");
- ipv = qeth_get_ip_version(skb);
+ if (!card->options.layer2)
+ ipv = qeth_get_ip_version(skb);
cast_type = qeth_get_cast_type(card, skb);
queue = card->qdio.out_qs
[qeth_get_priority_queue(card, skb, ipv, cast_type)];
return -EOPNOTSUPP;
if (!qeth_is_supported(card,IPA_ARP_PROCESSING)) {
PRINT_WARN("ARP processing not supported "
- "on %s!\n", card->info.if_name);
+ "on %s!\n", QETH_CARD_IFNAME(card));
return -EOPNOTSUPP;
}
rc = qeth_send_simple_setassparms(card, IPA_ARP_PROCESSING,
tmp = rc;
PRINT_WARN("Could not set number of ARP entries on %s: "
"%s (0x%x/%d)\n",
- card->info.if_name, qeth_arp_get_error_cause(&rc),
+ QETH_CARD_IFNAME(card), qeth_arp_get_error_cause(&rc),
tmp, tmp);
}
return rc;
* funcs flags); since all zeros is no valueable information,
* we say EOPNOTSUPP for all ARP functions
*/
- if (card->info.guestlan)
- return -EOPNOTSUPP;
+ /*if (card->info.guestlan)
+ return -EOPNOTSUPP; */
if (!qeth_is_supported(card,/*IPA_QUERY_ARP_ADDR_INFO*/
IPA_ARP_PROCESSING)) {
PRINT_WARN("ARP processing not supported "
- "on %s!\n", card->info.if_name);
+ "on %s!\n", QETH_CARD_IFNAME(card));
return -EOPNOTSUPP;
}
/* get size of userspace buffer and mask_bits -> 6 bytes */
tmp = rc;
PRINT_WARN("Error while querying ARP cache on %s: %s "
"(0x%x/%d)\n",
- card->info.if_name, qeth_arp_get_error_cause(&rc),
+ QETH_CARD_IFNAME(card), qeth_arp_get_error_cause(&rc),
tmp, tmp);
copy_to_user(udata, qinfo.udata, 4);
} else {
if (card->info.guestlan)
return -EOPNOTSUPP;
- if (!qeth_adp_supported(card,IPA_SETADP_SET_SNMP_CONTROL)) {
+
+ if ((!qeth_adp_supported(card,IPA_SETADP_SET_SNMP_CONTROL)) &&
+ (!card->options.layer2) ) {
PRINT_WARN("SNMP Query MIBS not supported "
- "on %s!\n", card->info.if_name);
+ "on %s!\n", QETH_CARD_IFNAME(card));
return -EOPNOTSUPP;
}
/* skip 4 bytes (data_len struct member) to get req_len */
qeth_snmp_command_cb, (void *)&qinfo);
if (rc)
PRINT_WARN("SNMP command failed on %s: (0x%x)\n",
- card->info.if_name, rc);
+ QETH_CARD_IFNAME(card), rc);
else
copy_to_user(udata, qinfo.udata, qinfo.udata_len);
return -EOPNOTSUPP;
if (!qeth_is_supported(card,IPA_ARP_PROCESSING)) {
PRINT_WARN("ARP processing not supported "
- "on %s!\n", card->info.if_name);
+ "on %s!\n", QETH_CARD_IFNAME(card));
return -EOPNOTSUPP;
}
qeth_ipaddr4_to_string((u8 *)entry->ipaddr, buf);
PRINT_WARN("Could not add ARP entry for address %s on %s: "
"%s (0x%x/%d)\n",
- buf, card->info.if_name,
+ buf, QETH_CARD_IFNAME(card),
qeth_arp_get_error_cause(&rc), tmp, tmp);
}
return rc;
return -EOPNOTSUPP;
if (!qeth_is_supported(card,IPA_ARP_PROCESSING)) {
PRINT_WARN("ARP processing not supported "
- "on %s!\n", card->info.if_name);
+ "on %s!\n", QETH_CARD_IFNAME(card));
return -EOPNOTSUPP;
}
memcpy(buf, entry, 12);
qeth_ipaddr4_to_string((u8 *)entry->ipaddr, buf);
PRINT_WARN("Could not delete ARP entry for address %s on %s: "
"%s (0x%x/%d)\n",
- buf, card->info.if_name,
+ buf, QETH_CARD_IFNAME(card),
qeth_arp_get_error_cause(&rc), tmp, tmp);
}
return rc;
return -EOPNOTSUPP;
if (!qeth_is_supported(card,IPA_ARP_PROCESSING)) {
PRINT_WARN("ARP processing not supported "
- "on %s!\n", card->info.if_name);
+ "on %s!\n", QETH_CARD_IFNAME(card));
return -EOPNOTSUPP;
}
rc = qeth_send_simple_setassparms(card, IPA_ARP_PROCESSING,
if (rc){
tmp = rc;
PRINT_WARN("Could not flush ARP cache on %s: %s (0x%x/%d)\n",
- card->info.if_name, qeth_arp_get_error_cause(&rc),
+ QETH_CARD_IFNAME(card), qeth_arp_get_error_cause(&rc),
tmp, tmp);
}
return rc;
switch (cmd){
case SIOC_QETH_ARP_SET_NO_ENTRIES:
- if (!capable(CAP_NET_ADMIN)){
+ if ( !capable(CAP_NET_ADMIN) ||
+ (card->options.layer2) ) {
rc = -EPERM;
break;
}
rc = qeth_arp_set_no_entries(card, rq->ifr_ifru.ifru_ivalue);
break;
case SIOC_QETH_ARP_QUERY_INFO:
- if (!capable(CAP_NET_ADMIN)){
+ if ( !capable(CAP_NET_ADMIN) ||
+ (card->options.layer2) ) {
rc = -EPERM;
break;
}
rc = qeth_arp_query(card, rq->ifr_ifru.ifru_data);
break;
case SIOC_QETH_ARP_ADD_ENTRY:
- if (!capable(CAP_NET_ADMIN)){
+ if ( !capable(CAP_NET_ADMIN) ||
+ (card->options.layer2) ) {
rc = -EPERM;
break;
}
rc = qeth_arp_add_entry(card, &arp_entry);
break;
case SIOC_QETH_ARP_REMOVE_ENTRY:
- if (!capable(CAP_NET_ADMIN)){
+ if ( !capable(CAP_NET_ADMIN) ||
+ (card->options.layer2) ) {
rc = -EPERM;
break;
}
rc = qeth_arp_remove_entry(card, &arp_entry);
break;
case SIOC_QETH_ARP_FLUSH_CACHE:
- if (!capable(CAP_NET_ADMIN)){
+ if ( !capable(CAP_NET_ADMIN) ||
+ (card->options.layer2) ) {
rc = -EPERM;
break;
}
in_dev = __in_dev_get(card->vlangrp->vlan_devices[vid]);
if (!in_dev)
goto out;
- for (ifa = in_dev->ifa_list; ifa; ifa = ifa->ifa_next){
+ for (ifa = in_dev->ifa_list; ifa; ifa = ifa->ifa_next) {
addr = qeth_get_addr_buffer(QETH_PROT_IPV4);
if (addr){
addr->u.a4.addr = ifa->ifa_address;
in6_dev_put(in6_dev);
}
+static void
+qeth_layer2_send_setdelvlan(struct qeth_card *card, __u16 i,
+ enum qeth_ipa_cmds ipacmd)
+{
+ int rc;
+ struct qeth_ipa_cmd *cmd;
+ struct qeth_cmd_buffer *iob;
+
+ QETH_DBF_TEXT_(trace, 4, "L2sdv%x",ipacmd);
+ iob = qeth_get_ipacmd_buffer(card, ipacmd, QETH_PROT_IPV4);
+ cmd = (struct qeth_ipa_cmd *)(iob->data+IPA_PDU_HEADER_SIZE);
+ cmd->data.setdelvlan.vlan_id = i;
+
+ rc = qeth_send_ipa_cmd(card, iob, NULL, NULL);
+ if (rc) {
+ PRINT_ERR("Error in processing VLAN %i on %s: 0x%x. "
+ "Continuing\n",i, QETH_CARD_IFNAME(card), rc);
+ QETH_DBF_TEXT_(trace, 2, "L2VL%4x", ipacmd);
+ QETH_DBF_TEXT_(trace, 2, "L2%s", CARD_BUS_ID(card));
+ QETH_DBF_TEXT_(trace, 2, "err%d", rc);
+ }
+}
+
+static void
+qeth_layer2_process_vlans(struct qeth_card *card, int clear)
+{
+ unsigned short i;
+
+ QETH_DBF_TEXT(trace, 3, "L2prcvln");
+
+ if (!card->vlangrp)
+ return;
+ for (i = 0; i < VLAN_GROUP_ARRAY_LEN; i++) {
+ if (card->vlangrp->vlan_devices[i] == NULL)
+ continue;
+ if (clear)
+ qeth_layer2_send_setdelvlan(card, i, IPA_CMD_DELVLAN);
+ else
+ qeth_layer2_send_setdelvlan(card, i, IPA_CMD_SETVLAN);
+ }
+}
+
+/*add_vid is layer 2 used only ....*/
+static void
+qeth_vlan_rx_add_vid(struct net_device *dev, unsigned short vid)
+{
+ struct qeth_card *card;
+
+ QETH_DBF_TEXT_(trace, 4, "aid:%d", vid);
+
+ card = (struct qeth_card *) dev->priv;
+ if (!card->options.layer2)
+ return;
+ qeth_layer2_send_setdelvlan(card, vid, IPA_CMD_SETVLAN);
+}
+
+/*... kill_vid used for both modes*/
static void
qeth_vlan_rx_kill_vid(struct net_device *dev, unsigned short vid)
{
struct qeth_card *card;
unsigned long flags;
- QETH_DBF_TEXT(trace,4,"vlkilvid");
+ QETH_DBF_TEXT_(trace, 4, "kid:%d", vid);
card = (struct qeth_card *) dev->priv;
/* free all skbs for the vlan device */
if (card->vlangrp)
card->vlangrp->vlan_devices[vid] = NULL;
spin_unlock_irqrestore(&card->vlanlock, flags);
- if ( (qeth_set_thread_start_bit(card, QETH_SET_IP_THREAD) == 0) ||
- (qeth_set_thread_start_bit(card, QETH_SET_MC_THREAD) == 0) )
- schedule_work(&card->kernel_thread_starter);
+ if (card->options.layer2)
+ qeth_layer2_send_setdelvlan(card, vid, IPA_CMD_DELVLAN);
+ qeth_set_multicast_list(card->dev);
}
#endif
-static int
-qeth_neigh_setup(struct net_device *dev, struct neigh_parms *np)
+/**
+ * set multicast address on card
+ */
+static void
+qeth_set_multicast_list(struct net_device *dev)
{
- return 0;
-}
+ struct qeth_card *card = (struct qeth_card *) dev->priv;
+ QETH_DBF_TEXT(trace,3,"setmulti");
+ qeth_delete_mc_addresses(card);
+ qeth_add_multicast_ipv4(card);
#ifdef CONFIG_QETH_IPV6
-int
-qeth_ipv6_generate_eui64(u8 * eui, struct net_device *dev)
-{
- switch (dev->type) {
- case ARPHRD_ETHER:
- case ARPHRD_FDDI:
- case ARPHRD_IEEE802_TR:
- if (dev->addr_len != ETH_ALEN)
- return -1;
- memcpy(eui, dev->dev_addr, 3);
- memcpy(eui + 5, dev->dev_addr + 3, 3);
- eui[3] = (dev->dev_id >> 8) & 0xff;
- eui[4] = dev->dev_id & 0xff;
- return 0;
- }
- return -1;
+ qeth_add_multicast_ipv6(card);
+#endif
+ if (qeth_set_thread_start_bit(card, QETH_SET_IP_THREAD) == 0)
+ schedule_work(&card->kernel_thread_starter);
+}
+static int
+qeth_neigh_setup(struct net_device *dev, struct neigh_parms *np)
+{
+ return 0;
}
-#endif
static void
qeth_get_mac_for_ipm(__u32 ipm, char *mac, struct net_device *dev)
static void
qeth_delete_mc_addresses(struct qeth_card *card)
{
- struct qeth_ipaddr *ipm, *iptodo;
+ struct qeth_ipaddr *iptodo;
unsigned long flags;
QETH_DBF_TEXT(trace,4,"delmc");
- spin_lock_irqsave(&card->ip_lock, flags);
- list_for_each_entry(ipm, &card->ip_list, entry){
- if (!ipm->is_multicast)
- continue;
- iptodo = qeth_get_addr_buffer(ipm->proto);
- if (!iptodo) {
- QETH_DBF_TEXT(trace, 2, "dmcnomem");
- continue;
- }
- memcpy(iptodo, ipm, sizeof(struct qeth_ipaddr));
- iptodo->users = iptodo->users * -1;
- if (!__qeth_insert_ip_todo(card, iptodo, 0))
- kfree(iptodo);
+ iptodo = qeth_get_addr_buffer(QETH_PROT_IPV4);
+ if (!iptodo) {
+ QETH_DBF_TEXT(trace, 2, "dmcnomem");
+ return;
}
+ iptodo->type = QETH_IP_TYPE_DEL_ALL_MC;
+ spin_lock_irqsave(&card->ip_lock, flags);
+ if (!__qeth_insert_ip_todo(card, iptodo, 0))
+ kfree(iptodo);
spin_unlock_irqrestore(&card->ip_lock, flags);
}
int i;
QETH_DBF_TEXT(trace,4,"addmcvl");
- if (!qeth_is_supported(card,IPA_FULL_VLAN) ||
- (card->vlangrp == NULL))
+ if ( ((card->options.layer2 == 0) &&
+ (!qeth_is_supported(card,IPA_FULL_VLAN))) ||
+ (card->vlangrp == NULL) )
return ;
vg = card->vlangrp;
int i;
QETH_DBF_TEXT(trace,4,"admc6vl");
- if (!qeth_is_supported(card,IPA_FULL_VLAN) ||
- (card->vlangrp == NULL))
+ if ( ((card->options.layer2 == 0) &&
+ (!qeth_is_supported(card,IPA_FULL_VLAN))) ||
+ (card->vlangrp == NULL))
return ;
vg = card->vlangrp;
}
#endif /* CONFIG_QETH_IPV6 */
-/**
- * set multicast address on card
- */
-static void
-qeth_set_multicast_list(struct net_device *dev)
+static int
+qeth_layer2_send_setdelmac(struct qeth_card *card, __u8 *mac,
+ enum qeth_ipa_cmds ipacmd,
+ int (*reply_cb) (struct qeth_card *,
+ struct qeth_reply*,
+ unsigned long))
+{
+ struct qeth_ipa_cmd *cmd;
+ struct qeth_cmd_buffer *iob;
+
+ QETH_DBF_TEXT(trace, 2, "L2sdmac");
+ iob = qeth_get_ipacmd_buffer(card, ipacmd, QETH_PROT_IPV4);
+ cmd = (struct qeth_ipa_cmd *)(iob->data+IPA_PDU_HEADER_SIZE);
+ cmd->data.setdelmac.mac_length = OSA_ADDR_LEN;
+ memcpy(&cmd->data.setdelmac.mac, mac, OSA_ADDR_LEN);
+ return qeth_send_ipa_cmd(card, iob, reply_cb, NULL);
+}
+
+static int
+qeth_layer2_send_setgroupmac_cb(struct qeth_card *card,
+ struct qeth_reply *reply,
+ unsigned long data)
{
+ struct qeth_ipa_cmd *cmd;
+ __u8 *mac;
+
+ QETH_DBF_TEXT(trace, 2, "L2Sgmacb");
+ cmd = (struct qeth_ipa_cmd *) data;
+ mac = &cmd->data.setdelmac.mac[0];
+ /* MAC already registered, needed in couple/uncouple case */
+ if (cmd->hdr.return_code == 0x2005) {
+ PRINT_WARN("Group MAC %02x:%02x:%02x:%02x:%02x:%02x " \
+ "already existing on %s \n",
+ mac[0], mac[1], mac[2], mac[3], mac[4], mac[5],
+ QETH_CARD_IFNAME(card));
+ cmd->hdr.return_code = 0;
+ }
+ if (cmd->hdr.return_code)
+ PRINT_ERR("Could not set group MAC " \
+ "%02x:%02x:%02x:%02x:%02x:%02x on %s: %x\n",
+ mac[0], mac[1], mac[2], mac[3], mac[4], mac[5],
+ QETH_CARD_IFNAME(card),cmd->hdr.return_code);
+ return 0;
+}
+
+static int
+qeth_layer2_send_setgroupmac(struct qeth_card *card, __u8 *mac)
+{
+ QETH_DBF_TEXT(trace, 2, "L2Sgmac");
+ return qeth_layer2_send_setdelmac(card, mac, IPA_CMD_SETGMAC,
+ qeth_layer2_send_setgroupmac_cb);
+}
+
+static int
+qeth_layer2_send_delgroupmac_cb(struct qeth_card *card,
+ struct qeth_reply *reply,
+ unsigned long data)
+{
+ struct qeth_ipa_cmd *cmd;
+ __u8 *mac;
+
+ QETH_DBF_TEXT(trace, 2, "L2Dgmacb");
+ cmd = (struct qeth_ipa_cmd *) data;
+ mac = &cmd->data.setdelmac.mac[0];
+ if (cmd->hdr.return_code)
+ PRINT_ERR("Could not delete group MAC " \
+ "%02x:%02x:%02x:%02x:%02x:%02x on %s: %x\n",
+ mac[0], mac[1], mac[2], mac[3], mac[4], mac[5],
+ QETH_CARD_IFNAME(card), cmd->hdr.return_code);
+ return 0;
+}
+
+static int
+qeth_layer2_send_delgroupmac(struct qeth_card *card, __u8 *mac)
+{
+ QETH_DBF_TEXT(trace, 2, "L2Dgmac");
+ return qeth_layer2_send_setdelmac(card, mac, IPA_CMD_DELGMAC,
+ qeth_layer2_send_delgroupmac_cb);
+}
+
+static int
+qeth_layer2_send_setmac_cb(struct qeth_card *card,
+ struct qeth_reply *reply,
+ unsigned long data)
+{
+ struct qeth_ipa_cmd *cmd;
+
+ QETH_DBF_TEXT(trace, 2, "L2Smaccb");
+ cmd = (struct qeth_ipa_cmd *) data;
+ if (cmd->hdr.return_code) {
+ QETH_DBF_TEXT_(trace, 2, "L2er%x", cmd->hdr.return_code);
+ PRINT_WARN("Error in registering MAC address on " \
+ "device %s: x%x\n", CARD_BUS_ID(card),
+ cmd->hdr.return_code);
+ card->info.layer2_mac_registered = 0;
+ cmd->hdr.return_code = -EIO;
+ } else {
+ card->info.layer2_mac_registered = 1;
+ memcpy(card->dev->dev_addr,cmd->data.setdelmac.mac,
+ OSA_ADDR_LEN);
+ PRINT_INFO("MAC address %2.2x:%2.2x:%2.2x:%2.2x:%2.2x:%2.2x "
+ "successfully registered on device %s\n",
+ card->dev->dev_addr[0], card->dev->dev_addr[1],
+ card->dev->dev_addr[2], card->dev->dev_addr[3],
+ card->dev->dev_addr[4], card->dev->dev_addr[5],
+ card->dev->name);
+ }
+ return 0;
+}
+
+static int
+qeth_layer2_send_setmac(struct qeth_card *card, __u8 *mac)
+{
+ QETH_DBF_TEXT(trace, 2, "L2Setmac");
+ return qeth_layer2_send_setdelmac(card, mac, IPA_CMD_SETVMAC,
+ qeth_layer2_send_setmac_cb);
+}
+
+static int
+qeth_layer2_send_delmac_cb(struct qeth_card *card,
+ struct qeth_reply *reply,
+ unsigned long data)
+{
+ struct qeth_ipa_cmd *cmd;
+
+ QETH_DBF_TEXT(trace, 2, "L2Dmaccb");
+ cmd = (struct qeth_ipa_cmd *) data;
+ if (cmd->hdr.return_code) {
+ PRINT_WARN("Error in deregistering MAC address on " \
+ "device %s: x%x\n", CARD_BUS_ID(card),
+ cmd->hdr.return_code);
+ QETH_DBF_TEXT_(trace, 2, "err%d", cmd->hdr.return_code);
+ cmd->hdr.return_code = -EIO;
+ return 0;
+ }
+ card->info.layer2_mac_registered = 0;
+
+ return 0;
+}
+static int
+qeth_layer2_send_delmac(struct qeth_card *card, __u8 *mac)
+{
+ QETH_DBF_TEXT(trace, 2, "L2Delmac");
+ if (!card->info.layer2_mac_registered)
+ return 0;
+ return qeth_layer2_send_setdelmac(card, mac, IPA_CMD_DELVMAC,
+ qeth_layer2_send_delmac_cb);
+}
+
+static int
+qeth_layer2_set_mac_address(struct net_device *dev, void *p)
+{
+ struct sockaddr *addr = p;
struct qeth_card *card;
+ int rc = 0;
- QETH_DBF_TEXT(trace,3,"setmulti");
+ QETH_DBF_TEXT(trace, 3, "setmac");
+
+ if (qeth_verify_dev(dev) != QETH_REAL_CARD) {
+ QETH_DBF_TEXT(trace, 3, "setmcINV");
+ return -EOPNOTSUPP;
+ }
card = (struct qeth_card *) dev->priv;
- if (qeth_set_thread_start_bit(card, QETH_SET_MC_THREAD) == 0)
- schedule_work(&card->kernel_thread_starter);
+ if (!card->options.layer2) {
+ PRINT_WARN("Setting MAC address on %s is not supported"
+ "in Layer 3 mode.\n", dev->name);
+ QETH_DBF_TEXT(trace, 3, "setmcLY3");
+ return -EOPNOTSUPP;
+ }
+ QETH_DBF_TEXT_(trace, 3, "%s", CARD_BUS_ID(card));
+ QETH_DBF_HEX(trace, 3, addr->sa_data, OSA_ADDR_LEN);
+ rc = qeth_layer2_send_delmac(card, &card->dev->dev_addr[0]);
+ if (!rc)
+ rc = qeth_layer2_send_setmac(card, addr->sa_data);
+ return rc;
}
static void
cmd->hdr.seqno = card->seqno.ipa;
cmd->hdr.adapter_type = qeth_get_ipa_adp_type(card->info.link_type);
cmd->hdr.rel_adapter_no = (__u8) card->info.portno;
- cmd->hdr.prim_version_no = 1;
+ if (card->options.layer2)
+ cmd->hdr.prim_version_no = 2;
+ else
+ cmd->hdr.prim_version_no = 1;
cmd->hdr.param_count = 1;
cmd->hdr.prot_version = prot;
cmd->hdr.ipa_supported = 0;
}
static int
-qeth_register_addr_entry(struct qeth_card *card, struct qeth_ipaddr *addr)
+qeth_layer2_register_addr_entry(struct qeth_card *card,
+ struct qeth_ipaddr *addr)
+{
+ if (!addr->is_multicast)
+ return 0;
+ QETH_DBF_TEXT(trace, 2, "setgmac");
+ QETH_DBF_HEX(trace,3,&addr->mac[0],OSA_ADDR_LEN);
+ return qeth_layer2_send_setgroupmac(card, &addr->mac[0]);
+}
+
+static int
+qeth_layer2_deregister_addr_entry(struct qeth_card *card,
+ struct qeth_ipaddr *addr)
+{
+ if (!addr->is_multicast)
+ return 0;
+ QETH_DBF_TEXT(trace, 2, "delgmac");
+ QETH_DBF_HEX(trace,3,&addr->mac[0],OSA_ADDR_LEN);
+ return qeth_layer2_send_delgroupmac(card, &addr->mac[0]);
+}
+
+static int
+qeth_layer3_register_addr_entry(struct qeth_card *card,
+ struct qeth_ipaddr *addr)
{
//char buf[50];
int rc;
}
static int
-qeth_deregister_addr_entry(struct qeth_card *card, struct qeth_ipaddr *addr)
+qeth_layer3_deregister_addr_entry(struct qeth_card *card,
+ struct qeth_ipaddr *addr)
{
//char buf[50];
int rc;
return rc;
}
+static int
+qeth_register_addr_entry(struct qeth_card *card, struct qeth_ipaddr *addr)
+{
+ if (card->options.layer2)
+ return qeth_layer2_register_addr_entry(card, addr);
+
+ return qeth_layer3_register_addr_entry(card, addr);
+}
+
+static int
+qeth_deregister_addr_entry(struct qeth_card *card, struct qeth_ipaddr *addr)
+{
+ if (card->options.layer2)
+ return qeth_layer2_deregister_addr_entry(card, addr);
+
+ return qeth_layer3_deregister_addr_entry(card, addr);
+}
+
static int
qeth_netdev_init(struct net_device *dev)
{
#ifdef CONFIG_QETH_VLAN
dev->vlan_rx_register = qeth_vlan_rx_register;
dev->vlan_rx_kill_vid = qeth_vlan_rx_kill_vid;
+ dev->vlan_rx_add_vid = qeth_vlan_rx_add_vid;
#endif
- if (qeth_get_netdev_flags(card->info.type) & IFF_NOARP) {
+ dev->hard_header = card->orig_hard_header;
+ if (qeth_get_netdev_flags(card) & IFF_NOARP) {
dev->rebuild_header = NULL;
dev->hard_header = NULL;
+ if (card->options.fake_ll)
+ dev->hard_header = qeth_fake_header;
dev->header_cache_update = NULL;
dev->hard_header_cache = NULL;
}
#ifdef CONFIG_QETH_IPV6
/*IPv6 address autoconfiguration stuff*/
- card->dev->dev_id = card->info.unique_id & 0xffff;
if (!(card->info.unique_id & UNIQUE_ID_NOT_BY_CARD))
- card->dev->generate_eui64 = qeth_ipv6_generate_eui64;
-
-
+ card->dev->dev_id = card->info.unique_id & 0xffff;
#endif
dev->hard_header_parse = NULL;
- dev->set_mac_address = NULL;
- dev->flags |= qeth_get_netdev_flags(card->info.type);
+ dev->set_mac_address = qeth_layer2_set_mac_address;
+ dev->flags |= qeth_get_netdev_flags(card);
if ((card->options.fake_broadcast) ||
(card->info.broadcast_capable))
dev->flags |= IFF_BROADCAST;
-
dev->hard_header_len =
- qeth_get_hlen(card->info.link_type) + card->options.add_hhlen;
+ qeth_get_hlen(card->info.link_type) + card->options.add_hhlen;
dev->addr_len = OSA_ADDR_LEN;
dev->mtu = card->info.initial_mtu;
QETH_DBF_TEXT_(setup, 2, "5err%d", rc);
goto out;
}
+ /*network device will be recovered*/
+ if (card->dev) {
+ card->dev->hard_header = card->orig_hard_header;
+ return 0;
+ }
/* at first set_online allocate netdev */
+ card->dev = qeth_get_netdevice(card->info.type,
+ card->info.link_type);
if (!card->dev){
- card->dev = qeth_get_netdevice(card->info.type,
- card->info.link_type);
- if (!card->dev){
- qeth_qdio_clear_card(card, card->info.type ==
- QETH_CARD_TYPE_OSAE);
- rc = -ENODEV;
- QETH_DBF_TEXT_(setup, 2, "6err%d", rc);
- goto out;
- }
- card->dev->priv = card;
- card->dev->type = qeth_get_arphdr_type(card->info.type,
- card->info.link_type);
- card->dev->init = qeth_netdev_init;
+ qeth_qdio_clear_card(card, card->info.type ==
+ QETH_CARD_TYPE_OSAE);
+ rc = -ENODEV;
+ QETH_DBF_TEXT_(setup, 2, "6err%d", rc);
+ goto out;
}
+ card->dev->priv = card;
+ card->orig_hard_header = card->dev->hard_header;
+ card->dev->type = qeth_get_arphdr_type(card->info.type,
+ card->info.link_type);
+ card->dev->init = qeth_netdev_init;
return 0;
out:
PRINT_ERR("Initialization in hardsetup failed! rc=%d\n", rc);
return rc;
}
+static int
+qeth_layer2_initialize(struct qeth_card *card)
+{
+ int rc = 0;
+
+
+ QETH_DBF_TEXT(setup, 2, "doL2init");
+ QETH_DBF_TEXT_(setup, 2, "doL2%s", CARD_BUS_ID(card));
+
+ rc = qeth_setadpparms_change_macaddr(card);
+ if (rc) {
+ PRINT_WARN("couldn't get MAC address on "
+ "device %s: x%x\n",
+ CARD_BUS_ID(card), rc);
+ QETH_DBF_TEXT_(setup, 2,"1err%d",rc);
+ return rc;
+ }
+ QETH_DBF_HEX(setup,2, card->dev->dev_addr, OSA_ADDR_LEN);
+
+ rc = qeth_layer2_send_setmac(card, &card->dev->dev_addr[0]);
+ if (rc)
+ QETH_DBF_TEXT_(setup, 2,"2err%d",rc);
+ return 0;
+}
+
static int
qeth_send_startstoplan(struct qeth_card *card, enum qeth_ipa_cmds ipacmd,
struct qeth_cmd_buffer *iob;
QETH_DBF_TEXT_(setup, 2, "qipassi%i", prot);
+ if (card->options.layer2) {
+ QETH_DBF_TEXT(setup, 2, "noprmly2");
+ return -EPERM;
+ }
iob = qeth_get_ipacmd_buffer(card,IPA_CMD_QIPASSIST,prot);
rc = qeth_send_ipa_cmd(card, iob, qeth_query_ipassists_cb, NULL);
if (!qeth_is_supported(card,IPA_ARP_PROCESSING)) {
PRINT_WARN("ARP processing not supported "
- "on %s!\n", card->info.if_name);
+ "on %s!\n", QETH_CARD_IFNAME(card));
return 0;
}
rc = qeth_send_simple_setassparms(card,IPA_ARP_PROCESSING,
if (rc) {
PRINT_WARN("Could not start ARP processing "
"assist on %s: 0x%x\n",
- card->info.if_name, rc);
+ QETH_CARD_IFNAME(card), rc);
}
return rc;
}
if (!qeth_is_supported(card, IPA_IP_FRAGMENTATION)) {
PRINT_INFO("Hardware IP fragmentation not supported on %s\n",
- card->info.if_name);
+ QETH_CARD_IFNAME(card));
return -EOPNOTSUPP;
}
if (rc) {
PRINT_WARN("Could not start Hardware IP fragmentation "
"assist on %s: 0x%x\n",
- card->info.if_name, rc);
+ QETH_CARD_IFNAME(card), rc);
} else
PRINT_INFO("Hardware IP fragmentation enabled \n");
return rc;
if (!qeth_is_supported(card, IPA_SOURCE_MAC)) {
PRINT_INFO("Inbound source address not "
- "supported on %s\n", card->info.if_name);
+ "supported on %s\n", QETH_CARD_IFNAME(card));
return -EOPNOTSUPP;
}
if (rc)
PRINT_WARN("Could not start inbound source "
"assist on %s: 0x%x\n",
- card->info.if_name, rc);
+ QETH_CARD_IFNAME(card), rc);
return rc;
}
#ifdef CONFIG_QETH_VLAN
if (!qeth_is_supported(card, IPA_FULL_VLAN)) {
- PRINT_WARN("VLAN not supported on %s\n", card->info.if_name);
+ PRINT_WARN("VLAN not supported on %s\n", QETH_CARD_IFNAME(card));
return -EOPNOTSUPP;
}
if (rc) {
PRINT_WARN("Could not start vlan "
"assist on %s: 0x%x\n",
- card->info.if_name, rc);
+ QETH_CARD_IFNAME(card), rc);
} else {
PRINT_INFO("VLAN enabled \n");
- card->dev->features |= NETIF_F_HW_VLAN_TX | NETIF_F_HW_VLAN_RX;
+ card->dev->features |=
+ NETIF_F_HW_VLAN_FILTER |
+ NETIF_F_HW_VLAN_TX |
+ NETIF_F_HW_VLAN_RX;
}
#endif /* QETH_VLAN */
return rc;
if (!qeth_is_supported(card, IPA_MULTICASTING)) {
PRINT_WARN("Multicast not supported on %s\n",
- card->info.if_name);
+ QETH_CARD_IFNAME(card));
return -EOPNOTSUPP;
}
if (rc) {
PRINT_WARN("Could not start multicast "
"assist on %s: rc=%i\n",
- card->info.if_name, rc);
+ QETH_CARD_IFNAME(card), rc);
} else {
PRINT_INFO("Multicast enabled\n");
card->dev->flags |= IFF_MULTICAST;
rc = qeth_send_startlan(card, QETH_PROT_IPV6);
if (rc) {
PRINT_ERR("IPv6 startlan failed on %s\n",
- card->info.if_name);
+ QETH_CARD_IFNAME(card));
return rc;
}
netif_wake_queue(card->dev);
rc = qeth_query_ipassists(card,QETH_PROT_IPV6);
if (rc) {
PRINT_ERR("IPv6 query ipassist failed on %s\n",
- card->info.if_name);
+ QETH_CARD_IFNAME(card));
return rc;
}
rc = qeth_send_simple_setassparms(card, IPA_IPV6,
if (rc) {
PRINT_WARN("IPv6 start assist (version 4) failed "
"on %s: 0x%x\n",
- card->info.if_name, rc);
+ QETH_CARD_IFNAME(card), rc);
return rc;
}
rc = qeth_send_simple_setassparms_ipv6(card, IPA_IPV6,
if (rc) {
PRINT_WARN("IPV6 start assist (version 6) failed "
"on %s: 0x%x\n",
- card->info.if_name, rc);
+ QETH_CARD_IFNAME(card), rc);
return rc;
}
rc = qeth_send_simple_setassparms_ipv6(card, IPA_PASSTHRU,
if (rc) {
PRINT_WARN("Could not enable passthrough "
"on %s: 0x%x\n",
- card->info.if_name, rc);
+ QETH_CARD_IFNAME(card), rc);
return rc;
}
PRINT_INFO("IPV6 enabled \n");
if (!qeth_is_supported(card, IPA_IPV6)) {
PRINT_WARN("IPv6 not supported on %s\n",
- card->info.if_name);
+ QETH_CARD_IFNAME(card));
return 0;
}
rc = qeth_softsetup_ipv6(card);
card->info.broadcast_capable = 0;
if (!qeth_is_supported(card, IPA_FILTERING)) {
PRINT_WARN("Broadcast not supported on %s\n",
- card->info.if_name);
+ QETH_CARD_IFNAME(card));
rc = -EOPNOTSUPP;
goto out;
}
if (rc) {
PRINT_WARN("Could not enable broadcasting filtering "
"on %s: 0x%x\n",
- card->info.if_name, rc);
+ QETH_CARD_IFNAME(card), rc);
goto out;
}
IPA_CMD_ASS_CONFIGURE, 1);
if (rc) {
PRINT_WARN("Could not set up broadcast filtering on %s: 0x%x\n",
- card->info.if_name, rc);
+ QETH_CARD_IFNAME(card), rc);
goto out;
}
card->info.broadcast_capable = QETH_BROADCAST_WITH_ECHO;
IPA_CMD_ASS_ENABLE, 1);
if (rc) {
PRINT_WARN("Could not set up broadcast echo filtering on "
- "%s: 0x%x\n", card->info.if_name, rc);
+ "%s: 0x%x\n", QETH_CARD_IFNAME(card), rc);
goto out;
}
card->info.broadcast_capable = QETH_BROADCAST_WITHOUT_ECHO;
if (rc) {
PRINT_WARN("Starting Inbound HW Checksumming failed on %s: "
"0x%x,\ncontinuing using Inbound SW Checksumming\n",
- card->info.if_name, rc);
+ QETH_CARD_IFNAME(card), rc);
return rc;
}
rc = qeth_send_simple_setassparms(card, IPA_INBOUND_CHECKSUM,
if (rc) {
PRINT_WARN("Enabling Inbound HW Checksumming failed on %s: "
"0x%x,\ncontinuing using Inbound SW Checksumming\n",
- card->info.if_name, rc);
+ QETH_CARD_IFNAME(card), rc);
return rc;
}
return 0;
if (card->options.checksum_type == NO_CHECKSUMMING) {
PRINT_WARN("Using no checksumming on %s.\n",
- card->info.if_name);
+ QETH_CARD_IFNAME(card));
return 0;
}
if (card->options.checksum_type == SW_CHECKSUMMING) {
PRINT_WARN("Using SW checksumming on %s.\n",
- card->info.if_name);
+ QETH_CARD_IFNAME(card));
return 0;
}
if (!qeth_is_supported(card, IPA_INBOUND_CHECKSUM)) {
PRINT_WARN("Inbound HW Checksumming not "
"supported on %s,\ncontinuing "
"using Inbound SW Checksumming\n",
- card->info.if_name);
+ QETH_CARD_IFNAME(card));
card->options.checksum_type = SW_CHECKSUMMING;
return 0;
}
card->options.route4.type = NO_ROUTER;
PRINT_WARN("Error (0x%04x) while setting routing type on %s. "
"Type set to 'no router'.\n",
- rc, card->info.if_name);
+ rc, QETH_CARD_IFNAME(card));
}
return rc;
}
card->options.route6.type = NO_ROUTER;
PRINT_WARN("Error (0x%04x) while setting routing type on %s. "
"Type set to 'no router'.\n",
- rc, card->info.if_name);
+ rc, QETH_CARD_IFNAME(card));
}
#endif
return rc;
return rc;
} else
card->lan_online = 1;
+ if (card->options.layer2) {
+ card->dev->features |=
+ NETIF_F_HW_VLAN_FILTER |
+ NETIF_F_HW_VLAN_TX |
+ NETIF_F_HW_VLAN_RX;
+ card->dev->flags|=IFF_MULTICAST|IFF_BROADCAST;
+ card->info.broadcast_capable=1;
+ if ((rc = qeth_layer2_initialize(card))) {
+ QETH_DBF_TEXT_(setup, 2, "L2err%d", rc);
+ return rc;
+ }
+#ifdef CONFIG_QETH_VLAN
+ qeth_layer2_process_vlans(card, 0);
+#endif
+ goto out;
+ }
if ((rc = qeth_setadapter_parms(card)))
QETH_DBF_TEXT_(setup, 2, "2err%d", rc);
if ((rc = qeth_start_ipassists(card)))
QETH_DBF_TEXT_(setup, 2, "4err%d", rc);
if ((rc = qeth_setrouting_v6(card)))
QETH_DBF_TEXT_(setup, 2, "5err%d", rc);
+out:
netif_stop_queue(card->dev);
return 0;
}
QETH_DBF_TEXT(trace,4,"clearip");
spin_lock_irqsave(&card->ip_lock, flags);
/* clear todo list */
- list_for_each_entry_safe(addr, tmp, &card->ip_tbd_list, entry){
+ list_for_each_entry_safe(addr, tmp, card->ip_tbd_list, entry){
list_del(&addr->entry);
kfree(addr);
}
kfree(addr);
continue;
}
- list_add_tail(&addr->entry, &card->ip_tbd_list);
+ list_add_tail(&addr->entry, card->ip_tbd_list);
}
spin_unlock_irqrestore(&card->ip_lock, flags);
}
static int
qeth_stop_card(struct qeth_card *card)
{
- int recover_flag = 0;
int rc = 0;
QETH_DBF_TEXT(setup ,2,"stopcard");
if (card->read.state == CH_STATE_UP &&
card->write.state == CH_STATE_UP &&
(card->state == CARD_STATE_UP)) {
- recover_flag = 1;
rtnl_lock();
dev_close(card->dev);
rtnl_unlock();
- if (!card->use_hard_stop)
+ if (!card->use_hard_stop) {
+ __u8 *mac = &card->dev->dev_addr[0];
+ rc = qeth_layer2_send_delmac(card, mac);
+ QETH_DBF_TEXT_(setup, 2, "Lerr%d", rc);
if ((rc = qeth_send_stoplan(card)))
QETH_DBF_TEXT_(setup, 2, "1err%d", rc);
+ }
card->state = CARD_STATE_SOFTSETUP;
}
if (card->state == CARD_STATE_SOFTSETUP) {
- qeth_clear_ip_list(card, !card->use_hard_stop, recover_flag);
+#ifdef CONFIG_QETH_VLAN
+ if (card->options.layer2)
+ qeth_layer2_process_vlans(card, 1);
+#endif
+ qeth_clear_ip_list(card, !card->use_hard_stop, 1);
qeth_clear_ipacmd_list(card);
card->state = CARD_STATE_HARDSETUP;
}
if (card->state == CARD_STATE_HARDSETUP) {
- if (!card->use_hard_stop)
+ if ((!card->use_hard_stop) &&
+ (!card->options.layer2))
if ((rc = qeth_put_unique_id(card)))
QETH_DBF_TEXT_(setup, 2, "2err%d", rc);
qeth_qdio_clear_card(card, 0);
static int
qeth_register_netdev(struct qeth_card *card)
{
- int rc;
-
QETH_DBF_TEXT(setup, 3, "regnetd");
- if (card->dev->reg_state != NETREG_UNINITIALIZED)
+ if (card->dev->reg_state != NETREG_UNINITIALIZED) {
+ qeth_netdev_init(card->dev);
return 0;
+ }
/* sysfs magic */
SET_NETDEV_DEV(card->dev, &card->gdev->dev);
- rc = register_netdev(card->dev);
- if (!rc)
- strcpy(card->info.if_name, card->dev->name);
-
- return rc;
+ return register_netdev(card->dev);
}
static void
rtnl_lock();
dev_open(card->dev);
rtnl_unlock();
- if (qeth_set_thread_start_bit(card, QETH_SET_MC_THREAD) == 0)
- schedule_work(&card->kernel_thread_starter);
+ /* this also sets saved unicast addresses */
+ qeth_set_multicast_list(card->dev);
+}
+
+
+/* Layer 2 specific stuff */
+#define IGNORE_PARAM_EQ(option,value,reset_value,msg) \
+ if (card->options.option == value) { \
+ PRINT_ERR("%s not supported with layer 2 " \
+ "functionality, ignoring option on read" \
+ "channel device %s .\n",msg,CARD_RDEV_ID(card)); \
+ card->options.option = reset_value; \
+ }
+#define IGNORE_PARAM_NEQ(option,value,reset_value,msg) \
+ if (card->options.option != value) { \
+ PRINT_ERR("%s not supported with layer 2 " \
+ "functionality, ignoring option on read" \
+ "channel device %s .\n",msg,CARD_RDEV_ID(card)); \
+ card->options.option = reset_value; \
+ }
+
+
+static void qeth_make_parameters_consistent(struct qeth_card *card)
+{
+
+ if (card->options.layer2) {
+ if (card->info.type == QETH_CARD_TYPE_IQD) {
+ PRINT_ERR("Device %s does not support " \
+ "layer 2 functionality. " \
+ "Ignoring layer2 option.\n",CARD_BUS_ID(card));
+ }
+ IGNORE_PARAM_NEQ(route4.type, NO_ROUTER, NO_ROUTER,
+ "Routing options are");
+#ifdef CONFIG_QETH_IPV6
+ IGNORE_PARAM_NEQ(route6.type, NO_ROUTER, NO_ROUTER,
+ "Routing options are");
+#endif
+ IGNORE_PARAM_EQ(checksum_type, HW_CHECKSUMMING,
+ QETH_CHECKSUM_DEFAULT,
+ "Checksumming options are");
+ IGNORE_PARAM_NEQ(broadcast_mode, QETH_TR_BROADCAST_ALLRINGS,
+ QETH_TR_BROADCAST_ALLRINGS,
+ "Broadcast mode options are");
+ IGNORE_PARAM_NEQ(macaddr_mode, QETH_TR_MACADDR_NONCANONICAL,
+ QETH_TR_MACADDR_NONCANONICAL,
+ "Canonical MAC addr options are");
+ IGNORE_PARAM_NEQ(fake_broadcast, 0, 0,
+ "Broadcast faking options are");
+ IGNORE_PARAM_NEQ(add_hhlen, DEFAULT_ADD_HHLEN,
+ DEFAULT_ADD_HHLEN,"Option add_hhlen is");
+ IGNORE_PARAM_NEQ(fake_ll, 0, 0,"Option fake_ll is");
+ }
}
+
static int
qeth_set_online(struct ccwgroup_device *gdev)
{
}
recover_flag = card->state;
- if (ccw_device_set_online(CARD_RDEV(card)) ||
- ccw_device_set_online(CARD_WDEV(card)) ||
- ccw_device_set_online(CARD_DDEV(card))){
+ if ((rc = ccw_device_set_online(CARD_RDEV(card))) ||
+ (rc = ccw_device_set_online(CARD_WDEV(card))) ||
+ (rc = ccw_device_set_online(CARD_DDEV(card)))){
QETH_DBF_TEXT_(setup, 2, "1err%d", rc);
return -EIO;
}
+ if (card->options.layer2)
+ qeth_make_parameters_consistent(card);
+
if ((rc = qeth_hardsetup_card(card))){
QETH_DBF_TEXT_(setup, 2, "2err%d", rc);
goto out_remove;
}
card->state = CARD_STATE_HARDSETUP;
- if ((rc = qeth_query_ipassists(card,QETH_PROT_IPV4))){
- QETH_DBF_TEXT_(setup, 2, "3err%d", rc);
- /*TODO: rc !=0*/
- } else
+ if (!(rc = qeth_query_ipassists(card,QETH_PROT_IPV4)))
rc = qeth_get_unique_id(card);
- if (rc) {
- QETH_DBF_TEXT_(setup, 2, "4err%d", rc);
+ if (rc && card->options.layer2 == 0) {
+ QETH_DBF_TEXT_(setup, 2, "3err%d", rc);
goto out_remove;
}
qeth_print_status_message(card);
if ((rc = qeth_register_netdev(card))){
- QETH_DBF_TEXT_(setup, 2, "5err%d", rc);
+ QETH_DBF_TEXT_(setup, 2, "4err%d", rc);
goto out_remove;
}
if ((rc = qeth_softsetup_card(card))){
- QETH_DBF_TEXT_(setup, 2, "6err%d", rc);
+ QETH_DBF_TEXT_(setup, 2, "5err%d", rc);
goto out_remove;
}
card->state = CARD_STATE_SOFTSETUP;
if ((rc = qeth_init_qdio_queues(card))){
- QETH_DBF_TEXT_(setup, 2, "7err%d", rc);
+ QETH_DBF_TEXT_(setup, 2, "6err%d", rc);
goto out_remove;
}
/*maybe it was set offline without ifconfig down
* we can also use this state for recovery purposes*/
- qeth_set_allowed_threads(card, 0xffffffff, 0);
+ if (card->options.layer2)
+ qeth_set_allowed_threads(card, QETH_RECOVER_THREAD, 0);
+ else
+ qeth_set_allowed_threads(card, 0xffffffff, 0);
if (recover_flag == CARD_STATE_RECOVER)
qeth_start_again(card);
qeth_notify_processes();
struct net_device *dev = neigh->dev;
struct in_device *in_dev;
struct neigh_parms *parms;
+ struct qeth_card *card;
- if (!qeth_verify_dev(dev)) {
- return qeth_old_arp_constructor(neigh);
- }
+ card = qeth_get_card_from_dev(dev);
+ if (card == NULL)
+ goto out;
+ if((card->options.layer2) ||
+ (card->dev->hard_header == qeth_fake_header))
+ goto out;
rcu_read_lock();
in_dev = rcu_dereference(__in_dev_get(dev));
neigh->ops = arp_direct_ops;
neigh->output = neigh->ops->queue_xmit;
return 0;
+out:
+ return qeth_old_arp_constructor(neigh);
}
#endif /*CONFIG_QETH_IPV6*/
return -ENOMEM;
spin_lock_irqsave(&card->ip_lock, flags);
if (__qeth_address_exists_in_list(&card->ip_list, ipaddr, 0) ||
- __qeth_address_exists_in_list(&card->ip_tbd_list, ipaddr, 0))
+ __qeth_address_exists_in_list(card->ip_tbd_list, ipaddr, 0))
rc = -EEXIST;
spin_unlock_irqrestore(&card->ip_lock, flags);
if (rc){
return -ENOMEM;
spin_lock_irqsave(&card->ip_lock, flags);
if (__qeth_address_exists_in_list(&card->ip_list, ipaddr, 0) ||
- __qeth_address_exists_in_list(&card->ip_tbd_list, ipaddr, 0))
+ __qeth_address_exists_in_list(card->ip_tbd_list, ipaddr, 0))
rc = -EEXIST;
spin_unlock_irqrestore(&card->ip_lock, flags);
if (rc){
card = qeth_get_card_from_dev(dev);
if (!card)
return NOTIFY_DONE;
+ if (card->options.layer2)
+ return NOTIFY_DONE;
addr = qeth_get_addr_buffer(QETH_PROT_IPV4);
if (addr != NULL) {