patch-2.4.22 linux-2.4.22/drivers/s390/net/qeth.c
Next file: linux-2.4.22/drivers/s390/net/qeth.h
Previous file: linux-2.4.22/drivers/s390/net/netiucv.c
Back to the patch index
Back to the overall index
- Lines: 10947
- Date:
2003-08-25 04:44:42.000000000 -0700
- Orig file:
linux-2.4.21/drivers/s390/net/qeth.c
- Orig date:
1969-12-31 16:00:00.000000000 -0800
diff -urN linux-2.4.21/drivers/s390/net/qeth.c linux-2.4.22/drivers/s390/net/qeth.c
@@ -0,0 +1,10946 @@
+/*
+ *
+ * linux/drivers/s390/net/qeth.c ($Revision: 1.337 $)
+ *
+ * Linux on zSeries OSA Express and HiperSockets support
+ *
+ * Copyright 2000,2003 IBM Corporation
+ *
+ * Author(s): Utz Bacher <utz.bacher@de.ibm.com>
+ * Cornelia Huck <cohuck@de.ibm.com> (chandev stuff,
+ * numerous bugfixes)
+ * Frank Pavlic <pavlic@de.ibm.com> (query/purge ARP, SNMP, fixes)
+ * Andreas Herrmann <aherrman@de.ibm.com> (bugfixes)
+ *
+ * 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
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+/*
+ * The driver supports in general all QDIO driven network devices on the
+ * Hydra card.
+ *
+ * For all devices, three channels must be available to the driver. One
+ * channel is the read channel, one is the write channel and the third
+ * one is the channel used to control QDIO.
+ *
+ * There are several stages from the channel recognition to the running
+ * network device:
+ * - The channels are scanned and ordered due to the parameters (see
+ * MODULE_PARM_DESC)
+ * - The card is hardsetup: this means, that the communication channels
+ * are prepared
+ * - The card is softsetup: this means, that commands are issued
+ * to activate the network parameters
+ * - After that, data can flow through the card (transported by QDIO)
+ *
+ *IPA Takeover:
+ * /proc/qeth_ipa_takeover provides the possibility to add and remove
+ * certain ranges of IP addresses to the driver. As soon as these
+ * addresses have to be set by the driver, the driver uses the OSA
+ * Address Takeover mechanism.
+ * reading out of the proc-file displays the registered addresses;
+ * writing into it changes the information. Only one command at one
+ * time must be written into the file. Subsequent commands are ignored.
+ * The following commands are available:
+ * inv4
+ * inv6
+ * add4 <ADDR>/<mask bits>[:<interface>]
+ * add6 <ADDR>/<mask bits>[:<interface>]
+ * del4 <ADDR>/<mask bits>[:<interface>]
+ * del6 <ADDR>/<mask bits>[:<interface>]
+ * inv4 and inv6 toggle the IPA takeover behaviour for all interfaces:
+ * when inv4 was input once, all addresses specified with add4 are not
+ * set using the takeover mechanism, but all other IPv4 addresses are set so.
+ *
+ * add# adds an address range, del# deletes an address range. # corresponds
+ * to the IP version (4 or 6).
+ * <ADDR> is a 8 or 32byte hexadecimal view of the IP address.
+ * <mask bits> specifies the number of bits which are set in the network mask.
+ * <interface> is optional and specifies the interface name to which the
+ * address range is bound.
+ * E. g.
+ * add4 C0a80100/24
+ * activates all addresses in the 192.168.10 subnet for address takeover.
+ * Note, that the address is not taken over before an according ifconfig
+ * is executed.
+ *
+ *VIPA:
+ * add_vipa4 <ADDR>:<interface>
+ * add_vipa6 <ADDR>:<interface>
+ * del_vipa4 <ADDR>:<interface>
+ * del_vipa6 <ADDR>:<interface>
+ *
+ * the specified address is set/unset as VIPA on the specified interface.
+ * use the src_vipa package to exploit this out of arbitrary applications.
+ *
+ *Proxy ARP:
+ *
+ * add_rxip4 <ADDR>:<interface>
+ * add_rxip6 <ADDR>:<interface>
+ * del_rxip4 <ADDR>:<interface>
+ * del_rxip6 <ADDR>:<interface>
+ *
+ * the specified address is set/unset as "do not fail a gratuitous ARP"
+ * on the specified interface. this can be used to act as a proxy ARP.
+ */
+
+void volatile qeth_eyecatcher(void)
+{
+ return;
+}
+
+#include <linux/config.h>
+
+#ifndef CONFIG_CHANDEV
+#error "qeth can only be compiled with chandev support"
+#endif /* CONFIG_CHANDEV */
+
+#include <linux/module.h>
+
+#include <linux/string.h>
+#include <linux/errno.h>
+#include <linux/mm.h>
+
+#include <linux/version.h>
+
+#include <asm/io.h>
+#include <asm/ebcdic.h>
+#include <linux/ctype.h>
+#include <asm/semaphore.h>
+#include <linux/if.h>
+#include <linux/if_arp.h>
+#include <linux/ip.h>
+#include <linux/inetdevice.h>
+#include <linux/netdevice.h>
+#include <linux/sched.h>
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/interrupt.h>
+#include <linux/tcp.h>
+#include <linux/icmp.h>
+#include <linux/skbuff.h>
+#ifdef CONFIG_PROC_FS
+#include <linux/proc_fs.h>
+#endif /* CONFIG_PROC_FS */
+#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 <net/ipv6.h>
+#include <linux/in6.h>
+#include <net/if_inet6.h>
+#include <net/addrconf.h>
+#include <linux/if_tr.h>
+#include <linux/trdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/reboot.h>
+
+#include <linux/if_vlan.h>
+#include <asm/chandev.h>
+
+#include <asm/irq.h>
+#include <asm/s390dyn.h>
+#include <asm/debug.h>
+
+#include <asm/qdio.h>
+
+#include "qeth_mpc.h"
+#include "qeth.h"
+
+/****************** MODULE PARAMETER VARIABLES ********************/
+static int qeth_sparebufs=0;
+MODULE_PARM(qeth_sparebufs,"i");
+MODULE_PARM_DESC(qeth_sparebufs,"the number of pre-allocated spare buffers " \
+ "reserved for low memory situations");
+
+static int global_stay_in_mem=0;
+
+/****************** MODULE STUFF **********************************/
+#define VERSION_QETH_C "$Revision: 1.337 $"
+static const char *version="qeth S/390 OSA-Express driver (" \
+ VERSION_QETH_C "/" VERSION_QETH_H "/" VERSION_QETH_MPC_H
+ QETH_VERSION_IPV6 QETH_VERSION_VLAN ")";
+
+MODULE_AUTHOR("Utz Bacher <utz.bacher@de.ibm.com>");
+MODULE_DESCRIPTION("Linux on zSeries OSA Express and HiperSockets support\n" \
+ "Copyright 2000,2003 IBM Corporation\n");
+MODULE_LICENSE("GPL");
+
+/******************** HERE WE GO ***********************************/
+
+#define PROCFILE_SLEEP_SEM_MAX_VALUE 0
+#define PROCFILE_IOCTL_SEM_MAX_VALUE 3
+static struct semaphore qeth_procfile_ioctl_lock;
+static struct semaphore qeth_procfile_ioctl_sem;
+static qeth_card_t *firstcard=NULL;
+
+static sparebufs_t sparebufs[MAX_SPARE_BUFFERS];
+static int sparebuffer_count;
+
+static unsigned int known_devices[][10]=QETH_MODELLIST_ARRAY;
+
+static spinlock_t setup_lock;
+static rwlock_t list_lock=RW_LOCK_UNLOCKED;
+
+static debug_info_t *qeth_dbf_setup=NULL;
+static debug_info_t *qeth_dbf_data=NULL;
+static debug_info_t *qeth_dbf_misc=NULL;
+static debug_info_t *qeth_dbf_control=NULL;
+static debug_info_t *qeth_dbf_trace=NULL;
+static debug_info_t *qeth_dbf_sense=NULL;
+static debug_info_t *qeth_dbf_qerr=NULL;
+
+static int proc_file_registration;
+#ifdef QETH_PERFORMANCE_STATS
+static int proc_perf_file_registration;
+#define NOW qeth_get_micros()
+#endif /* QETH_PERFORMANCE_STATS */
+static int proc_ipato_file_registration;
+
+static int ipato_inv4=0,ipato_inv6=0;
+static ipato_entry_t *ipato_entries=NULL;
+static spinlock_t ipato_list_lock;
+
+typedef struct {
+ char *data;
+ int len;
+} tempinfo_t;
+
+/* thought I could get along without forward declarations...
+ * just lazyness here */
+static int qeth_reinit_thread(void*);
+static void qeth_schedule_recovery(qeth_card_t *card);
+
+inline static int QETH_IP_VERSION(struct sk_buff *skb)
+{
+ switch (skb->protocol) {
+ case ETH_P_IPV6: return 6;
+ case ETH_P_IP: return 4;
+ default: return 0;
+ }
+}
+/* not a macro, as one of the arguments is atomic_read */
+static inline int qeth_min(int a,int b)
+{
+ if (a<b)
+ return a;
+ else
+ return b;
+}
+
+static inline unsigned int qeth_get_millis(void)
+{
+ __u64 time;
+
+ asm volatile ("STCK %0" : "=m" (time));
+ return (int) (time>>22); /* time>>12 is microseconds, we divide it
+ by 1024 */
+}
+
+#ifdef QETH_PERFORMANCE_STATS
+static inline unsigned int qeth_get_micros(void)
+{
+ __u64 time;
+
+ asm volatile ("STCK %0" : "=m" (time));
+ return (int) (time>>12);
+}
+#endif /* QETH_PERFORMANCE_STATS */
+
+static void qeth_delay_millis(unsigned long msecs)
+{
+ unsigned int start;
+
+ start=qeth_get_millis();
+ while (qeth_get_millis()-start<msecs)
+ ;
+}
+
+static void qeth_wait_nonbusy(unsigned int timeout)
+{
+ unsigned int start;
+ char dbf_text[15];
+
+ sprintf(dbf_text,"wtnb%4x",timeout);
+ QETH_DBF_TEXT3(0,trace,dbf_text);
+
+ start=qeth_get_millis();
+ for (;;) {
+ set_task_state(current,TASK_INTERRUPTIBLE);
+ if (qeth_get_millis()-start>timeout) {
+ goto out;
+ }
+ schedule_timeout(((start+timeout-qeth_get_millis())>>10)*HZ);
+ }
+ out:
+ set_task_state(current,TASK_RUNNING);
+}
+
+static void qeth_get_mac_for_ipm(__u32 ipm,char *mac,struct net_device *dev) {
+ if (dev->type==ARPHRD_IEEE802_TR)
+ ip_tr_mc_map(ipm,mac);
+ else
+ ip_eth_mc_map(ipm,mac);
+}
+
+#define HEXDUMP16(importance,header,ptr) \
+PRINT_##importance(header "%02x %02x %02x %02x %02x %02x %02x %02x " \
+ "%02x %02x %02x %02x %02x %02x %02x %02x\n", \
+ *(((char*)ptr)),*(((char*)ptr)+1),*(((char*)ptr)+2), \
+ *(((char*)ptr)+3),*(((char*)ptr)+4),*(((char*)ptr)+5), \
+ *(((char*)ptr)+6),*(((char*)ptr)+7),*(((char*)ptr)+8), \
+ *(((char*)ptr)+9),*(((char*)ptr)+10),*(((char*)ptr)+11), \
+ *(((char*)ptr)+12),*(((char*)ptr)+13), \
+ *(((char*)ptr)+14),*(((char*)ptr)+15)); \
+PRINT_##importance(header "%02x %02x %02x %02x %02x %02x %02x %02x " \
+ "%02x %02x %02x %02x %02x %02x %02x %02x\n", \
+ *(((char*)ptr)+16),*(((char*)ptr)+17), \
+ *(((char*)ptr)+18),*(((char*)ptr)+19), \
+ *(((char*)ptr)+20),*(((char*)ptr)+21), \
+ *(((char*)ptr)+22),*(((char*)ptr)+23), \
+ *(((char*)ptr)+24),*(((char*)ptr)+25), \
+ *(((char*)ptr)+26),*(((char*)ptr)+27), \
+ *(((char*)ptr)+28),*(((char*)ptr)+29), \
+ *(((char*)ptr)+30),*(((char*)ptr)+31));
+
+#define atomic_swap(a,b) xchg((int*)a.counter,b)
+
+#ifdef QETH_DBF_LIKE_HELL
+
+#define my_read_lock(x) do { \
+ void *ptr=x; \
+ QETH_DBF_TEXT6(0,trace,"rd_lck"); \
+ QETH_DBF_HEX6(0,trace,&ptr,sizeof(void*)); \
+ read_lock(x); \
+} while (0)
+#define my_read_unlock(x) do { \
+ void *ptr=x; \
+ QETH_DBF_TEXT6(0,trace,"rd_unlck"); \
+ QETH_DBF_HEX6(0,trace,&ptr,sizeof(void*)); \
+ read_unlock(x); \
+} while (0)
+#define my_write_lock(x) do { \
+ void *ptr=x; \
+ QETH_DBF_TEXT6(0,trace,"wr_lck"); \
+ QETH_DBF_HEX6(0,trace,&ptr,sizeof(void*)); \
+ write_lock(x); \
+} while (0)
+#define my_write_unlock(x) do { \
+ void *ptr=x; \
+ QETH_DBF_TEXT6(0,trace,"wr_unlck"); \
+ QETH_DBF_HEX6(0,trace,&ptr,sizeof(void*)); \
+ write_unlock(x); \
+} while (0)
+
+#define my_spin_lock(x) do { \
+ void *ptr=x; \
+ QETH_DBF_TEXT6(0,trace,"sp_lck"); \
+ QETH_DBF_HEX6(0,trace,&ptr,sizeof(void*)); \
+ spin_lock(x); \
+} while (0)
+#define my_spin_unlock(x) do { \
+ void *ptr=x; \
+ QETH_DBF_TEXT6(0,trace,"sp_unlck"); \
+ QETH_DBF_HEX6(0,trace,&ptr,sizeof(void*)); \
+ spin_unlock(x); \
+} while (0)
+#define my_spin_lock_irqsave(x,y) do { \
+ void *ptr=x; \
+ QETH_DBF_TEXT6(0,trace,"sp_lck_i"); \
+ QETH_DBF_HEX6(0,trace,&ptr,sizeof(void*)); \
+ spin_lock_irqsave(x,y); \
+} while (0)
+#define my_spin_unlock_irqrestore(x,y) do { \
+ void *ptr=x; \
+ QETH_DBF_TEXT6(0,trace,"sp_nlk_i"); \
+ QETH_DBF_HEX6(0,trace,&ptr,sizeof(void*)); \
+ spin_unlock_irqrestore(x,y); \
+} while (0)
+
+#else /* QETH_DBF_LIKE_HELL */
+
+#define my_read_lock(x) read_lock(x)
+#define my_write_lock(x) write_lock(x)
+#define my_read_unlock(x) read_unlock(x)
+#define my_write_unlock(x) write_unlock(x)
+
+#define my_spin_lock(x) spin_lock(x)
+#define my_spin_unlock(x) spin_unlock(x)
+#define my_spin_lock_irqsave(x,y) spin_lock_irqsave(x,y)
+#define my_spin_unlock_irqrestore(x,y) spin_unlock_irqrestore(x,y)
+
+#endif /* QETH_DBF_LIKE_HELL */
+
+static int inline my_spin_lock_nonbusy(qeth_card_t *card,spinlock_t *lock)
+{
+ for (;;) {
+ if (card) {
+ if (atomic_read(&card->shutdown_phase)) return -1;
+ }
+ if (spin_trylock(lock)) return 0;
+ qeth_wait_nonbusy(QETH_IDLE_WAIT_TIME);
+ }
+}
+
+#ifdef CONFIG_ARCH_S390X
+#define QETH_GET_ADDR(x) ((__u32)(unsigned long)x)
+#else /* CONFIG_ARCH_S390X */
+#define QETH_GET_ADDR(x) ((__u32)x)
+#endif /* CONFIG_ARCH_S390X */
+
+static int qeth_does_card_exist(qeth_card_t *card)
+{
+ qeth_card_t *c=firstcard;
+ int rc=0;
+
+ my_read_lock(&list_lock);
+ while (c) {
+ if (c==card) {
+ rc=1;
+ break;
+ }
+ c=c->next;
+ }
+ my_read_unlock(&list_lock);
+ return rc;
+}
+
+static inline qeth_card_t *qeth_get_card_by_irq(int irq)
+{
+ qeth_card_t *card;
+
+ my_read_lock(&list_lock);
+ card=firstcard;
+ while (card) {
+ if ((card->irq0==irq)&&
+ (atomic_read(&card->shutdown_phase)!=
+ QETH_REMOVE_CARD_QUICK)) break;
+ if ((card->irq1==irq)&&
+ (atomic_read(&card->shutdown_phase)!=
+ QETH_REMOVE_CARD_QUICK)) break;
+ if ((card->irq2==irq)&&
+ (atomic_read(&card->shutdown_phase)!=
+ QETH_REMOVE_CARD_QUICK)) break;
+ card=card->next;
+ }
+ my_read_unlock(&list_lock);
+
+ return card;
+}
+
+static int qeth_getxdigit(char c)
+{
+ if ((c>='0') && (c<='9')) return c-'0';
+ if ((c>='a') && (c<='f')) return c+10-'a';
+ if ((c>='A') && (c<='F')) return c+10-'A';
+ return -1;
+}
+
+static qeth_card_t *qeth_get_card_by_name(char *name)
+{
+ qeth_card_t *card;
+
+ my_read_lock(&list_lock);
+ card=firstcard;
+ while (card) {
+ if (!strncmp(name,card->dev_name,DEV_NAME_LEN)) break;
+ card=card->next;
+ }
+ my_read_unlock(&list_lock);
+
+ return card;
+}
+
+static void qeth_convert_addr_to_text(int version,__u8 *addr,char *text)
+{
+ if (version==4) {
+ sprintf(text,"%02x%02x%02x%02x",
+ addr[0],addr[1],addr[2],addr[3]);
+ } else {
+ sprintf(text,"%02x%02x%02x%02x%02x%02x%02x%02x" \
+ "%02x%02x%02x%02x%02x%02x%02x%02x",
+ addr[0],addr[1],addr[2],addr[3],
+ addr[4],addr[5],addr[6],addr[7],
+ addr[8],addr[9],addr[10],addr[11],
+ addr[12],addr[13],addr[14],addr[15]);
+ }
+}
+
+static int qeth_convert_text_to_addr(int version,char *text,__u8 *addr)
+{
+ int olen=(version==4)?4:16;
+
+ while (olen--) {
+ if ( (!isxdigit(*text)) || (!isxdigit(*(text+1))) )
+ return -EINVAL;
+ *addr=(qeth_getxdigit(*text)<<4)+qeth_getxdigit(*(text+1));
+ addr++;
+ text+=2;
+ }
+ return 0;
+}
+
+static void qeth_add_ipato_entry(int version,__u8 *addr,int mask_bits,
+ char *dev_name)
+{
+ ipato_entry_t *entry,*e;
+ int len=(version==4)?4:16;
+
+ entry=(ipato_entry_t*)kmalloc(sizeof(ipato_entry_t),GFP_KERNEL);
+ if (!entry) {
+ PRINT_ERR("not enough memory for ipato allocation\n");
+ return;
+ }
+ entry->version=version;
+ memcpy(entry->addr,addr,len);
+ if (dev_name) {
+ strncpy(entry->dev_name,dev_name,DEV_NAME_LEN);
+ if (qeth_get_card_by_name(dev_name)->options.ena_ipat!=
+ ENABLE_TAKEOVER)
+ PRINT_WARN("IP takeover is not enabled on %s! " \
+ "Ignoring line\n",dev_name);
+ } else
+ memset(entry->dev_name,0,DEV_NAME_LEN);
+ entry->mask_bits=mask_bits;
+ entry->next=NULL;
+
+ my_spin_lock(&ipato_list_lock);
+ if (ipato_entries) {
+ e=ipato_entries;
+ while (e) {
+ if ( (e->version==version) &&
+ (e->mask_bits==mask_bits) &&
+ ( ((dev_name)&&!strncmp(e->dev_name,dev_name,
+ DEV_NAME_LEN)) ||
+ (!dev_name) ) &&
+ (!memcmp(e->addr,addr,len)) ) {
+ PRINT_INFO("ipato to be added does already " \
+ "exist\n");
+ kfree(entry);
+ goto out;
+ }
+ if (e->next) e=e->next; else break;
+ }
+ e->next=entry;
+ } else
+ ipato_entries=entry;
+out:
+ my_spin_unlock(&ipato_list_lock);
+}
+
+static void qeth_del_ipato_entry(int version,__u8 *addr,int mask_bits,
+ char *dev_name)
+{
+ ipato_entry_t *e,*e_before;
+ int len=(version==4)?4:16;
+ int found=0;
+
+ my_spin_lock(&ipato_list_lock);
+ e=ipato_entries;
+ if ( (e->version==version) &&
+ (e->mask_bits==mask_bits) &&
+ (!memcmp(e->addr,addr,len)) ) {
+ ipato_entries=e->next;
+ kfree(e);
+ } else while (e) {
+ e_before=e;
+ e=e->next;
+ if (!e) break;
+ if ( (e->version==version) &&
+ (e->mask_bits==mask_bits) &&
+ ( ((dev_name)&&!strncmp(e->dev_name,dev_name,
+ DEV_NAME_LEN)) ||
+ (!dev_name) ) &&
+ (!memcmp(e->addr,addr,len)) ) {
+ e_before->next=e->next;
+ kfree(e);
+ found=1;
+ break;
+ }
+ }
+ if (!found)
+ PRINT_INFO("ipato to be deleted does not exist\n");
+ my_spin_unlock(&ipato_list_lock);
+}
+
+static void qeth_convert_addr_to_bits(__u8 *addr,char *bits,int len)
+{
+ int i,j;
+ __u8 octet;
+
+ for (i=0;i<len;i++) {
+ octet=addr[i];
+ for (j=7;j>=0;j--) {
+ bits[i*8+j]=(octet&1)?1:0;
+ octet>>=1;
+ }
+ }
+}
+
+static int qeth_is_ipa_covered_by_ipato_entries(int version,__u8 *addr,
+ qeth_card_t *card)
+{
+ char *memarea,*addr_bits,*entry_bits;
+ int len=(version==4)?4:16;
+ int invert=(version==4)?ipato_inv4:ipato_inv6;
+ int result=0;
+ ipato_entry_t *e;
+
+ if (card->options.ena_ipat!=ENABLE_TAKEOVER) {
+ return 0;
+ }
+
+ memarea=kmalloc(256,GFP_KERNEL);
+ if (!memarea) {
+ PRINT_ERR("not enough memory to check out whether to " \
+ "use ipato\n");
+ return 0;
+ }
+ addr_bits=memarea;
+ entry_bits=memarea+128;
+ qeth_convert_addr_to_bits(addr,addr_bits,len);
+ e=ipato_entries;
+ while (e) {
+ qeth_convert_addr_to_bits(e->addr,entry_bits,len);
+ if ( (!memcmp(addr_bits,entry_bits,
+ __min(len*8,e->mask_bits))) &&
+ ( (e->dev_name[0]&&
+ (!strncmp(e->dev_name,card->dev_name,DEV_NAME_LEN))) ||
+ (!e->dev_name[0]) ) ) {
+ result=1;
+ break;
+ }
+ e=e->next;
+ }
+
+ kfree(memarea);
+ if (invert)
+ return !result;
+ else
+ return result;
+}
+
+static void qeth_set_dev_flag_running(qeth_card_t *card)
+{
+ if (card) {
+ card->dev->flags|=IFF_RUNNING;
+/*
+ clear_bit(__LINK_STATE_DOWN,&dev->flags);
+*/
+ }
+}
+
+static void qeth_set_dev_flag_norunning(qeth_card_t *card)
+{
+ if (card) {
+ card->dev->flags&=~IFF_RUNNING;
+/*
+ set_bit(__LINK_STATE_DOWN,&dev->flags);
+*/
+ }
+}
+
+static void qeth_restore_dev_flag_state(qeth_card_t *card)
+{
+ if (card) {
+ if (card->saved_dev_flags&IFF_RUNNING)
+ card->dev->flags|=IFF_RUNNING;
+ else
+ card->dev->flags&=~IFF_RUNNING;
+/*
+ if (card->saved_dev_flags&__LINK_STATE_DOWN)
+ set_bit(__LINK_STATE_DOWN,&card->dev->flags);
+ else
+ clear_bit(__LINK_STATE_DOWN,&card->dev->flags);
+*/
+ }
+}
+
+static void qeth_save_dev_flag_state(qeth_card_t *card)
+{
+ if (card) {
+ card->saved_dev_flags=card->dev->flags&IFF_RUNNING;
+/*
+ card->saved_dev_flags=card->dev->flags&__LINK_STATE_DOWN;
+*/
+ }
+}
+
+static inline int netif_is_busy(struct net_device *dev)
+{
+ return(test_bit(__LINK_STATE_XOFF,&dev->flags));
+}
+
+static int qeth_open(struct net_device *dev)
+{
+ char dbf_text[15];
+ qeth_card_t *card;
+
+ card=(qeth_card_t *)dev->priv;
+ sprintf(dbf_text,"open%4x",card->irq0);
+ QETH_DBF_TEXT2(0,trace,dbf_text);
+ QETH_DBF_TEXT2(0,setup,dbf_text);
+
+ qeth_save_dev_flag_state(card);
+
+ netif_start_queue(dev);
+ if (!atomic_swap(&((qeth_card_t*)dev->priv)->is_open,1)) {
+ MOD_INC_USE_COUNT;
+ }
+ return 0;
+}
+
+static int qeth_set_config(struct net_device *dev,struct ifmap *map)
+{
+ qeth_card_t *card=(qeth_card_t*)dev->priv;
+ char dbf_text[15];
+
+ sprintf(dbf_text,"nscf%04x",card->irq0);
+ QETH_DBF_TEXT3(0,trace,dbf_text);
+
+ return -EOPNOTSUPP;
+}
+
+static int qeth_is_multicast_skb_at_all(struct sk_buff *skb,int version)
+{
+ int i;
+ if (skb->dst && skb->dst->neighbour) {
+ i=skb->dst->neighbour->type;
+ return ((i==RTN_BROADCAST)||
+ (i==RTN_MULTICAST)||
+ (i==RTN_ANYCAST))?i:0;
+ }
+ /* ok, we've to try it somehow else */
+ if (version==4) {
+ return ((skb->nh.raw[16]&0xf0)==0xe0)?RTN_MULTICAST:0;
+ } else if (version==6) {
+ return (skb->nh.raw[24]==0xff)?RTN_MULTICAST:0;
+ }
+ return 0;
+}
+
+static int qeth_get_prioqueue(qeth_card_t *card,struct sk_buff *skb,
+ int multicast,int version)
+{
+ if (!version) return QETH_DEFAULT_QUEUE;
+ switch (card->no_queues) {
+ case 1:
+ return 0;
+ case 4:
+ if ( (card->can_do_async_iqd) &&
+ (card->options.async_iqd==ASYNC_IQD) ) {
+ return card->no_queues-1;
+ }
+ if (card->is_multicast_different) {
+ if (multicast) {
+ return card->is_multicast_different&
+ (card->no_queues-1);
+ } else {
+ return 0;
+ }
+ }
+ if (card->options.do_prio_queueing) {
+ if (version==4) {
+ if (card->options.do_prio_queueing==
+ PRIO_QUEUEING_TOS) {
+ if (skb->nh.iph->tos&
+ IP_TOS_NOTIMPORTANT) {
+ return 3;
+ }
+ if (skb->nh.iph->tos&
+ IP_TOS_LOWDELAY) {
+ return 0;
+ }
+ if (skb->nh.iph->tos&
+ IP_TOS_HIGHTHROUGHPUT) {
+ return 1;
+ }
+ if (skb->nh.iph->tos&
+ IP_TOS_HIGHRELIABILITY) {
+ return 2;
+ }
+ return QETH_DEFAULT_QUEUE;
+ }
+ if (card->options.do_prio_queueing==
+ PRIO_QUEUEING_PREC) {
+ return 3-(skb->nh.iph->tos>>6);
+ }
+ } else if (version==6) {
+/********************
+ ********************
+TODO: IPv6!!!
+********************/
+ }
+ return card->options.default_queue;
+ } else return card->options.default_queue;
+ default:
+ return 0;
+ }
+}
+
+static void qeth_wakeup(qeth_card_t *card) {
+ char dbf_text[15];
+
+ sprintf(dbf_text,"wkup%4x",card->irq0);
+ QETH_DBF_TEXT5(0,trace,dbf_text);
+
+ atomic_set(&card->data_has_arrived,1);
+ spin_lock(&card->wait_q_lock);
+ if (atomic_read(&card->wait_q_active)) {
+ wake_up(&card->wait_q);
+ }
+ spin_unlock(&card->wait_q_lock);
+}
+
+static int qeth_check_idx_response(unsigned char *buffer)
+{
+ if (!buffer)
+ return 0;
+ if ((buffer[2]&0xc0)==0xc0) {
+ return -EIO;
+ }
+ return 0;
+}
+
+static int qeth_get_cards_problem(qeth_card_t *card,unsigned char *buffer,
+ int irq,int dstat,int cstat,int rqparam,
+ char *irb,char *sense)
+{
+ char dbf_text[15];
+ int problem=0;
+
+ if (atomic_read(&card->shutdown_phase)) return 0;
+ if (dstat&DEV_STAT_UNIT_CHECK) {
+ if (irq==card->irq2) {
+ sprintf(dbf_text,"ACHK%04x",card->irq0);
+ QETH_DBF_TEXT1(0,trace,dbf_text);
+ problem=PROBLEM_ACTIVATE_CHECK_CONDITION;
+ goto out;
+ }
+ if (sense[SENSE_RESETTING_EVENT_BYTE]&
+ SENSE_RESETTING_EVENT_FLAG) {
+ sprintf(dbf_text,"REVN%04x",card->irq0);
+ QETH_DBF_TEXT1(0,trace,dbf_text);
+ problem=PROBLEM_RESETTING_EVENT_INDICATOR;
+ goto out;
+ }
+ if (sense[SENSE_COMMAND_REJECT_BYTE]&
+ SENSE_COMMAND_REJECT_FLAG) {
+ sprintf(dbf_text,"CREJ%04x",card->irq0);
+ QETH_DBF_TEXT1(0,trace,dbf_text);
+ problem=PROBLEM_COMMAND_REJECT;
+ goto out;
+ }
+ if ( (sense[2]==0xaf)&&(sense[3]==0xfe) ) {
+ sprintf(dbf_text,"AFFE%04x",card->irq0);
+ QETH_DBF_TEXT1(0,trace,dbf_text);
+ problem=PROBLEM_AFFE;
+ goto out;
+ }
+ if ( (!sense[0]) && (!sense[1]) &&
+ (!sense[2]) && (!sense[3]) ) {
+ sprintf(dbf_text,"ZSNS%04x",card->irq0);
+ QETH_DBF_TEXT1(0,trace,dbf_text);
+ problem=PROBLEM_ZERO_SENSE_DATA;
+ goto out;
+ }
+ sprintf(dbf_text,"GCHK%04x",card->irq0);
+ QETH_DBF_TEXT1(0,trace,dbf_text);
+ problem=PROBLEM_GENERAL_CHECK;
+ goto out;
+ }
+ if (cstat& (SCHN_STAT_CHN_CTRL_CHK|SCHN_STAT_INTF_CTRL_CHK|
+ SCHN_STAT_CHN_DATA_CHK|SCHN_STAT_CHAIN_CHECK|
+ SCHN_STAT_PROT_CHECK|SCHN_STAT_PROG_CHECK) ) {
+ sprintf(dbf_text,"GCHK%04x",card->irq0);
+ QETH_DBF_TEXT1(0,trace,dbf_text);
+ QETH_DBF_HEX1(0,misc,irb,__max(QETH_DBF_MISC_LEN,64));
+ PRINT_WARN("check on irq x%x, dstat=x%x, cstat=x%x, " \
+ "rqparam=x%x\n",irq,dstat,cstat,rqparam);
+ HEXDUMP16(WARN,"irb: ",irb);
+ HEXDUMP16(WARN,"irb: ",((char*)irb)+32);
+ problem=PROBLEM_GENERAL_CHECK;
+ goto out;
+ }
+ if (qeth_check_idx_response(buffer)) {
+ PRINT_WARN("received an IDX TERMINATE on irq 0x%X/0x%X " \
+ "with cause code 0x%02x%s\n",
+ card->irq0,card->irq1,buffer[4],
+ (buffer[4]==0x22)?" -- try another portname":"");
+ sprintf(dbf_text,"RTRM%04x",card->irq0);
+ QETH_DBF_TEXT1(0,trace,dbf_text);
+ problem=PROBLEM_RECEIVED_IDX_TERMINATE;
+ goto out;
+ }
+ if (IS_IPA(buffer) && !IS_IPA_REPLY(buffer)) {
+ if ( *(PDU_ENCAPSULATION(buffer))==IPA_CMD_STOPLAN ) {
+ atomic_set(&card->is_startlaned,0);
+ /* we don't do a netif_stop_queue(card->dev);
+ we better discard all packets --
+ the outage could take longer */
+ PRINT_WARN("Link failure on %s (CHPID 0x%X) -- " \
+ "there is a network problem or someone " \
+ "pulled the cable or disabled the port."
+ "Discarding outgoing packets.\n",
+ card->dev_name,card->chpid);
+ sprintf(dbf_text,"CBOT%04x",card->irq0);
+ QETH_DBF_TEXT1(0,trace,dbf_text);
+ qeth_set_dev_flag_norunning(card);
+ problem=0;
+ goto out;
+ }
+ /* we checked for buffer!=0 in IS_IPA */
+ if ( *(PDU_ENCAPSULATION(buffer))==IPA_CMD_STARTLAN ) {
+ if (!atomic_read(&card->is_startlaned)) {
+ atomic_set(&card->is_startlaned,1);
+ problem=PROBLEM_CARD_HAS_STARTLANED;
+ }
+ goto out;
+ }
+ if ( *(PDU_ENCAPSULATION(buffer))==
+ IPA_CMD_REGISTER_LOCAL_ADDR ) {
+ sprintf(dbf_text,"irla%04x",card->irq0);
+ QETH_DBF_TEXT3(0,trace,dbf_text);
+ }
+ if ( *(PDU_ENCAPSULATION(buffer))==
+ IPA_CMD_UNREGISTER_LOCAL_ADDR ) {
+ sprintf(dbf_text,"irla%04x",card->irq0);
+ QETH_DBF_TEXT3(0,trace,dbf_text);
+ }
+ PRINT_WARN("probably a problem on %s: received data is " \
+ "IPA, but not a reply: command=0x%x\n",
+ card->dev_name,*(PDU_ENCAPSULATION(buffer)+1));
+ sprintf(dbf_text,"INRP%04x",card->irq0);
+ QETH_DBF_TEXT1(0,trace,dbf_text);
+ goto out;
+ }
+ /* no probs */
+out:
+ if (problem) {
+ sprintf(dbf_text,"gcpr%4x",card->irq0);
+ QETH_DBF_TEXT3(0,trace,dbf_text);
+ sprintf(dbf_text,"%2x%2x%4x",dstat,cstat,problem);
+ QETH_DBF_TEXT3(0,trace,dbf_text);
+ sprintf(dbf_text,"%8x",rqparam);
+ QETH_DBF_TEXT3(0,trace,dbf_text);
+ if (buffer)
+ QETH_DBF_HEX3(0,trace,&buffer,sizeof(void*));
+ QETH_DBF_HEX3(0,trace,&irb,sizeof(void*));
+ QETH_DBF_HEX3(0,trace,&sense,sizeof(void*));
+ }
+ atomic_set(&card->problem,problem);
+ return problem;
+}
+
+
+static void qeth_issue_next_read(qeth_card_t *card)
+{
+ int result,result2;
+ char dbf_text[15];
+
+ sprintf(dbf_text,"isnr%04x",card->irq0);
+ QETH_DBF_TEXT5(0,trace,dbf_text);
+
+ /* set up next read ccw */
+ memcpy(&card->dma_stuff->read_ccw,READ_CCW,sizeof(ccw1_t));
+ card->dma_stuff->read_ccw.count=QETH_BUFSIZE;
+ /* recbuf is not yet used by read channel program */
+ card->dma_stuff->read_ccw.cda=QETH_GET_ADDR(card->dma_stuff->recbuf);
+
+ /* we don't s390irq_spin_lock_irqsave(card->irq0,flags), as
+ we are only called in the interrupt handler */
+ result=do_IO(card->irq0,&card->dma_stuff->read_ccw,
+ MPC_SETUP_STATE,0,0);
+ if (result) {
+ qeth_delay_millis(QETH_WAIT_BEFORE_2ND_DOIO);
+ result2=do_IO(card->irq0,&card->dma_stuff->read_ccw,
+ MPC_SETUP_STATE,0,0);
+ PRINT_WARN("read handler on irq x%x, read: do_IO " \
+ "returned %i, next try returns %i\n",
+ card->irq0,result,result2);
+ sprintf(dbf_text,"IsNR%04x",card->irq0);
+ QETH_DBF_TEXT1(0,trace,dbf_text);
+ sprintf(dbf_text,"%04x%04x",(__s16)result,(__s16)result2);
+ QETH_DBF_TEXT1(0,trace,dbf_text);
+ }
+}
+
+static int qeth_is_to_recover(qeth_card_t *card,int problem)
+{
+ switch (problem) {
+ case PROBLEM_CARD_HAS_STARTLANED:
+ return 1;
+ case PROBLEM_RECEIVED_IDX_TERMINATE:
+ if (atomic_read(&card->in_recovery)) {
+ return 1;
+ } else {
+ qeth_set_dev_flag_norunning(card);
+ return 0;
+ }
+ case PROBLEM_ACTIVATE_CHECK_CONDITION:
+ return 1;
+ case PROBLEM_RESETTING_EVENT_INDICATOR:
+ return 1;
+ case PROBLEM_COMMAND_REJECT:
+ return 0;
+ case PROBLEM_ZERO_SENSE_DATA:
+ return 0;
+ case PROBLEM_GENERAL_CHECK:
+ return 1;
+ case PROBLEM_BAD_SIGA_RESULT:
+ return 1;
+ case PROBLEM_USER_TRIGGERED_RECOVERY:
+ return 1;
+ case PROBLEM_AFFE:
+ return 1;
+ case PROBLEM_MACHINE_CHECK:
+ return 1;
+ case PROBLEM_TX_TIMEOUT:
+ return 1;
+ }
+ return 0;
+}
+
+static int qeth_wait_for_event(atomic_t *var,unsigned int timeout)
+{
+ unsigned int start;
+ int retval;
+ char dbf_text[15];
+
+ QETH_DBF_TEXT5(0,trace,"wait4evn");
+ sprintf(dbf_text,"%08x",timeout);
+ QETH_DBF_TEXT5(0,trace,dbf_text);
+ QETH_DBF_HEX5(0,trace,&var,sizeof(void*));
+
+ start=qeth_get_millis();
+ for (;;) {
+ set_task_state(current,TASK_INTERRUPTIBLE);
+ if (atomic_read(var)) {
+ retval=0;
+ goto out;
+ }
+ if (qeth_get_millis()-start>timeout) {
+ retval=-ETIME;
+ goto out;
+ }
+ schedule_timeout(((start+timeout-qeth_get_millis())>>10)*HZ);
+ }
+ out:
+ set_task_state(current,TASK_RUNNING);
+
+ return retval;
+}
+
+static int qeth_get_spare_buf(void)
+{
+ int i=0;
+ char dbf_text[15];
+
+ while (i<sparebuffer_count) {
+ if (!atomic_compare_and_swap(SPAREBUF_FREE,SPAREBUF_USED,
+ &sparebufs[i].status)) {
+ sprintf(dbf_text,"gtspb%3x",i);
+ QETH_DBF_TEXT4(0,trace,dbf_text);
+ return i;
+ }
+ i++;
+ }
+ QETH_DBF_TEXT3(0,trace,"nospbuf");
+
+ return -1;
+}
+
+static void qeth_put_spare_buf(int no)
+{
+ char dbf_text[15];
+
+ sprintf(dbf_text,"ptspb%3x",no);
+ QETH_DBF_TEXT4(0,trace,dbf_text);
+ atomic_set(&sparebufs[no].status, SPAREBUF_FREE);
+}
+
+static inline void qeth_put_buffer_pool_entry(qeth_card_t *card,int entry_no)
+{
+ if (entry_no&SPAREBUF_MASK)
+ qeth_put_spare_buf(entry_no&(~SPAREBUF_MASK));
+ else
+ card->inbound_buffer_pool_entry_used[entry_no]=BUFFER_UNUSED;
+}
+
+static inline int qeth_get_empty_buffer_pool_entry(qeth_card_t *card)
+{
+ int i;
+ int max_buffers=card->options.inbound_buffer_count;
+
+ for (i=0;i<max_buffers;i++) {
+ if (xchg((int*)&card->inbound_buffer_pool_entry_used[i],
+ BUFFER_USED)==BUFFER_UNUSED) return i;
+ }
+ return -1;
+}
+
+static inline void qeth_clear_input_buffer(qeth_card_t *card,int bufno)
+{
+ qdio_buffer_t *buffer;
+ int i;
+ int elements,el_m_1;
+ void *ptr;
+#ifdef QETH_DBF_LIKE_HELL
+ char dbf_text[15];
+
+ sprintf(dbf_text,"clib%4x",card->irq0);
+ QETH_DBF_TEXT6(0,trace,dbf_text);
+ sprintf(dbf_text,"bufno%3x",bufno);
+ QETH_DBF_TEXT6(0,trace,dbf_text);
+#endif /* QETH_DBF_LIKE_HELL */
+
+ buffer=&card->inbound_qdio_buffers[bufno];
+ elements=BUFFER_MAX_ELEMENTS;
+ el_m_1=elements-1;
+
+ for (i=0;i<elements;i++) {
+ if (i==el_m_1)
+ buffer->element[i].flags=SBAL_FLAGS_LAST_ENTRY;
+ else
+ buffer->element[i].flags=0;
+
+ buffer->element[i].length=PAGE_SIZE;
+ ptr=INBOUND_BUFFER_POS(card,bufno,i);
+ if (card->do_pfix) {
+ /* we assume here, that ptr&(PAGE_SIZE-1)==0 */
+ buffer->element[i].addr=(void *)pfix_get_page_addr(ptr);
+ card->real_inb_buffer_addr[bufno][i]=ptr;
+ } else {
+ buffer->element[i].addr=ptr;
+ }
+ }
+}
+
+static void qeth_queue_input_buffer(qeth_card_t *card,int bufno,
+ unsigned int under_int)
+{
+ int count=0,start=0,stop=0,pos;
+ int result;
+ int cnt1,cnt2=0;
+ int wrapped=0;
+ int i;
+ int requeue_counter;
+ char dbf_text[15];
+ int no;
+
+#ifdef QETH_DBF_LIKE_HELL
+ sprintf(dbf_text,"qibf%4x",card->irq0);
+ QETH_DBF_TEXT5(0,trace,dbf_text);
+ sprintf(dbf_text,"%4x%4x",under_int,bufno);
+ QETH_DBF_TEXT5(0,trace,dbf_text);
+#endif /* QETH_DBF_LIKE_HELL */
+ atomic_inc(&card->requeue_counter);
+ if (atomic_read(&card->requeue_counter) > QETH_REQUEUE_THRESHOLD) {
+ if (!spin_trylock(&card->requeue_input_lock)) {
+#ifndef QETH_DBF_LIKE_HELL
+ sprintf(dbf_text,"qibl%4x",card->irq0);
+ QETH_DBF_TEXT5(0,trace,dbf_text);
+#endif /* QETH_DBF_LIKE_HELL */
+ return;
+ }
+ requeue_counter=atomic_read(&card->requeue_counter);
+ pos=atomic_read(&card->requeue_position);
+
+ start=pos;
+ /* omit the situation with 128 simultaneously
+ enqueued buffers, as then we can't benefit from PCI
+ avoidance anymore -- therefore we let count not grow as
+ big as requeue_counter */
+ while ( (!atomic_read(&card->inbound_buffer_refcnt[pos])) &&
+ (count<requeue_counter-1) ) {
+ no=qeth_get_empty_buffer_pool_entry(card);
+ if (no==-1) {
+ if (count) break;
+ no=qeth_get_spare_buf();
+ if (no==-1) {
+ PRINT_ERR("%s: no more input "\
+ "buffers available! " \
+ "Inbound traffic could " \
+ "be lost! Try to spend " \
+ "more memory for qeth\n",
+ card->dev_name);
+ sprintf(dbf_text,"QINB%4x",card->irq0);
+ QETH_DBF_TEXT2(1,trace,dbf_text);
+ goto out;
+ }
+ card->inbound_buffer_entry_no[pos]=
+ no|SPAREBUF_MASK;
+ }
+ card->inbound_buffer_entry_no[pos]=no;
+ atomic_set(&card->inbound_buffer_refcnt[pos],1);
+ count++;
+ if (pos>=QDIO_MAX_BUFFERS_PER_Q-1) {
+ pos=0;
+ wrapped=1;
+ } else pos++;
+ }
+ /* stop points to the position after the last element */
+ stop=pos;
+
+#ifdef QETH_DBF_LIKE_HELL
+ sprintf(dbf_text,"qibi%4x",card->irq0);
+ QETH_DBF_TEXT3(0,trace,dbf_text);
+ sprintf(dbf_text,"%4x",requeue_counter);
+ QETH_DBF_TEXT3(0,trace,dbf_text);
+ sprintf(dbf_text,"%4x%4x",start,stop);
+ QETH_DBF_TEXT3(0,trace,dbf_text);
+#endif /* QETH_DBF_LIKE_HELL */
+ if (wrapped) {
+ cnt1=QDIO_MAX_BUFFERS_PER_Q-start;
+ cnt2=stop;
+ } else {
+ cnt1=count;
+ /* cnt2 is already set to 0 */
+ }
+
+ atomic_sub(count,&card->requeue_counter);
+ /* this is the only place where card->requeue_position is
+ written to, so that's ok (as it is in a lock) */
+ atomic_set(&card->requeue_position,
+ (atomic_read(&card->requeue_position)+count)
+ &(QDIO_MAX_BUFFERS_PER_Q-1));
+
+ if (cnt1) {
+ for (i=start;i<start+cnt1;i++) {
+ qeth_clear_input_buffer(card,i);
+ }
+ result=do_QDIO(card->irq2,
+ QDIO_FLAG_SYNC_INPUT|under_int,
+ 0,start,cnt1,
+ NULL);
+ if (result) {
+ PRINT_WARN("qeth_queue_input_buffer's " \
+ "do_QDIO returnd %i " \
+ "(irq 0x%x)\n",
+ result,card->irq2);
+ sprintf(dbf_text,"QIDQ%4x",card->irq0);
+ QETH_DBF_TEXT1(0,trace,dbf_text);
+ sprintf(dbf_text,"%4x%4x",result,
+ requeue_counter);
+ QETH_DBF_TEXT1(0,trace,dbf_text);
+ sprintf(dbf_text,"%4x%4x",start,cnt1);
+ QETH_DBF_TEXT1(1,trace,dbf_text);
+ }
+ }
+ if (cnt2) {
+ for (i=0;i<cnt2;i++) {
+ qeth_clear_input_buffer(card,i);
+ }
+ result=do_QDIO(card->irq2,
+ QDIO_FLAG_SYNC_INPUT|under_int,0,
+ 0,cnt2,
+ NULL);
+ if (result) {
+ PRINT_WARN("qeth_queue_input_buffer's " \
+ "do_QDIO returnd %i " \
+ "(irq 0x%x)\n",
+ result,card->irq2);
+ sprintf(dbf_text,"QIDQ%4x",card->irq0);
+ QETH_DBF_TEXT1(0,trace,dbf_text);
+ sprintf(dbf_text,"%4x%4x",result,
+ requeue_counter);
+ QETH_DBF_TEXT1(0,trace,dbf_text);
+ sprintf(dbf_text,"%4x%4x",0,cnt2);
+ QETH_DBF_TEXT1(1,trace,dbf_text);
+ }
+ }
+out:
+ my_spin_unlock(&card->requeue_input_lock);
+ }
+
+}
+
+static inline struct sk_buff *qeth_get_skb(unsigned int len)
+{
+ struct sk_buff *skb;
+
+#ifdef QETH_VLAN
+ skb=dev_alloc_skb(len+VLAN_HLEN);
+ if (skb) skb_reserve(skb,VLAN_HLEN);
+#else /* QETH_VLAN */
+ skb=dev_alloc_skb(len);
+#endif /* QETH_VLAN */
+ return skb;
+}
+
+static struct sk_buff *qeth_get_next_skb(qeth_card_t *card,
+ int *element_ptr,int *pos_in_el_ptr,
+ void **hdr_ptr,
+ qdio_buffer_t *buffer)
+{
+ int length;
+ char *data_ptr;
+ int step,len_togo,element,pos_in_el;
+ int curr_len;
+ int max_elements;
+ struct sk_buff *skb;
+ char dbf_text[15];
+
+ max_elements=BUFFER_MAX_ELEMENTS;
+
+#define SBALE_LEN(x) ((x>=max_elements)?0:(buffer->element[x].length))
+#define SBALE_ADDR(x) (buffer->element[x].addr)
+
+ element=*element_ptr;
+
+ if (element>=max_elements) {
+ PRINT_WARN("irq 0x%x: error in interpreting buffer (data " \
+ "too long), %i elements.\n",card->irq0,element);
+ sprintf(dbf_text,"IEDL%4x",card->irq0);
+ QETH_DBF_TEXT0(0,trace,dbf_text);
+ sprintf(dbf_text,"%4x%4x",*element_ptr,*pos_in_el_ptr);
+ QETH_DBF_TEXT0(1,trace,dbf_text);
+ QETH_DBF_HEX0(0,misc,buffer,QETH_DBF_MISC_LEN);
+ QETH_DBF_HEX0(0,misc,buffer+QETH_DBF_MISC_LEN,
+ QETH_DBF_MISC_LEN);
+ return NULL;
+ }
+
+ pos_in_el=*pos_in_el_ptr;
+
+ curr_len=SBALE_LEN(element);
+ if (curr_len>PAGE_SIZE) {
+ PRINT_WARN("irq 0x%x: bad element length in element %i: " \
+ "0x%x\n",card->irq0,element,curr_len);
+ sprintf(dbf_text,"BELN%4x",card->irq0);
+ QETH_DBF_TEXT0(0,trace,dbf_text);
+ sprintf(dbf_text,"%4x",curr_len);
+ QETH_DBF_TEXT0(0,trace,dbf_text);
+ sprintf(dbf_text,"%4x%4x",*element_ptr,*pos_in_el_ptr);
+ QETH_DBF_TEXT0(1,trace,dbf_text);
+ QETH_DBF_HEX0(0,misc,buffer,QETH_DBF_MISC_LEN);
+ QETH_DBF_HEX0(0,misc,buffer+QETH_DBF_MISC_LEN,
+ QETH_DBF_MISC_LEN);
+ return NULL;
+ }
+ /* header fits in current element? */
+ if (curr_len<pos_in_el+QETH_HEADER_SIZE) {
+ if (!pos_in_el) {
+#ifdef QETH_DBF_LIKE_HELL
+ sprintf(dbf_text,"gnmh%4x",card->irq0);
+ QETH_DBF_TEXT6(0,trace,dbf_text);
+#endif /* QETH_DBF_LIKE_HELL */
+ return NULL; /* no more data in buffer */
+ }
+ /* set hdr to next element */
+ element++;
+ pos_in_el=0;
+ curr_len=SBALE_LEN(element);
+ /* does it fit in there? */
+ if (curr_len<QETH_HEADER_SIZE) {
+#ifdef QETH_DBF_LIKE_HELL
+ sprintf(dbf_text,"gdnf%4x",card->irq0);
+ QETH_DBF_TEXT6(0,trace,dbf_text);
+#endif /* QETH_DBF_LIKE_HELL */
+ return NULL;
+ }
+ }
+
+ *hdr_ptr=SBALE_ADDR(element)+pos_in_el;
+
+ length=*(__u16*)((char*)(*hdr_ptr)+QETH_HEADER_LEN_POS);
+
+#ifdef QETH_DBF_LIKE_HELL
+ sprintf(dbf_text,"gnHd%4x",card->irq0);
+ QETH_DBF_TEXT6(0,trace,dbf_text);
+ QETH_DBF_HEX6(0,trace,hdr_ptr,sizeof(void*));
+#endif /* QETH_DBF_LIKE_HELL */
+
+ pos_in_el+=QETH_HEADER_SIZE;
+ if (curr_len<=pos_in_el) {
+ /* switch to next element for data */
+ pos_in_el=0;
+ element++;
+ curr_len=SBALE_LEN(element);
+ if (!curr_len) {
+ PRINT_WARN("irq 0x%x: inb. buffer with more headers " \
+ "than data areas (%i elements).\n",
+ card->irq0,element);
+ sprintf(dbf_text,"IEMH%4x",card->irq0);
+ QETH_DBF_TEXT0(0,trace,dbf_text);
+ sprintf(dbf_text,"%2x%2x%4x",element,*element_ptr,
+ *pos_in_el_ptr);
+ QETH_DBF_TEXT0(1,trace,dbf_text);
+ QETH_DBF_HEX0(0,misc,buffer,QETH_DBF_MISC_LEN);
+ QETH_DBF_HEX0(0,misc,buffer+QETH_DBF_MISC_LEN,
+ QETH_DBF_MISC_LEN);
+ return NULL;
+ }
+ }
+
+ data_ptr=SBALE_ADDR(element)+pos_in_el;
+
+ if (card->options.fake_ll==FAKE_LL) {
+ skb=qeth_get_skb(length+QETH_FAKE_LL_LEN);
+ if (!skb) goto nomem;
+ skb_pull(skb,QETH_FAKE_LL_LEN);
+ if (!skb) {
+ dev_kfree_skb_irq(skb);
+ goto nomem;
+ }
+ } else {
+ skb=qeth_get_skb(length);
+ if (!skb) goto nomem;
+ }
+
+ if (card->easy_copy_cap)
+ memcpy(skb_put(skb,length),data_ptr,length);
+
+#ifdef QETH_DBF_LIKE_HELL
+ QETH_DBF_HEX6(0,trace,&data_ptr,sizeof(void*));
+ QETH_DBF_HEX6(0,trace,&skb,sizeof(void*));
+#endif /* QETH_DBF_LIKE_HELL */
+
+ len_togo=length;
+ while (1) {
+ step=qeth_min(len_togo,curr_len-pos_in_el);
+ if (!step) {
+ PRINT_WARN("irq 0x%x: unexpected end of buffer, " \
+ "length of element %i is 0. Discarding " \
+ "packet.\n",card->irq0,element);
+ sprintf(dbf_text,"IEUE%4x",card->irq0);
+ QETH_DBF_TEXT0(0,trace,dbf_text);
+ sprintf(dbf_text,"%2x%2x%4x",element,*element_ptr,
+ *pos_in_el_ptr);
+ QETH_DBF_TEXT0(0,trace,dbf_text);
+ sprintf(dbf_text,"%4x%4x",len_togo,step);
+ QETH_DBF_TEXT0(0,trace,dbf_text);
+ sprintf(dbf_text,"%4x%4x",curr_len,pos_in_el);
+ QETH_DBF_TEXT0(1,trace,dbf_text);
+ QETH_DBF_HEX0(0,misc,buffer,QETH_DBF_MISC_LEN);
+ QETH_DBF_HEX0(0,misc,buffer+QETH_DBF_MISC_LEN,
+ QETH_DBF_MISC_LEN);
+ dev_kfree_skb_irq(skb);
+ return NULL;
+ }
+ if (!card->easy_copy_cap)
+ memcpy(skb_put(skb,step),data_ptr,step);
+ len_togo-=step;
+ if (len_togo) {
+ pos_in_el=0;
+ element++;
+ curr_len=SBALE_LEN(element);
+ data_ptr=SBALE_ADDR(element);
+ } else {
+#ifdef QETH_INBOUND_PACKING_1_PACKET_PER_SBALE
+ element++;
+ /* we don't need to calculate curr_len */
+ pos_in_el=0;
+#else /* QETH_INBOUND_PACKING_1_PACKET_PER_SBALE */
+ pos_in_el+=step;
+#endif /* QETH_INBOUND_PACKING_1_PACKET_PER_SBALE */
+ break;
+ }
+ }
+
+#ifdef QETH_DBF_LIKE_HELL
+ sprintf(dbf_text,"%4x%4x",element,pos_in_el);
+ QETH_DBF_TEXT6(0,trace,dbf_text);
+#endif /* QETH_DBF_LIKE_HELL */
+
+ *element_ptr=element;
+ *pos_in_el_ptr=pos_in_el;
+
+ return skb;
+
+nomem:
+ if (net_ratelimit()) {
+ PRINT_WARN("no memory for packet from %s\n",card->dev_name);
+ }
+ sprintf(dbf_text,"NOMM%4x",card->irq0);
+ QETH_DBF_TEXT0(0,trace,dbf_text);
+ return NULL;
+}
+
+static void qeth_transform_outbound_addrs(qeth_card_t *card,
+ qdio_buffer_t *buffer)
+{
+ int i;
+ void *ptr;
+
+ if (card->do_pfix) {
+ for (i=0;i<QDIO_MAX_ELEMENTS_PER_BUFFER;i++) {
+ ptr=buffer->element[i].addr;
+ buffer->element[i].addr=(void *)pfix_get_addr(ptr);
+ }
+ }
+}
+static void qeth_get_linux_addrs_for_buffer(qeth_card_t *card,int buffer_no)
+{
+ int i;
+ void *ptr;
+
+ if (card->do_pfix) {
+ for (i=0;i<QDIO_MAX_ELEMENTS_PER_BUFFER;i++) {
+ ptr=card->inbound_qdio_buffers[buffer_no].
+ element[i].addr;
+ card->inbound_qdio_buffers[buffer_no].element[i].addr=
+ card->real_inb_buffer_addr[buffer_no][i]+
+ ((unsigned long)ptr&(PAGE_SIZE-1));
+ }
+ }
+}
+
+static void qeth_read_in_buffer(qeth_card_t *card,int buffer_no)
+{
+ struct sk_buff *skb;
+ void *hdr_ptr;
+ int element=0,pos_in_el=0;
+ int version;
+ qdio_buffer_t *buffer;
+ unsigned short cast_type;
+#ifdef QETH_VLAN
+ __u16 *vlan_tag;
+#endif
+ int i;
+ int max_elements;
+ char dbf_text[15];
+ struct net_device *dev;
+
+ dev=card->dev;
+ max_elements=BUFFER_MAX_ELEMENTS;
+
+ buffer=&card->inbound_qdio_buffers[buffer_no];
+
+ /* inform about errors */
+ if (buffer->element[15].flags&0xff) {
+ PRINT_WARN("on irq 0x%x: incoming SBALF 15 on buffer " \
+ "0x%x are 0x%x\n",card->irq0,buffer_no,
+ buffer->element[15].flags&0xff);
+ sprintf(dbf_text,"SF##%2x%2x",buffer_no,
+ buffer->element[15].flags&0xff);
+ *((__u16*)(&dbf_text[2]))=(__u16)card->irq0;
+ QETH_DBF_HEX1(1,trace,dbf_text,QETH_DBF_TRACE_LEN);
+ }
+
+ for (i=0;i<max_elements-1;i++) {
+ if (buffer->element[i].flags&SBAL_FLAGS_LAST_ENTRY) {
+ buffer->element[i+1].length=0;
+ break;
+ }
+ }
+#ifdef QETH_PERFORMANCE_STATS
+ card->perf_stats.bufs_rec++;
+#endif /* QETH_PERFORMANCE_STATS */
+
+#ifdef QETH_DBF_LIKE_HELL
+ sprintf(dbf_text,"ribX%4x",card->irq0);
+ dbf_text[3]=buffer_no;
+ QETH_DBF_HEX6(0,trace,dbf_text,QETH_DBF_TRACE_LEN);
+#endif /* QETH_DBF_LIKE_HELL */
+
+ while ((skb=qeth_get_next_skb(card,&element,&pos_in_el,
+ &hdr_ptr,buffer))) {
+
+#ifdef QETH_PERFORMANCE_STATS
+ card->perf_stats.skbs_rec++;
+#endif /* QETH_PERFORMANCE_STATS */
+
+ if (skb) {
+ skb->dev=dev;
+
+#ifdef QETH_IPV6
+ if ( (*(__u16 *)(hdr_ptr))&(QETH_HEADER_PASSTHRU) ) {
+ skb->protocol=card->type_trans(skb,dev);
+ } else
+#endif /* QETH_IPV6 */
+ {
+ version=((*(__u16 *)(hdr_ptr))&
+ (QETH_HEADER_IPV6))?6:4;
+ skb->protocol=htons((version==4)?ETH_P_IP:
+ (version==6)?ETH_P_IPV6:
+ ETH_P_ALL);
+ cast_type=(*(__u16 *)(hdr_ptr))&
+ (QETH_CAST_FLAGS);
+ if (cast_type==QETH_CAST_UNICAST) {
+ skb->pkt_type=PACKET_HOST;
+ } else if (cast_type==QETH_CAST_MULTICAST) {
+ skb->pkt_type=PACKET_MULTICAST;
+ } else if (cast_type==QETH_CAST_BROADCAST) {
+ skb->pkt_type=PACKET_BROADCAST;
+ } else if ( (cast_type==QETH_CAST_ANYCAST) ||
+ (cast_type==QETH_CAST_NOCAST) ) {
+ sprintf(dbf_text,"ribf%4x",card->irq0);
+ QETH_DBF_TEXT2(0,trace,dbf_text);
+ sprintf(dbf_text,"castan%2x",cast_type);
+ QETH_DBF_TEXT2(1,trace,dbf_text);
+ skb->pkt_type=PACKET_HOST;
+ } else {
+ PRINT_WARN("adapter is using an " \
+ "unknown casting value " \
+ "of 0x%x. Using " \
+ "unicasting instead.\n",
+ cast_type);
+ skb->pkt_type=PACKET_HOST;
+ sprintf(dbf_text,"ribf%4x",card->irq0);
+ QETH_DBF_TEXT2(0,trace,dbf_text);
+ sprintf(dbf_text,"castun%2x",cast_type);
+ QETH_DBF_TEXT2(1,trace,dbf_text);
+ }
+
+ if (card->options.fake_ll==FAKE_LL) {
+ skb->mac.raw=skb->data-QETH_FAKE_LL_LEN;
+ if (skb->pkt_type==PACKET_MULTICAST) {
+ switch (skb->protocol) {
+#ifdef QETH_IPV6
+ case __constant_htons(ETH_P_IPV6):
+ ndisc_mc_map((struct in6_addr *)
+ skb->data+QETH_FAKE_LL_V6_ADDR_POS,
+ skb->mac.raw+
+ QETH_FAKE_LL_DEST_MAC_POS,
+ card->dev,0);
+ break;
+#endif /* QETH_IPV6 */
+ case __constant_htons(ETH_P_IP):
+ qeth_get_mac_for_ipm(*(__u32*)
+ skb->data+QETH_FAKE_LL_V4_ADDR_POS,
+ skb->mac.raw+
+ QETH_FAKE_LL_DEST_MAC_POS,
+ card->dev);
+ break;
+ default:
+ memcpy(skb->mac.raw+
+ QETH_FAKE_LL_DEST_MAC_POS,
+ card->dev->dev_addr,
+ QETH_FAKE_LL_ADDR_LEN);
+ }
+ } else if (skb->pkt_type==PACKET_BROADCAST) {
+ memset(skb->mac.raw+
+ QETH_FAKE_LL_DEST_MAC_POS,0xff,
+ QETH_FAKE_LL_ADDR_LEN);
+ } else {
+ memcpy(skb->mac.raw+
+ QETH_FAKE_LL_DEST_MAC_POS,
+ card->dev->dev_addr,
+ QETH_FAKE_LL_ADDR_LEN);
+ }
+ if (*(__u8*)(hdr_ptr+11)&
+ QETH_EXT_HEADER_SRC_MAC_ADDRESS) {
+ memcpy(skb->mac.raw+
+ QETH_FAKE_LL_SRC_MAC_POS,
+ hdr_ptr+QETH_FAKE_LL_SRC_MAC_POS_IN_QDIO_HDR,
+ QETH_FAKE_LL_ADDR_LEN);
+ } else {
+ /* clear source MAC for security reasons */
+ memset(skb->mac.raw+
+ QETH_FAKE_LL_DEST_MAC_POS,0,
+ QETH_FAKE_LL_ADDR_LEN);
+ }
+ memcpy(skb->mac.raw+
+ QETH_FAKE_LL_PROT_POS,
+ &skb->protocol,
+ QETH_FAKE_LL_PROT_LEN);
+ } else {
+ skb->mac.raw=skb->data;
+ }
+
+ skb->ip_summed=card->options.checksum_type;
+ if (card->options.checksum_type==HW_CHECKSUMMING) {
+ /* do we have a checksummed packet? */
+ if (*(__u8*)(hdr_ptr+11)&
+ QETH_EXT_HEADER_CSUM_TRANSP_REQ) {
+ /* skb->ip_summed is set already */
+
+ /* vlan is not an issue here, it's still in
+ * the QDIO header, not pushed in the
+ * skb yet */
+ int ip_len=(skb->data[0]&0x0f)<<2;
+
+ if (*(__u8*)(hdr_ptr+11)&
+ QETH_EXT_HEADER_CSUM_TRANSP_FRAME_TYPE) {
+ /* get the UDP checksum */
+ skb->csum=*(__u16*)
+ (&skb->data[ip_len+
+ QETH_UDP_CSUM_OFFSET]);
+ } else {
+ /* get the TCP checksum */
+ skb->csum=*(__u16*)
+ (&skb->data[ip_len+
+ QETH_TCP_CSUM_OFFSET]);
+ }
+ } else {
+ /* make the stack check it */
+ skb->ip_summed=SW_CHECKSUMMING;
+ }
+ }
+
+#ifdef QETH_VLAN
+ if (*(__u8*)(hdr_ptr+11)&
+ QETH_EXT_HEADER_VLAN_FRAME) {
+ vlan_tag=(__u16 *)skb_push(skb,
+ VLAN_HLEN);
+ /*
+ if (*(__u8*)(hdr_ptr+11) &
+ QETH_EXT_HEADER_INCLUDE_VLAN_TAG) {
+ *vlan_tag = *(__u16*)(hdr_ptr+28);
+ *(vlan_tag+1)= *(__u16*)(hdr_ptr+30);
+ } else {
+ */
+ *vlan_tag = *(__u16*)(hdr_ptr+12);
+ *(vlan_tag+1) = skb->protocol;
+ /*
+ }
+ */
+ skb->protocol=
+ __constant_htons(ETH_P_8021Q);
+ }
+#endif
+ }
+
+#ifdef QETH_PERFORMANCE_STATS
+ card->perf_stats.inbound_time+=
+ NOW-card->perf_stats.inbound_start_time;
+ card->perf_stats.inbound_cnt++;
+#endif /* QETH_PERFORMANCE_STATS */
+
+#ifdef QETH_DBF_LIKE_HELL
+ sprintf(dbf_text,"rxpk%4x",card->irq0);
+ QETH_DBF_TEXT6(0,trace,dbf_text);
+#endif /* QETH_DBF_LIKE_HELL */
+
+ netif_rx(skb);
+
+ card->stats->rx_packets++;
+ card->stats->rx_bytes+=skb->len;
+ } else {
+ PRINT_WARN("%s: dropped packet, no buffers " \
+ "available.\n",card->dev_name);
+ sprintf(dbf_text,"DROP%4x",card->irq0);
+ QETH_DBF_TEXT2(1,trace,dbf_text);
+ card->stats->rx_dropped++;
+ }
+ }
+ atomic_set(&card->inbound_buffer_refcnt[buffer_no],0);
+ qeth_put_buffer_pool_entry(card,card->inbound_buffer_entry_no[
+ buffer_no]);
+}
+
+static void qeth_fill_header(qeth_hdr_t *hdr,struct sk_buff *skb,
+ int version,int multicast)
+{
+#ifdef QETH_DBF_LIKE_HELL
+ char dbf_text[15];
+#endif /* QETH_DBF_LIKE_HELL */
+#ifdef QETH_VLAN
+ qeth_card_t *card;
+#endif
+
+ hdr->id=1;
+ hdr->ext_flags=0;
+
+#ifdef QETH_VLAN
+ /* before we're going to overwrite
+ this location with next hop ip
+ */
+ card = (qeth_card_t *)skb->dev->priv;
+ if ((card->vlangrp != NULL) &&
+ (version == 4) &&
+ vlan_tx_tag_present(skb))
+ {
+ hdr->ext_flags = QETH_EXT_HEADER_VLAN_FRAME;
+ hdr->vlan_id = vlan_tx_tag_get(skb);
+ }
+#endif
+
+ hdr->length=skb->len-QETH_HEADER_SIZE; /* as skb->len includes
+ the header now */
+
+ /* yes, I know this is doubled code, but a small little bit
+ faster maybe */
+ if (version==4) { /* IPv4 */
+ if (multicast==RTN_MULTICAST) {
+ hdr->flags=QETH_CAST_MULTICAST;
+ } else if (multicast==RTN_BROADCAST) {
+ hdr->flags=QETH_CAST_BROADCAST;
+ } else {
+ hdr->flags=QETH_CAST_UNICAST;
+ }
+ *((__u32*)(&hdr->dest_addr[0]))=0;
+ *((__u32*)(&hdr->dest_addr[4]))=0;
+ *((__u32*)(&hdr->dest_addr[8]))=0;
+ if ((skb->dst) && (skb->dst->neighbour)) {
+ *((__u32*)(&hdr->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;
+ }
+ } else if (version==6) { /* IPv6 or passthru */
+ if (multicast==RTN_MULTICAST) {
+ hdr->flags=QETH_CAST_MULTICAST|
+ QETH_HEADER_PASSTHRU|
+ QETH_HEADER_IPV6;
+ } else if (multicast==RTN_ANYCAST) {
+ hdr->flags=QETH_CAST_ANYCAST|
+ QETH_HEADER_PASSTHRU|
+ QETH_HEADER_IPV6;
+ } else if (multicast==RTN_BROADCAST) {
+ hdr->flags=QETH_CAST_BROADCAST|
+ QETH_HEADER_PASSTHRU|
+ QETH_HEADER_IPV6;
+ } else { /* default: RTN_UNICAST */
+ hdr->flags=QETH_CAST_UNICAST|
+ QETH_HEADER_PASSTHRU|
+ QETH_HEADER_IPV6;
+ }
+
+ if ((skb->dst) && (skb->dst->neighbour)) {
+ memcpy(hdr->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);
+ }
+ } else { /* passthrough */
+ if (!memcmp(skb->data+QETH_HEADER_SIZE,
+ skb->dev->broadcast,6)) { /* broadcast? */
+ hdr->flags=QETH_CAST_BROADCAST|QETH_HEADER_PASSTHRU;
+ } else {
+ hdr->flags=QETH_CAST_UNICAST|QETH_HEADER_PASSTHRU;
+ }
+ }
+#ifdef QETH_DBF_LIKE_HELL
+ sprintf(dbf_text,"filhdr%2x",version);
+ QETH_DBF_TEXT6(0,trace,dbf_text);
+ sprintf(dbf_text,"%2x",multicast);
+ QETH_DBF_TEXT6(0,trace,dbf_text);
+ QETH_DBF_HEX6(0,trace,&skb,sizeof(void*));
+ QETH_DBF_HEX6(0,trace,&skb->data,sizeof(void*));
+ QETH_DBF_HEX6(0,misc,hdr,__max(QETH_HEADER_SIZE,QETH_DBF_MISC_LEN));
+ QETH_DBF_HEX6(0,data,skb->data,
+ __max(QETH_DBF_DATA_LEN,QETH_DBF_DATA_LEN));
+#endif /* QETH_DBF_LIKE_HELL */
+}
+
+static int inline qeth_fill_buffer(qdio_buffer_t *buffer,char *dataptr,
+ int length,int element)
+{
+ int length_here;
+ int first_lap=1;
+#ifdef QETH_DBF_LIKE_HELL
+ char dbf_text[15];
+#endif /* QETH_DBF_LIKE_HELL */
+ int first_element=element;
+
+ while (length>0) {
+ /* length_here is the remaining amount of data in this page */
+ length_here=PAGE_SIZE-((unsigned long)dataptr&(PAGE_SIZE-1));
+ if (length<length_here) length_here=length;
+
+ buffer->element[element].addr=dataptr;
+ buffer->element[element].length=length_here;
+ length-=length_here;
+ if (!length) {
+ if (first_lap) {
+ buffer->element[element].flags=0;
+ } else {
+ buffer->element[element].flags=
+ SBAL_FLAGS_LAST_FRAG;
+ }
+ } else {
+ if (first_lap) {
+ buffer->element[element].flags=
+ SBAL_FLAGS_FIRST_FRAG;
+ } else {
+ buffer->element[element].flags=
+ SBAL_FLAGS_MIDDLE_FRAG;
+ }
+ }
+ dataptr=dataptr+length_here;
+ element++;
+ if (element>QDIO_MAX_ELEMENTS_PER_BUFFER) {
+ PRINT_ERR("qeth_fill_buffer: IP packet too big!\n");
+ QETH_DBF_TEXT1(0,trace,"IPpktobg");
+ QETH_DBF_HEX1(1,trace,&dataptr,sizeof(void*));
+ buffer->element[first_element].length=0;
+ break;
+ }
+ first_lap=0;
+ }
+#ifdef QETH_DBF_LIKE_HELL
+ sprintf(dbf_text,"filbuf%2x",element);
+ QETH_DBF_TEXT6(0,trace,dbf_text);
+ QETH_DBF_HEX3(0,misc,buffer,QETH_DBF_MISC_LEN);
+ QETH_DBF_HEX3(0,misc,buffer+QETH_DBF_MISC_LEN,QETH_DBF_MISC_LEN);
+#endif /* QETH_DBF_LIKE_HELL */
+
+ return element;
+}
+
+static void qeth_flush_packed_packets(qeth_card_t *card,int queue,
+ int under_int)
+{
+ qdio_buffer_t *buffer;
+ int result;
+ int position;
+ int position_for_do_qdio;
+ char dbf_text[15];
+ int last_pci;
+
+ position=card->outbound_first_free_buffer[queue];
+ /* can happen, when in the time between deciding to pack and sending
+ the next packet the lower mark was reached: */
+ if (!card->outbound_ringbuffer[queue]->ringbuf_element[position].
+ next_element_to_fill)
+ return;
+
+ buffer=&card->outbound_ringbuffer[queue]->buffer[position];
+ buffer->element[card->outbound_ringbuffer[queue]->
+ ringbuf_element[position].
+ next_element_to_fill-1].flags|=SBAL_FLAGS_LAST_ENTRY;
+
+ card->dev->trans_start=jiffies;
+
+#ifdef QETH_PERFORMANCE_STATS
+ if (card->outbound_buffer_send_state[queue][position]==
+ SEND_STATE_DONT_PACK) {
+ card->perf_stats.bufs_sent_dont_pack++;
+ } else if (card->outbound_buffer_send_state[queue][position]==
+ SEND_STATE_PACK) {
+ card->perf_stats.bufs_sent_pack++;
+ }
+ card->perf_stats.bufs_sent++;
+#endif /* QETH_PERFORMANCE_STATS */
+
+ position_for_do_qdio=position;
+
+ position=(position+1)&(QDIO_MAX_BUFFERS_PER_Q-1);
+ card->outbound_first_free_buffer[queue]=position;
+
+ card->outbound_bytes_in_buffer[queue]=0;
+ /* we can override that, as we have at most 127 buffers enqueued */
+ card->outbound_ringbuffer[queue]->ringbuf_element[position].
+ next_element_to_fill=0;
+
+ atomic_inc(&card->outbound_used_buffers[queue]);
+
+#ifdef QETH_DBF_LIKE_HELL
+ sprintf(dbf_text,"flsp%4x",card->irq0);
+ QETH_DBF_TEXT5(0,trace,dbf_text);
+ sprintf(dbf_text,"%4x%2x%2x",position_for_do_qdio,under_int,queue);
+ QETH_DBF_TEXT5(0,trace,dbf_text);
+ QETH_DBF_HEX5(0,misc,buffer,QETH_DBF_MISC_LEN);
+ QETH_DBF_HEX5(0,misc,buffer+QETH_DBF_MISC_LEN,QETH_DBF_MISC_LEN);
+#endif /* QETH_DBF_LIKE_HELL */
+
+ /* we always set the outbound pci flag, don't care, whether the
+ * adapter honors it or not */
+ switch (card->send_state[queue]) {
+ case SEND_STATE_DONT_PACK:
+ if (atomic_read(&card->outbound_used_buffers[queue])
+ <HIGH_WATERMARK_PACK-WATERMARK_FUZZ) break;
+ /* set the PCI bit */
+ card->outbound_ringbuffer[queue]->
+ buffer[position_for_do_qdio].element[0].flags|=0x40;
+ atomic_set(&card->last_pci_pos[queue],position_for_do_qdio);
+ break;
+ case SEND_STATE_PACK:
+ last_pci=atomic_read(&card->last_pci_pos[queue]);
+ if (position_for_do_qdio<last_pci)
+ last_pci-=QDIO_MAX_BUFFERS_PER_Q;
+ /* so:
+ * last_pci is the position of the last pci we've set
+ * position_for_do_qdio is the position we will send out now
+ * outbound_used_buffers is the number of buffers used (means
+ * all buffers hydra has, inclusive position_for_do_qdio)
+ *
+ * we have to request a pci, if we have got the buffer of the
+ * last_pci position back.
+ *
+ * position_for_do_qdio-outbound_used_buffers is the newest
+ * buffer that we got back from hydra
+ *
+ * if this is greater or equal than the last_pci position,
+ * we should request a pci, as no pci request is
+ * outstanding anymore
+ */
+ if (position_for_do_qdio-
+ atomic_read(&card->outbound_used_buffers[queue])>=
+ last_pci) {
+ /* set the PCI bit */
+ card->outbound_ringbuffer[queue]->
+ buffer[position_for_do_qdio].
+ element[0].flags|=0x40;
+ atomic_set(&card->last_pci_pos[queue],
+ position_for_do_qdio);
+ }
+ }
+
+ qeth_transform_outbound_addrs(card,
+ &card->outbound_ringbuffer[queue]->
+ buffer[position_for_do_qdio]);
+ /* this has to be at the end, otherwise a buffer could be flushed
+ twice (see coment in qeth_do_send_packet) */
+ result=do_QDIO(card->irq2,QDIO_FLAG_SYNC_OUTPUT|under_int,queue,
+ position_for_do_qdio,1,
+ NULL);
+
+ if (result) {
+ PRINT_WARN("Outbound do_QDIO returned %i " \
+ "(irq 0x%x)\n",result,card->irq2);
+ sprintf(dbf_text,"FLSP%4x",card->irq0);
+ QETH_DBF_TEXT5(0,trace,dbf_text);
+ sprintf(dbf_text,"odoQ%4x",result);
+ QETH_DBF_TEXT5(0,trace,dbf_text);
+ sprintf(dbf_text,"%4x%2x%2x",position_for_do_qdio,
+ under_int,queue);
+ QETH_DBF_TEXT5(0,trace,dbf_text);
+ QETH_DBF_HEX5(0,misc,buffer,QETH_DBF_MISC_LEN);
+ QETH_DBF_HEX5(0,misc,buffer+QETH_DBF_MISC_LEN,
+ QETH_DBF_MISC_LEN);
+ }
+}
+
+#define ERROR_NONE 0
+#define ERROR_RETRY 1
+#define ERROR_LINK_FAILURE 2
+#define ERROR_KICK_THAT_PUPPY 3
+static inline int qeth_determine_send_error(int cc,int qdio_error,int sbalf15)
+{
+ char dbf_text[15];
+
+ switch (cc&3) {
+ case 0:
+ if (qdio_error)
+ return ERROR_LINK_FAILURE;
+ return ERROR_NONE;
+ case 2:
+ if (cc&QDIO_SIGA_ERROR_B_BIT_SET) {
+ QETH_DBF_TEXT3(0,trace,"sigacc2b");
+ return ERROR_KICK_THAT_PUPPY;
+ }
+ if (qeth_sbalf15_in_retrieable_range(sbalf15))
+ return ERROR_RETRY;
+ return ERROR_LINK_FAILURE;
+ /* look at qdio_error and sbalf 15 */
+ case 1:
+ PRINT_WARN("siga returned cc 1! cc=0x%x, " \
+ "qdio_error=0x%x, sbalf15=0x%x\n",
+ cc,qdio_error,sbalf15);
+
+ QETH_DBF_TEXT3(0,trace,"siga-cc1");
+ QETH_DBF_TEXT2(0,qerr,"siga-cc1");
+ sprintf(dbf_text,"%1x%2x%2x",cc,qdio_error,sbalf15);
+ QETH_DBF_TEXT3(0,trace,dbf_text);
+ QETH_DBF_TEXT2(0,qerr,dbf_text);
+ return ERROR_LINK_FAILURE;
+ case 3:
+ QETH_DBF_TEXT3(0,trace,"siga-cc3");
+ return ERROR_KICK_THAT_PUPPY;
+ }
+ return ERROR_LINK_FAILURE; /* should never happen */
+}
+
+static void qeth_free_buffer(qeth_card_t *card,int queue,int bufno,
+ int qdio_error,int siga_error)
+{
+ struct sk_buff *skb;
+ int error;
+ int retries;
+ int sbalf15;
+ char dbf_text[15];
+ qdio_buffer_t *buffer;
+
+ switch (card->outbound_buffer_send_state[queue][bufno]) {
+ case SEND_STATE_DONT_PACK: /* fallthrough */
+ case SEND_STATE_PACK:
+#ifdef QETH_DBF_LIKE_HELL
+ sprintf(dbf_text,"frbf%4x",card->irq0);
+ QETH_DBF_TEXT5(0,trace,dbf_text);
+ sprintf(dbf_text,"%2x%2x%4x",queue,bufno,
+ card->outbound_buffer_send_state[queue][bufno]);
+ QETH_DBF_TEXT5(0,trace,dbf_text);
+#endif /* QETH_DBF_LIKE_HELL */
+
+ buffer=&card->outbound_ringbuffer[queue]->buffer[bufno];
+ sbalf15=buffer->element[15].flags&0xff;
+ error=qeth_determine_send_error(siga_error,qdio_error,sbalf15);
+ if (error==ERROR_KICK_THAT_PUPPY) {
+ sprintf(dbf_text,"KP%4x%2x",card->irq0,queue);
+ QETH_DBF_TEXT2(0,trace,dbf_text);
+ QETH_DBF_TEXT2(0,qerr,dbf_text);
+ QETH_DBF_TEXT2(1,setup,dbf_text);
+ sprintf(dbf_text,"%2x%2x%2x%2x",bufno,
+ siga_error,qdio_error,sbalf15);
+ QETH_DBF_TEXT2(1,trace,dbf_text);
+ QETH_DBF_TEXT2(1,qerr,dbf_text);
+ PRINT_ERR("Outbound queue x%x on irq x%x (%s); " \
+ "errs: siga: x%x, qdio: x%x, flags15: " \
+ "x%x. The device will be taken down.\n",
+ queue,card->irq0,card->dev_name,
+ siga_error,qdio_error,sbalf15);
+ netif_stop_queue(card->dev);
+ qeth_set_dev_flag_norunning(card);
+ atomic_set(&card->problem,PROBLEM_BAD_SIGA_RESULT);
+ qeth_schedule_recovery(card);
+ } else if (error==ERROR_RETRY) {
+ /* analyze, how many retries we did so far */
+ retries=card->send_retries[queue][bufno];
+
+ sprintf(dbf_text,"Rt%4x%2x",card->irq0,queue);
+ QETH_DBF_TEXT4(0,trace,dbf_text);
+ sprintf(dbf_text,"b%2x:%2x%2x",bufno,
+ sbalf15,retries);
+ QETH_DBF_TEXT4(0,trace,dbf_text);
+
+ if (++retries>SEND_RETRIES_ALLOWED) {
+ error=ERROR_LINK_FAILURE;
+ QETH_DBF_TEXT4(1,trace,"ndegelnd");
+ }
+ /* else error stays RETRY for the switch statemnet */
+ } else if (error==ERROR_LINK_FAILURE) {
+ /* we don't want to log failures resulting from
+ * too many retries */
+ sprintf(dbf_text,"Fail%4x",card->irq0);
+ QETH_DBF_TEXT3(1,trace,dbf_text);
+ QETH_DBF_HEX3(0,misc,buffer,QETH_DBF_MISC_LEN);
+ QETH_DBF_HEX3(0,misc,buffer+QETH_DBF_MISC_LEN,
+ QETH_DBF_MISC_LEN);
+ }
+
+ while ((skb=skb_dequeue(&card->outbound_ringbuffer[queue]->
+ ringbuf_element[bufno].skb_list))) {
+ switch (error) {
+ case ERROR_NONE:
+ atomic_dec(&skb->users);
+ dev_kfree_skb_irq(skb);
+ break;
+ case ERROR_RETRY:
+ QETH_DBF_TEXT3(0,qerr,"RETRY!!!");
+ QETH_DBF_TEXT4(0,trace,"RETRY!!!");
+ /* retry packet async (quickly) ... */
+ atomic_dec(&skb->users);
+ dev_kfree_skb_irq(skb);
+ break;
+ case ERROR_LINK_FAILURE:
+ case ERROR_KICK_THAT_PUPPY:
+ QETH_DBF_TEXT4(0,trace,"endeglnd");
+ dst_link_failure(skb);
+ atomic_dec(&skb->users);
+ dev_kfree_skb_irq(skb);
+ break;
+ }
+ }
+ break;
+ default:
+ PRINT_WARN("oops... wrong send_state on %s. " \
+ "shouldn't happen " \
+ "(line %i). q=%i, bufno=x%x, state=%i\n",
+ card->dev_name,__LINE__,queue,bufno,
+ card->outbound_buffer_send_state[queue][bufno]);
+ sprintf(dbf_text,"UPSf%4x",card->irq0);
+ QETH_DBF_TEXT0(1,trace,dbf_text);
+ QETH_DBF_TEXT0(1,qerr,dbf_text);
+ sprintf(dbf_text,"%2x%2x%4x",queue,bufno,
+ card->outbound_buffer_send_state[queue][bufno]);
+ QETH_DBF_TEXT0(1,trace,dbf_text);
+ QETH_DBF_TEXT0(1,qerr,dbf_text);
+ }
+ card->outbound_buffer_send_state[queue][bufno]=SEND_STATE_INACTIVE;
+ card->send_retries[queue][bufno]=0;
+}
+
+static void qeth_free_all_skbs(qeth_card_t *card)
+{
+ int q,b;
+
+ for (q=0;q<card->no_queues;q++)
+ for (b=0;b<QDIO_MAX_BUFFERS_PER_Q;b++)
+ if (card->outbound_buffer_send_state[q][b]!=
+ SEND_STATE_INACTIVE)
+ qeth_free_buffer(card,q,b,0,0);
+}
+
+static inline void qeth_flush_buffer(qeth_card_t *card,int queue,
+ int under_int)
+{
+#ifdef QETH_DBF_LIKE_HELL
+ char dbf_text[15];
+ sprintf(dbf_text,"flsb%4x",card->irq0);
+ QETH_DBF_TEXT5(0,trace,dbf_text);
+ sprintf(dbf_text,"%2x%2x%2x",queue,under_int,
+ card->outbound_buffer_send_state[queue][
+ card->outbound_first_free_buffer[queue] ]);
+ QETH_DBF_TEXT5(0,trace,dbf_text);
+#endif /* QETH_DBF_LIKE_HELL */
+
+ switch (card->outbound_buffer_send_state[queue][
+ card->outbound_first_free_buffer[queue] ]) {
+ case SEND_STATE_DONT_PACK: break;
+ case SEND_STATE_PACK:
+ qeth_flush_packed_packets(card,queue,under_int);
+ break;
+ default:break;
+ }
+}
+#ifdef QETH_VLAN
+
+void qeth_insert_ipv6_vlan_tag(struct sk_buff *__skb)
+{
+
+ /* Move the mac addresses to the beginning of the new header.
+ * We are using three memcpys instead of one memmove to save
+ * cycles.
+ */
+#define TMP_CPYSIZE 4
+ __u16 *tag;
+ tag = (__u16*)skb_push(__skb, VLAN_HLEN);
+ memcpy(__skb->data,
+ __skb->data+TMP_CPYSIZE,TMP_CPYSIZE);
+ memcpy(__skb->data+TMP_CPYSIZE,
+ __skb->data+(2*TMP_CPYSIZE),TMP_CPYSIZE);
+ memcpy(__skb->data+(2*TMP_CPYSIZE),
+ __skb->data+(3*TMP_CPYSIZE),TMP_CPYSIZE);
+ tag = (__u16*)(__skb->data+(3*TMP_CPYSIZE));
+
+ /*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));
+#undef TMP_CPYSIZE
+}
+#endif
+
+
+
+static void qeth_send_packet_fast(qeth_card_t *card,struct sk_buff *skb,
+ struct net_device *dev,
+ int queue,int version,int multicast)
+{
+ qeth_ringbuffer_element_t *mybuffer;
+ int position;
+ qeth_hdr_t *hdr;
+ char *dataptr;
+ char dbf_text[15];
+ struct sk_buff *nskb;
+
+ position=card->outbound_first_free_buffer[queue];
+
+ card->outbound_buffer_send_state[queue][position]=SEND_STATE_DONT_PACK;
+
+ mybuffer=&card->outbound_ringbuffer[queue]->ringbuf_element[position];
+ if (skb_headroom(skb)<QETH_HEADER_SIZE) {
+ if ((version)&&(!card->realloc_message)) {
+ card->realloc_message=1;
+ PRINT_WARN("%s: not enough headroom in skb. " \
+ "Try increasing the " \
+ "add_hhlen parameter by %i.\n",
+ card->dev_name,
+ QETH_HEADER_SIZE-skb_headroom(skb));
+ }
+ PRINT_STUPID("%s: not enough headroom in skb (missing: %i)\n",
+ card->dev_name,QETH_HEADER_SIZE-skb_headroom(skb));
+ sprintf(dbf_text,"NHRf%4x",card->irq0);
+ QETH_DBF_TEXT3(0,trace,dbf_text);
+ sprintf(dbf_text,"%2x%2x%2x%2x",skb_headroom(skb),
+ version,multicast,queue);
+ QETH_DBF_TEXT3(0,trace,dbf_text);
+ QETH_DBF_HEX3(0,trace,&skb->head,sizeof(void*));
+ QETH_DBF_HEX3(0,trace,&skb->data,sizeof(void*));
+ nskb=skb_realloc_headroom(skb,QETH_HEADER_SIZE);
+ if (!nskb) {
+ PRINT_WARN("%s: could not realloc headroom\n",
+ card->dev_name);
+ sprintf(dbf_text,"CNRf%4x",card->irq0);
+ QETH_DBF_TEXT2(0,trace,dbf_text);
+ dev_kfree_skb_irq(skb);
+ return;
+ }
+ dev_kfree_skb_irq(skb);
+ skb=nskb;
+ }
+#ifdef QETH_VLAN
+ if ( (card->vlangrp != NULL) &&
+ vlan_tx_tag_present(skb) &&
+ (version==6)) {
+ qeth_insert_ipv6_vlan_tag(skb);
+ }
+#endif
+ hdr=(qeth_hdr_t*)(skb_push(skb,QETH_HEADER_SIZE));
+ /* sanity check, the Linux memory allocation scheme should
+ never present us cases like this one (the 32bytes header plus
+ the first 40 bytes of the paket cross a 4k boundary) */
+ dataptr=(char*)hdr;
+ if ( (((unsigned long)dataptr)&(~(PAGE_SIZE-1))) !=
+ ( ((unsigned long)dataptr+QETH_HEADER_SIZE+QETH_IP_HEADER_SIZE)&
+ (~(PAGE_SIZE-1)) ) ) {
+ PRINT_ERR("%s: packet misaligned -- the first %i bytes " \
+ "are not in the same page. Discarding packet!\n",
+ card->dev_name,
+ QETH_HEADER_SIZE+QETH_IP_HEADER_SIZE);
+ PRINT_ERR("head=%p, data=%p\n",skb->head,skb->data);
+ sprintf(dbf_text,"PMAf%4x",card->irq0);
+ QETH_DBF_TEXT1(0,trace,dbf_text);
+ sprintf(dbf_text,"%2x%2x%2x%2x",skb_headroom(skb),
+ version,multicast,queue);
+ QETH_DBF_TEXT1(0,trace,dbf_text);
+ QETH_DBF_HEX1(0,trace,&skb->head,sizeof(void*));
+ QETH_DBF_HEX1(1,trace,&skb->data,sizeof(void*));
+ dev_kfree_skb_irq(skb);
+ return;
+ }
+
+ atomic_inc(&skb->users);
+ skb_queue_tail(&mybuffer->skb_list,skb);
+ qeth_fill_header(hdr,skb,version,multicast);
+ /* we need to write to next_element_to_fill as
+ qeth_flush_packed_packets checks it */
+ card->outbound_ringbuffer[queue]->ringbuf_element[position].
+ next_element_to_fill=
+ qeth_fill_buffer(&card->outbound_ringbuffer[queue]->
+ buffer[position],(char *)hdr,
+ skb->len,0);
+
+#ifdef QETH_PERFORMANCE_STATS
+ card->perf_stats.skbs_sent_dont_pack++;
+#endif /* QETH_PERFORMANCE_STATS */
+
+ qeth_flush_packed_packets(card,queue,0);
+}
+
+/* no checks, if all elements are used, as then we would not be here (at most
+ 127 buffers are enqueued) */
+static void qeth_send_packet_packed(qeth_card_t *card,struct sk_buff *skb,
+ struct net_device *dev,
+ int queue,int version,int multicast)
+{
+ qeth_ringbuffer_element_t *mybuffer;
+ int elements_needed;
+ int element_to_fill;
+ int buffer_no;
+ int length;
+ char *dataptr;
+ qeth_hdr_t *hdr;
+ char dbf_text[15];
+ struct sk_buff *nskb;
+
+ /* sanity check, dev->hard_header_len should prevent this */
+ if (skb_headroom(skb)<QETH_HEADER_SIZE) {
+ if ((version)&&(!card->realloc_message)) {
+ card->realloc_message=1;
+ PRINT_WARN("%s: not enough headroom in skb. " \
+ "Try increasing the " \
+ "add_hhlen parameter by %i.\n",
+ card->dev_name,
+ QETH_HEADER_SIZE-skb_headroom(skb));
+ }
+ PRINT_STUPID("%s: not enough headroom in skb (missing: %i)\n",
+ card->dev_name,QETH_HEADER_SIZE-skb_headroom(skb));
+ sprintf(dbf_text,"NHRp%4x",card->irq0);
+ QETH_DBF_TEXT3(0,trace,dbf_text);
+ sprintf(dbf_text,"%2x%2x%2x%2x",skb_headroom(skb),
+ version,multicast,queue);
+ QETH_DBF_TEXT3(0,trace,dbf_text);
+ QETH_DBF_HEX3(0,trace,&skb->head,sizeof(void*));
+ QETH_DBF_HEX3(0,trace,&skb->data,sizeof(void*));
+ nskb=skb_realloc_headroom(skb,QETH_HEADER_SIZE);
+ if (!nskb) {
+ PRINT_WARN("%s: could not realloc headroom\n",
+ card->dev_name);
+ sprintf(dbf_text,"CNRp%4x",card->irq0);
+ QETH_DBF_TEXT2(0,trace,dbf_text);
+ dev_kfree_skb_irq(skb);
+ return;
+ }
+ dev_kfree_skb_irq(skb);
+ skb=nskb;
+ }
+#ifdef QETH_VLAN
+ if ( (card->vlangrp != NULL) &&
+ vlan_tx_tag_present(skb) &&
+ (version==6)) {
+ qeth_insert_ipv6_vlan_tag(skb);
+ }
+
+#endif
+ hdr=(qeth_hdr_t*)(skb_push(skb,QETH_HEADER_SIZE));
+
+ length=skb->len;
+
+ /* sanity check, the Linux memory allocation scheme should
+ never present us cases like this one (the 32bytes header plus
+ the first 40 bytes of the paket cross a 4k boundary) */
+ dataptr=(char*)hdr;
+ if ( (((unsigned long)dataptr)&(~(PAGE_SIZE-1))) !=
+ ( ((unsigned long)dataptr+QETH_HEADER_SIZE+QETH_IP_HEADER_SIZE)&
+ (~(PAGE_SIZE-1)) ) ) {
+ PRINT_ERR("%s: packet misaligned -- the first %i bytes " \
+ "are not in the same page. Discarding packet!\n",
+ card->dev_name,
+ QETH_HEADER_SIZE+QETH_IP_HEADER_SIZE);
+ sprintf(dbf_text,"PMAp%4x",card->irq0);
+ QETH_DBF_TEXT1(0,trace,dbf_text);
+ sprintf(dbf_text,"%2x%2x%2x%2x",skb_headroom(skb),
+ version,multicast,queue);
+ QETH_DBF_TEXT1(0,trace,dbf_text);
+ QETH_DBF_HEX1(0,trace,&skb->head,sizeof(void*));
+ QETH_DBF_HEX1(1,trace,&skb->data,sizeof(void*));
+ dev_kfree_skb_irq(skb);
+ return;
+ }
+
+ buffer_no=card->outbound_first_free_buffer[queue];
+
+ element_to_fill=card->outbound_ringbuffer[queue]->
+ ringbuf_element[buffer_no].
+ next_element_to_fill;
+
+ elements_needed=1+( ( (((unsigned long)dataptr)&(PAGE_SIZE-1))+
+ length ) >>PAGE_SHIFT);
+ if ( (elements_needed>(QDIO_MAX_ELEMENTS_PER_BUFFER-element_to_fill)) ||
+ ( (elements_needed==
+ (QDIO_MAX_ELEMENTS_PER_BUFFER-element_to_fill)) &&
+ ((element_to_fill>>PAGE_SHIFT)==
+ card->outbound_bytes_in_buffer[queue]) ) ) {
+ qeth_flush_packed_packets(card,queue,0);
+ element_to_fill=0;
+ card->outbound_bytes_in_buffer[queue]=0;
+ buffer_no=(buffer_no+1)&(QDIO_MAX_BUFFERS_PER_Q-1);
+ }
+
+ if (!element_to_fill)
+ card->outbound_buffer_send_state[queue][buffer_no]
+ =SEND_STATE_PACK;
+
+#ifdef QETH_PERFORMANCE_STATS
+ card->perf_stats.skbs_sent_pack++;
+#endif /* QETH_PERFORMANCE_STATS */
+
+ mybuffer=&card->outbound_ringbuffer[queue]->ringbuf_element[buffer_no];
+ atomic_inc(&skb->users);
+ skb_queue_tail(&mybuffer->skb_list,skb);
+ qeth_fill_header(hdr,skb,version,multicast);
+ card->outbound_bytes_in_buffer[queue]+=length+QETH_HEADER_SIZE;
+ card->outbound_ringbuffer[queue]->ringbuf_element[buffer_no].
+ next_element_to_fill=
+ qeth_fill_buffer(&card->outbound_ringbuffer[queue]->
+ buffer[buffer_no],
+ dataptr,length,element_to_fill);
+}
+
+static void qeth_alloc_spare_bufs(void)
+{
+ int i;
+ int dont_alloc_more=0;
+ char dbf_text[15];
+
+ sparebuffer_count=0;
+ for (i=0;i<qeth_sparebufs;i++) {
+ if (!dont_alloc_more) {
+ sparebufs[i].buf=(char*)
+ kmalloc(DEFAULT_BUFFER_SIZE,GFP_KERNEL);
+ if (sparebufs[i].buf)
+ sparebuffer_count++;
+ else
+ dont_alloc_more=1;
+ }
+ atomic_set(&sparebufs[i].status,(dont_alloc_more)?
+ SPAREBUF_UNAVAIL:SPAREBUF_FREE);
+ }
+ sprintf(dbf_text,"alspb%3x",sparebuffer_count);
+ QETH_DBF_TEXT2(0,trace,dbf_text);
+
+ PRINT_INFO("allocated %i spare buffers\n",sparebuffer_count);
+}
+
+static void qeth_free_all_spare_bufs(void)
+{
+ int i;
+
+ QETH_DBF_TEXT2(0,trace,"frealspb");
+
+ for (i=0;i<qeth_sparebufs;i++)
+ if (atomic_read(&sparebufs[i].status)!=SPAREBUF_UNAVAIL) {
+ kfree(sparebufs[i].buf);
+ atomic_set(&sparebufs[i].status,SPAREBUF_UNAVAIL);
+ }
+}
+
+static __inline__ int atomic_return_sub(int i, atomic_t *v)
+{
+ int old_val, new_val;
+ __CS_LOOP(old_val, new_val, v, i, "sr");
+ return old_val;
+}
+
+static int qeth_do_send_packet(qeth_card_t *card,struct sk_buff *skb,
+ struct net_device *dev)
+{
+ int queue,result=0;
+ int multicast,version;
+ char dbf_text[15];
+ char dbf_text2[15]="stchupXX";
+
+ version=QETH_IP_VERSION(skb);
+ multicast=qeth_is_multicast_skb_at_all(skb,version);
+ queue=qeth_get_prioqueue(card,skb,multicast,version);
+
+#ifdef QETH_DBF_LIKE_HELL
+ sprintf(dbf_text,"dsp:%4x",card->irq0);
+ QETH_DBF_TEXT6(0,trace,dbf_text);
+ sprintf(dbf_text,"%c %c%4x",(version==4)?'4':((version==6)?'6':'0'),
+ (multicast)?'m':'_',queue);
+ QETH_DBF_TEXT6(0,trace,dbf_text);
+ sprintf(dbf_text,"%4x%4x",
+ card->outbound_first_free_buffer[queue],
+ atomic_read(&card->outbound_used_buffers[queue]));
+ QETH_DBF_TEXT6(0,trace,dbf_text);
+ if (qeth_sbal_packing_on_card(card->type)) {
+ switch (card->send_state[queue]) {
+ case SEND_STATE_DONT_PACK:
+ QETH_DBF_TEXT6(0,trace,"usngfast");
+ break;
+ case SEND_STATE_PACK:
+ QETH_DBF_TEXT6(0,trace,"usngpack");
+ break;
+ }
+ } else {
+ QETH_DBF_TEXT6(0,trace,"usngfast");
+ }
+#endif /* QETH_DBF_LIKE_HELL */
+
+ if (atomic_read(&card->outbound_used_buffers[queue])
+ >=QDIO_MAX_BUFFERS_PER_Q-1) {
+ sprintf(dbf_text,"cdbs%4x",card->irq0);
+ QETH_DBF_TEXT2(1,trace,dbf_text);
+ netif_stop_queue(dev);
+ return -EBUSY;
+ }
+
+ /* we are not called under int, so we just spin */
+ /* happens around once a second under heavy traffic. takes a little
+ * bit less than 10usec in avg. on a z900 */
+ if (atomic_compare_and_swap(QETH_LOCK_UNLOCKED,QETH_LOCK_NORMAL,
+ &card->outbound_ringbuffer_lock[queue])) {
+ sprintf(dbf_text,"SPIN%4x",card->irq0);
+ QETH_DBF_TEXT2(0,trace,dbf_text);
+ while (atomic_compare_and_swap
+ (QETH_LOCK_UNLOCKED,QETH_LOCK_NORMAL,
+ &card->outbound_ringbuffer_lock[queue]))
+ ;
+ sprintf(dbf_text,"spin%4x",card->irq0);
+ QETH_DBF_TEXT2(0,trace,dbf_text);
+ }
+
+#ifdef QETH_PERFORMANCE_STATS
+ card->perf_stats.skbs_sent++;
+#endif /* QETH_PERFORMANCE_STATS */
+
+ if (qeth_sbal_packing_on_card(card->type)) {
+ switch (card->send_state[queue]) {
+ case SEND_STATE_DONT_PACK:
+ qeth_send_packet_fast(card,skb,dev,queue,
+ version,multicast);
+ if (atomic_read(&card->outbound_used_buffers[queue])
+ >=HIGH_WATERMARK_PACK) {
+ card->send_state[queue]=SEND_STATE_PACK;
+ *((__u16*)(&dbf_text2[6]))=card->irq0;
+ QETH_DBF_HEX3(0,trace,dbf_text2,
+ QETH_DBF_TRACE_LEN);
+#ifdef QETH_PERFORMANCE_STATS
+ card->perf_stats.sc_dp_p++;
+#endif /* QETH_PERFORMANCE_STATS */
+ }
+ break;
+ case SEND_STATE_PACK:
+ qeth_send_packet_packed(card,skb,dev,queue,
+ version,multicast);
+ break;
+ default:
+ result=-EBUSY;
+ sprintf(dbf_text,"UPSs%4x",card->irq0);
+ QETH_DBF_TEXT0(1,trace,dbf_text);
+ PRINT_ALL("oops... shouldn't happen (line %i:%i).\n",
+ __LINE__,card->send_state[queue]);
+ }
+ } else {
+ qeth_send_packet_fast(card,skb,dev,queue,
+ version,multicast);
+ }
+
+again:
+ /* ATOMIC: (NORMAL->UNLOCKED, FLUSH->NORMAL) */
+ if (atomic_dec_return(&card->outbound_ringbuffer_lock[queue])) {
+ qeth_flush_buffer(card,queue,0);
+ card->send_state[queue]=SEND_STATE_DONT_PACK;
+ goto again;
+ }
+
+#ifdef QETH_PERFORMANCE_STATS
+ card->perf_stats.outbound_time+=
+ NOW-card->perf_stats.outbound_start_time;
+ card->perf_stats.outbound_cnt++;
+#endif /* QETH_PERFORMANCE_STATS */
+
+ card->stats->tx_packets++;
+ card->stats->tx_bytes+=skb->len;
+
+ return result;
+}
+
+static int qeth_hard_start_xmit(struct sk_buff *skb,struct net_device *dev)
+{
+ qeth_card_t *card;
+ char dbf_text[15];
+ int result;
+
+ card=(qeth_card_t*)(dev->priv);
+
+ if (skb==NULL)
+ return 0;
+
+#ifdef QETH_DBF_LIKE_HELL
+ QETH_DBF_HEX4(0,data,skb->data,__max(QETH_DBF_DATA_LEN,skb->len));
+#endif /* QETH_DBF_LIKE_HELL */
+
+ netif_stop_queue(dev);
+
+ if (!card) {
+ QETH_DBF_TEXT2(0,trace,"XMNSNOCD");
+ dst_link_failure(skb);
+ dev_kfree_skb_irq(skb);
+ return 0;
+ }
+
+#ifdef QETH_PERFORMANCE_STATS
+ card->perf_stats.outbound_start_time=NOW;
+#endif /* QETH_PERFORMANCE_STATS */
+
+ if (!atomic_read(&card->is_startlaned)) {
+ card->stats->tx_carrier_errors++;
+ sprintf(dbf_text,"XMNS%4x",card->irq0);
+ QETH_DBF_TEXT2(0,trace,dbf_text);
+ dst_link_failure(skb);
+ dev_kfree_skb_irq(skb);
+ return 0;
+ }
+
+ result=qeth_do_send_packet(card,skb,dev);
+
+ if (!result)
+ netif_wake_queue(card->dev);
+
+ return result;
+}
+
+static struct net_device_stats* qeth_get_stats(struct net_device *dev)
+{
+ qeth_card_t *card;
+ char dbf_text[15];
+
+ card=(qeth_card_t*)(dev->priv);
+
+ sprintf(dbf_text,"gtst%4x",card->irq0);
+ QETH_DBF_TEXT3(0,trace,dbf_text);
+
+ return card->stats;
+}
+
+static int qeth_change_mtu(struct net_device *dev,int new_mtu)
+{
+ qeth_card_t *card;
+ char dbf_text[15];
+
+ card=(qeth_card_t*)(dev->priv);
+
+ sprintf(dbf_text,"mtu %4x",card->irq0);
+ QETH_DBF_TEXT2(0,trace,dbf_text);
+ sprintf(dbf_text,"%8x",new_mtu);
+ QETH_DBF_TEXT2(0,trace,dbf_text);
+
+ if (new_mtu<64) return -EINVAL;
+ if (new_mtu>65535) return -EINVAL;
+ if ((!qeth_is_supported(IPA_IP_FRAGMENTATION)) &&
+ (!qeth_mtu_is_valid(card,new_mtu)))
+ return -EINVAL;
+ dev->mtu=new_mtu;
+ return 0;
+}
+
+static void qeth_start_softsetup_thread(qeth_card_t *card)
+{
+ char dbf_text[15];
+ if (!atomic_read(&card->shutdown_phase)) {
+ sprintf(dbf_text,"stss%4x",card->irq0);
+ QETH_DBF_TEXT2(0,trace,dbf_text);
+ up(&card->softsetup_thread_sem);
+ }
+}
+
+static int qeth_sleepon(qeth_card_t *card,int timeout)
+{
+ unsigned long flags;
+ unsigned long start;
+ int retval;
+ char dbf_text[15];
+
+ DECLARE_WAITQUEUE (current_wait_q,current);
+
+ sprintf(dbf_text,"slpn%4x",card->irq0);
+ QETH_DBF_TEXT5(0,trace,dbf_text);
+ sprintf(dbf_text,"%08x",timeout);
+ QETH_DBF_TEXT5(0,trace,dbf_text);
+
+ add_wait_queue(&card->wait_q,¤t_wait_q);
+ atomic_set(&card->wait_q_active,1);
+ start=qeth_get_millis();
+ for (;;) {
+ set_task_state(current,TASK_INTERRUPTIBLE);
+ if (atomic_read(&card->data_has_arrived)) {
+ atomic_set(&card->data_has_arrived,0);
+ retval=0;
+ goto out;
+ }
+ if (qeth_get_millis()-start>timeout) {
+ retval=-ETIME;
+ goto out;
+ }
+ schedule_timeout(((start+timeout-qeth_get_millis())>>10)*HZ);
+ }
+ out:
+ spin_lock_irqsave(&card->wait_q_lock,flags);
+ atomic_set(&card->wait_q_active,0);
+ spin_unlock_irqrestore(&card->wait_q_lock,flags);
+
+ /* we've got to check once again to close the window */
+ if (atomic_read(&card->data_has_arrived)) {
+ atomic_set(&card->data_has_arrived,0);
+ retval=0;
+ }
+
+ set_task_state(current,TASK_RUNNING);
+ remove_wait_queue(&card->wait_q,¤t_wait_q);
+
+ return retval;
+}
+
+static void qeth_wakeup_ioctl(qeth_card_t *card) {
+ char dbf_text[15];
+
+ sprintf(dbf_text,"wkup%4x",card->irq0);
+ QETH_DBF_TEXT5(0,trace,dbf_text);
+
+ atomic_set(&card->ioctl_data_has_arrived,1);
+ spin_lock(&card->ioctl_wait_q_lock);
+ if (atomic_read(&card->ioctl_wait_q_active)) {
+ wake_up(&card->ioctl_wait_q);
+ }
+ spin_unlock(&card->ioctl_wait_q_lock);
+}
+
+static int qeth_sleepon_ioctl(qeth_card_t *card,int timeout)
+{
+ unsigned long flags;
+ unsigned long start;
+ int retval;
+ char dbf_text[15];
+
+ DECLARE_WAITQUEUE (current_wait_q,current);
+
+ sprintf(dbf_text,"ioctlslpn%4x",card->irq0);
+ QETH_DBF_TEXT5(0,trace,dbf_text);
+ sprintf(dbf_text,"%08x",timeout);
+ QETH_DBF_TEXT5(0,trace,dbf_text);
+
+ save_flags(flags);
+ add_wait_queue(&card->ioctl_wait_q,¤t_wait_q);
+ atomic_set(&card->ioctl_wait_q_active,1);
+ start=qeth_get_millis();
+ for (;;) {
+ set_task_state(current,TASK_INTERRUPTIBLE);
+ if (atomic_read(&card->ioctl_data_has_arrived)) {
+ atomic_set(&card->ioctl_data_has_arrived,0);
+ retval=0;
+ goto out;
+ }
+ if (qeth_get_millis()-start>timeout) {
+ retval=-ETIME;
+ goto out;
+ }
+ schedule_timeout(((start+timeout-qeth_get_millis())>>10)*HZ);
+ }
+ out:
+ spin_lock_irqsave(&card->ioctl_wait_q_lock,flags);
+ atomic_set(&card->ioctl_wait_q_active,0);
+ spin_unlock_irqrestore(&card->ioctl_wait_q_lock,flags);
+
+ /* we've got to check once again to close the window */
+ if (atomic_read(&card->ioctl_data_has_arrived)) {
+ atomic_set(&card->ioctl_data_has_arrived,0);
+ retval=0;
+ }
+
+ set_task_state(current,TASK_RUNNING);
+ remove_wait_queue(&card->ioctl_wait_q,¤t_wait_q);
+
+ return retval;
+}
+
+static void qeth_wakeup_procfile(void)
+{
+ QETH_DBF_TEXT5(0,trace,"procwkup");
+ if (atomic_read(&qeth_procfile_ioctl_sem.count)<
+ PROCFILE_SLEEP_SEM_MAX_VALUE)
+ up(&qeth_procfile_ioctl_sem);
+}
+
+
+static int qeth_sleepon_procfile(void)
+{
+ QETH_DBF_TEXT5(0,trace,"procslp");
+ if (down_interruptible(&qeth_procfile_ioctl_sem)) {
+ up(&qeth_procfile_ioctl_sem);
+ return -ERESTARTSYS;
+ }
+ return 0;
+}
+
+static char* qeth_send_control_data(qeth_card_t *card,unsigned char *buffer,
+ int len,unsigned long intparam)
+{
+ unsigned long flags;
+ int result,result2;
+ char dbf_text[15];
+ unsigned char *rec_buf;
+ int setip=(intparam&IPA_SETIP_FLAG)?1:0;
+
+again:
+ if (atomic_read(&card->shutdown_phase)==
+ QETH_REMOVE_CARD_QUICK) return NULL;
+ if (atomic_read(&card->escape_softsetup))
+ return NULL;
+
+ /* we lock very early to synchronize access to seqnos */
+ if (atomic_swap(&card->write_busy,1)) {
+ qeth_wait_nonbusy(QETH_IDLE_WAIT_TIME);
+ sprintf(dbf_text,"LSCD%4x",card->irq0);
+ QETH_DBF_TEXT2(0,trace,dbf_text);
+ goto again;
+ }
+ memcpy(card->dma_stuff->sendbuf,card->send_buf,QETH_BUFSIZE);
+
+ memcpy(QETH_TRANSPORT_HEADER_SEQ_NO(buffer),
+ &card->seqno.trans_hdr,QETH_SEQ_NO_LENGTH);
+ card->seqno.trans_hdr++;
+
+ memcpy(QETH_PDU_HEADER_SEQ_NO(buffer),
+ &card->seqno.pdu_hdr,QETH_SEQ_NO_LENGTH);
+ card->seqno.pdu_hdr++;
+ memcpy(QETH_PDU_HEADER_ACK_SEQ_NO(buffer),
+ &card->seqno.pdu_hdr_ack,QETH_SEQ_NO_LENGTH);
+
+ /* there is noone doing this except sleep and this function */
+ atomic_set(&card->data_has_arrived,0);
+
+ memcpy(&card->dma_stuff->write_ccw,WRITE_CCW,sizeof(ccw1_t));
+ card->dma_stuff->write_ccw.count=len;
+ card->dma_stuff->write_ccw.cda=
+ QETH_GET_ADDR(card->dma_stuff->sendbuf);
+
+ sprintf(dbf_text,"scdw%4x",card->irq0);
+ QETH_DBF_TEXT2(0,trace,dbf_text);
+ sprintf(dbf_text,"%8x",len);
+ QETH_DBF_TEXT4(0,trace,dbf_text);
+ QETH_DBF_HEX4(0,trace,&intparam,QETH_DBF_TRACE_LEN);
+ QETH_DBF_HEX2(0,control,buffer,QETH_DBF_CONTROL_LEN);
+
+ s390irq_spin_lock_irqsave(card->irq1,flags);
+ result=do_IO(card->irq1,&card->dma_stuff->write_ccw,intparam,0,0);
+ if (result) {
+ qeth_delay_millis(QETH_WAIT_BEFORE_2ND_DOIO);
+ result2=do_IO(card->irq1,&card->dma_stuff->write_ccw,
+ intparam,0,0);
+ if (result2!=-ENODEV)
+ PRINT_WARN("qeth_send_control_data: do_IO " \
+ "returned %i, next try returns %i\n",
+ result,result2);
+ result=result2;
+ }
+ s390irq_spin_unlock_irqrestore(card->irq1,flags);
+
+ if (result) {
+ QETH_DBF_TEXT2(0,trace,"scd:doio");
+ sprintf(dbf_text,"%4x",(__s16)result);
+ QETH_DBF_TEXT2(0,trace,dbf_text);
+ return NULL;
+ }
+
+ if (intparam==IPA_IOCTL_STATE) {
+ if (qeth_sleepon_ioctl(card,QETH_IPA_TIMEOUT)) {
+ QETH_DBF_TEXT2(0,trace,"scd:ioctime");
+ /* re-enable qeth_send_control_data again */
+ atomic_set(&card->write_busy,0);
+ return NULL;
+ }
+ rec_buf=card->ipa_buf;
+ sprintf(dbf_text,"scro%4x",card->irq0);
+ } else {
+ if (qeth_sleepon(card,(setip)?QETH_IPA_TIMEOUT:
+ QETH_MPC_TIMEOUT)) {
+ QETH_DBF_TEXT2(0,trace,"scd:time");
+ /* re-enable qeth_send_control_data again */
+ atomic_set(&card->write_busy,0);
+ return NULL;
+ }
+ rec_buf=card->ipa_buf;
+ sprintf(dbf_text,"scri%4x",card->irq0);
+ }
+ QETH_DBF_TEXT2(0,trace,dbf_text);
+ QETH_DBF_HEX2(0,control,rec_buf,QETH_DBF_CONTROL_LEN);
+
+ memcpy(&card->seqno.pdu_hdr_ack,
+ QETH_PDU_HEADER_SEQ_NO(rec_buf),
+ QETH_SEQ_NO_LENGTH);
+
+ return rec_buf;
+}
+
+static int qeth_send_ipa_cmd(qeth_card_t *card,ipa_cmd_t *cmd,int update_cmd,
+ int ipatype)
+{
+ unsigned char *buffer;
+ ipa_cmd_t *reply;
+ int ipa_cmd;
+ int result;
+
+ /* don't muck around with ipv6 if there's no use to do so */
+ if ( (cmd->prot_version==6) &&
+ (!qeth_is_supported(IPA_IPv6)) ) return 0;
+
+ ipa_cmd=cmd->command;
+
+ memcpy(card->send_buf,IPA_PDU_HEADER,
+ IPA_PDU_HEADER_SIZE);
+
+ memcpy(QETH_IPA_CMD_DEST_ADDR(card->send_buf),
+ &card->token.ulp_connection_r,QETH_MPC_TOKEN_LENGTH);
+
+ memcpy(card->send_buf+IPA_PDU_HEADER_SIZE,
+ cmd,sizeof(ipa_cmd_t));
+
+ buffer=qeth_send_control_data(card,card->send_buf,
+ IPA_PDU_HEADER_SIZE+sizeof(ipa_cmd_t),
+ ipatype);
+
+ if (!buffer) {
+ if (atomic_read(&card->escape_softsetup)) result=0;
+ else result=-1;
+ } else {
+ reply=(ipa_cmd_t*)PDU_ENCAPSULATION(buffer);
+ if ((update_cmd)&&(reply)) memcpy(cmd,reply,sizeof(ipa_cmd_t));
+ result=reply->return_code;
+
+ if ((ipa_cmd==IPA_CMD_SETASSPARMS)&&(result==0)) {
+ result=reply->data.setassparms.return_code;
+ }
+ if ((ipa_cmd==IPA_CMD_SETADAPTERPARMS)&&(result==0)) {
+ result=reply->data.setadapterparms.return_code;
+ }
+ }
+ return result;
+}
+
+static void qeth_fill_ipa_cmd(qeth_card_t *card,ipa_cmd_t *cmd,
+ __u8 command,int ip_vers)
+{
+ memset(cmd,0,sizeof(ipa_cmd_t));
+ cmd->command=command;
+ cmd->initiator=INITIATOR_HOST;
+ cmd->seq_no=card->seqno.ipa++;
+ cmd->adapter_type=qeth_get_adapter_type_for_ipa(card->link_type);
+ cmd->rel_adapter_no=(__u8)card->options.portno;
+ cmd->prim_version_no=1;
+ cmd->param_count=1;
+ cmd->prot_version=ip_vers;
+ cmd->ipa_supported=0;
+ cmd->ipa_enabled=0;
+}
+
+static int qeth_send_startstoplan(qeth_card_t *card,__u8 ipacmd,__u16 ip_vers)
+{
+ ipa_cmd_t cmd;
+ int result;
+
+ qeth_fill_ipa_cmd(card,&cmd,ipacmd,0);
+ cmd.param_count=0;
+ cmd.prot_version=ip_vers;
+ cmd.ipa_supported=0;
+ cmd.ipa_enabled=0;
+
+ result=qeth_send_ipa_cmd(card,&cmd,0,IPA_CMD_STATE);
+ return result;
+}
+
+static int qeth_send_startlan(qeth_card_t *card,__u16 ip_vers)
+{
+ int result;
+ char dbf_text[15];
+
+ sprintf(dbf_text,"stln%4x",card->irq0);
+ QETH_DBF_TEXT4(0,trace,dbf_text);
+
+ result=qeth_send_startstoplan(card,IPA_CMD_STARTLAN,ip_vers);
+ if (!result) atomic_set(&card->is_startlaned,1);
+
+ if (result) {
+ QETH_DBF_TEXT2(0,trace,"STRTLNFL");
+ sprintf(dbf_text,"%4x%4x",card->irq0,result);
+ QETH_DBF_TEXT2(0,trace,dbf_text);
+ }
+
+ return result;
+}
+
+static int qeth_send_stoplan(qeth_card_t *card)
+{
+#ifdef QETH_SEND_STOPLAN_ON_SHUTDOWN
+ int result;
+ char dbf_text[15];
+
+ atomic_set(&card->is_startlaned,0);
+
+ sprintf(dbf_text,"spln%4x",card->irq0);
+ QETH_DBF_TEXT4(0,trace,dbf_text);
+
+ result=qeth_send_startstoplan(card,IPA_CMD_STOPLAN,4);
+
+ if (result) {
+ QETH_DBF_TEXT2(0,trace,"STPLNFLD");
+ sprintf(dbf_text,"%4x%4x",card->irq0,result);
+ QETH_DBF_TEXT2(0,trace,dbf_text);
+ }
+
+ return result;
+#else /* QETH_SEND_STOPLAN_ON_SHUTDOWN */
+ return 0;
+#endif /* QETH_SEND_STOPLAN_ON_SHUTDOWN */
+}
+
+static int qeth_send_qipassist(qeth_card_t *card,short ip_vers)
+{
+ ipa_cmd_t cmd;
+ int result;
+
+ qeth_fill_ipa_cmd(card,&cmd,IPA_CMD_QIPASSIST,ip_vers);
+
+ result=qeth_send_ipa_cmd(card,&cmd,1,IPA_CMD_STATE);
+
+ if (!result) {
+ if (ip_vers==4) {
+ card->ipa_supported=cmd.ipa_supported;
+ card->ipa_enabled=cmd.ipa_enabled;
+ } else {
+ card->ipa6_supported=cmd.ipa_supported;
+ card->ipa6_enabled=cmd.ipa_enabled;
+ }
+ }
+
+ return result;
+}
+
+static int qeth_send_ipa_arpcmd(qeth_card_t *card,arp_cmd_t *cmd,
+ int update_cmd,int ipatype,__u32 req_size)
+{
+ unsigned char *buffer;
+ int ipa_cmd;
+ int result;
+ __u16 s1,s2;
+
+ /* don't muck around with ipv6 if there's no use to do so */
+ if ( (cmd->prot_version==6) &&
+ (!qeth_is_supported(IPA_IPv6)) ) return 0;
+ result = 0;
+ ipa_cmd=cmd->command;
+
+ memcpy(card->send_buf,IPA_PDU_HEADER,
+ IPA_PDU_HEADER_SIZE);
+ memcpy(QETH_IPA_CMD_DEST_ADDR(card->send_buf),
+ &card->token.ulp_connection_r,QETH_MPC_TOKEN_LENGTH);
+ memcpy(card->send_buf+IPA_PDU_HEADER_SIZE,
+ cmd,sizeof(arp_cmd_t));
+
+ if (req_size) {
+ /* adjust sizes for big requests */
+ s1=(__u32)IPA_PDU_HEADER_SIZE+SNMP_BASE_CMDLENGTH+req_size;
+ s2=(__u32)SNMP_BASE_CMDLENGTH+req_size;
+ memcpy(QETH_IPA_PDU_LEN_TOTAL(card->send_buf),&s1,2);
+ memcpy(QETH_IPA_PDU_LEN_PDU1(card->send_buf),&s2,2);
+ memcpy(QETH_IPA_PDU_LEN_PDU2(card->send_buf),&s2,2);
+ memcpy(QETH_IPA_PDU_LEN_PDU3(card->send_buf),&s2,2);
+ }
+
+ buffer=qeth_send_control_data(card,card->send_buf,
+ IPA_PDU_HEADER_SIZE+sizeof(arp_cmd_t),
+ ipatype);
+ if (!buffer)
+ result = -ENODATA;
+ else
+ result = card->ioctl_returncode;
+ return result;
+}
+
+static int qeth_ioctl_handle_snmp_data(qeth_card_t *card,arp_cmd_t *reply)
+{
+ __u16 data_len;
+
+#define SNMP_HEADER_SIZE_WITH_TOKEN 36
+
+ data_len = *((__u16*)QETH_IPA_PDU_LEN_PDU1(card->dma_stuff->recbuf));
+
+ if (reply->data.setadapterparms.frame_seq_no == 1) {
+ data_len = data_len -
+ (__u16)((char*)reply->data.setadapterparms.
+ data.snmp_subcommand.
+ snmp_data - (char*)reply);
+ } else {
+ data_len = data_len -
+ (__u16)((char*)&reply->data.setadapterparms.data.
+ snmp_subcommand.
+ snmp_request - (char*)reply);
+ }
+ if (reply->data.setadapterparms.frame_seq_no == 1) {
+ if (card->ioctl_buffersize <= (SNMP_HEADER_SIZE_WITH_TOKEN +
+ reply->data.setadapterparms.frames_used_total *
+ ARP_DATA_SIZE)) {
+ card->ioctl_returncode = ARP_RETURNCODE_ERROR;
+ reply->data.setadapterparms.data.
+ snmp_subcommand.snmp_returncode = -ENOMEM;
+ } else {
+ card->ioctl_returncode = ARP_RETURNCODE_SUCCESS;
+ card->number_of_entries = 0;
+ memcpy(((char *)card->ioctl_data_buffer),
+ reply->data.setadapterparms.snmp_token,
+ SNMP_HEADER_SIZE_WITH_TOKEN);
+ card->ioctl_buffer_pointer = card->ioctl_data_buffer+
+ SNMP_HEADER_SIZE_WITH_TOKEN;
+ }
+ }
+
+ if (card->ioctl_returncode != ARP_RETURNCODE_ERROR &&
+ reply->data.setadapterparms.frame_seq_no <=
+ reply->data.setadapterparms.frames_used_total) {
+ if (reply->data.setadapterparms.return_code==
+ IPA_REPLY_SUCCESS) {
+ if (reply->data.setadapterparms.frame_seq_no == 1) {
+ memcpy(card->ioctl_buffer_pointer,
+ reply->data.setadapterparms.data.
+ snmp_subcommand.snmp_data,data_len);
+ } else {
+ memcpy(card->ioctl_buffer_pointer,
+ (char*)&reply->data.setadapterparms.
+ data.snmp_subcommand.
+ snmp_request,data_len);
+ }
+ card->ioctl_buffer_pointer =
+ card->ioctl_buffer_pointer + data_len;
+ card->ioctl_returncode = ARP_RETURNCODE_SUCCESS;
+
+ if (reply->data.setadapterparms.frame_seq_no ==
+ reply->data.setadapterparms.frames_used_total) {
+ card->ioctl_returncode =
+ ARP_RETURNCODE_LASTREPLY;
+ }
+ } else {
+ card->ioctl_returncode = ARP_RETURNCODE_ERROR;
+ memset(card->ioctl_data_buffer,0,
+ card->ioctl_buffersize);
+ reply->data.setadapterparms.data.
+ snmp_subcommand.snmp_returncode =
+ reply->data.setadapterparms.return_code;
+ }
+ }
+#undef SNMP_HEADER_SIZE_WITH_TOKEN
+
+ return card->ioctl_returncode;
+}
+
+static int qeth_ioctl_handle_arp_data(qeth_card_t *card, arp_cmd_t *reply)
+{
+ if (reply->data.setassparms.seq_no == 1) {
+ if (card->ioctl_buffersize <=
+ (sizeof(__u16) + sizeof(int) + reply->data.
+ setassparms.number_of_replies * ARP_DATA_SIZE)) {
+ card->ioctl_returncode = ARP_RETURNCODE_ERROR;
+ } else {
+ card->ioctl_returncode = ARP_RETURNCODE_SUCCESS;
+ card->number_of_entries = 0;
+ card->ioctl_buffer_pointer = card->ioctl_data_buffer+
+ sizeof(__u16) + sizeof(int);
+ }
+ }
+
+ if (card->ioctl_returncode != ARP_RETURNCODE_ERROR &&
+ reply->data.setassparms.seq_no <=
+ reply->data.setassparms.number_of_replies) {
+
+ if (reply->data.setassparms.return_code==IPA_REPLY_SUCCESS) {
+
+ card->number_of_entries = card->number_of_entries +
+ reply->data.setassparms.
+ data.queryarp_data.
+ number_of_entries;
+ memcpy(card->ioctl_buffer_pointer,
+ reply->data.setassparms.data.queryarp_data.
+ arp_data,ARP_DATA_SIZE);
+ card->ioctl_buffer_pointer = card->
+ ioctl_buffer_pointer + ARP_DATA_SIZE;
+ card->ioctl_returncode = ARP_RETURNCODE_SUCCESS;
+ if (reply->data.setassparms.seq_no ==
+ reply->data.setassparms.number_of_replies) {
+ memcpy(card->ioctl_data_buffer,
+ &reply->data.setassparms.data.
+ queryarp_data.osa_setbitmask,
+ sizeof(__u16));
+ card->ioctl_returncode=
+ ARP_RETURNCODE_LASTREPLY;
+ }
+ } else {
+ card->ioctl_returncode = ARP_RETURNCODE_ERROR;
+ memset(card->ioctl_data_buffer,0,
+ card->ioctl_buffersize);
+ }
+ }
+ return card->ioctl_returncode;
+}
+
+static int qeth_look_for_arp_data(qeth_card_t *card)
+{
+ arp_cmd_t *reply;
+ int result;
+
+
+ reply=(arp_cmd_t*)PDU_ENCAPSULATION(card->dma_stuff->recbuf);
+
+ if ( (reply->command==IPA_CMD_SETASSPARMS) &&
+ (reply->data.setassparms.assist_no==IPA_ARP_PROCESSING) &&
+ (reply->data.setassparms.command_code==
+ IPA_CMD_ASS_ARP_FLUSH_CACHE) ) {
+ result=ARP_FLUSH;
+ } else if ( (reply->command == IPA_CMD_SETASSPARMS) &&
+ (reply->data.setassparms.assist_no == IPA_ARP_PROCESSING) &&
+ (reply->data.setassparms.command_code ==
+ IPA_CMD_ASS_ARP_QUERY_INFO) &&
+ (card->ioctl_returncode == ARP_RETURNCODE_SUCCESS)) {
+ result = qeth_ioctl_handle_arp_data(card,reply);
+ } else if ( (reply->command == IPA_CMD_SETADAPTERPARMS) &&
+ (reply->data.setadapterparms.command_code ==
+ IPA_SETADP_SET_SNMP_CONTROL) &&
+ (card->ioctl_returncode == ARP_RETURNCODE_SUCCESS) ){
+ result = qeth_ioctl_handle_snmp_data(card,reply);
+ } else
+ result = ARP_RETURNCODE_NOARPDATA;
+
+ return result;
+}
+
+
+
+static int qeth_queryarp(qeth_card_t *card,struct ifreq *req,int version,
+ __u32 assist_no, __u16 command_code,char *c_data,
+ __u16 len)
+{
+ int data_size;
+ arp_cmd_t *cmd;
+ int result;
+
+
+ cmd = (arp_cmd_t *) kmalloc(sizeof(arp_cmd_t),GFP_KERNEL);
+ if (!cmd) {
+ return IPA_REPLY_FAILED;
+ }
+
+ memcpy(&data_size,c_data,sizeof(int));
+
+ qeth_fill_ipa_cmd(card,(ipa_cmd_t*)cmd,IPA_CMD_SETASSPARMS,version);
+
+ cmd->data.setassparms.assist_no=assist_no;
+ cmd->data.setassparms.length=8+len;
+ cmd->data.setassparms.command_code=command_code;
+ cmd->data.setassparms.return_code=0;
+ cmd->data.setassparms.seq_no=0;
+
+ card->ioctl_buffersize = data_size;
+ card->ioctl_data_buffer = (char *) vmalloc(data_size);
+ if (!card->ioctl_data_buffer) {
+ kfree(cmd);
+ return IPA_REPLY_FAILED;
+ }
+
+ card->ioctl_returncode = ARP_RETURNCODE_SUCCESS;
+
+ result=qeth_send_ipa_arpcmd(card,cmd,1,IPA_IOCTL_STATE,0);
+
+ if ((result == ARP_RETURNCODE_ERROR) ||
+ (result == -ENODATA)) {
+ result = IPA_REPLY_FAILED;
+ }
+ else {
+ result = IPA_REPLY_SUCCESS;
+ memcpy(((char *)(card->ioctl_data_buffer)) + sizeof(__u16),
+ &(card->number_of_entries),sizeof(int));
+ copy_to_user(req->ifr_ifru.ifru_data,
+ card->ioctl_data_buffer,data_size);
+ }
+ card->ioctl_buffer_pointer = NULL;
+ vfree(card->ioctl_data_buffer);
+ kfree(cmd);
+ card->number_of_entries = 0;
+ card->ioctl_buffersize = 0;
+
+ return result;
+}
+
+static int snmp_set_setadapterparms_command(qeth_card_t *card,
+ arp_cmd_t *cmd,struct ifreq *req,
+ char *data,__u16 len,
+ __u16 command_code,int req_size)
+{
+ __u32 data_size;
+
+ memcpy(&data_size,data,sizeof(__u32));
+
+ card->ioctl_buffersize = data_size;
+ card->ioctl_data_buffer = (char *) vmalloc(data_size);
+ if (!card->ioctl_data_buffer) {
+ return -ENOMEM;
+ }
+ card->ioctl_returncode = ARP_RETURNCODE_SUCCESS;
+
+ memcpy(cmd->data.setadapterparms.snmp_token,
+ data+SNMP_REQUEST_DATA_OFFSET,req_size);
+
+ cmd->data.setadapterparms.cmdlength=SNMP_SETADP_CMDLENGTH+req_size;
+ cmd->data.setadapterparms.command_code = command_code;
+ cmd->data.setadapterparms.frames_used_total=1;
+ cmd->data.setadapterparms.frame_seq_no=1;
+
+ return 0;
+}
+
+static int qeth_send_snmp_control(qeth_card_t *card,struct ifreq *req,
+ __u32 command,__u16 command_code,
+ char *c_data,__u16 len)
+{
+ arp_cmd_t *cmd;
+ __u32 result,req_size;
+
+ cmd = (arp_cmd_t *) kmalloc(sizeof(arp_cmd_t),GFP_KERNEL);
+ if (!cmd) {
+ return IPA_REPLY_FAILED;
+ }
+
+ qeth_fill_ipa_cmd(card,(ipa_cmd_t*)cmd,command,4);
+
+ memcpy(&req_size,((char*)c_data)+sizeof(__u32),sizeof(__u32));
+
+ if (snmp_set_setadapterparms_command(card,cmd,req,c_data,
+ len,command_code,req_size))
+ {
+ kfree(cmd);
+ return IPA_REPLY_FAILED;
+ }
+
+ result=qeth_send_ipa_arpcmd(card,cmd,1,IPA_IOCTL_STATE,req_size);
+
+ if (result == -ENODATA) {
+ result = IPA_REPLY_FAILED;
+ goto snmp_out;
+ }
+ if (result == ARP_RETURNCODE_ERROR ) {
+ copy_to_user(req->ifr_ifru.ifru_data+SNMP_REQUEST_DATA_OFFSET,
+ card->ioctl_data_buffer,card->ioctl_buffersize);
+ result = IPA_REPLY_FAILED;
+ }
+ else {
+ copy_to_user(req->ifr_ifru.ifru_data+SNMP_REQUEST_DATA_OFFSET,
+ card->ioctl_data_buffer,card->ioctl_buffersize);
+ result = IPA_REPLY_SUCCESS;
+ }
+snmp_out:
+ card->number_of_entries = 0;
+ card->ioctl_buffersize = 0;
+ card->ioctl_buffer_pointer = NULL;
+ vfree(card->ioctl_data_buffer);
+ kfree(cmd);
+
+ return result;
+}
+
+static int qeth_send_setassparms(qeth_card_t *card,int version,
+ __u32 assist_no,__u16 command_code,
+ long data,__u16 len)
+{
+ ipa_cmd_t cmd;
+ int result;
+
+ qeth_fill_ipa_cmd(card,&cmd,IPA_CMD_SETASSPARMS,version);
+
+ cmd.data.setassparms.assist_no=assist_no;
+ cmd.data.setassparms.length=8+len;
+ cmd.data.setassparms.command_code=command_code;
+ cmd.data.setassparms.return_code=0;
+ cmd.data.setassparms.seq_no=0;
+
+ if (len<=sizeof(__u32))
+ cmd.data.setassparms.data.flags_32bit=(__u32)data;
+ else if (len>sizeof(__u32))
+ memcpy(&cmd.data.setassparms.data,(void*)data,
+ /* limit here to a page or so */
+ qeth_min(len,PAGE_SIZE));
+ if (command_code != IPA_CMD_ASS_START) {
+ result=qeth_send_ipa_cmd(card,&cmd,0,
+ ((assist_no==IPA_ARP_PROCESSING)&&
+ (command_code!=IPA_CMD_ASS_ARP_FLUSH_CACHE))?
+ IPA_IOCTL_STATE:IPA_CMD_STATE);
+
+ } else
+ result=qeth_send_ipa_cmd(card,&cmd,0,IPA_CMD_STATE);
+
+ return result;
+}
+
+static int qeth_send_setadapterparms_query(qeth_card_t *card)
+{
+ ipa_cmd_t cmd;
+ int result;
+
+ qeth_fill_ipa_cmd(card,&cmd,IPA_CMD_SETADAPTERPARMS,
+ IPA_SETADAPTERPARMS_IP_VERSION);
+ cmd.data.setadapterparms.cmdlength=sizeof(struct ipa_setadp_cmd);
+ cmd.data.setadapterparms.command_code=
+ IPA_SETADP_QUERY_COMMANDS_SUPPORTED;
+ cmd.data.setadapterparms.frames_used_total=1;
+ cmd.data.setadapterparms.frame_seq_no=1;
+ result=qeth_send_ipa_cmd(card,&cmd,1,IPA_CMD_STATE);
+
+ if (cmd.data.setadapterparms.data.query_cmds_supp.lan_type&0x7f)
+ card->link_type=cmd.data.setadapterparms.data.
+ query_cmds_supp.lan_type;
+
+ card->adp_supported=
+ cmd.data.setadapterparms.data.query_cmds_supp.supported_cmds;
+
+ return result;
+}
+
+static int qeth_send_setadapterparms_mode(qeth_card_t *card,__u32 command,
+ __u32 mode)
+{
+ ipa_cmd_t cmd;
+ int result;
+
+ qeth_fill_ipa_cmd(card,&cmd,IPA_CMD_SETADAPTERPARMS,
+ IPA_SETADAPTERPARMS_IP_VERSION);
+ cmd.data.setadapterparms.cmdlength=sizeof(struct ipa_setadp_cmd);
+ cmd.data.setadapterparms.command_code=command;
+ cmd.data.setadapterparms.frames_used_total=1;
+ cmd.data.setadapterparms.frame_seq_no=1;
+ cmd.data.setadapterparms.data.mode=mode;
+ result=qeth_send_ipa_cmd(card,&cmd,0,IPA_CMD_STATE);
+
+ return result;
+}
+
+static int qeth_send_setadapterparms_change_addr(qeth_card_t *card,
+ __u32 command,
+ __u32 subcmd,__u8 *mac_addr,
+ int addr_len)
+{
+ ipa_cmd_t cmd;
+ int result;
+
+ qeth_fill_ipa_cmd(card,&cmd,IPA_CMD_SETADAPTERPARMS,
+ IPA_SETADAPTERPARMS_IP_VERSION);
+ cmd.data.setadapterparms.cmdlength=sizeof(struct ipa_setadp_cmd);
+ cmd.data.setadapterparms.command_code=command;
+ cmd.data.setadapterparms.frames_used_total=1;
+ cmd.data.setadapterparms.frame_seq_no=1;
+ cmd.data.setadapterparms.data.change_addr.cmd=subcmd;
+ cmd.data.setadapterparms.data.change_addr.addr_size=addr_len;
+ memcpy(&cmd.data.setadapterparms.data.change_addr.addr,
+ mac_addr,addr_len);
+
+ result=qeth_send_ipa_cmd(card,&cmd,1,IPA_CMD_STATE);
+
+ memcpy(mac_addr,&cmd.data.setadapterparms.data.change_addr.addr,
+ addr_len);
+
+ return result;
+}
+
+static int qeth_send_setassparms_simple_with_data(qeth_card_t *card,
+ __u32 assist_no,
+ __u16 command_code,
+ long data)
+{
+ return qeth_send_setassparms(card,4,assist_no,command_code,data,4);
+}
+
+static int qeth_send_setassparms_simple_without_data(qeth_card_t *card,
+ __u32 assist_no,
+ __u16 command_code)
+{
+ return qeth_send_setassparms(card,4,assist_no,command_code,0,0);
+}
+
+static int qeth_send_setassparms_simple_without_data6(qeth_card_t *card,
+ __u32 assist_no,
+ __u16 command_code)
+{
+ return qeth_send_setassparms(card,6,assist_no,command_code,0,0);
+}
+
+static int qeth_send_setdelip(qeth_card_t *card,__u8 *ip,__u8 *netmask,
+ int ipacmd,short ip_vers,unsigned int flags)
+{
+ ipa_cmd_t cmd;
+ int ip_len=(ip_vers==6)?16:4;
+
+ qeth_fill_ipa_cmd(card,&cmd,ipacmd,ip_vers);
+ if (ip_vers==6) {
+ memcpy(&cmd.data.setdelip6.ip,ip,ip_len);
+ memcpy(&cmd.data.setdelip6.netmask,netmask,ip_len);
+ cmd.data.setdelip6.flags=flags;
+ } else {
+ memcpy(&cmd.data.setdelip4.ip,ip,ip_len);
+ memcpy(&cmd.data.setdelip4.netmask,netmask,ip_len);
+ cmd.data.setdelip4.flags=flags;
+ }
+
+ return qeth_send_ipa_cmd(card,&cmd,0,IPA_CMD_STATE|
+ ((ipacmd==IPA_CMD_SETIP)?IPA_SETIP_FLAG:0));
+}
+
+static int qeth_send_setdelipm(qeth_card_t *card,__u8 *ip,__u8 *mac,
+ int ipacmd,short ip_vers)
+{
+ ipa_cmd_t cmd;
+ int ip_len=(ip_vers==6)?16:4;
+
+ qeth_fill_ipa_cmd(card,&cmd,ipacmd,ip_vers);
+ memcpy(&cmd.data.setdelipm.mac,mac,6);
+ if (ip_vers==6) {
+ memcpy(&cmd.data.setdelipm.ip6,ip,ip_len);
+ } else {
+ memcpy(&cmd.data.setdelipm.ip4_6,ip,ip_len);
+ }
+
+ return qeth_send_ipa_cmd(card,&cmd,0,IPA_CMD_STATE|
+ ((ipacmd==IPA_CMD_SETIPM)?IPA_SETIP_FLAG:0));
+}
+
+#define PRINT_SETIP_ERROR(x) \
+ if (result) \
+ PRINT_ERR("setip%c: return code 0x%x (%s)\n",x,result, \
+ (result==0xe002)?"invalid mtu size": \
+ (result==0xe005)?"duplicate ip address": \
+ (result==0xe0a5)?"duplicate ip address": \
+ (result==0xe006)?"ip table full": \
+ (result==0xe008)?"startlan not received": \
+ (result==0xe009)?"setip already received": \
+ (result==0xe00a)?"dup network ip address": \
+ (result==0xe00b)?"mblk no free main task entry": \
+ (result==0xe00d)?"invalid ip version": \
+ (result==0xe00e)?"unsupported arp assist cmd": \
+ (result==0xe00f)?"arp assist not enabled": \
+ (result==0xe080)?"startlan disabled": \
+ (result==-1)?"IPA communication timeout": \
+ "unknown return code")
+
+static inline int qeth_send_setip(qeth_card_t *card,__u8 *ip,
+ __u8 *netmask,short ip_vers,int use_retries)
+{
+ int result;
+ int retries;
+ char dbf_text[15];
+ int takeover=0;
+
+ retries=(use_retries)?QETH_SETIP_RETRIES:1;
+ if (qeth_is_ipa_covered_by_ipato_entries(ip_vers,ip,card)) {
+ sprintf(dbf_text,"ipto%4x",card->irq0);
+ QETH_DBF_TEXT2(0,trace,dbf_text);
+ if (ip_vers==4) {
+ *((__u32*)(&dbf_text[0]))=*((__u32*)ip);
+ *((__u32*)(&dbf_text[4]))=*((__u32*)netmask);
+ QETH_DBF_HEX2(0,trace,dbf_text,QETH_DBF_TRACE_LEN);
+ } else {
+ QETH_DBF_HEX2(0,trace,ip,QETH_DBF_TRACE_LEN);
+ QETH_DBF_HEX2(0,trace,ip+QETH_DBF_TRACE_LEN,
+ QETH_DBF_TRACE_LEN);
+ QETH_DBF_HEX2(0,trace,netmask,QETH_DBF_TRACE_LEN);
+ QETH_DBF_HEX2(0,trace,netmask+QETH_DBF_TRACE_LEN,
+ QETH_DBF_TRACE_LEN);
+ }
+ takeover=1;
+ }
+retry:
+ result=qeth_send_setdelip(card,ip,netmask,IPA_CMD_SETIP,ip_vers,
+ (takeover)?IPA_SETIP_TAKEOVER_FLAGS:
+ IPA_SETIP_FLAGS);
+ PRINT_SETIP_ERROR(' ');
+
+ if (result) {
+ QETH_DBF_TEXT2(0,trace,"SETIPFLD");
+ sprintf(dbf_text,"%4x%4x",card->irq0,result);
+ QETH_DBF_TEXT2(0,trace,dbf_text);
+ }
+
+ if (((result==-1)||(result==0xe080))&&(retries--)) {
+ sprintf(dbf_text,"sipr%4x",card->irq0);
+ QETH_DBF_TEXT2(0,trace,dbf_text);
+ if (ip_vers==4) {
+ *((__u32*)(&dbf_text[0]))=*((__u32*)ip);
+ *((__u32*)(&dbf_text[4]))=*((__u32*)netmask);
+ QETH_DBF_HEX2(0,trace,dbf_text,QETH_DBF_TRACE_LEN);
+ } else {
+ QETH_DBF_HEX2(0,trace,ip,QETH_DBF_TRACE_LEN);
+ QETH_DBF_HEX2(0,trace,ip+QETH_DBF_TRACE_LEN,
+ QETH_DBF_TRACE_LEN);
+ QETH_DBF_HEX2(0,trace,netmask,QETH_DBF_TRACE_LEN);
+ QETH_DBF_HEX2(0,trace,netmask+QETH_DBF_TRACE_LEN,
+ QETH_DBF_TRACE_LEN);
+ }
+ PRINT_WARN("trying again...\n");
+ goto retry;
+ }
+
+ return result;
+}
+
+static inline int qeth_send_delip(qeth_card_t *card,__u8 *ip,
+ __u8 *netmask,short ip_vers)
+{
+ return qeth_send_setdelip(card,ip,netmask,IPA_CMD_DELIP,ip_vers,
+ IPA_DELIP_FLAGS);
+}
+
+static inline int qeth_send_setipm(qeth_card_t *card,__u8 *ip,
+ __u8 *mac,short ip_vers,int use_retries)
+{
+ int result;
+ int retries;
+ char dbf_text[15];
+
+ retries=(use_retries)?QETH_SETIP_RETRIES:1;
+ if (qeth_is_ipa_covered_by_ipato_entries(ip_vers,ip,card)) {
+ sprintf(dbf_text,"imto%4x",card->irq0);
+ QETH_DBF_TEXT2(0,trace,dbf_text);
+ if (ip_vers==4) {
+ *((__u32*)(&dbf_text[0]))=*((__u32*)ip);
+ QETH_DBF_HEX2(0,trace,dbf_text,QETH_DBF_TRACE_LEN);
+ } else {
+ QETH_DBF_HEX2(0,trace,ip,QETH_DBF_TRACE_LEN);
+ QETH_DBF_HEX2(0,trace,ip+QETH_DBF_TRACE_LEN,
+ QETH_DBF_TRACE_LEN);
+ }
+ }
+
+retry:
+ result=qeth_send_setdelipm(card,ip,mac,IPA_CMD_SETIPM,ip_vers);
+ PRINT_SETIP_ERROR('m');
+
+ if (result) {
+ QETH_DBF_TEXT2(0,trace,"SETIMFLD");
+ sprintf(dbf_text,"%4x%4x",card->irq0,result);
+ QETH_DBF_TEXT2(0,trace,dbf_text);
+ }
+
+ if ((result==-1)&&(retries--)) {
+ sprintf(dbf_text,"simr%4x",card->irq0);
+ QETH_DBF_TEXT2(0,trace,dbf_text);
+ if (ip_vers==4) {
+ sprintf(dbf_text,"%08x",*((__u32*)ip));
+ QETH_DBF_TEXT2(0,trace,dbf_text);
+ } else {
+ QETH_DBF_HEX2(0,trace,ip,QETH_DBF_TRACE_LEN);
+ QETH_DBF_HEX2(0,trace,ip+QETH_DBF_TRACE_LEN,
+ QETH_DBF_TRACE_LEN);
+ }
+ QETH_DBF_HEX2(0,trace,mac,OSA_ADDR_LEN);
+ PRINT_WARN("trying again...\n");
+ goto retry;
+ }
+
+ return result;
+}
+
+static inline int qeth_send_delipm(qeth_card_t *card,__u8 *ip,
+ __u8 *mac,short ip_vers)
+{
+ return qeth_send_setdelipm(card,ip,mac,IPA_CMD_DELIPM,ip_vers);
+}
+
+static int qeth_add_vipa_entry(qeth_card_t *card,int version,__u8 *addr,
+ int flag)
+{
+ qeth_vipa_entry_t *entry,*e;
+ int result=0;
+
+ entry=(qeth_vipa_entry_t*)kmalloc(sizeof(qeth_vipa_entry_t),
+ GFP_KERNEL);
+ if (!entry) {
+ PRINT_ERR("not enough memory for vipa handling\n");
+ return -ENOMEM;
+ }
+ entry->version=version;
+ entry->flag=flag;
+ memcpy(entry->ip,addr,16);
+ entry->state=VIPA_2_B_ADDED;
+
+ my_write_lock(&card->vipa_list_lock);
+ e=card->vipa_list;
+ while (e) {
+ if (e->version!=version) goto next;
+ if (memcmp(e->ip,addr,(version==4)?4:16)) goto next;
+ if (flag==IPA_SETIP_VIPA_FLAGS) {
+ PRINT_ERR("vipa already set\n");
+ } else {
+ PRINT_ERR("rxip already set\n");
+ }
+ kfree(entry);
+ result=-EALREADY;
+ goto out;
+next:
+ e=e->next;
+ }
+ entry->next=card->vipa_list;
+ card->vipa_list=entry;
+out:
+ my_write_unlock(&card->vipa_list_lock);
+ return result;
+}
+
+static int qeth_del_vipa_entry(qeth_card_t *card,int version,__u8 *addr,
+ int flag)
+{
+ qeth_vipa_entry_t *e;
+ int result=0;
+
+ my_write_lock(&card->vipa_list_lock);
+ e=card->vipa_list;
+ while (e) {
+ if (e->version!=version) goto next;
+ if (e->flag!=flag) goto next;
+ if (memcmp(e->ip,addr,(version==4)?4:16)) goto next;
+ e->state=VIPA_2_B_REMOVED;
+ goto out;
+next:
+ e=e->next;
+ }
+ if (flag==IPA_SETIP_VIPA_FLAGS) {
+ PRINT_ERR("vipa not found\n");
+ } else {
+ PRINT_ERR("rxip not found\n");
+ }
+ result=-ENOENT;
+out:
+ my_write_unlock(&card->vipa_list_lock);
+ return result;
+}
+
+static void qeth_set_vipas(qeth_card_t *card,int set_only)
+{
+ qeth_vipa_entry_t *e,*le=NULL,*ne; /* ne stands for new entry,
+ le is last entry */
+ char dbf_text[15];
+ int result;
+ __u8 netmask[16]={0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff};
+ qeth_vipa_entry_t *priv_add_list=NULL;
+ qeth_vipa_entry_t *priv_del_list=NULL;
+
+ my_write_lock(&card->vipa_list_lock);
+ e=card->vipa_list;
+ while (e) {
+ switch (e->state) {
+ case VIPA_2_B_ADDED:
+ if (!set_only) break;
+ if (!atomic_read(&card->is_open)) break;
+ /* we don't want to hold the lock for a long time...
+ * so we clone the entry */
+ ne=(qeth_vipa_entry_t*)
+ kmalloc(sizeof(qeth_vipa_entry_t),
+ GFP_KERNEL);
+ if (ne) {
+ ne->version=e->version;
+ memcpy(ne->ip,e->ip,16);
+ ne->next=priv_add_list;
+ priv_add_list=ne;
+
+ e->state=VIPA_ESTABLISHED;
+ } else {
+ PRINT_ERR("not enough for internal vipa " \
+ "handling... trying to set " \
+ "vipa next time.\n");
+ qeth_start_softsetup_thread(card);
+ }
+ break;
+ case VIPA_2_B_REMOVED:
+ if (set_only) break;
+ if (le)
+ le->next=e->next;
+ else card->vipa_list=e->next;
+ ne=e->next;
+ e->next=priv_del_list;
+ priv_del_list=e;
+ e=ne;
+ continue;
+ case VIPA_ESTABLISHED:
+ if (atomic_read(&card->is_open)) break;
+ /* we don't want to hold the lock for a long time...
+ * so we clone the entry */
+ ne=(qeth_vipa_entry_t*)
+ kmalloc(sizeof(qeth_vipa_entry_t),
+ GFP_KERNEL);
+ if (ne) {
+ ne->version=e->version;
+ memcpy(ne->ip,e->ip,16);
+ ne->next=priv_del_list;
+ priv_del_list=ne;
+
+ e->state=VIPA_2_B_ADDED;
+ } else {
+ PRINT_ERR("not enough for internal vipa " \
+ "handling... VIPA/RXIP remains set " \
+ "although device is stopped.\n");
+ qeth_start_softsetup_thread(card);
+ }
+ break;
+ default:
+ break;
+ }
+ le=e;
+ e=e->next;
+ }
+ my_write_unlock(&card->vipa_list_lock);
+
+ while (priv_add_list) {
+ result=qeth_send_setdelip(card,priv_add_list->ip,netmask,
+ IPA_CMD_SETIP,priv_add_list->version,
+ priv_add_list->flag);
+ PRINT_SETIP_ERROR('s');
+
+ if (result) {
+ QETH_DBF_TEXT2(0,trace,"SETSVFLD");
+ sprintf(dbf_text,"%4x%4x",card->irq0,result);
+ QETH_DBF_TEXT2(0,trace,dbf_text);
+ if (priv_add_list->version==4) {
+ PRINT_ERR("going to leave vipa/rxip %08x" \
+ "unset...\n",
+ *((__u32*)&priv_add_list->ip[0]));
+ sprintf(dbf_text,"%08x",
+ *((__u32*)&priv_add_list->ip[0]));
+ QETH_DBF_TEXT2(0,trace,dbf_text);
+ } else {
+ PRINT_ERR("going to leave vipa/rxip " \
+ "%08x%08x%08x%08x unset...\n",
+ *((__u32*)&priv_add_list->ip[0]),
+ *((__u32*)&priv_add_list->ip[4]),
+ *((__u32*)&priv_add_list->ip[8]),
+ *((__u32*)&priv_add_list->ip[12]));
+ QETH_DBF_HEX2(0,trace,&priv_add_list->ip[0],
+ QETH_DBF_TRACE_LEN);
+ QETH_DBF_HEX2(0,trace,&priv_add_list->ip[8],
+ QETH_DBF_TRACE_LEN);
+ }
+ }
+ e=priv_add_list;
+ priv_add_list=priv_add_list->next;
+ kfree(e);
+ }
+
+ while (priv_del_list) {
+ result=qeth_send_setdelip(card,priv_del_list->ip,netmask,
+ IPA_CMD_DELIP,priv_del_list->version,
+ priv_del_list->flag);
+ if (result) {
+ QETH_DBF_TEXT2(0,trace,"DELSVFLD");
+ sprintf(dbf_text,"%4x%4x",card->irq0,result);
+ QETH_DBF_TEXT2(0,trace,dbf_text);
+ if (priv_del_list->version==4) {
+ PRINT_ERR("could not delete vipa/rxip " \
+ "%08x...\n",
+ *((__u32*)&priv_del_list->ip[0]));
+ sprintf(dbf_text,"%08x",
+ *((__u32*)&priv_del_list->ip[0]));
+ QETH_DBF_TEXT2(0,trace,dbf_text);
+ } else {
+ PRINT_ERR("could not delete vipa/rxip " \
+ "%08x%08x%08x%08x...\n",
+ *((__u32*)&priv_del_list->ip[0]),
+ *((__u32*)&priv_del_list->ip[4]),
+ *((__u32*)&priv_del_list->ip[8]),
+ *((__u32*)&priv_del_list->ip[12]));
+ QETH_DBF_HEX2(0,trace,&priv_del_list->ip[0],
+ QETH_DBF_TRACE_LEN);
+ QETH_DBF_HEX2(0,trace,&priv_del_list->ip[8],
+ QETH_DBF_TRACE_LEN);
+ }
+ }
+ e=priv_del_list;
+ priv_del_list=priv_del_list->next;
+ kfree(e);
+ }
+}
+
+static void qeth_refresh_vipa_states(qeth_card_t *card)
+{
+ qeth_vipa_entry_t *e;
+
+ my_write_lock(&card->vipa_list_lock);
+ e=card->vipa_list;
+ while (e) {
+ if (e->state==VIPA_ESTABLISHED) e->state=VIPA_2_B_ADDED;
+ e=e->next;
+ }
+ my_write_unlock(&card->vipa_list_lock);
+}
+
+static inline int qeth_send_setrtg(qeth_card_t *card,int routing_type,
+ short ip_vers)
+{
+ ipa_cmd_t cmd;
+
+ qeth_fill_ipa_cmd(card,&cmd,IPA_CMD_SETRTG,ip_vers);
+ /* strip off RESET_ROUTING_FLAG */
+ cmd.data.setrtg.type=(routing_type)&(ROUTER_MASK);
+
+ return qeth_send_ipa_cmd(card,&cmd,0,IPA_CMD_STATE);
+}
+
+static int qeth_is_ipa_in_list(struct in_ifaddr *ip,struct in_ifaddr *list)
+{
+ while (list) {
+ if (ip->ifa_address==list->ifa_address) return 1;
+ list=list->ifa_next;
+ }
+ return 0;
+}
+
+#ifdef QETH_IPV6
+static int qeth_is_ipa_in_list6(struct inet6_ifaddr *ip,
+ struct inet6_ifaddr *list)
+{
+ while (list) {
+ if (!memcmp(&ip->addr.s6_addr,&list->addr.s6_addr,16))
+ return 1;
+ list=list->if_next;
+ }
+ return 0;
+}
+
+static int qeth_add_ifa6_to_list(struct inet6_ifaddr **list,
+ struct inet6_ifaddr *ifa)
+{
+ struct inet6_ifaddr *i;
+
+ if (*list==NULL) {
+ *list=ifa;
+ } else {
+ if (qeth_is_ipa_in_list6(ifa,*list))
+ return -EALREADY;
+ i=*list;
+ while (i->if_next) {
+ i=i->if_next;
+ }
+ i->if_next=ifa;
+ }
+ ifa->if_next=NULL;
+ return 0;
+}
+#endif /* QETH_IPV6 */
+
+static int qeth_add_ifa_to_list(struct in_ifaddr **list,struct in_ifaddr *ifa)
+{
+ struct in_ifaddr *i;
+
+ if (*list==NULL) {
+ *list=ifa;
+ } else {
+ if (qeth_is_ipa_in_list(ifa,*list))
+ return -EALREADY;
+ i=*list;
+ while (i->ifa_next) {
+ i=i->ifa_next;
+ }
+ i->ifa_next=ifa;
+ }
+ ifa->ifa_next=NULL;
+ return 0;
+}
+
+static int qeth_setips(qeth_card_t *card,int use_setip_retries)
+{
+ struct in_ifaddr *addr;
+ int result;
+ char dbf_text[15];
+#ifdef QETH_IPV6
+ struct inet6_ifaddr *addr6;
+ __u8 netmask[16];
+#endif /* QETH_IPV6 */
+
+ sprintf(dbf_text,"stip%4x",card->irq0);
+ QETH_DBF_TEXT3(0,trace,dbf_text);
+
+ addr=card->ip_current_state.ip_ifa;
+ while (addr) {
+ if (!qeth_is_ipa_in_list(addr,card->ip_new_state.ip_ifa)) {
+ QETH_DBF_TEXT3(0,trace,"setipdel");
+ *((__u32*)(&dbf_text[0]))=*((__u32*)&addr->ifa_address);
+ *((__u32*)(&dbf_text[4]))=*((__u32*)&addr->ifa_mask);
+ QETH_DBF_HEX3(0,trace,dbf_text,QETH_DBF_TRACE_LEN);
+ result=qeth_send_delip(card,(__u8*)&addr->ifa_address,
+ (__u8*)&addr->ifa_mask,4);
+ if (result) {
+ PRINT_ERR("was not able to delete ip " \
+ "%08x/%08x on irq x%x " \
+ "(result: 0x%x), " \
+ "trying to continue\n",
+ addr->ifa_address,
+ addr->ifa_mask,card->irq0,result);
+ sprintf(dbf_text,"stdl%4x",result);
+ QETH_DBF_TEXT3(0,trace,dbf_text);
+ }
+ }
+ addr=addr->ifa_next;
+ }
+
+ addr=card->ip_new_state.ip_ifa;
+ while (addr) {
+ if (!qeth_is_ipa_in_list(addr,
+ card->ip_current_state.ip_ifa)) {
+ QETH_DBF_TEXT3(0,trace,"setipset");
+ *((__u32*)(&dbf_text[0]))=
+ *((__u32*)&addr->ifa_address);
+ *((__u32*)(&dbf_text[4]))=
+ *((__u32*)&addr->ifa_mask);
+ QETH_DBF_HEX3(0,trace,dbf_text,QETH_DBF_TRACE_LEN);
+ result=qeth_send_setip(card,(__u8*)&addr->ifa_address,
+ (__u8*)&addr->ifa_mask,4,
+ use_setip_retries);
+ if (result) {
+ PRINT_ERR("was not able to set ip " \
+ "%08x/%08x on irq x%x, trying to " \
+ "continue\n",
+ addr->ifa_address,
+ addr->ifa_mask,card->irq0);
+ sprintf(dbf_text,"stst%4x",result);
+ QETH_DBF_TEXT3(0,trace,dbf_text);
+ }
+ }
+ addr=addr->ifa_next;
+ }
+
+#ifdef QETH_IPV6
+#define FILL_NETMASK(len) { \
+ int i,j; \
+ for (i=0;i<16;i++) { \
+ j=(len)-(i*8); \
+ if (j>=8) netmask[i]=0xff; else \
+ if (j<=0) netmask[i]=0x0; else \
+ netmask[i]=(__u8)(0xFF00>>j); \
+ } \
+}
+ /* here we go with IPv6 */
+ addr6=card->ip_current_state.ip6_ifa;
+ while (addr6) {
+ if (!qeth_is_ipa_in_list6(addr6,card->ip_new_state.ip6_ifa)) {
+ QETH_DBF_TEXT3(0,trace,"setipdl6");
+ QETH_DBF_HEX3(0,trace,&addr6->addr.s6_addr,
+ QETH_DBF_TRACE_LEN);
+ QETH_DBF_HEX3(0,trace,
+ ((char *)(&addr6->addr.s6_addr))+
+ QETH_DBF_TRACE_LEN,QETH_DBF_TRACE_LEN);
+ sprintf(dbf_text,"nmsk%4u",addr6->prefix_len);
+ QETH_DBF_TEXT3(0,trace,dbf_text);
+ FILL_NETMASK(addr6->prefix_len);
+ result=qeth_send_delip(card,
+ (__u8*)&addr6->addr.s6_addr,
+ (__u8*)&netmask,6);
+ if (result) {
+ PRINT_ERR("was not able to delete ip " \
+ "%04x:%04x:%04x:%04x:%04x:%04x:" \
+ "%04x:%04x/%u on irq x%x " \
+ "(result: 0x%x), " \
+ "trying to continue\n",
+ addr6->addr.s6_addr16[0],
+ addr6->addr.s6_addr16[1],
+ addr6->addr.s6_addr16[2],
+ addr6->addr.s6_addr16[3],
+ addr6->addr.s6_addr16[4],
+ addr6->addr.s6_addr16[5],
+ addr6->addr.s6_addr16[6],
+ addr6->addr.s6_addr16[7],
+ addr6->prefix_len,
+ card->irq0,result);
+ sprintf(dbf_text,"std6%4x",result);
+ QETH_DBF_TEXT3(0,trace,dbf_text);
+ }
+ }
+ addr6=addr6->if_next;
+ }
+
+ addr6=card->ip_new_state.ip6_ifa;
+ while (addr6) {
+ if (!qeth_is_ipa_in_list6(addr6,
+ card->ip_current_state.ip6_ifa)) {
+ QETH_DBF_TEXT3(0,trace,"setipst6");
+ QETH_DBF_HEX3(0,trace,&addr6->addr.s6_addr,
+ QETH_DBF_TRACE_LEN);
+ QETH_DBF_HEX3(0,trace,
+ ((char *)(&addr6->addr.s6_addr))+
+ QETH_DBF_TRACE_LEN,QETH_DBF_TRACE_LEN);
+ sprintf(dbf_text,"nmsk%4u",addr6->prefix_len);
+ QETH_DBF_TEXT3(0,trace,dbf_text);
+ FILL_NETMASK(addr6->prefix_len);
+ result=qeth_send_setip(card,
+ (__u8*)&addr6->addr.s6_addr,
+ (__u8*)&netmask,6,
+ use_setip_retries);
+ if (result) {
+ PRINT_ERR("was not able to set ip " \
+ "%04x:%04x:%04x:%04x:%04x:%04x:" \
+ "%04x:%04x/%u on irq x%x " \
+ "(result: 0x%x), " \
+ "trying to continue\n",
+ addr6->addr.s6_addr16[0],
+ addr6->addr.s6_addr16[1],
+ addr6->addr.s6_addr16[2],
+ addr6->addr.s6_addr16[3],
+ addr6->addr.s6_addr16[4],
+ addr6->addr.s6_addr16[5],
+ addr6->addr.s6_addr16[6],
+ addr6->addr.s6_addr16[7],
+ addr6->prefix_len,
+ card->irq0,result);
+ sprintf(dbf_text,"sts6%4x",result);
+ QETH_DBF_TEXT3(0,trace,dbf_text);
+ }
+ }
+ addr6=addr6->if_next;
+ }
+#endif /* QETH_IPV6 */
+
+ return 0;
+}
+
+static int qeth_is_ipma_in_list(struct qeth_ipm_mac *ipma,
+ struct qeth_ipm_mac *list)
+{
+ while (list) {
+ if ( (!memcmp(ipma->ip,list->ip,16)) &&
+ (!memcmp(ipma->mac,list->mac,6)) ) return 1;
+ list=list->next;
+ }
+ return 0;
+}
+
+static void qeth_remove_mc_ifa_from_list(struct qeth_ipm_mac **list,
+ struct qeth_ipm_mac *ipma)
+{
+ struct qeth_ipm_mac *i,*li=NULL;
+
+ if ((!(*list)) || (!ipma)) return;
+
+ if (*list==ipma) {
+ *list=ipma->next;
+ } else {
+ i=*list;
+ while (i) {
+ if (i==ipma) {
+ li->next=i->next;
+ } else {
+ li=i;
+ }
+ i=i->next;
+ }
+ }
+}
+
+static int qeth_add_mc_ifa_to_list(struct qeth_ipm_mac **list,
+ struct qeth_ipm_mac *ipma)
+{
+ struct qeth_ipm_mac *i;
+
+ if (qeth_is_ipma_in_list(ipma,*list))
+ return -EALREADY;
+
+ if (*list==NULL) {
+ *list=ipma;
+ } else {
+ i=*list;
+ while (i->next) {
+ i=i->next;
+ }
+ i->next=ipma;
+ }
+ ipma->next=NULL;
+ return 0;
+}
+
+static int qeth_setipms(qeth_card_t *card,int use_setipm_retries)
+{
+ struct qeth_ipm_mac *addr;
+ int result;
+ char dbf_text[15];
+
+ sprintf(dbf_text,"stim%4x",card->irq0);
+ QETH_DBF_TEXT3(0,trace,dbf_text);
+
+ if (qeth_is_supported(IPA_MULTICASTING)) {
+ addr=card->ip_mc_current_state.ipm_ifa;
+ while (addr) {
+ if (!qeth_is_ipma_in_list(addr,card->
+ ip_mc_new_state.ipm_ifa)) {
+ QETH_DBF_TEXT3(0,trace,"setimdel");
+ sprintf(dbf_text,"%08x",
+ *((__u32*)&addr->ip[0]));
+ QETH_DBF_TEXT3(0,trace,dbf_text);
+ *((__u32*)(&dbf_text[0]))=
+ *((__u32*)&addr->mac);
+ *((__u32*)(&dbf_text[4]))=
+ *(((__u32*)&addr->mac)+1);
+ QETH_DBF_HEX3(0,trace,dbf_text,
+ QETH_DBF_TRACE_LEN);
+ result=qeth_send_delipm(
+ card,(__u8*)&addr->ip[0],
+ (__u8*)addr->mac,4);
+ if (result) {
+ PRINT_ERR("was not able to delete " \
+ "multicast ip %08x/" \
+ "%02x%02x%02x%02x%02x%02x " \
+ "on irq x%x " \
+ "(result: 0x%x), " \
+ "trying to continue\n",
+ *((__u32*)&addr->ip[0]),
+ addr->mac[0],addr->mac[1],
+ addr->mac[2],addr->mac[3],
+ addr->mac[4],addr->mac[5],
+ card->irq0,result);
+ sprintf(dbf_text,"smdl%4x",result);
+ QETH_DBF_TEXT3(0,trace,dbf_text);
+ }
+ }
+ addr=addr->next;
+ }
+
+ addr=card->ip_mc_new_state.ipm_ifa;
+ while (addr) {
+ if (!qeth_is_ipma_in_list(addr,card->
+ ip_mc_current_state.
+ ipm_ifa)) {
+ QETH_DBF_TEXT3(0,trace,"setimset");
+ sprintf(dbf_text,"%08x",
+ *((__u32*)&addr->ip[0]));
+ QETH_DBF_TEXT3(0,trace,dbf_text);
+ *((__u32*)(&dbf_text[0]))=
+ *((__u32*)&addr->mac);
+ *((__u32*)(&dbf_text[4]))=
+ *(((__u32*)&addr->mac)+1);
+ QETH_DBF_HEX3(0,trace,dbf_text,
+ QETH_DBF_TRACE_LEN);
+ result=qeth_send_setipm(
+ card,(__u8*)&addr->ip[0],
+ (__u8*)addr->mac,4,
+ use_setipm_retries);
+ if (result) {
+ PRINT_ERR("was not able to set " \
+ "multicast ip %08x/" \
+ "%02x%02x%02x%02x%02x%02x " \
+ "on irq x%x " \
+ "(result: 0x%x), " \
+ "trying to continue\n",
+ *((__u32*)&addr->ip[0]),
+ addr->mac[0],addr->mac[1],
+ addr->mac[2],addr->mac[3],
+ addr->mac[4],addr->mac[5],
+ card->irq0,result);
+ sprintf(dbf_text,"smst%4x",result);
+ QETH_DBF_TEXT3(0,trace,dbf_text);
+ qeth_remove_mc_ifa_from_list(
+ &card->ip_mc_current_state.
+ ipm_ifa,addr);
+ }
+ }
+ addr=addr->next;
+ }
+
+#ifdef QETH_IPV6
+ /* here we go with IPv6 */
+ addr=card->ip_mc_current_state.ipm6_ifa;
+ while (addr) {
+ if (!qeth_is_ipma_in_list(addr,card->
+ ip_mc_new_state.ipm6_ifa)) {
+ QETH_DBF_TEXT3(0,trace,"setimdl6");
+ QETH_DBF_HEX3(0,trace,&addr->ip[0],
+ QETH_DBF_TRACE_LEN);
+ QETH_DBF_HEX3(0,trace,(&addr->ip[0])+
+ QETH_DBF_TRACE_LEN,
+ QETH_DBF_TRACE_LEN);
+ QETH_DBF_HEX3(0,trace,&addr->mac,
+ QETH_DBF_TRACE_LEN);
+ result=qeth_send_delipm(
+ card,(__u8*)&addr->ip[0],
+ (__u8*)addr->mac,6);
+ if (result) {
+ PRINT_ERR("was not able to delete " \
+ "multicast ip %04x:%04x:" \
+ "%04x:%04x:%04x:%04x:" \
+ "%04x:%04x/" \
+ "%02x%02x%02x%02x%02x%02x " \
+ "on irq x%x " \
+ "(result: 0x%x), " \
+ "trying to continue\n",
+ *((__u16*)&addr->ip[0]),
+ *((__u16*)&addr->ip[2]),
+ *((__u16*)&addr->ip[4]),
+ *((__u16*)&addr->ip[6]),
+ *((__u16*)&addr->ip[8]),
+ *((__u16*)&addr->ip[10]),
+ *((__u16*)&addr->ip[12]),
+ *((__u16*)&addr->ip[14]),
+ addr->mac[0],addr->mac[1],
+ addr->mac[2],addr->mac[3],
+ addr->mac[4],addr->mac[5],
+ card->irq0,result);
+ sprintf(dbf_text,"smd6%4x",result);
+ QETH_DBF_TEXT3(0,trace,dbf_text);
+ }
+ }
+ addr=addr->next;
+ }
+
+ addr=card->ip_mc_new_state.ipm6_ifa;
+ while (addr) {
+ if (!qeth_is_ipma_in_list(addr,card->
+ ip_mc_current_state.
+ ipm6_ifa)) {
+ QETH_DBF_TEXT3(0,trace,"setimst6");
+ QETH_DBF_HEX3(0,trace,&addr->ip[0],
+ QETH_DBF_TRACE_LEN);
+ QETH_DBF_HEX3(0,trace,(&addr->ip[0])+
+ QETH_DBF_TRACE_LEN,
+ QETH_DBF_TRACE_LEN);
+ QETH_DBF_HEX3(0,trace,&addr->mac,
+ QETH_DBF_TRACE_LEN);
+ result=qeth_send_setipm(
+ card,(__u8*)&addr->ip[0],
+ (__u8*)addr->mac,6,
+ use_setipm_retries);
+ if (result) {
+ PRINT_ERR("was not able to set " \
+ "multicast ip %04x:%04x:" \
+ "%04x:%04x:%04x:%04x:" \
+ "%04x:%04x/" \
+ "%02x%02x%02x%02x%02x%02x " \
+ "on irq x%x " \
+ "(result: 0x%x), " \
+ "trying to continue\n",
+ *((__u16*)&addr->ip[0]),
+ *((__u16*)&addr->ip[2]),
+ *((__u16*)&addr->ip[4]),
+ *((__u16*)&addr->ip[6]),
+ *((__u16*)&addr->ip[8]),
+ *((__u16*)&addr->ip[10]),
+ *((__u16*)&addr->ip[12]),
+ *((__u16*)&addr->ip[14]),
+ addr->mac[0],addr->mac[1],
+ addr->mac[2],addr->mac[3],
+ addr->mac[4],addr->mac[5],
+ card->irq0,result);
+ sprintf(dbf_text,"sms6%4x",result);
+ QETH_DBF_TEXT3(0,trace,dbf_text);
+ qeth_remove_mc_ifa_from_list(
+ &card->ip_mc_current_state.
+ ipm6_ifa,addr);
+ }
+ }
+ addr=addr->next;
+ }
+#endif /* QETH_IPV6 */
+ return 0;
+ } else return 0;
+}
+
+static void qeth_clone_ifa(struct in_ifaddr *src,struct in_ifaddr *dest)
+{
+ memcpy(dest,src,sizeof(struct in_ifaddr));
+ dest->ifa_next=NULL;
+}
+
+#ifdef QETH_IPV6
+static void qeth_clone_ifa6(struct inet6_ifaddr *src,
+ struct inet6_ifaddr *dest)
+{
+ memcpy(dest,src,sizeof(struct inet6_ifaddr));
+ dest->if_next=NULL;
+}
+#endif /* QETH_IPV6 */
+
+#define QETH_STANDARD_RETVALS \
+ ret_val=-EIO; \
+ if (result==IPA_REPLY_SUCCESS) ret_val=0; \
+ if (result==IPA_REPLY_FAILED) ret_val=-EIO; \
+ if (result==IPA_REPLY_OPNOTSUPP) ret_val=-EOPNOTSUPP
+
+static int qeth_do_ioctl(struct net_device *dev,struct ifreq *rq,int cmd)
+{
+ char *data;
+ int result,i,ret_val;
+ int version=4;
+ qeth_card_t *card;
+ char dbf_text[15];
+ char buff[100];
+
+ card=(qeth_card_t*)dev->priv;
+
+ sprintf(dbf_text,"ioct%4x",card->irq0);
+ QETH_DBF_TEXT2(0,trace,dbf_text);
+ sprintf(dbf_text,"cmd=%4x",cmd);
+ QETH_DBF_TEXT2(0,trace,dbf_text);
+ QETH_DBF_HEX2(0,trace,&rq,sizeof(void*));
+
+ if ((cmd<SIOCDEVPRIVATE) || (cmd>SIOCDEVPRIVATE+5))
+ return -EOPNOTSUPP;
+ copy_from_user(buff,rq->ifr_ifru.ifru_data,sizeof(buff));
+ data=buff;
+
+ if ( (!atomic_read(&card->is_registered))||
+ (!atomic_read(&card->is_hardsetup))||
+ (atomic_read(&card->is_gone)) ) return -ENODEV;
+
+ if (atomic_read(&card->shutdown_phase)) return -ENODEV;
+
+ my_spin_lock(&card->ioctl_lock);
+
+ if (atomic_read(&card->shutdown_phase)) return -ENODEV;
+ if ( (!atomic_read(&card->is_registered))||
+ (!atomic_read(&card->is_hardsetup))||
+ (atomic_read(&card->is_gone)) ) {
+ ret_val=-ENODEV;
+ goto out;
+ }
+
+ switch (cmd) {
+ case SIOCDEVPRIVATE+0:
+ if (!capable(CAP_NET_ADMIN)) {
+ ret_val=-EPERM;
+ break;
+ }
+ result=qeth_send_setassparms(card,version,IPA_ARP_PROCESSING,
+ IPA_CMD_ASS_ARP_SET_NO_ENTRIES,
+ rq->ifr_ifru.ifru_ivalue,4);
+ QETH_STANDARD_RETVALS;
+ if (result==3) ret_val=-EINVAL;
+ break;
+ case SIOCDEVPRIVATE+1:
+ if (!capable(CAP_NET_ADMIN)) {
+ ret_val=-EPERM;
+ break;
+ }
+ result = qeth_queryarp(card,rq,version,IPA_ARP_PROCESSING,
+ IPA_CMD_ASS_ARP_QUERY_INFO,data,4);
+
+ QETH_STANDARD_RETVALS;
+ break;
+ case SIOCDEVPRIVATE+2:
+ if (!capable(CAP_NET_ADMIN)) {
+ ret_val=-EPERM;
+ break;
+ }
+ for (i=12;i<24;i++) if (data[i]) version=6;
+ result=qeth_send_setassparms(card,version,IPA_ARP_PROCESSING,
+ IPA_CMD_ASS_ARP_ADD_ENTRY,
+ (long)data,56);
+ QETH_STANDARD_RETVALS;
+ break;
+ case SIOCDEVPRIVATE+3:
+ if (!capable(CAP_NET_ADMIN)) {
+ ret_val=-EPERM;
+ break;
+ }
+ for (i=4;i<12;i++) if (data[i]) version=6;
+ result=qeth_send_setassparms(card,version,IPA_ARP_PROCESSING,
+ IPA_CMD_ASS_ARP_REMOVE_ENTRY,
+ (long)data,16);
+ QETH_STANDARD_RETVALS;
+ break;
+ case SIOCDEVPRIVATE+4:
+ if (!capable(CAP_NET_ADMIN)) {
+ ret_val=-EPERM;
+ break;
+ }
+ result=qeth_send_setassparms(card,version,IPA_ARP_PROCESSING,
+ IPA_CMD_ASS_ARP_FLUSH_CACHE,
+ 0,0);
+ QETH_STANDARD_RETVALS;
+ break;
+ case SIOCDEVPRIVATE+5:
+ result=qeth_send_snmp_control(card,rq,IPA_CMD_SETADAPTERPARMS,
+ IPA_SETADP_SET_SNMP_CONTROL,
+ data,4);
+ QETH_STANDARD_RETVALS;
+ break;
+
+ default:
+ return -EOPNOTSUPP;
+ }
+out:
+ my_spin_unlock(&card->ioctl_lock);
+
+ sprintf(dbf_text,"ret=%4x",ret_val);
+ QETH_DBF_TEXT2(0,trace,dbf_text);
+
+ return ret_val;
+}
+
+static void qeth_clear_ifamc_list(struct qeth_ipm_mac **ifa_list)
+{
+ struct qeth_ipm_mac *ifa;
+ while (*ifa_list) {
+ ifa=*ifa_list;
+ *ifa_list=ifa->next;
+ kfree(ifa);
+ }
+}
+
+#ifdef QETH_IPV6
+static void qeth_clear_ifa6_list(struct inet6_ifaddr **ifa_list)
+{
+ struct inet6_ifaddr *ifa;
+ while (*ifa_list) {
+ ifa=*ifa_list;
+ *ifa_list=ifa->if_next;
+ kfree(ifa);
+ }
+}
+
+static void qeth_takeover_ip_ipms6(qeth_card_t *card)
+{
+ struct inet6_ifaddr *ifa,*ifanew;
+ char dbf_text[15];
+ int remove;
+#ifdef QETH_VLAN
+ struct vlan_group *card_group;
+ int i;
+#endif
+
+ struct qeth_ipm_mac *ipmanew;
+ struct ifmcaddr6 *im6;
+ struct inet6_dev *in6_dev;
+#ifdef QETH_VLAN
+ struct inet6_dev *in6_vdev;
+#endif
+ char buf[MAX_ADDR_LEN];
+
+ sprintf(dbf_text,"tip6%4x",card->irq0);
+ QETH_DBF_TEXT3(0,trace,dbf_text);
+ /* unicast */
+ /* clear ip_current_state */
+ qeth_clear_ifa6_list(&card->ip_current_state.ip6_ifa);
+ /* take it over */
+ card->ip_current_state.ip6_ifa=card->ip_new_state.ip6_ifa;
+ card->ip_new_state.ip6_ifa=NULL;
+
+ /* multicast */
+ /* clear ip_mc_current_state */
+ qeth_clear_ifamc_list(&card->ip_mc_current_state.ipm6_ifa);
+ /* take it over */
+ card->ip_mc_current_state.ipm6_ifa=card->ip_mc_new_state.ipm6_ifa;
+ /* get new one, we try to have the same order as ifa_list in device
+ structure, for what reason ever*/
+ card->ip_mc_new_state.ipm6_ifa=NULL;
+
+ if((in6_dev=in6_dev_get(card->dev))==NULL) {
+ sprintf(dbf_text,"id16%4x",card->irq0);
+ QETH_DBF_TEXT2(0,trace,dbf_text);
+ goto out;
+ }
+ read_lock(&in6_dev->lock);
+
+ /* get new one, we try to have the same order as ifa_list in device
+ structure, for what reason ever*/
+ QETH_DBF_TEXT4(0,trace,"to-ip6s");
+ if ( (atomic_read(&card->is_open)) && (card->dev->ip6_ptr) &&
+ (((struct inet6_dev*)card->dev->ip6_ptr)->addr_list) ) {
+ ifa=((struct inet6_dev*)card->dev->ip6_ptr)->addr_list;
+
+ while (ifa) {
+ ifanew=kmalloc(sizeof(struct inet6_ifaddr),GFP_KERNEL);
+ if (!ifanew) {
+ PRINT_WARN("No memory for IP address " \
+ "handling. Some of the IPs " \
+ "will not be set on %s.\n",
+ card->dev_name);
+ QETH_DBF_TEXT2(0,trace,"TOIPNMEM");
+ } else {
+ qeth_clone_ifa6(ifa,ifanew);
+ remove=qeth_add_ifa6_to_list(
+ &card->ip_new_state.ip6_ifa,ifanew);
+ QETH_DBF_HEX4(0,trace,&ifanew->addr.s6_addr,
+ QETH_DBF_TRACE_LEN);
+ QETH_DBF_HEX4(0,trace,&ifanew->addr.s6_addr+
+ QETH_DBF_TRACE_LEN,
+ QETH_DBF_TRACE_LEN);
+ sprintf(dbf_text,"pref%4u",ifanew->prefix_len);
+ QETH_DBF_TEXT4(0,trace,dbf_text);
+ if (remove) {
+ kfree(ifanew);
+ QETH_DBF_TEXT4(0,trace,"alrdy6rm");
+ }
+ }
+ ifa=ifa->if_next;
+ }
+ }
+#ifdef QETH_VLAN
+/*append all known VLAN IP Addresses corresponding
+ to the real device card->dev->ifindex
+*/
+ QETH_DBF_TEXT4(0,trace,"to-vip6s");
+ if ( (qeth_is_supported(IPA_FULL_VLAN)) &&
+ (atomic_read(&card->is_open)) ) {
+ card_group = (struct vlan_group *) card->vlangrp;
+ if (card_group) for (i=0;i<VLAN_GROUP_ARRAY_LEN;i++) {
+ if ( (card_group->vlan_devices[i]) &&
+ (card_group->vlan_devices[i]->flags&IFF_UP)&&
+ ((struct inet6_dev *) card_group->
+ vlan_devices[i]->ip6_ptr) ) {
+ ifa=((struct inet6_dev *)
+ card_group->vlan_devices[i]->ip6_ptr)->
+ addr_list;
+
+ while (ifa) {
+ ifanew=kmalloc(sizeof(struct inet6_ifaddr),GFP_KERNEL);
+ if (!ifanew) {
+ PRINT_WARN("No memory for IP address " \
+ "handling. Some of the IPs " \
+ "will not be set on %s.\n",
+ card->dev_name);
+ QETH_DBF_TEXT2(0,trace,"TOIPNMEM");
+ } else {
+ qeth_clone_ifa6(ifa,ifanew);
+ remove=qeth_add_ifa6_to_list
+ (&card->ip_new_state.ip6_ifa,ifanew);
+ QETH_DBF_HEX4(0,trace,&ifanew->addr.s6_addr,
+ QETH_DBF_TRACE_LEN);
+ QETH_DBF_HEX4(0,trace,&ifanew->addr.s6_addr+
+ QETH_DBF_TRACE_LEN,
+ QETH_DBF_TRACE_LEN);
+ sprintf(dbf_text,"pref%4u",ifanew->prefix_len);
+ QETH_DBF_TEXT4(0,trace,dbf_text);
+ if (remove) {
+ kfree(ifanew);
+ QETH_DBF_TEXT4(0,trace,"alrdv6rm");
+ }
+ }
+ ifa=ifa->if_next;
+ }
+ }
+ }
+ }
+#endif
+
+ QETH_DBF_TEXT4(0,trace,"to-ipm6s");
+ if (atomic_read(&card->is_open))
+ for (im6=in6_dev->mc_list;im6;im6=im6->next) {
+ ndisc_mc_map(&im6->mca_addr,buf,card->dev,0);
+ ipmanew=(struct qeth_ipm_mac*)kmalloc(
+ sizeof(struct qeth_ipm_mac),GFP_KERNEL);
+ if (!ipmanew) {
+ PRINT_WARN("No memory for IPM address " \
+ "handling. Multicast IP " \
+ "%04x:%04x:%04x:%04x:%04x:%04x:%04x:%04x" \
+ "will not be set on %s.\n",
+ im6->mca_addr.s6_addr16[0],
+ im6->mca_addr.s6_addr16[1],
+ im6->mca_addr.s6_addr16[2],
+ im6->mca_addr.s6_addr16[3],
+ im6->mca_addr.s6_addr16[4],
+ im6->mca_addr.s6_addr16[5],
+ im6->mca_addr.s6_addr16[6],
+ im6->mca_addr.s6_addr16[7],
+ card->dev_name);
+ QETH_DBF_TEXT2(0,trace,"TOIPMNMM");
+ } else {
+ memset(ipmanew,0,sizeof(struct qeth_ipm_mac));
+ memcpy(ipmanew->mac,buf,OSA_ADDR_LEN);
+ memcpy(ipmanew->ip,im6->mca_addr.s6_addr,16);
+ ipmanew->next=NULL;
+ remove=qeth_add_mc_ifa_to_list(
+ &card->ip_mc_new_state.ipm6_ifa,ipmanew);
+ QETH_DBF_HEX4(0,trace,&ipmanew->ip,
+ QETH_DBF_TRACE_LEN);
+ QETH_DBF_HEX4(0,trace,&ipmanew->ip+
+ QETH_DBF_TRACE_LEN,
+ QETH_DBF_TRACE_LEN);
+ QETH_DBF_HEX4(0,trace,&ipmanew->mac,
+ QETH_DBF_TRACE_LEN);
+ if (remove) {
+ QETH_DBF_TEXT4(0,trace,"mlrdy6rm");
+ kfree(ipmanew);
+ }
+ }
+ }
+#ifdef QETH_VLAN
+ QETH_DBF_TEXT4(0,trace,"tovipm6s");
+ if ( (qeth_is_supported(IPA_FULL_VLAN)) &&
+ (atomic_read(&card->is_open)) ) {
+ card_group = (struct vlan_group *) card->vlangrp;
+ if (card_group) for (i=0;i<VLAN_GROUP_ARRAY_LEN;i++) {
+ if ((card_group->vlan_devices[i])&&
+ (card_group->vlan_devices[i]->flags&IFF_UP)) {
+ in6_vdev=in6_dev_get(card_group->
+ vlan_devices[i]);
+ if(!(in6_vdev==NULL)) {
+ read_lock(&in6_vdev->lock);
+ for (im6=in6_vdev->mc_list;
+ im6;im6=im6->next) {
+ ndisc_mc_map(&im6->mca_addr,
+ buf,card_group->vlan_devices[i],
+ 0);
+ ipmanew=(struct qeth_ipm_mac*)
+ kmalloc(sizeof(struct qeth_ipm_mac),
+ GFP_KERNEL);
+ if (!ipmanew) {
+ PRINT_WARN("No memory for IPM address " \
+ "handling. Multicast IP " \
+ "%04x:%04x:%04x:%04x:" \
+ "%04x:%04x:%04x:%04x" \
+ "will not be set on %s.\n",
+ im6->mca_addr.s6_addr16[0],
+ im6->mca_addr.s6_addr16[1],
+ im6->mca_addr.s6_addr16[2],
+ im6->mca_addr.s6_addr16[3],
+ im6->mca_addr.s6_addr16[4],
+ im6->mca_addr.s6_addr16[5],
+ im6->mca_addr.s6_addr16[6],
+ im6->mca_addr.s6_addr16[7],
+ card->dev_name);
+ QETH_DBF_TEXT2(0,trace,"TOIPMNMM");
+ } else {
+ memset(ipmanew,0,
+ sizeof(struct qeth_ipm_mac));
+ memcpy(ipmanew->mac,buf,OSA_ADDR_LEN);
+ memcpy(ipmanew->ip,
+ im6->mca_addr.s6_addr,16);
+ ipmanew->next=NULL;
+ remove=qeth_add_mc_ifa_to_list
+ (&card->ip_mc_new_state.ipm6_ifa,
+ ipmanew);
+ QETH_DBF_HEX4(0,trace,&ipmanew->ip,
+ QETH_DBF_TRACE_LEN);
+ QETH_DBF_HEX4(0,trace,&ipmanew->ip+
+ QETH_DBF_TRACE_LEN,
+ QETH_DBF_TRACE_LEN);
+ QETH_DBF_HEX4(0,trace,&ipmanew->mac,
+ QETH_DBF_TRACE_LEN);
+
+ if (remove) {
+ QETH_DBF_TEXT4(0,trace,"mlrdv6rm");
+ kfree(ipmanew);
+ }
+ }
+ }
+ read_unlock(&in6_vdev->lock);
+ in6_dev_put(in6_vdev);
+ } else {
+ sprintf(dbf_text,"id26%4x",card->irq0);
+ QETH_DBF_TEXT2(0,trace,dbf_text);
+ }
+ }
+ }
+ }
+#endif
+ read_unlock(&in6_dev->lock);
+ in6_dev_put(in6_dev);
+ out:
+ ;
+}
+#endif /* QETH_IPV6 */
+
+static void qeth_clear_ifa4_list(struct in_ifaddr **ifa_list)
+{
+ struct in_ifaddr *ifa;
+ while (*ifa_list) {
+ ifa=*ifa_list;
+ *ifa_list=ifa->ifa_next;
+ kfree(ifa);
+ }
+}
+
+static void qeth_takeover_ip_ipms(qeth_card_t *card)
+{
+ struct in_ifaddr *ifa,*ifanew;
+ char dbf_text[15];
+ int remove;
+#ifdef QETH_VLAN
+ struct vlan_group *card_group;
+ int i;
+ struct in_device *vin4_dev;
+#endif
+
+ struct qeth_ipm_mac *ipmanew;
+ struct ip_mc_list *im4;
+ struct in_device *in4_dev;
+ char buf[MAX_ADDR_LEN];
+ __u32 maddr;
+
+ sprintf(dbf_text,"tips%4x",card->irq0);
+ QETH_DBF_TEXT3(0,trace,dbf_text);
+ /* unicast */
+ /* clear ip_current_state */
+ qeth_clear_ifa4_list(&card->ip_current_state.ip_ifa);
+ /* take it over */
+ card->ip_current_state.ip_ifa=card->ip_new_state.ip_ifa;
+ card->ip_new_state.ip_ifa=NULL;
+
+ /* multicast */
+ /* clear ip_mc_current_state */
+ qeth_clear_ifamc_list(&card->ip_mc_current_state.ipm_ifa);
+ /* take it over */
+ card->ip_mc_current_state.ipm_ifa=card->ip_mc_new_state.ipm_ifa;
+ /* get new one, we try to have the same order as ifa_list in device
+ structure, for what reason ever*/
+ card->ip_mc_new_state.ipm_ifa=NULL;
+
+ if((in4_dev=in_dev_get(card->dev))==NULL) {
+ QETH_DBF_TEXT2(0,trace,"nodvhol1");
+ QETH_DBF_TEXT2(0,trace,card->dev_name);
+ return;
+ }
+ read_lock(&in4_dev->lock);
+
+ /* get new one, we try to have the same order as ifa_list in device
+ structure, for what reason ever*/
+ QETH_DBF_TEXT4(0,trace,"to-ips");
+ if ( (atomic_read(&card->is_open)) && (card->dev->ip_ptr) &&
+ (((struct in_device*)card->dev->ip_ptr)->ifa_list) ) {
+ ifa=((struct in_device*)card->dev->ip_ptr)->ifa_list;
+
+ while (ifa) {
+ ifanew=kmalloc(sizeof(struct in_ifaddr),GFP_KERNEL);
+ if (!ifanew) {
+ PRINT_WARN("No memory for IP address " \
+ "handling. Some of the IPs " \
+ "will not be set on %s.\n",
+ card->dev_name);
+ QETH_DBF_TEXT2(0,trace,"TOIPNMEM");
+ } else {
+ qeth_clone_ifa(ifa,ifanew);
+ remove=qeth_add_ifa_to_list(
+ &card->ip_new_state.ip_ifa,ifanew);
+ *((__u32*)(&dbf_text[0]))=
+ *((__u32*)&ifanew->ifa_address);
+ *((__u32*)(&dbf_text[4]))=
+ *((__u32*)&ifanew->ifa_mask);
+ QETH_DBF_TEXT4(0,trace,dbf_text);
+ if (remove) {
+ kfree(ifanew);
+ QETH_DBF_TEXT4(0,trace,"alrdy4rm");
+ }
+ }
+
+ ifa=ifa->ifa_next;
+ }
+ }
+
+#ifdef QETH_VLAN
+ /* append all known VLAN IP Addresses corresponding to the
+ * real device card->dev->ifindex */
+ QETH_DBF_TEXT4(0,trace,"to-vips");
+ if ( (qeth_is_supported(IPA_FULL_VLAN)) &&
+ (atomic_read(&card->is_open)) ) {
+ card_group = (struct vlan_group *) card->vlangrp;
+ if (card_group) {
+ for (i=0;i<VLAN_GROUP_ARRAY_LEN;i++) {
+ if ((vin4_dev=in_dev_get(card->dev))) {
+ read_lock(&vin4_dev->lock);
+ if ((card_group->vlan_devices[i])&&
+ (card_group->vlan_devices[i]->flags&IFF_UP)) {
+ ifa=((struct in_device*)
+ card_group->vlan_devices[i]->ip_ptr)->
+ ifa_list;
+ while (ifa) {
+ ifanew=kmalloc(sizeof(struct in_ifaddr),
+ GFP_KERNEL);
+ if (!ifanew) {
+ PRINT_WARN("No memory for IP address " \
+ "handling. Some of the IPs " \
+ "will not be set on %s.\n",
+ card->dev_name);
+ QETH_DBF_TEXT2(0,trace,"TOIPNMEM");
+ } else {
+ qeth_clone_ifa(ifa,ifanew);
+ remove=qeth_add_ifa_to_list(
+ &card->ip_new_state.ip_ifa,
+ ifanew);
+ *((__u32*)(&dbf_text[0]))=
+ *((__u32*)&ifanew->
+ ifa_address);
+ *((__u32*)(&dbf_text[4]))=
+ *((__u32*)&ifanew->ifa_mask);
+ QETH_DBF_TEXT4(0,trace,dbf_text);
+ if (remove) {
+ kfree(ifanew);
+ QETH_DBF_TEXT4(0,trace,
+ "alrdv4rm");
+ }
+ }
+ ifa=ifa->ifa_next;
+ }
+ }
+ read_unlock(&vin4_dev->lock);
+ in_dev_put(vin4_dev);
+ } else {
+ QETH_DBF_TEXT2(0,trace,"nodvhol2");
+ QETH_DBF_TEXT2(0,trace,card->dev_name);
+ }
+ }
+ }
+ }
+#endif /* QETH_VLAN */
+
+ QETH_DBF_TEXT4(0,trace,"to-ipms");
+ if (atomic_read(&card->is_open))
+ for (im4=in4_dev->mc_list;im4;im4=im4->next) {
+ qeth_get_mac_for_ipm(im4->multiaddr,buf,in4_dev->dev);
+ ipmanew=(struct qeth_ipm_mac*)kmalloc(
+ sizeof(struct qeth_ipm_mac),GFP_KERNEL);
+ if (!ipmanew) {
+ PRINT_WARN("No memory for IPM address " \
+ "handling. Multicast IP %08x" \
+ "will not be set on %s.\n",
+ (__u32)im4->multiaddr,
+ card->dev_name);
+ QETH_DBF_TEXT2(0,trace,"TOIPMNMM");
+ } else {
+ memset(ipmanew,0,sizeof(struct qeth_ipm_mac));
+ memcpy(ipmanew->mac,buf,OSA_ADDR_LEN);
+ maddr=im4->multiaddr;
+ memcpy(&(ipmanew->ip[0]),&maddr,4);
+ memset(&(ipmanew->ip[4]),0xff,12);
+ ipmanew->next=NULL;
+ remove=qeth_add_mc_ifa_to_list(
+ &card->ip_mc_new_state.ipm_ifa,ipmanew);
+ sprintf(dbf_text,"%08x",*((__u32*)&ipmanew->ip));
+ QETH_DBF_TEXT4(0,trace,dbf_text);
+ QETH_DBF_HEX4(0,trace,&ipmanew->mac,
+ QETH_DBF_TRACE_LEN);
+ if (remove) {
+ QETH_DBF_TEXT4(0,trace,"mlrdy4rm");
+ kfree(ipmanew);
+ }
+ }
+ }
+
+#ifdef QETH_VLAN
+ QETH_DBF_TEXT4(0,trace,"to-vipms");
+ if ( (qeth_is_supported(IPA_FULL_VLAN)) &&
+ (atomic_read(&card->is_open)) ) {
+ card_group = (struct vlan_group *) card->vlangrp;
+ if (card_group) for (i=0;i<VLAN_GROUP_ARRAY_LEN;i++) {
+ if ((card_group->vlan_devices[i])&&
+ (card_group->vlan_devices[i]->flags&IFF_UP)) {
+ if ((vin4_dev=in_dev_get(card_group->
+ vlan_devices[i]))) {
+ read_lock(&vin4_dev->lock);
+ for (im4=vin4_dev->mc_list;im4;im4=im4->next) {
+ qeth_get_mac_for_ipm(im4->multiaddr,buf,vin4_dev->dev);
+ ipmanew=(struct qeth_ipm_mac*)kmalloc(
+ sizeof(struct qeth_ipm_mac),GFP_KERNEL);
+ if (!ipmanew) {
+ PRINT_WARN("No memory for IPM address " \
+ "handling. Multicast VLAN IP %08x" \
+ "will not be set on %s.\n",
+ (__u32)im4->multiaddr,
+ card->dev_name);
+ QETH_DBF_TEXT2(0,trace,"TOIPMNMM");
+ } else {
+ memset(ipmanew,0,sizeof(struct qeth_ipm_mac));
+ memcpy(ipmanew->mac,buf,OSA_ADDR_LEN);
+ maddr=im4->multiaddr;
+ memcpy(&(ipmanew->ip[0]),&maddr,4);
+ memset(&(ipmanew->ip[4]),0xff,12);
+ ipmanew->next=NULL;
+ remove=qeth_add_mc_ifa_to_list(
+ &card->ip_mc_new_state.ipm_ifa,
+ ipmanew);
+ sprintf(dbf_text,"%08x",
+ *((__u32*)&ipmanew->ip));
+ QETH_DBF_TEXT4(0,trace,dbf_text);
+ QETH_DBF_HEX4(0,trace,&ipmanew->mac,
+ QETH_DBF_TRACE_LEN);
+ if (remove) {
+ QETH_DBF_TEXT4(0,trace,"mlrdv4rm");
+ kfree(ipmanew);
+ }
+ }
+ }
+ read_unlock(&vin4_dev->lock);
+ in_dev_put(vin4_dev);
+ } else {
+ QETH_DBF_TEXT2(0,trace,"novdhol3");
+ QETH_DBF_TEXT2(0,trace,card->dev_name);
+ QETH_DBF_TEXT2(0,trace,card_group->
+ vlan_devices[i]->name);
+ }
+ }
+ }
+ }
+#endif /* QETH_VLAN */
+
+ read_unlock(&in4_dev->lock);
+ in_dev_put(in4_dev);
+}
+
+static void qeth_get_unique_id(qeth_card_t *card)
+{
+#ifdef QETH_IPV6
+#ifdef CONFIG_SHARED_IPV6_CARDS
+ ipa_cmd_t cmd;
+ int result;
+ char dbf_text[15];
+
+ if (!qeth_is_supported(IPA_IPv6)) {
+ card->unique_id=UNIQUE_ID_IF_CREATE_ADDR_FAILED|
+ UNIQUE_ID_NOT_BY_CARD;
+ return;
+ }
+ qeth_fill_ipa_cmd(card,&cmd,IPA_CMD_CREATE_ADDR,6);
+
+ *((__u16*)&cmd.data.create_destroy_addr.unique_id[6])=card->unique_id;
+
+ result=qeth_send_ipa_cmd(card,&cmd,1,IPA_CMD_STATE);
+
+ if (result) {
+ card->unique_id=UNIQUE_ID_IF_CREATE_ADDR_FAILED|
+ UNIQUE_ID_NOT_BY_CARD;
+ PRINT_WARN("couldn't get a unique id from the card on irq " \
+ "x%x (result=x%x), using default id. ipv6 " \
+ "autoconfig on other lpars may lead to duplicate " \
+ "ip addresses. please use manually " \
+ "configured ones.\n",card->irq0,result);
+ QETH_DBF_TEXT2(0,trace,"unid fld");
+ sprintf(dbf_text,"%4x%4x",card->irq0,result);
+ QETH_DBF_TEXT2(0,trace,dbf_text);
+ } else {
+ card->unique_id=
+ *((__u16*)&cmd.data.create_destroy_addr.unique_id[6]);
+ QETH_DBF_TEXT2(0,setup,"uniqueid");
+ sprintf(dbf_text,"%4x%4x",card->irq0,card->unique_id);
+ QETH_DBF_TEXT2(0,setup,dbf_text);
+ }
+#else /* CONFIG_SHARED_IPV6_CARDS */
+ card->unique_id=UNIQUE_ID_IF_CREATE_ADDR_FAILED|UNIQUE_ID_NOT_BY_CARD;
+#endif /* CONFIG_SHARED_IPV6_CARDS */
+#else /* QETH_IPV6 */
+ card->unique_id=UNIQUE_ID_IF_CREATE_ADDR_FAILED|UNIQUE_ID_NOT_BY_CARD;
+#endif /* QETH_IPV6 */
+}
+
+static void qeth_put_unique_id(qeth_card_t *card)
+{
+#ifdef QETH_IPV6
+#ifdef CONFIG_SHARED_IPV6_CARDS
+ ipa_cmd_t cmd;
+ int result;
+ char dbf_text[15];
+
+ /* is also true, if ipv6 is not supported on the card */
+ if ((card->unique_id&UNIQUE_ID_NOT_BY_CARD)==UNIQUE_ID_NOT_BY_CARD)
+ return;
+
+ qeth_fill_ipa_cmd(card,&cmd,IPA_CMD_DESTROY_ADDR,6);
+ *((__u16*)&cmd.data.create_destroy_addr.unique_id[6])=card->unique_id;
+ memcpy(&cmd.data.create_destroy_addr.unique_id[0],
+ card->dev->dev_addr,OSA_ADDR_LEN);
+
+ result=qeth_send_ipa_cmd(card,&cmd,1,IPA_CMD_STATE);
+
+ if (result) {
+ QETH_DBF_TEXT2(0,trace,"unibkfld");
+ sprintf(dbf_text,"%4x%4x",card->irq0,result);
+ QETH_DBF_TEXT2(0,trace,dbf_text);
+ }
+#else /* CONFIG_SHARED_IPV6_CARDS */
+ card->unique_id=UNIQUE_ID_IF_CREATE_ADDR_FAILED|UNIQUE_ID_NOT_BY_CARD;
+#endif /* CONFIG_SHARED_IPV6_CARDS */
+#else /* QETH_IPV6 */
+ card->unique_id=UNIQUE_ID_IF_CREATE_ADDR_FAILED|UNIQUE_ID_NOT_BY_CARD;
+#endif /* QETH_IPV6 */
+}
+
+static void qeth_do_setadapterparms_stuff(qeth_card_t *card)
+{
+ int result;
+ char dbf_text[15];
+
+ if (!qeth_is_supported(IPA_SETADAPTERPARMS)) {
+ return;
+ }
+
+ sprintf(dbf_text,"stap%4x",card->irq0);
+ QETH_DBF_TEXT4(0,trace,dbf_text);
+
+ result=qeth_send_setadapterparms_query(card);
+
+ if (result) {
+ PRINT_WARN("couldn't set adapter parameters on irq 0x%x: " \
+ "x%x\n",card->irq0,result);
+ QETH_DBF_TEXT1(0,trace,"SETADPFL");
+ sprintf(dbf_text,"%4x%4x",card->irq0,result);
+ QETH_DBF_TEXT1(1,trace,dbf_text);
+ return;
+ }
+
+ sprintf(dbf_text,"spap%4x",card->adp_supported);
+ QETH_DBF_TEXT2(0,trace,dbf_text);
+
+ if (qeth_is_adp_supported(IPA_SETADP_ALTER_MAC_ADDRESS)) {
+ sprintf(dbf_text,"rdmc%4x",card->irq0);
+ QETH_DBF_TEXT3(0,trace,dbf_text);
+ QETH_DBF_TEXT2(0,setup,dbf_text);
+
+ result=qeth_send_setadapterparms_change_addr(card,
+ IPA_SETADP_ALTER_MAC_ADDRESS,
+ CHANGE_ADDR_READ_MAC,card->dev->dev_addr,
+ OSA_ADDR_LEN);
+ if (result) {
+ PRINT_WARN("couldn't get MAC address on " \
+ "irq 0x%x: x%x\n",card->irq0,result);
+ QETH_DBF_TEXT1(0,trace,"NOMACADD");
+ sprintf(dbf_text,"%4x%4x",card->irq0,result);
+ QETH_DBF_TEXT1(1,trace,dbf_text);
+ } else {
+ QETH_DBF_HEX2(0,setup,card->dev->dev_addr,
+ __max(OSA_ADDR_LEN,QETH_DBF_SETUP_LEN));
+ QETH_DBF_HEX3(0,trace,card->dev->dev_addr,
+ __max(OSA_ADDR_LEN,QETH_DBF_TRACE_LEN));
+ }
+ }
+
+ if ( (card->link_type==QETH_MPC_LINK_TYPE_HSTR) ||
+ (card->link_type==QETH_MPC_LINK_TYPE_LANE_TR) ) {
+ sprintf(dbf_text,"hstr%4x",card->irq0);
+ QETH_DBF_TEXT3(0,trace,dbf_text);
+
+ if (qeth_is_adp_supported(IPA_SETADP_SET_BROADCAST_MODE)) {
+ result=qeth_send_setadapterparms_mode(card,
+ IPA_SETADP_SET_BROADCAST_MODE,
+ card->options.broadcast_mode);
+ if (result) {
+ PRINT_WARN("couldn't set broadcast mode on " \
+ "irq 0x%x: x%x\n",
+ card->irq0,result);
+ QETH_DBF_TEXT1(0,trace,"STBRDCST");
+ sprintf(dbf_text,"%4x%4x",card->irq0,result);
+ QETH_DBF_TEXT1(1,trace,dbf_text);
+ }
+ } else if (card->options.broadcast_mode) {
+ PRINT_WARN("set adapter parameters not available " \
+ "to set broadcast mode, using ALLRINGS " \
+ "on irq 0x%x:\n",card->irq0);
+ sprintf(dbf_text,"NOBC%4x",card->irq0);
+ QETH_DBF_TEXT1(0,trace,dbf_text);
+ }
+
+ if (qeth_is_adp_supported(IPA_SETADP_SET_BROADCAST_MODE)) {
+ result=qeth_send_setadapterparms_mode(card,
+ IPA_SETADP_ALTER_MAC_ADDRESS,
+ card->options.macaddr_mode);
+ if (result) {
+ PRINT_WARN("couldn't set macaddr mode on " \
+ "irq 0x%x: x%x\n",
+ card->irq0,result);
+ QETH_DBF_TEXT1(0,trace,"STMACMOD");
+ sprintf(dbf_text,"%4x%4x",card->irq0,result);
+ QETH_DBF_TEXT1(1,trace,dbf_text);
+ }
+ } else if (card->options.macaddr_mode) {
+ PRINT_WARN("set adapter parameters not available " \
+ "to set macaddr mode, using NONCANONICAL " \
+ "on irq 0x%x:\n",card->irq0);
+ sprintf(dbf_text,"NOMA%4x",card->irq0);
+ QETH_DBF_TEXT1(0,trace,dbf_text);
+ }
+ }
+}
+
+static int qeth_softsetup_card(qeth_card_t *card,int wait_for_lock)
+{
+ int result;
+ int use_setip_retries=1;
+ char dbf_text[15];
+ int do_a_startlan6=0;
+
+ if (wait_for_lock==QETH_WAIT_FOR_LOCK) {
+ my_spin_lock(&card->softsetup_lock);
+ } else if (wait_for_lock==QETH_DONT_WAIT_FOR_LOCK) {
+ if (!spin_trylock(&card->softsetup_lock)) {
+ return -EAGAIN;
+ }
+ } else if (wait_for_lock==QETH_LOCK_ALREADY_HELD) {
+ use_setip_retries=0; /* we are in recovery and don't want
+ to repeat setting ips on and on */
+ } else {
+ return -EINVAL;
+ }
+
+ qeth_save_dev_flag_state(card);
+
+ sprintf(dbf_text,"ssc%c%4x",wait_for_lock?'w':'n',card->irq0);
+ QETH_DBF_TEXT1(0,trace,dbf_text);
+
+ if (!atomic_read(&card->is_softsetup)) {
+ atomic_set(&card->enable_routing_attempts4,
+ QETH_ROUTING_ATTEMPTS);
+#ifdef QETH_IPV6
+ atomic_set(&card->enable_routing_attempts6,
+ QETH_ROUTING_ATTEMPTS);
+#endif /* QETH_IPV6 */
+ if ( (!atomic_read(&card->is_startlaned)) &&
+ (atomic_read(&card->startlan_attempts)) ) {
+ atomic_dec(&card->startlan_attempts);
+ QETH_DBF_TEXT2(0,trace,"startlan");
+ netif_stop_queue(card->dev);
+ result=qeth_send_startlan(card,4);
+ if (result) {
+ PRINT_WARN("couldn't send STARTLAN on %s " \
+ "(CHPID 0x%X): 0x%x (%s)\n",
+ card->dev_name,card->chpid,result,
+ (result==0xe080)?
+ "startlan disabled (link " \
+ "failure -- please check the " \
+ "network, plug in the cable or " \
+ "enable the OSA port":
+ "unknown return code");
+ sprintf(dbf_text,"stln%4x",result);
+ QETH_DBF_TEXT2(0,trace,dbf_text);
+ atomic_set(&card->is_softsetup,0);
+ atomic_set(&card->is_startlaned,0);
+ /* do not return an error */
+ if (result==0xe080) {
+ result=0;
+ }
+ goto out;
+ }
+ do_a_startlan6=1;
+ }
+ netif_wake_queue(card->dev);
+
+ qeth_do_setadapterparms_stuff(card);
+
+ if (!qeth_is_supported(IPA_ARP_PROCESSING)) {
+ PRINT_WARN("oops... ARP processing not supported " \
+ "on %s!\n",card->dev_name);
+ QETH_DBF_TEXT1(0,trace,"NOarpPRC");
+ } else {
+ QETH_DBF_TEXT2(0,trace,"enaARPpr");
+ result=qeth_send_setassparms_simple_without_data(
+ card,IPA_ARP_PROCESSING,IPA_CMD_ASS_START);
+ if (result) {
+ PRINT_WARN("Could not start ARP processing " \
+ "assist on %s: 0x%x\n",
+ card->dev_name,result);
+ sprintf(dbf_text,"ARPp%4x",result);
+ QETH_DBF_TEXT2(0,trace,dbf_text);
+ atomic_set(&card->is_softsetup,0);
+ goto out;
+ }
+ }
+
+ if (qeth_is_supported(IPA_IP_FRAGMENTATION)) {
+ PRINT_INFO("IP fragmentation supported on " \
+ "%s... :-)\n",card->dev_name);
+ QETH_DBF_TEXT2(0,trace,"enaipfrg");
+ result=qeth_send_setassparms_simple_without_data(
+ card,IPA_IP_FRAGMENTATION,IPA_CMD_ASS_START);
+ if (result) {
+ PRINT_WARN("Could not start IP fragmenting " \
+ "assist on %s: 0x%x, continuing\n",
+ card->dev_name,result);
+ sprintf(dbf_text,"IFRG%4x",result);
+ QETH_DBF_TEXT2(0,trace,dbf_text);
+ /* go on */
+ }
+ }
+
+ if (card->options.fake_ll==FAKE_LL) {
+ if (qeth_is_supported(IPA_SOURCE_MAC_AVAIL)) {
+ QETH_DBF_TEXT2(0,trace,"enainsrc");
+ result=qeth_send_setassparms_simple_without_data(
+ card,IPA_SOURCE_MAC_AVAIL,IPA_CMD_ASS_START);
+ if (result) {
+ PRINT_WARN("Could not start " \
+ "inbound source " \
+ "assist on %s: 0x%x, " \
+ "continuing\n",
+ card->dev_name,result);
+ sprintf(dbf_text,"INSR%4x",result);
+ QETH_DBF_TEXT2(0,trace,dbf_text);
+ /* go on */
+ }
+ } else {
+ PRINT_INFO("Inbound source addresses not " \
+ "supported on %s\n",card->dev_name);
+ }
+ }
+#ifdef QETH_VLAN
+ if (!qeth_is_supported(IPA_FULL_VLAN)) {
+ PRINT_WARN("VLAN not supported on %s\n",
+ card->dev_name);
+ QETH_DBF_TEXT2(0,trace,"vlnotsup");
+ } else {
+ result=qeth_send_setassparms_simple_without_data(
+ card,IPA_VLAN_PRIO,IPA_CMD_ASS_START);
+ QETH_DBF_TEXT2(0,trace,"enavlan");
+ if (result) {
+ PRINT_WARN("Could not start vlan "
+ "assist on %s: 0x%x, continuing\n",
+ card->dev_name,result);
+ sprintf(dbf_text,"VLAN%4x",result);
+ QETH_DBF_TEXT2(0,trace,dbf_text);
+ /* go on*/
+ }
+ else {
+ card->dev->features |=
+ NETIF_F_HW_VLAN_TX |
+ NETIF_F_HW_VLAN_RX;
+
+ }
+ }
+#endif /* QETH_VLAN */
+
+ if (!qeth_is_supported(IPA_MULTICASTING)) {
+ PRINT_WARN("multicasting not supported on %s\n",
+ card->dev_name);
+ QETH_DBF_TEXT2(0,trace,"mcnotsup");
+ } else {
+ result=qeth_send_setassparms_simple_without_data(
+ card,IPA_MULTICASTING,IPA_CMD_ASS_START);
+ QETH_DBF_TEXT2(0,trace,"enamcass");
+ if (result) {
+ PRINT_WARN("Could not start multicast "
+ "assist on %s: 0x%x, continuing\n",
+ card->dev_name,result);
+ sprintf(dbf_text,"MCAS%4x",result);
+ QETH_DBF_TEXT2(0,trace,dbf_text);
+ /* go on*/
+ } else {
+ card->dev->flags|=IFF_MULTICAST;
+ }
+ }
+
+ if (!qeth_is_supported(IPA_IPv6)) {
+ QETH_DBF_TEXT2(0,trace,"ipv6ntsp");
+ PRINT_WARN("IPv6 not supported on %s\n",
+ card->dev_name);
+ } else {
+ if (do_a_startlan6) {
+ QETH_DBF_TEXT2(0,trace,"startln6");
+ netif_stop_queue(card->dev);
+ result=qeth_send_startlan(card,6);
+ if (result) {
+ sprintf(dbf_text,"stl6%4x",result);
+ QETH_DBF_TEXT2(0,trace,dbf_text);
+ atomic_set(&card->is_softsetup,0);
+ /* do not return an error */
+ if (result==0xe080) {
+ result=0;
+ }
+ goto out;
+ }
+ }
+ netif_wake_queue(card->dev);
+ QETH_DBF_TEXT2(0,trace,"qipassi6");
+ result=qeth_send_qipassist(card,6);
+ if (result) {
+ PRINT_WARN("couldn't send QIPASSIST6 on %s: " \
+ "0x%x\n",card->dev_name,result);
+ sprintf(dbf_text,"QIP6%4x",result);
+ QETH_DBF_TEXT2(0,trace,dbf_text);
+ atomic_set(&card->is_softsetup,0);
+ goto out;
+ }
+
+ sprintf(dbf_text,"%4x%4x",card->ipa6_supported,
+ card->ipa6_enabled);
+ QETH_DBF_TEXT2(0,trace,dbf_text);
+ QETH_DBF_TEXT2(0,trace,"enaipv46");
+ result=qeth_send_setassparms_simple_with_data(
+ card,IPA_IPv6,IPA_CMD_ASS_START,3);
+ if (result) {
+ PRINT_WARN("Could not enable IPv4&6 assist " \
+ "on %s: " \
+ "0x%x, continuing\n",
+ card->dev_name,result);
+ sprintf(dbf_text,"I46A%4x",result);
+ QETH_DBF_TEXT2(0,trace,dbf_text);
+ /* go on */
+ }
+ QETH_DBF_TEXT2(0,trace,"enaipv6");
+ result=qeth_send_setassparms_simple_without_data6(
+ card,IPA_IPv6,IPA_CMD_ASS_START);
+ if (result) {
+ PRINT_WARN("Could not start IPv6 assist " \
+ "on %s: " \
+ "0x%x, continuing\n",
+ card->dev_name,result);
+ sprintf(dbf_text,"I6AS%4x",result);
+ QETH_DBF_TEXT2(0,trace,dbf_text);
+ /* go on */
+ }
+ QETH_DBF_TEXT2(0,trace,"enapstr6");
+ result=qeth_send_setassparms_simple_without_data6(
+ card,IPA_PASSTHRU,IPA_CMD_ASS_START);
+ if (result) {
+ PRINT_WARN("Could not enable passthrough " \
+ "on %s: " \
+ "0x%x, continuing\n",
+ card->dev_name,result);
+ sprintf(dbf_text,"PSTR%4x",result);
+ QETH_DBF_TEXT2(0,trace,dbf_text);
+ /* go on */
+ }
+ }
+
+ card->broadcast_capable=0;
+ if (!qeth_is_supported(IPA_FILTERING)) {
+ QETH_DBF_TEXT2(0,trace,"filtntsp");
+ PRINT_WARN("Broadcasting not supported on %s\n",
+ card->dev_name);
+ } else {
+ QETH_DBF_TEXT2(0,trace,"enafiltr");
+ result=qeth_send_setassparms_simple_without_data(
+ card,IPA_FILTERING,IPA_CMD_ASS_START);
+ if (result) {
+ PRINT_WARN("Could not enable broadcast " \
+ "filtering on %s: " \
+ "0x%x, continuing\n",
+ card->dev_name,result);
+ sprintf(dbf_text,"FLT1%4x",result);
+ QETH_DBF_TEXT2(0,trace,dbf_text);
+ goto go_on_filt;
+ }
+ result=qeth_send_setassparms_simple_with_data(
+ card,IPA_FILTERING,IPA_CMD_ASS_CONFIGURE,1);
+ if (result) {
+ PRINT_WARN("Could not set up broadcast " \
+ "filtering on %s: " \
+ "0x%x, continuing\n",
+ card->dev_name,result);
+ sprintf(dbf_text,"FLT2%4x",result);
+ QETH_DBF_TEXT2(0,trace,dbf_text);
+ goto go_on_filt;
+ }
+ card->dev->flags|=IFF_BROADCAST;
+ card->broadcast_capable=1;
+
+ }
+go_on_filt:
+ if (card->options.checksum_type==HW_CHECKSUMMING) {
+ if (!qeth_is_supported(IPA_INBOUND_CHECKSUM)) {
+ PRINT_WARN("Inbound HW checksumming not " \
+ "supported on %s, continuing " \
+ "using inbound sw checksumming\n",
+ card->dev_name);
+ QETH_DBF_TEXT2(0,trace,"ibckntsp");
+ card->options.checksum_type=SW_CHECKSUMMING;
+ } else {
+ QETH_DBF_TEXT2(0,trace,"ibcksupp");
+ result=qeth_send_setassparms_simple_without_data(
+ card,IPA_INBOUND_CHECKSUM,
+ IPA_CMD_ASS_START);
+ if (result) {
+ PRINT_WARN("Could not start inbound " \
+ "checksumming on %s: " \
+ "0x%x, " \
+ "continuing using " \
+ "inbound sw checksumming\n",
+ card->dev_name,result);
+ sprintf(dbf_text,"SIBC%4x",result);
+ QETH_DBF_TEXT2(0,trace,dbf_text);
+ card->options.checksum_type=
+ SW_CHECKSUMMING;
+ goto go_on_checksum;
+ }
+ result=qeth_send_setassparms_simple_with_data(
+ card,IPA_INBOUND_CHECKSUM,
+ IPA_CMD_ASS_ENABLE,
+ IPA_CHECKSUM_ENABLE_MASK);
+ if (result) {
+ PRINT_WARN("Could not enable inbound " \
+ "checksumming on %s: " \
+ "0x%x, " \
+ "continuing using " \
+ "inbound sw checksumming\n",
+ card->dev_name,result);
+ sprintf(dbf_text,"EIBC%4x",result);
+ QETH_DBF_TEXT2(0,trace,dbf_text);
+ card->options.checksum_type=
+ SW_CHECKSUMMING;
+ goto go_on_checksum;
+ }
+ }
+ }
+go_on_checksum:
+
+ atomic_set(&card->is_softsetup,1);
+ }
+
+ if (atomic_read(&card->enable_routing_attempts4)) {
+ if (card->options.routing_type4) {
+ sprintf(dbf_text,"strtg4%2x",
+ card->options.routing_type4);
+ QETH_DBF_TEXT2(0,trace,dbf_text);
+ result=qeth_send_setrtg(card,card->options.
+ routing_type4,4);
+ if (result) {
+ if (atomic_dec_return(&card->
+ enable_routing_attempts4)) {
+ PRINT_WARN("couldn't set up v4 routing type " \
+ "on %s: 0x%x (%s).\nWill try " \
+ "next time again.\n",
+ card->dev_name,result,
+ ((result==0xe010)||
+ (result==0xe008))?
+ "primary already defined":
+ ((result==0xe011)||
+ (result==0xe009))?
+ "secondary already defined":
+ (result==0xe012)?
+ "invalid indicator":
+ "unknown return code");
+ sprintf(dbf_text,"sRT4%4x",result);
+ QETH_DBF_TEXT2(0,trace,dbf_text);
+ atomic_set(&card->rt4fld,1);
+ } else {
+ PRINT_WARN("couldn't set up v4 routing type " \
+ "on %s: 0x%x (%s).\nTrying to " \
+ "continue without routing.\n",
+ card->dev_name,result,
+ ((result==0xe010)||
+ (result==0xe008))?
+ "primary already defined":
+ ((result==0xe011)||
+ (result==0xe009))?
+ "secondary already defined":
+ (result==0xe012)?
+ "invalid indicator":
+ "unknown return code");
+ sprintf(dbf_text,"SRT4%4x",result);
+ QETH_DBF_TEXT2(0,trace,dbf_text);
+ atomic_set(&card->rt4fld,1);
+ }
+ } else { /* routing set correctly */
+ atomic_set(&card->enable_routing_attempts4,0);
+ atomic_set(&card->rt4fld,0);
+ }
+ } else {
+ atomic_set(&card->enable_routing_attempts4,0);
+ atomic_set(&card->rt4fld,0);
+ }
+ }
+
+#ifdef QETH_IPV6
+ if (atomic_read(&card->enable_routing_attempts6)) {
+ if (card->options.routing_type6) {
+ sprintf(dbf_text,"strtg6%2x",
+ card->options.routing_type6);
+ QETH_DBF_TEXT2(0,trace,dbf_text);
+ result=qeth_send_setrtg(card,card->options.
+ routing_type6,6);
+ if (result) {
+ if (atomic_dec_return(&card->
+ enable_routing_attempts6)) {
+ PRINT_WARN("couldn't set up v6 routing type " \
+ "on %s: 0x%x (%s).\nWill try " \
+ "next time again.\n",
+ card->dev_name,result,
+ ((result==0xe010)||
+ (result==0xe008))?
+ "primary already defined":
+ ((result==0xe011)||
+ (result==0xe009))?
+ "secondary already defined":
+ (result==0xe012)?
+ "invalid indicator":
+ "unknown return code");
+ sprintf(dbf_text,"sRT6%4x",result);
+ QETH_DBF_TEXT2(0,trace,dbf_text);
+ atomic_set(&card->rt6fld,1);
+ } else {
+ PRINT_WARN("couldn't set up v6 routing type " \
+ "on %s: 0x%x (%s).\nTrying to " \
+ "continue without routing.\n",
+ card->dev_name,result,
+ ((result==0xe010)||
+ (result==0xe008))?
+ "primary already defined":
+ ((result==0xe011)||
+ (result==0xe009))?
+ "secondary already defined":
+ (result==0xe012)?
+ "invalid indicator":
+ "unknown return code");
+ sprintf(dbf_text,"SRT6%4x",result);
+ QETH_DBF_TEXT2(0,trace,dbf_text);
+ atomic_set(&card->rt6fld,1);
+ }
+ } else { /* routing set correctly */
+ atomic_set(&card->enable_routing_attempts6,0);
+ atomic_set(&card->rt6fld,0);
+ }
+ } else {
+ atomic_set(&card->enable_routing_attempts6,0);
+ atomic_set(&card->rt6fld,0);
+ }
+ }
+#endif /* QETH_IPV6 */
+
+ QETH_DBF_TEXT2(0,trace,"delvipa");
+ qeth_set_vipas(card,0);
+ QETH_DBF_TEXT2(0,trace,"toip/ms");
+ qeth_takeover_ip_ipms(card);
+#ifdef QETH_IPV6
+ qeth_takeover_ip_ipms6(card);
+#endif /* QETH_IPV6 */
+ QETH_DBF_TEXT2(0,trace,"setvipa");
+ qeth_set_vipas(card,1);
+
+ result=qeth_setips(card,use_setip_retries);
+ if (result) { /* by now, qeth_setips does not return errors */
+ PRINT_WARN("couldn't set up IPs on %s: 0x%x\n",
+ card->dev_name,result);
+ sprintf(dbf_text,"SSIP%4x",result);
+ QETH_DBF_TEXT2(0,trace,dbf_text);
+ atomic_set(&card->is_softsetup,0);
+ goto out;
+ }
+
+ result=qeth_setipms(card,use_setip_retries);
+ if (result) { /* by now, qeth_setipms does not return errors */
+ PRINT_WARN("couldn't set up multicast IPs on %s: 0x%x\n",
+ card->dev_name,result);
+ sprintf(dbf_text,"ssim%4x",result);
+ QETH_DBF_TEXT2(0,trace,dbf_text);
+ atomic_set(&card->is_softsetup,0);
+ goto out;
+ }
+ out:
+ if (!result) {
+ netif_wake_queue(card->dev);
+ }
+ if (wait_for_lock!=QETH_LOCK_ALREADY_HELD)
+ my_spin_unlock(&card->softsetup_lock);
+ return result;
+}
+
+static int qeth_softsetup_thread(void *param)
+{
+ char dbf_text[15];
+ char name[15];
+ qeth_card_t *card=(qeth_card_t*)param;
+
+ daemonize();
+
+ /* set a nice name ... */
+ sprintf(name, "qethsoftd%04x", card->irq0);
+ strcpy(current->comm, name);
+
+ sprintf(dbf_text,"ssth%4x",card->irq0);
+ QETH_DBF_TEXT2(0,trace,dbf_text);
+
+ atomic_set(&card->softsetup_thread_is_running,1);
+ for (;;) {
+ if (atomic_read(&card->shutdown_phase)) goto out;
+ down_interruptible(&card->softsetup_thread_sem);
+ sprintf(dbf_text,"ssst%4x",card->irq0);
+ QETH_DBF_TEXT2(0,trace,dbf_text);
+ if (atomic_read(&card->shutdown_phase)) goto out;
+ while (qeth_softsetup_card(card,QETH_DONT_WAIT_FOR_LOCK)
+ ==-EAGAIN) {
+ if (atomic_read(&card->shutdown_phase)) goto out;
+ qeth_wait_nonbusy(QETH_IDLE_WAIT_TIME);
+ }
+ sprintf(dbf_text,"sstd%4x",card->irq0);
+ QETH_DBF_TEXT2(0,trace,dbf_text);
+ netif_wake_queue(card->dev);
+ }
+ out:
+ atomic_set(&card->softsetup_thread_is_running,0);
+
+ sprintf(dbf_text,"lsst%4x",card->irq0);
+ QETH_DBF_TEXT2(0,trace,dbf_text);
+
+ return 0;
+}
+
+static void qeth_softsetup_thread_starter(void *data)
+{
+ char dbf_text[15];
+ qeth_card_t *card=(qeth_card_t *)data;
+
+ sprintf(dbf_text,"ssts%4x",card->irq0);
+ QETH_DBF_TEXT4(0,trace,dbf_text);
+ sema_init(&card->softsetup_thread_sem,0);
+ kernel_thread(qeth_softsetup_thread,card,SIGCHLD);
+}
+
+static void qeth_start_reinit_thread(qeth_card_t *card)
+{
+ char dbf_text[15];
+
+ /* we allow max 2 reinit threads, one could be just about to
+ * finish and the next would be waiting. another waiting
+ * reinit_thread is not necessary. */
+ if (atomic_read(&card->reinit_counter)<2) {
+ atomic_inc(&card->reinit_counter);
+ if (atomic_read(&card->shutdown_phase)) {
+ atomic_dec(&card->reinit_counter);
+ return;
+ }
+ sprintf(dbf_text,"stri%4x",card->irq0);
+ QETH_DBF_TEXT2(0,trace,dbf_text);
+ PRINT_STUPID("starting reinit-thread\n");
+ kernel_thread(qeth_reinit_thread,card,SIGCHLD);
+ }
+}
+
+static void qeth_recover(void *data)
+{
+ qeth_card_t *card;
+ int i;
+ char dbf_text[15];
+
+ card=(qeth_card_t*)data;
+
+ sprintf(dbf_text,"recv%4x",card->irq0);
+ QETH_DBF_TEXT2(0,trace,dbf_text);
+
+ if (atomic_compare_and_swap(0,1,&card->in_recovery))
+ return;
+
+ i=atomic_read(&card->problem);
+
+ sprintf(dbf_text,"PROB%4x",i);
+ QETH_DBF_TEXT2(0,trace,dbf_text);
+
+ PRINT_WARN("recovery was scheduled on irq 0x%x (%s) with " \
+ "problem 0x%x\n",
+ card->irq0,card->dev_name,i);
+ switch (i) {
+ case PROBLEM_RECEIVED_IDX_TERMINATE:
+ if (atomic_read(&card->in_recovery))
+ atomic_set(&card->break_out,QETH_BREAKOUT_AGAIN);
+ break;
+ case PROBLEM_CARD_HAS_STARTLANED:
+ PRINT_WARN("You are lucky! Somebody either fixed the " \
+ "network problem, plugged the cable back in " \
+ "or enabled the OSA port on %s (CHPID 0x%X). " \
+ "The link has come up.\n",
+ card->dev_name,card->chpid);
+ sprintf(dbf_text,"CBIN%4x",i);
+ QETH_DBF_TEXT1(0,trace,dbf_text);
+ atomic_set(&card->is_softsetup,0);
+ qeth_set_dev_flag_running(card);
+ atomic_set(&card->enable_routing_attempts4,
+ QETH_ROUTING_ATTEMPTS);
+ qeth_clear_ifa4_list(&card->ip_new_state.ip_ifa);
+#ifdef QETH_IPV6
+ atomic_set(&card->enable_routing_attempts6,
+ QETH_ROUTING_ATTEMPTS);
+ qeth_clear_ifa6_list(&card->ip_new_state.ip6_ifa);
+#endif /* QETH_IPV6 */
+ qeth_clear_ifamc_list(&card->ip_mc_new_state.ipm_ifa);
+#ifdef QETH_IPV6
+ qeth_clear_ifamc_list(&card->ip_mc_new_state.ipm6_ifa);
+#endif /* QETH_IPV6 */
+ qeth_refresh_vipa_states(card);
+ qeth_start_softsetup_thread(card);
+ atomic_set(&card->in_recovery,0);
+ break;
+ case PROBLEM_RESETTING_EVENT_INDICATOR:
+ /* we do nothing here */
+ break;
+ case PROBLEM_ACTIVATE_CHECK_CONDITION:
+ case PROBLEM_GENERAL_CHECK:
+ case PROBLEM_USER_TRIGGERED_RECOVERY:
+ case PROBLEM_AFFE:
+ case PROBLEM_MACHINE_CHECK:
+ case PROBLEM_BAD_SIGA_RESULT:
+ case PROBLEM_TX_TIMEOUT:
+ qeth_start_reinit_thread(card);
+ break;
+ }
+}
+
+static void qeth_schedule_recovery(qeth_card_t *card)
+{
+ if (card) {
+ INIT_LIST_HEAD(&card->tqueue.list);
+ card->tqueue.routine=qeth_recover;
+ card->tqueue.data=card;
+ card->tqueue.sync=0;
+ schedule_task(&card->tqueue);
+ } else {
+ QETH_DBF_TEXT2(1,trace,"scdnocrd");
+ PRINT_WARN("recovery requested to be scheduled " \
+ "with no card!\n");
+ }
+}
+
+static void qeth_qdio_input_handler(int irq,unsigned int status,
+ unsigned int qdio_error,unsigned int siga_error,
+ unsigned int queue,
+ int first_element,int count,
+ unsigned long card_ptr)
+{
+ struct net_device *dev;
+ qeth_card_t *card;
+ int problem;
+ int sbalf15;
+ char dbf_text[15]="qinbhnXX";
+
+ *((__u16*)(&dbf_text[6]))=(__u16)irq;
+ QETH_DBF_HEX6(0,trace,dbf_text,QETH_DBF_TRACE_LEN);
+
+ card=(qeth_card_t *)card_ptr;
+
+#ifdef QETH_PERFORMANCE_STATS
+ card->perf_stats.inbound_start_time=NOW;
+#endif /* QETH_PERFORMANCE_STATS */
+ dev=card->dev;
+
+ if (status&QDIO_STATUS_LOOK_FOR_ERROR) {
+ if (status&QDIO_STATUS_ACTIVATE_CHECK_CONDITION) {
+ problem=PROBLEM_ACTIVATE_CHECK_CONDITION;
+ PRINT_WARN("activate queues on irq 0x%x: " \
+ "dstat=0x%x, cstat=0x%x\n",irq,
+ card->devstat2->dstat,
+ card->devstat2->cstat);
+ atomic_set(&card->problem,problem);
+ QETH_DBF_TEXT1(0,trace,"IHACTQCK");
+ sprintf(dbf_text,"%4x%4x",first_element,count);
+ QETH_DBF_TEXT1(0,trace,dbf_text);
+ sprintf(dbf_text,"%4x%4x",queue,status);
+ QETH_DBF_TEXT1(0,trace,dbf_text);
+ sprintf(dbf_text,"qscd%4x",card->irq0);
+ QETH_DBF_TEXT1(1,trace,dbf_text);
+ qeth_schedule_recovery(card);
+ return;
+ }
+ sbalf15=(card->inbound_qdio_buffers[(first_element+count-1)&
+ QDIO_MAX_BUFFERS_PER_Q].
+ element[15].flags)&&0xff;
+ PRINT_STUPID("inbound qdio transfer error on irq 0x%04x. " \
+ "qdio_error=0x%x (more than one: %c), " \
+ "siga_error=0x%x (more than one: %c), " \
+ "sbalf15=x%x, bufno=x%x\n",
+ irq,qdio_error,
+ (status&QDIO_STATUS_MORE_THAN_ONE_QDIO_ERROR)?
+ 'y':'n',siga_error,
+ (status&QDIO_STATUS_MORE_THAN_ONE_SIGA_ERROR)?
+ 'y':'n',
+ sbalf15,first_element);
+ sprintf(dbf_text,"IQTI%4x",card->irq0);
+ QETH_DBF_TEXT1(0,trace,dbf_text);
+ QETH_DBF_TEXT1(0,qerr,dbf_text);
+ sprintf(dbf_text,"%4x%4x",first_element,count);
+ QETH_DBF_TEXT1(0,trace,dbf_text);
+ QETH_DBF_TEXT1(0,qerr,dbf_text);
+ sprintf(dbf_text,"%2x%4x%2x",queue,status,sbalf15);
+ QETH_DBF_TEXT1(0,trace,dbf_text);
+ QETH_DBF_TEXT1(0,qerr,dbf_text);
+ sprintf(dbf_text,"%4x%4x",qdio_error,siga_error);
+ QETH_DBF_TEXT1(0,trace,dbf_text);
+ QETH_DBF_TEXT1(0,qerr,dbf_text);
+ /* we inform about error more detailed in
+ * qeth_read_in_buffer() */
+ }
+
+ for (;;) {
+ qeth_get_linux_addrs_for_buffer(card,first_element);
+ qeth_read_in_buffer(card,first_element);
+ qeth_queue_input_buffer(card,first_element,
+ QDIO_FLAG_UNDER_INTERRUPT);
+ count--;
+ if (count)
+ first_element=(first_element+1)&
+ (QDIO_MAX_BUFFERS_PER_Q-1);
+ else break;
+ }
+}
+
+static void qeth_qdio_output_handler(int irq,unsigned int status,
+ unsigned int qdio_error,unsigned int siga_error,
+ unsigned int queue,
+ int first_element,int count,
+ unsigned long card_ptr)
+{
+ qeth_card_t *card;
+ int mycnt,problem,buffers_used;
+ int sbalf15;
+ char dbf_text[15]="qouthnXX";
+ char dbf_text2[15]="stchdwXX";
+ int last_pci_hit=0,switch_state;
+ int last_pci;
+
+ *((__u16*)(&dbf_text[6]))=(__u16)irq;
+ QETH_DBF_HEX6(0,trace,dbf_text,QETH_DBF_TRACE_LEN);
+
+ mycnt=count;
+ card=(qeth_card_t *)card_ptr;
+
+ if (status&QDIO_STATUS_LOOK_FOR_ERROR) {
+ if (status&QDIO_STATUS_ACTIVATE_CHECK_CONDITION) {
+ problem=PROBLEM_ACTIVATE_CHECK_CONDITION;
+ PRINT_WARN("activate queues on irq 0x%x: " \
+ "dstat=0x%x, cstat=0x%x\n",irq,
+ card->devstat2->dstat,
+ card->devstat2->cstat);
+ atomic_set(&card->problem,problem);
+ QETH_DBF_TEXT1(0,trace,"OHACTQCK");
+ sprintf(dbf_text,"%4x%4x",first_element,count);
+ QETH_DBF_TEXT1(0,trace,dbf_text);
+ sprintf(dbf_text,"%4x%4x",queue,status);
+ QETH_DBF_TEXT1(0,trace,dbf_text);
+ sprintf(dbf_text,"qscd%4x",card->irq0);
+ QETH_DBF_TEXT1(1,trace,dbf_text);
+ qeth_schedule_recovery(card);
+ goto out;
+ }
+ sbalf15=(card->outbound_ringbuffer[queue]->buffer[
+ (first_element+count-1)&
+ QDIO_MAX_BUFFERS_PER_Q].element[15].flags)&0xff;
+ PRINT_STUPID("outbound qdio transfer error on irq %04x, " \
+ "queue=%i. qdio_error=0x%x (more than one: %c)," \
+ " siga_error=0x%x (more than one: %c), " \
+ "sbalf15=x%x, bufno=x%x\n",
+ irq,queue,qdio_error,status&
+ QDIO_STATUS_MORE_THAN_ONE_QDIO_ERROR?'y':'n',
+ siga_error,status&
+ QDIO_STATUS_MORE_THAN_ONE_SIGA_ERROR?'y':'n',
+ sbalf15,first_element);
+ sprintf(dbf_text,"IQTO%4x",card->irq0);
+ QETH_DBF_TEXT1(0,trace,dbf_text);
+ QETH_DBF_TEXT1(0,qerr,dbf_text);
+ sprintf(dbf_text,"%4x%4x",first_element,count);
+ QETH_DBF_TEXT1(0,trace,dbf_text);
+ QETH_DBF_TEXT1(0,qerr,dbf_text);
+ sprintf(dbf_text,"%2x%4x%2x",queue,status,sbalf15);
+ QETH_DBF_TEXT1(0,trace,dbf_text);
+ QETH_DBF_TEXT1(0,qerr,dbf_text);
+ sprintf(dbf_text,"%4x%4x",qdio_error,siga_error);
+ QETH_DBF_TEXT1(0,trace,dbf_text);
+ QETH_DBF_TEXT1(0,qerr,dbf_text);
+ /* we maybe do recovery or dst_link_failures
+ * in qeth_free_buffer */
+ }
+
+ if (mycnt) {
+ last_pci=atomic_read(&card->last_pci_pos[queue]);
+ for (;;) {
+ qeth_free_buffer(card,queue,first_element,
+ qdio_error,siga_error);
+ if (first_element==last_pci) last_pci_hit=1;
+ mycnt--;
+ if (mycnt>0)
+ first_element=(first_element+1)&
+ (QDIO_MAX_BUFFERS_PER_Q-1);
+ else break;
+ }
+ }
+
+ buffers_used=atomic_return_sub(count,
+ &card->outbound_used_buffers[queue]);
+
+ switch (card->send_state[queue]) {
+ case SEND_STATE_PACK:
+ switch_state=(atomic_read
+ (&card->outbound_used_buffers[queue])<=
+ LOW_WATERMARK_PACK);
+ /* first_element is the last buffer that we got back
+ * from hydra */
+ if (switch_state||last_pci_hit) {
+ *((__u16*)(&dbf_text2[6]))=card->irq0;
+ QETH_DBF_HEX3(0,trace,dbf_text2,QETH_DBF_TRACE_LEN);
+ if (atomic_swap(&card->outbound_ringbuffer_lock
+ [queue],QETH_LOCK_FLUSH)
+ ==QETH_LOCK_UNLOCKED) {
+ /* we stop the queue as we try to not run
+ * onto the outbound_ringbuffer_lock --
+ * this will not prevent it totally, but
+ * reduce it. in high traffic situations,
+ * it saves around 20us per second, hopefully
+ * this is amortized by calling netif_... */
+ netif_stop_queue(card->dev);
+ qeth_flush_packed_packets
+ (card,queue,
+ QDIO_FLAG_UNDER_INTERRUPT);
+ /* only switch state to non-packing, if
+ * the amount of used buffers decreased */
+ if (switch_state)
+ card->send_state[queue]=
+ SEND_STATE_DONT_PACK;
+ netif_wake_queue(card->dev);
+ atomic_set(&card->outbound_ringbuffer_lock[
+ queue],QETH_LOCK_UNLOCKED);
+ } /* if the lock was UNLOCKED, we flush ourselves,
+ otherwise this is done in do_send_packet when
+ the lock is released */
+#ifdef QETH_PERFORMANCE_STATS
+ card->perf_stats.sc_p_dp++;
+#endif /* QETH_PERFORMANCE_STATS */
+ }
+ break;
+ default: break;
+ }
+
+ /* we don't have to start the queue, if it was started already */
+ if (buffers_used<QDIO_MAX_BUFFERS_PER_Q-1)
+ return;
+
+ out:
+ netif_wake_queue(card->dev);
+}
+
+static void qeth_interrupt_handler_read(int irq,void *devstat,
+ struct pt_regs *p)
+{
+ devstat_t *stat;
+ int cstat,dstat;
+ int problem;
+ qeth_card_t *card;
+ int rqparam;
+ char dbf_text[15];
+ int result;
+
+ stat=(devstat_t *)devstat;
+ cstat=stat->cstat;
+ dstat=stat->dstat;
+ rqparam=stat->intparm;
+
+ sprintf(dbf_text,"rint%4x",irq);
+ QETH_DBF_TEXT4(0,trace,dbf_text);
+ sprintf(dbf_text,"%4x%4x",cstat,dstat);
+ QETH_DBF_TEXT4(0,trace,dbf_text);
+ sprintf(dbf_text,"%4x",rqparam);
+ QETH_DBF_TEXT4(0,trace,dbf_text);
+
+ if (!rqparam) {
+ PRINT_STUPID("got unsolicited interrupt in read handler, " \
+ "irq 0x%x\n",irq);
+ return;
+ }
+
+ card=qeth_get_card_by_irq(irq);
+ if (!card) return;
+
+ if ((rqparam==CLEAR_STATE) || (stat->flag&DEVSTAT_CLEAR_FUNCTION)) {
+ atomic_set(&card->clear_succeeded0,1);
+ return;
+ }
+
+ if ((dstat==0)&&(cstat==0)) return;
+
+ if (card->devstat0->flag&DEVSTAT_FLAG_SENSE_AVAIL) {
+ PRINT_WARN("sense data available on read channel.\n");
+ HEXDUMP16(WARN,"irb: ",&card->devstat0->ii.irb);
+ HEXDUMP16(WARN,"sense data: ",&card->devstat0->ii.
+ sense.data[0]);
+ sprintf(dbf_text,"RSNS%4x",irq);
+ QETH_DBF_TEXT1(0,trace,dbf_text);
+ QETH_DBF_HEX0(0,sense,&card->devstat0->ii.irb,
+ QETH_DBF_SENSE_LEN);
+ }
+
+ if (cstat!=0) {
+ PRINT_WARN("got nonzero-nonpci channel status in read_" \
+ "handler (irq 0x%x, devstat 0x%02x, schstat " \
+ "0x%02x, rqparam 0x%x)\n",irq,dstat,cstat,rqparam);
+ }
+
+ problem=qeth_get_cards_problem(card,card->dma_stuff->recbuf,
+ irq,dstat,cstat,rqparam,
+ (char*)&card->devstat0->ii.irb,
+ (char*)&card->devstat0->
+ ii.sense.data[0]);
+
+ /* detect errors in dstat here */
+ if ( (dstat&DEV_STAT_UNIT_EXCEP) || (dstat&DEV_STAT_UNIT_CHECK) ) {
+ PRINT_WARN("unit check/exception in read_handler " \
+ "(irq 0x%x, devstat 0x%02x, schstat 0x%02x, " \
+ "rqparam 0x%x)\n",irq,dstat,cstat,rqparam);
+ if (!atomic_read(&card->is_hardsetup)) {
+ if ((problem)&&(qeth_is_to_recover(card,problem)))
+ atomic_set(&card->break_out,
+ QETH_BREAKOUT_AGAIN);
+ else
+ atomic_set(&card->break_out,
+ QETH_BREAKOUT_LEAVE);
+ goto wakeup_out;
+ } else goto recover;
+ }
+
+ if (!(dstat&DEV_STAT_CHN_END)) {
+ PRINT_WARN("didn't get device end in read_handler " \
+ "(irq 0x%x, devstat 0x%02x, schstat 0x%02x, " \
+ "rqparam 0x%x)\n",irq,dstat,cstat,rqparam);
+ goto wakeup_out;
+ }
+
+ if ( (rqparam==IDX_ACTIVATE_WRITE_STATE) ||
+ (rqparam==NOP_STATE) ) {
+ goto wakeup_out;
+ }
+
+ /* at this point, (maybe channel end and) device end has appeared */
+
+ /* we don't start the next read until we have examined the buffer. */
+ if ( (rqparam!=IDX_ACTIVATE_READ_STATE) &&
+ (rqparam!=IDX_ACTIVATE_WRITE_STATE) )
+ qeth_issue_next_read(card);
+
+recover:
+ if (qeth_is_to_recover(card,problem)) {
+ sprintf(dbf_text,"rscd%4x",card->irq0);
+ QETH_DBF_TEXT2(1,trace,dbf_text);
+ qeth_schedule_recovery(card);
+ goto wakeup_out;
+ }
+
+ if (!IS_IPA(card->dma_stuff->recbuf)||
+ IS_IPA_REPLY(card->dma_stuff->recbuf)) {
+ /* setup or unknown data */
+ result = qeth_look_for_arp_data(card);
+ switch (result) {
+ case ARP_RETURNCODE_ERROR:
+ case ARP_RETURNCODE_LASTREPLY:
+ qeth_wakeup_ioctl(card);
+ return;
+ default:
+ break;
+ }
+ }
+
+ wakeup_out:
+ memcpy(card->ipa_buf,card->dma_stuff->recbuf,QETH_BUFSIZE);
+ qeth_wakeup(card);
+}
+
+static void qeth_interrupt_handler_write(int irq,void *devstat,
+ struct pt_regs *p)
+{
+ devstat_t *stat;
+ int cstat,dstat,rqparam;
+ qeth_card_t *card;
+ int problem;
+ char dbf_text[15];
+
+ stat = (devstat_t *)devstat;
+ cstat = stat->cstat;
+ dstat = stat->dstat;
+ rqparam=stat->intparm;
+
+ sprintf(dbf_text,"wint%4x",irq);
+ QETH_DBF_TEXT4(0,trace,dbf_text);
+ sprintf(dbf_text,"%4x%4x",cstat,dstat);
+ QETH_DBF_TEXT4(0,trace,dbf_text);
+ sprintf(dbf_text,"%4x",rqparam);
+ QETH_DBF_TEXT4(0,trace,dbf_text);
+
+ if (!rqparam) {
+ PRINT_STUPID("got unsolicited interrupt in write handler, " \
+ "irq 0x%x\n",irq);
+ return;
+ }
+
+ card=qeth_get_card_by_irq(irq);
+ if (!card) return;
+
+ if ((rqparam==CLEAR_STATE) || (stat->flag&DEVSTAT_CLEAR_FUNCTION)) {
+ atomic_set(&card->clear_succeeded1,1);
+ goto out;
+ }
+
+ if ((dstat==0)&&(cstat==0)) goto out;
+
+ if (card->devstat1->flag&DEVSTAT_FLAG_SENSE_AVAIL) {
+ PRINT_WARN("sense data available on write channel.\n");
+ HEXDUMP16(WARN,"irb: ",&card->devstat1->ii.irb);
+ HEXDUMP16(WARN,"sense data: ",&card->devstat1->ii.
+ sense.data[0]);
+ sprintf(dbf_text,"WSNS%4x",irq);
+ QETH_DBF_TEXT1(0,trace,dbf_text);
+ QETH_DBF_HEX0(0,sense,&card->devstat1->ii.irb,
+ QETH_DBF_SENSE_LEN);
+ }
+
+ if (cstat != 0) {
+ PRINT_WARN("got nonzero channel status in write_handler " \
+ "(irq 0x%x, devstat 0x%02x, schstat 0x%02x, " \
+ "rqparam 0x%x)\n",irq,dstat,cstat,rqparam);
+ }
+
+ problem=qeth_get_cards_problem(card,NULL,
+ irq,dstat,cstat,rqparam,
+ (char*)&card->devstat1->ii.irb,
+ (char*)&card->devstat1->
+ ii.sense.data[0]);
+
+ /* detect errors in dstat here */
+ if ( (dstat&DEV_STAT_UNIT_EXCEP) || (dstat&DEV_STAT_UNIT_CHECK) ) {
+ PRINT_WARN("unit check/exception in write_handler " \
+ "(irq 0x%x, devstat 0x%02x, schstat 0x%02x, " \
+ "rqparam 0x%x)\n",irq,dstat,cstat,rqparam);
+ if (!atomic_read(&card->is_hardsetup)) {
+ if (problem==PROBLEM_RESETTING_EVENT_INDICATOR) {
+ atomic_set(&card->break_out,
+ QETH_BREAKOUT_AGAIN);
+ qeth_wakeup(card);
+ goto out;
+ }
+ atomic_set(&card->break_out,QETH_BREAKOUT_LEAVE);
+ goto out;
+ } else goto recover;
+ }
+
+ if (dstat==DEV_STAT_DEV_END) goto out;
+
+ if (!(dstat&DEV_STAT_CHN_END)) {
+ PRINT_WARN("didn't get device end in write_handler " \
+ "(irq 0x%x, devstat 0x%02x, schstat 0x%02x, " \
+ "rqparam 0x%x)\n",irq,dstat,cstat,rqparam);
+ goto out;
+ }
+
+recover:
+ if (qeth_is_to_recover(card,problem)) {
+ sprintf(dbf_text,"wscd%4x",card->irq1);
+ QETH_DBF_TEXT2(1,trace,dbf_text);
+ qeth_schedule_recovery(card);
+ goto out;
+ }
+
+ /* at this point, (maybe channel end and) device end has appeared */
+ if ( (rqparam==IDX_ACTIVATE_READ_STATE) ||
+ (rqparam==IDX_ACTIVATE_WRITE_STATE) ||
+ (rqparam==NOP_STATE) ) {
+ qeth_wakeup(card);
+ goto out;
+ }
+
+ /* well, a write has been done successfully. */
+
+ out:
+ /* all statuses are final statuses on the write channel */
+ atomic_set(&card->write_busy,0);
+}
+
+static void qeth_interrupt_handler_qdio(int irq,void *devstat,
+ struct pt_regs *p)
+{
+ devstat_t *stat;
+ int cstat,dstat,rqparam;
+ char dbf_text[15];
+
+ qeth_card_t *card;
+
+ stat = (devstat_t *)devstat;
+ cstat = stat->cstat;
+ dstat = stat->dstat;
+ rqparam=stat->intparm;
+
+ sprintf(dbf_text,"qint%4x",irq);
+ QETH_DBF_TEXT4(0,trace,dbf_text);
+ sprintf(dbf_text,"%4x%4x",cstat,dstat);
+ QETH_DBF_TEXT4(0,trace,dbf_text);
+ sprintf(dbf_text,"%4x",rqparam);
+ QETH_DBF_TEXT4(0,trace,dbf_text);
+
+
+ if (!rqparam) {
+ PRINT_STUPID("got unsolicited interrupt in qdio handler, " \
+ "irq 0x%x\n",irq);
+ return;
+ }
+
+ card=qeth_get_card_by_irq(irq);
+ if (!card) return;
+
+ if ((rqparam==CLEAR_STATE) || (stat->flag&DEVSTAT_CLEAR_FUNCTION)) {
+ atomic_set(&card->clear_succeeded2,1);
+ return;
+ }
+
+ if ((dstat==0)&&(cstat==0)) return;
+
+ if (card->devstat2->flag&DEVSTAT_FLAG_SENSE_AVAIL) {
+ PRINT_WARN("sense data available on qdio channel.\n");
+ HEXDUMP16(WARN,"irb: ",&card->devstat2->ii.irb);
+ HEXDUMP16(WARN,"sense data: ",&card->devstat2->ii.
+ sense.data[0]);
+ sprintf(dbf_text,"QSNS%4x",irq);
+ QETH_DBF_TEXT1(0,trace,dbf_text);
+ QETH_DBF_HEX0(0,sense,&card->devstat2->ii.irb,
+ QETH_DBF_SENSE_LEN);
+ }
+
+ if ( (rqparam==READ_CONF_DATA_STATE) ||
+ (rqparam==NOP_STATE) ) {
+ qeth_wakeup(card);
+ return;
+ }
+
+ if (cstat != 0) {
+ sprintf(dbf_text,"qchk%4x",irq);
+ QETH_DBF_TEXT2(0,trace,dbf_text);
+ sprintf(dbf_text,"%4x%4x",cstat,dstat);
+ QETH_DBF_TEXT2(0,trace,dbf_text);
+ sprintf(dbf_text,"%4x",rqparam);
+ QETH_DBF_TEXT2(0,trace,dbf_text);
+ PRINT_WARN("got nonzero channel status in qdio_handler " \
+ "(irq 0x%x, devstat 0x%02x, schstat 0x%02x)\n",
+ irq,dstat,cstat);
+ }
+
+ if (dstat&~(DEV_STAT_CHN_END|DEV_STAT_DEV_END)) {
+ PRINT_WARN("got the following dstat on the qdio channel: " \
+ "irq 0x%x, dstat 0x%02x, cstat 0x%02x, " \
+ "rqparam=%i\n",
+ irq,dstat,cstat,rqparam);
+ }
+
+}
+
+static int qeth_register_netdev(qeth_card_t *card)
+{
+ int result;
+ char dbf_text[15];
+
+ sprintf(dbf_text,"rgnd%4x",card->irq0);
+ QETH_DBF_TEXT3(0,trace,dbf_text);
+
+ result=register_netdev(card->dev);
+
+ return result;
+}
+
+static void qeth_unregister_netdev(qeth_card_t *card)
+{
+ char dbf_text[15];
+
+ sprintf(dbf_text,"nrgn%4x",card->irq0);
+ QETH_DBF_TEXT3(0,trace,dbf_text);
+
+ unregister_netdev(card->dev);
+}
+
+static int qeth_stop(struct net_device *dev)
+{
+ char dbf_text[15];
+ qeth_card_t *card;
+
+ card=(qeth_card_t *)dev->priv;
+ sprintf(dbf_text,"stop%4x",card->irq0);
+ QETH_DBF_TEXT2(0,trace,dbf_text);
+ QETH_DBF_TEXT2(0,setup,dbf_text);
+
+ qeth_save_dev_flag_state(card);
+
+ netif_stop_queue(dev);
+ if (atomic_swap(&((qeth_card_t*)dev->priv)->is_open,0)) {
+ MOD_DEC_USE_COUNT;
+ }
+
+ return 0;
+}
+
+static void qeth_softshutdown(qeth_card_t *card)
+{
+ char dbf_text[15];
+
+ sprintf(dbf_text,"ssht%4x",card->irq0);
+ QETH_DBF_TEXT3(0,trace,dbf_text);
+
+ qeth_send_stoplan(card);
+}
+
+static void qeth_clear_card(qeth_card_t *card,int qdio_clean,int use_halt)
+{
+ unsigned long flags0,flags1,flags2;
+ char dbf_text[15];
+
+ sprintf(dbf_text,"clr%c%4x",(qdio_clean)?'q':' ',card->irq0);
+ QETH_DBF_TEXT3(0,trace,dbf_text);
+ QETH_DBF_TEXT1(0,setup,dbf_text);
+
+ atomic_set(&card->write_busy,0);
+ if (qdio_clean)
+ qdio_cleanup(card->irq2,
+ (card->type==QETH_CARD_TYPE_IQD)?
+ QDIO_FLAG_CLEANUP_USING_HALT:
+ QDIO_FLAG_CLEANUP_USING_CLEAR);
+
+ if (use_halt) {
+ atomic_set(&card->clear_succeeded0,0);
+ atomic_set(&card->clear_succeeded1,0);
+ atomic_set(&card->clear_succeeded2,0);
+
+ s390irq_spin_lock_irqsave(card->irq0,flags0);
+ halt_IO(card->irq0,CLEAR_STATE,0);
+ s390irq_spin_unlock_irqrestore(card->irq0,flags0);
+
+ s390irq_spin_lock_irqsave(card->irq1,flags1);
+ halt_IO(card->irq1,CLEAR_STATE,0);
+ s390irq_spin_unlock_irqrestore(card->irq1,flags1);
+
+ s390irq_spin_lock_irqsave(card->irq2,flags2);
+ halt_IO(card->irq2,CLEAR_STATE,0);
+ s390irq_spin_unlock_irqrestore(card->irq2,flags2);
+
+ if (qeth_wait_for_event(&card->clear_succeeded0,
+ QETH_CLEAR_TIMEOUT)==-ETIME) {
+ if (atomic_read(&card->shutdown_phase)!=
+ QETH_REMOVE_CARD_QUICK) {
+ PRINT_ERR("Did not get interrupt on halt_IO " \
+ "on irq 0x%x.\n",card->irq0);
+ }
+ }
+ if (qeth_wait_for_event(&card->clear_succeeded1,
+ QETH_CLEAR_TIMEOUT)==-ETIME) {
+ if (atomic_read(&card->shutdown_phase)!=
+ QETH_REMOVE_CARD_QUICK) {
+ PRINT_ERR("Did not get interrupt on halt_IO " \
+ "on irq 0x%x.\n",card->irq1);
+ }
+ }
+ if (qeth_wait_for_event(&card->clear_succeeded2,
+ QETH_CLEAR_TIMEOUT)==-ETIME) {
+ if (atomic_read(&card->shutdown_phase)!=
+ QETH_REMOVE_CARD_QUICK) {
+ PRINT_ERR("Did not get interrupt on halt_IO " \
+ "on irq 0x%x.\n",card->irq2);
+ }
+ }
+ }
+
+ atomic_set(&card->clear_succeeded0,0);
+ atomic_set(&card->clear_succeeded1,0);
+ atomic_set(&card->clear_succeeded2,0);
+
+ s390irq_spin_lock_irqsave(card->irq0,flags0);
+ clear_IO(card->irq0,CLEAR_STATE,0);
+ s390irq_spin_unlock_irqrestore(card->irq0,flags0);
+
+ s390irq_spin_lock_irqsave(card->irq1,flags1);
+ clear_IO(card->irq1,CLEAR_STATE,0);
+ s390irq_spin_unlock_irqrestore(card->irq1,flags1);
+
+ s390irq_spin_lock_irqsave(card->irq2,flags2);
+ clear_IO(card->irq2,CLEAR_STATE,0);
+ s390irq_spin_unlock_irqrestore(card->irq2,flags2);
+
+ if (qeth_wait_for_event(&card->clear_succeeded0,
+ QETH_CLEAR_TIMEOUT)==-ETIME) {
+ if (atomic_read(&card->shutdown_phase)!=
+ QETH_REMOVE_CARD_QUICK) {
+ PRINT_ERR("Did not get interrupt on clear_IO on " \
+ "irq 0x%x.\n",card->irq0);
+ }
+ }
+ if (qeth_wait_for_event(&card->clear_succeeded1,
+ QETH_CLEAR_TIMEOUT)==-ETIME) {
+ if (atomic_read(&card->shutdown_phase)!=
+ QETH_REMOVE_CARD_QUICK) {
+ PRINT_ERR("Did not get interrupt on clear_IO on " \
+ "irq 0x%x.\n",card->irq1);
+ }
+ }
+ if (qeth_wait_for_event(&card->clear_succeeded2,
+ QETH_CLEAR_TIMEOUT)==-ETIME) {
+ if (atomic_read(&card->shutdown_phase)!=
+ QETH_REMOVE_CARD_QUICK) {
+ PRINT_ERR("Did not get interrupt on clear_IO on " \
+ "irq 0x%x.\n",card->irq2);
+ }
+ }
+}
+
+static void qeth_free_card(qeth_card_t *card)
+{
+ int i,j;
+ char dbf_text[15];
+ int element_count;
+ qeth_vipa_entry_t *e,*e2;
+
+ if (!card) return;
+
+ sprintf(dbf_text,"free%4x",card->irq0);
+ QETH_DBF_TEXT3(0,trace,dbf_text);
+ QETH_DBF_TEXT1(0,setup,dbf_text);
+
+ my_write_lock(&card->vipa_list_lock);
+ e=card->vipa_list;
+ while (e) {
+ e2=e->next;
+ kfree(e);
+ e=e2;
+ }
+ my_write_unlock(&card->vipa_list_lock);
+
+ element_count=(card->options.memusage==MEMUSAGE_DISCONTIG)?
+ BUFFER_MAX_ELEMENTS:1;
+ for (i=0;i<card->options.inbound_buffer_count;i++) {
+ for (j=0;j<element_count;j++) {
+ if (card->inbound_buffer_pool_entry[i][j]) {
+ kfree(card->inbound_buffer_pool_entry[i][j]);
+ card->inbound_buffer_pool_entry[i][j]=NULL;
+ }
+ }
+ }
+ for (i=0;i<card->no_queues;i++)
+ if (card->outbound_ringbuffer[i])
+ vfree(card->outbound_ringbuffer[i]);
+
+ if (card->stats) kfree(card->stats);
+ if (card->dma_stuff) kfree(card->dma_stuff);
+ if (card->dev) kfree(card->dev);
+ if (card->devstat2) kfree(card->devstat2);
+ if (card->devstat1) kfree(card->devstat1);
+ if (card->devstat0) kfree(card->devstat0);
+ vfree(card); /* we checked against NULL already */
+}
+
+/* also locked from outside (setup_lock) */
+static void qeth_remove_card_from_list(qeth_card_t *card)
+{
+ qeth_card_t *cn;
+ unsigned long flags0,flags1,flags2;
+ char dbf_text[15];
+
+ my_write_lock(&list_lock);
+ if (!card) {
+ QETH_DBF_TEXT2(0,trace,"RMCWNOCD");
+ PRINT_WARN("qeth_remove_card_from_list call with no card!\n");
+ return;
+ }
+
+ sprintf(dbf_text,"rmcl%4x",card->irq0);
+ QETH_DBF_TEXT3(0,trace,dbf_text);
+
+ /* check first, if card is in list */
+ if (!firstcard) {
+ QETH_DBF_TEXT2(0,trace,"NOCRDINL");
+ PRINT_WARN("qeth_remove_card_from_list called on " \
+ "empty card list!!\n");
+ return;
+ }
+
+ s390irq_spin_lock_irqsave(card->irq0,flags0);
+ s390irq_spin_lock_irqsave(card->irq1,flags1);
+ s390irq_spin_lock_irqsave(card->irq2,flags2);
+
+ if (firstcard==card)
+ firstcard=card->next;
+ else {
+ cn=firstcard;
+ while (cn->next) {
+ if (cn->next==card) {
+ cn->next=card->next;
+ card->next=NULL;
+ break;
+ }
+ cn = cn->next;
+ }
+ }
+
+ s390irq_spin_unlock_irqrestore(card->irq2,flags2);
+ s390irq_spin_unlock_irqrestore(card->irq1,flags1);
+ s390irq_spin_unlock_irqrestore(card->irq0,flags0);
+
+
+ my_write_unlock(&list_lock);
+
+}
+
+static void qeth_delete_all_ips(qeth_card_t *card)
+{
+ qeth_vipa_entry_t *e;
+
+ if (atomic_read(&card->is_softsetup)) {
+ qeth_clear_ifa4_list(&card->ip_new_state.ip_ifa);
+ qeth_clear_ifamc_list(&card->ip_mc_new_state.ipm_ifa);
+
+#ifdef QETH_IPV6
+ qeth_clear_ifa6_list(&card->ip_new_state.ip6_ifa);
+ qeth_clear_ifamc_list(&card->ip_mc_new_state.ipm6_ifa);
+#endif /* QETH_IPV6 */
+
+ my_write_lock(&card->vipa_list_lock);
+ e=card->vipa_list;
+ while (e) {
+ e->state=VIPA_2_B_REMOVED;
+ e=e->next;
+ }
+ my_write_unlock(&card->vipa_list_lock);
+ qeth_start_softsetup_thread(card);
+ }
+}
+
+static void qeth_remove_card(qeth_card_t *card,int method)
+{
+ char dbf_text[15];
+
+ if (!card) {
+ return;
+ }
+ sprintf(dbf_text,"rmcd%4x",card->irq0);
+ QETH_DBF_TEXT2(0,trace,dbf_text);
+ QETH_DBF_TEXT1(0,setup,dbf_text);
+
+ if (method==QETH_REMOVE_CARD_PROPER) {
+ atomic_set(&card->shutdown_phase,QETH_REMOVE_CARD_PROPER);
+ if (atomic_read(&card->is_open)) {
+ qeth_stop(card->dev);
+ qeth_wait_nonbusy(QETH_REMOVE_WAIT_TIME);
+ }
+ qeth_delete_all_ips(card);
+ } else {
+ atomic_set(&card->shutdown_phase,QETH_REMOVE_CARD_QUICK);
+ }
+ atomic_set(&card->write_busy,0);
+
+ QETH_DBF_TEXT4(0,trace,"freeskbs");
+ qeth_free_all_skbs(card);
+
+ QETH_DBF_TEXT2(0,trace,"upthrsem");
+
+ up(&card->softsetup_thread_sem);
+ up(&card->reinit_thread_sem);
+ while ( (atomic_read(&card->softsetup_thread_is_running)) ||
+ (atomic_read(&card->reinit_counter)) ) {
+ qeth_wait_nonbusy(QETH_WAIT_FOR_THREAD_TIME);
+ }
+
+ if (method==QETH_REMOVE_CARD_PROPER) {
+ QETH_DBF_TEXT4(0,trace,"softshut");
+ qeth_softshutdown(card);
+ qeth_wait_nonbusy(QETH_REMOVE_WAIT_TIME);
+ }
+
+ atomic_set(&card->is_startlaned,0); /* paranoia, qeth_stop
+ should prevent
+ further calls of
+ hard_start_xmit */
+
+ if (atomic_read(&card->is_registered)) {
+ QETH_DBF_TEXT2(0,trace,"unregdev");
+ qeth_unregister_netdev(card);
+ qeth_wait_nonbusy(QETH_REMOVE_WAIT_TIME);
+ atomic_set(&card->is_registered,0);
+ }
+
+ qeth_put_unique_id(card);
+
+ QETH_DBF_TEXT2(0,trace,"clrcard");
+ if (atomic_read(&card->is_hardsetup)) {
+ PRINT_STUPID("clearing card %s\n",card->dev_name);
+ qeth_clear_card(card,1,0);
+ }
+
+ atomic_set(&card->is_hardsetup,0);
+ atomic_set(&card->is_softsetup,0);
+
+
+ if (card->has_irq>=3) {
+ QETH_DBF_TEXT2(0,trace,"freeirq2");
+ chandev_free_irq(card->irq2,card->devstat2);
+ }
+ if (card->has_irq>=2) {
+ QETH_DBF_TEXT2(0,trace,"freeirq1");
+ chandev_free_irq(card->irq1,card->devstat1);
+ }
+ if (card->has_irq>=1) {
+ QETH_DBF_TEXT2(0,trace,"freeirq0");
+ chandev_free_irq(card->irq0,card->devstat0);
+ }
+}
+
+static void qeth_destructor(struct net_device *dev)
+{
+ char dbf_text[15];
+ qeth_card_t *card;
+
+ card=(qeth_card_t *)(dev->priv);
+ sprintf(dbf_text,"dstr%4x",card->irq0);
+ QETH_DBF_TEXT2(0,trace,dbf_text);
+}
+
+static void qeth_set_multicast_list(struct net_device *dev)
+{
+ char dbf_text[15];
+ qeth_card_t *card=dev->priv;
+
+ sprintf(dbf_text,"smcl%4x",card->irq0);
+ QETH_DBF_TEXT2(0,trace,dbf_text);
+
+ qeth_start_softsetup_thread(card);
+}
+
+static int qeth_set_mac_address(struct net_device *dev,void *addr)
+{
+ char dbf_text[15];
+
+ sprintf(dbf_text,"stmc%4x",((qeth_card_t *)dev->priv)->irq0);
+ QETH_DBF_TEXT2(0,trace,dbf_text);
+ return -EOPNOTSUPP;
+}
+
+static int qeth_neigh_setup(struct net_device *dev,struct neigh_parms *np)
+{
+ char dbf_text[15];
+
+ sprintf(dbf_text,"ngst%4x",((qeth_card_t *)dev->priv)->irq0);
+ QETH_DBF_TEXT2(0,trace,dbf_text);
+ return 0;
+}
+
+static void qeth_generate_tokens(qeth_card_t *card)
+{
+ card->token.issuer_rm_w=0x00010103UL;
+ card->token.cm_filter_w=0x00010108UL;
+ card->token.cm_connection_w=0x0001010aUL;
+ card->token.ulp_filter_w=0x0001010bUL;
+ card->token.ulp_connection_w=0x0001010dUL;
+}
+
+static int qeth_peer_func_level(int level)
+{
+ if ((level&0xff)==8) return (level&0xff)+0x400;
+ if ( ((level>>8)&3)==1) return (level&0xff)+0x200;
+ return level; /* hmmm... don't know what to do with that level. */
+}
+
+static int qeth_idx_activate_read(qeth_card_t *card)
+{
+ int result,result2;
+ __u16 temp;
+ unsigned long flags;
+ char dbf_text[15];
+
+ result=result2=0;
+
+ memcpy(&card->dma_stuff->write_ccw,WRITE_CCW,sizeof(ccw1_t));
+ card->dma_stuff->write_ccw.count=IDX_ACTIVATE_SIZE;
+ card->dma_stuff->write_ccw.cda=
+ QETH_GET_ADDR(card->dma_stuff->sendbuf);
+
+ memcpy(card->dma_stuff->sendbuf,IDX_ACTIVATE_READ,
+ IDX_ACTIVATE_SIZE);
+ memcpy(QETH_TRANSPORT_HEADER_SEQ_NO(card->dma_stuff->sendbuf),
+ &card->seqno.trans_hdr,QETH_SEQ_NO_LENGTH);
+
+ memcpy(QETH_IDX_ACT_ISSUER_RM_TOKEN(card->dma_stuff->sendbuf),
+ &card->token.issuer_rm_w,QETH_MPC_TOKEN_LENGTH);
+ memcpy(QETH_IDX_ACT_FUNC_LEVEL(card->dma_stuff->sendbuf),
+ &card->func_level,2);
+
+ temp=card->devno2;
+ memcpy(QETH_IDX_ACT_QDIO_DEV_CUA(card->dma_stuff->sendbuf),
+ &temp,2);
+ temp=(card->cula<<8)+card->unit_addr2;
+ memcpy(QETH_IDX_ACT_QDIO_DEV_REALADDR(card->dma_stuff->sendbuf),
+ &temp,2);
+
+ sprintf(dbf_text,"iarw%4x",card->irq0);
+ QETH_DBF_TEXT2(0,trace,dbf_text);
+ QETH_DBF_HEX2(0,control,card->dma_stuff->sendbuf,
+ QETH_DBF_CONTROL_LEN);
+
+ s390irq_spin_lock_irqsave(card->irq0,flags);
+ result=do_IO(card->irq0,&card->dma_stuff->write_ccw,
+ IDX_ACTIVATE_WRITE_STATE,0,0);
+ if (result) {
+ qeth_delay_millis(QETH_WAIT_BEFORE_2ND_DOIO);
+ result2=do_IO(card->irq0,&card->dma_stuff->write_ccw,
+ IDX_ACTIVATE_WRITE_STATE,0,0);
+ sprintf(dbf_text,"IRW1%4x",result);
+ QETH_DBF_TEXT2(0,trace,dbf_text);
+ sprintf(dbf_text,"IRW2%4x",result2);
+ QETH_DBF_TEXT2(0,trace,dbf_text);
+ PRINT_WARN("qeth_idx_activate_read (write): do_IO returned " \
+ "%i, next try returns %i\n",result,result2);
+ }
+ s390irq_spin_unlock_irqrestore(card->irq0,flags);
+
+ if (atomic_read(&card->break_out)) {
+ QETH_DBF_TEXT3(0,trace,"IARWBRKO");
+ result=-EIO;
+ goto exit;
+ }
+
+ if (qeth_sleepon(card,QETH_MPC_TIMEOUT)) {
+ sprintf(dbf_text,"IRWT%4x",card->irq0);
+ QETH_DBF_TEXT1(0,trace,dbf_text);
+ PRINT_ERR("IDX_ACTIVATE(wr) on read channel irq 0x%x: " \
+ "timeout\n",card->irq0);
+ result=-EIO;
+ goto exit;
+ }
+
+ /* start reading on read channel, card->read_ccw is not yet used */
+ memcpy(&card->dma_stuff->read_ccw,READ_CCW,sizeof(ccw1_t));
+ card->dma_stuff->read_ccw.count=QETH_BUFSIZE;
+ card->dma_stuff->read_ccw.cda=QETH_GET_ADDR(card->dma_stuff->recbuf);
+
+ s390irq_spin_lock_irqsave(card->irq0,flags);
+ result2=0;
+ result=do_IO(card->irq0,&card->dma_stuff->read_ccw,
+ IDX_ACTIVATE_READ_STATE,0,0);
+ if (result) {
+ qeth_delay_millis(QETH_WAIT_BEFORE_2ND_DOIO);
+ result2=do_IO(card->irq0,&card->dma_stuff->read_ccw,
+ IDX_ACTIVATE_READ_STATE,0,0);
+ sprintf(dbf_text,"IRR1%4x",result);
+ QETH_DBF_TEXT2(0,trace,dbf_text);
+ sprintf(dbf_text,"IRR2%4x",result2);
+ QETH_DBF_TEXT2(0,trace,dbf_text);
+ PRINT_WARN("qeth_idx_activate_read (read): do_IO " \
+ "returned %i, next try returns %i\n",
+ result,result2);
+ }
+ s390irq_spin_unlock_irqrestore(card->irq0,flags);
+
+ if (result2) {
+ result=result2;
+ if (result) goto exit;
+ }
+
+ if (qeth_sleepon(card,QETH_MPC_TIMEOUT)) {
+ sprintf(dbf_text,"IRRT%4x",card->irq0);
+ QETH_DBF_TEXT1(0,trace,dbf_text);
+ PRINT_ERR("IDX_ACTIVATE(rd) on read channel irq 0x%x: " \
+ "timeout\n",card->irq0);
+ result=-EIO;
+ goto exit;
+ }
+ sprintf(dbf_text,"iarr%4x",card->irq0);
+ QETH_DBF_TEXT2(0,trace,dbf_text);
+ QETH_DBF_HEX2(0,control,card->dma_stuff->recbuf,
+ QETH_DBF_CONTROL_LEN);
+
+ if (!(QETH_IS_IDX_ACT_POS_REPLY(card->dma_stuff->recbuf))) {
+ sprintf(dbf_text,"IRNR%4x",card->irq0);
+ QETH_DBF_TEXT1(0,trace,dbf_text);
+ PRINT_ERR("IDX_ACTIVATE on read channel irq 0x%x: negative " \
+ "reply\n",card->irq0);
+ result=-EIO;
+ goto exit;
+ }
+
+ card->portname_required=
+ ((!QETH_IDX_NO_PORTNAME_REQUIRED(card->dma_stuff->recbuf))&&
+ (card->type==QETH_CARD_TYPE_OSAE));
+
+ memcpy(&temp,QETH_IDX_ACT_FUNC_LEVEL(card->dma_stuff->recbuf),2);
+ if (temp!=qeth_peer_func_level(card->func_level)) {
+ sprintf(dbf_text,"IRFL%4x",card->irq0);
+ QETH_DBF_TEXT1(0,trace,dbf_text);
+ sprintf(dbf_text,"%4x%4x",card->func_level,temp);
+ QETH_DBF_TEXT1(0,trace,dbf_text);
+ PRINT_WARN("IDX_ACTIVATE on read channel irq 0x%x: " \
+ "function level mismatch (sent: 0x%x, " \
+ "received: 0x%x)\n",
+ card->irq0,card->func_level,temp);
+ result=-EIO;
+ }
+
+ memcpy(&card->token.issuer_rm_r,
+ QETH_IDX_ACT_ISSUER_RM_TOKEN(card->dma_stuff->recbuf),
+ QETH_MPC_TOKEN_LENGTH);
+
+ memcpy(&card->level[0],
+ QETH_IDX_REPLY_LEVEL(card->dma_stuff->recbuf),
+ QETH_MCL_LENGTH);
+ exit:
+ return result;
+}
+
+static int qeth_idx_activate_write(qeth_card_t *card)
+{
+ int result,result2;
+ __u16 temp;
+ unsigned long flags;
+ char dbf_text[15];
+
+ result=result2=0;
+
+ memcpy(&card->dma_stuff->write_ccw,WRITE_CCW,sizeof(ccw1_t));
+ card->dma_stuff->write_ccw.count=IDX_ACTIVATE_SIZE;
+ card->dma_stuff->write_ccw.cda=
+ QETH_GET_ADDR(card->dma_stuff->sendbuf);
+
+ memcpy(card->dma_stuff->sendbuf,IDX_ACTIVATE_WRITE,
+ IDX_ACTIVATE_SIZE);
+ memcpy(QETH_TRANSPORT_HEADER_SEQ_NO(card->dma_stuff->sendbuf),
+ &card->seqno.trans_hdr,QETH_SEQ_NO_LENGTH);
+ card->seqno.trans_hdr++;
+
+ memcpy(QETH_IDX_ACT_ISSUER_RM_TOKEN(card->dma_stuff->sendbuf),
+ &card->token.issuer_rm_w,QETH_MPC_TOKEN_LENGTH);
+ memcpy(QETH_IDX_ACT_FUNC_LEVEL(card->dma_stuff->sendbuf),
+ &card->func_level,2);
+
+ temp=card->devno2;
+ memcpy(QETH_IDX_ACT_QDIO_DEV_CUA(card->dma_stuff->sendbuf),
+ &temp,2);
+ temp=(card->cula<<8)+card->unit_addr2;
+ memcpy(QETH_IDX_ACT_QDIO_DEV_REALADDR(card->dma_stuff->sendbuf),
+ &temp,2);
+
+ sprintf(dbf_text,"iaww%4x",card->irq0);
+ QETH_DBF_TEXT2(0,trace,dbf_text);
+ QETH_DBF_HEX2(0,control,card->dma_stuff->sendbuf,
+ QETH_DBF_CONTROL_LEN);
+
+ s390irq_spin_lock_irqsave(card->irq1,flags);
+ result=do_IO(card->irq1,&card->dma_stuff->write_ccw,
+ IDX_ACTIVATE_WRITE_STATE,0,0);
+ if (result) {
+ qeth_delay_millis(QETH_WAIT_BEFORE_2ND_DOIO);
+ result2=do_IO(card->irq1,&card->dma_stuff->write_ccw,
+ IDX_ACTIVATE_WRITE_STATE,0,0);
+ sprintf(dbf_text,"IWW1%4x",result);
+ QETH_DBF_TEXT2(0,trace,dbf_text);
+ sprintf(dbf_text,"IWW2%4x",result2);
+ QETH_DBF_TEXT2(0,trace,dbf_text);
+ PRINT_WARN("qeth_idx_activate_write (write): do_IO " \
+ "returned %i, next try returns %i\n",
+ result,result2);
+ }
+ s390irq_spin_unlock_irqrestore(card->irq1,flags);
+
+ if (atomic_read(&card->break_out)) {
+ QETH_DBF_TEXT3(0,trace,"IAWWBRKO");
+ result=-EIO;
+ goto exit;
+ }
+
+ if (qeth_sleepon(card,QETH_MPC_TIMEOUT)) {
+ sprintf(dbf_text,"IWWT%4x",card->irq0);
+ QETH_DBF_TEXT1(0,trace,dbf_text);
+ PRINT_ERR("IDX_ACTIVATE(wr) on write channel irq 0x%x: " \
+ "timeout\n",card->irq1);
+ result=-EIO;
+ goto exit;
+ }
+
+ QETH_DBF_TEXT3(0,trace,"idxawrrd");
+
+ /* start one read on write channel */
+ memcpy(&card->dma_stuff->read_ccw,READ_CCW,sizeof(ccw1_t));
+ card->dma_stuff->read_ccw.count=QETH_BUFSIZE;
+
+ /* recbuf and card->read_ccw is not yet used by any other
+ read channel program */
+ card->dma_stuff->read_ccw.cda=QETH_GET_ADDR(card->dma_stuff->recbuf);
+
+ s390irq_spin_lock_irqsave(card->irq1,flags);
+ result2=0;
+ result=do_IO(card->irq1,&card->dma_stuff->read_ccw,
+ IDX_ACTIVATE_READ_STATE,0,0);
+ if (result) {
+ qeth_delay_millis(QETH_WAIT_BEFORE_2ND_DOIO);
+ result2=do_IO(card->irq1,&card->dma_stuff->read_ccw,
+ IDX_ACTIVATE_READ_STATE,0,0);
+ sprintf(dbf_text,"IWR1%4x",result);
+ QETH_DBF_TEXT2(0,trace,dbf_text);
+ sprintf(dbf_text,"IWR2%4x",result2);
+ QETH_DBF_TEXT2(0,trace,dbf_text);
+ PRINT_WARN("qeth_idx_activate_write (read): do_IO returned " \
+ "%i, next try returns %i\n",result,result2);
+ }
+
+ s390irq_spin_unlock_irqrestore(card->irq1,flags);
+
+ if (result2) {
+ result=result2;
+ if (result) goto exit;
+ }
+
+ if (qeth_sleepon(card,QETH_MPC_TIMEOUT)) {
+ sprintf(dbf_text,"IWRT%4x",card->irq0);
+ QETH_DBF_TEXT1(0,trace,dbf_text);
+ PRINT_ERR("IDX_ACTIVATE(rd) on write channel irq 0x%x: " \
+ "timeout\n",card->irq1);
+ result=-EIO;
+ goto exit;
+ }
+ sprintf(dbf_text,"iawr%4x",card->irq0);
+ QETH_DBF_TEXT2(0,trace,dbf_text);
+ QETH_DBF_HEX2(0,control,card->dma_stuff->recbuf,
+ QETH_DBF_CONTROL_LEN);
+
+ if (!(QETH_IS_IDX_ACT_POS_REPLY(card->dma_stuff->recbuf))) {
+ sprintf(dbf_text,"IWNR%4x",card->irq0);
+ QETH_DBF_TEXT1(0,trace,dbf_text);
+ PRINT_ERR("IDX_ACTIVATE on write channel irq 0x%x: " \
+ "negative reply\n",card->irq1);
+ result=-EIO;
+ goto exit;
+ }
+
+ memcpy(&temp,QETH_IDX_ACT_FUNC_LEVEL(card->dma_stuff->recbuf),2);
+ if ((temp&~0x0100)!=qeth_peer_func_level(card->func_level)) {
+ sprintf(dbf_text,"IWFM%4x",card->irq0);
+ QETH_DBF_TEXT1(0,trace,dbf_text);
+ sprintf(dbf_text,"%4x%4x",card->func_level,temp);
+ QETH_DBF_TEXT1(0,trace,dbf_text);
+ PRINT_WARN("IDX_ACTIVATE on write channel irq 0x%x: " \
+ "function level mismatch (sent: 0x%x, " \
+ "received: 0x%x)\n",
+ card->irq1,card->func_level,temp);
+ result=-EIO;
+ }
+
+ exit:
+ return result;
+}
+
+static int qeth_cm_enable(qeth_card_t *card)
+{
+ unsigned char *buffer;
+ int result;
+ char dbf_text[15];
+
+ memcpy(card->send_buf,CM_ENABLE,CM_ENABLE_SIZE);
+
+ memcpy(QETH_CM_ENABLE_ISSUER_RM_TOKEN(card->send_buf),
+ &card->token.issuer_rm_r,QETH_MPC_TOKEN_LENGTH);
+ memcpy(QETH_CM_ENABLE_FILTER_TOKEN(card->send_buf),
+ &card->token.cm_filter_w,QETH_MPC_TOKEN_LENGTH);
+
+ buffer=qeth_send_control_data(card,card->send_buf,
+ CM_ENABLE_SIZE,MPC_SETUP_STATE);
+
+ if (!buffer) {
+ QETH_DBF_TEXT2(0,trace,"CME:NOBF");
+ return -EIO;
+ }
+
+ memcpy(&card->token.cm_filter_r,
+ QETH_CM_ENABLE_RESP_FILTER_TOKEN(buffer),
+ QETH_MPC_TOKEN_LENGTH);
+
+ result=qeth_check_idx_response(buffer);
+
+ sprintf(dbf_text,"cme=%4x",result);
+ QETH_DBF_TEXT3(0,trace,dbf_text);
+
+ return result;
+}
+
+static int qeth_cm_setup(qeth_card_t *card)
+{
+ unsigned char *buffer;
+ int result;
+ char dbf_text[15];
+
+ memcpy(card->send_buf,CM_SETUP,CM_SETUP_SIZE);
+
+ memcpy(QETH_CM_SETUP_DEST_ADDR(card->send_buf),
+ &card->token.issuer_rm_r,QETH_MPC_TOKEN_LENGTH);
+ memcpy(QETH_CM_SETUP_CONNECTION_TOKEN(card->send_buf),
+ &card->token.cm_connection_w,QETH_MPC_TOKEN_LENGTH);
+ memcpy(QETH_CM_SETUP_FILTER_TOKEN(card->send_buf),
+ &card->token.cm_filter_r,QETH_MPC_TOKEN_LENGTH);
+
+ buffer=qeth_send_control_data(card,card->send_buf,
+ CM_SETUP_SIZE,MPC_SETUP_STATE);
+
+ if (!buffer) {
+ QETH_DBF_TEXT2(0,trace,"CMS:NOBF");
+ return -EIO;
+ }
+
+ memcpy(&card->token.cm_connection_r,
+ QETH_CM_SETUP_RESP_DEST_ADDR(buffer),
+ QETH_MPC_TOKEN_LENGTH);
+
+ result=qeth_check_idx_response(buffer);
+
+ sprintf(dbf_text,"cms=%4x",result);
+ QETH_DBF_TEXT3(0,trace,dbf_text);
+
+ return result;
+}
+
+static int qeth_ulp_enable(qeth_card_t *card)
+{
+ unsigned char *buffer;
+ __u16 mtu,framesize;
+ __u16 len;
+ __u8 link_type;
+ int result;
+ char dbf_text[15];
+
+ memcpy(card->send_buf,ULP_ENABLE,ULP_ENABLE_SIZE);
+
+ *(QETH_ULP_ENABLE_LINKNUM(card->send_buf))=(__u8)card->options.portno;
+
+ memcpy(QETH_ULP_ENABLE_DEST_ADDR(card->send_buf),
+ &card->token.cm_connection_r,QETH_MPC_TOKEN_LENGTH);
+ memcpy(QETH_ULP_ENABLE_FILTER_TOKEN(card->send_buf),
+ &card->token.ulp_filter_w,QETH_MPC_TOKEN_LENGTH);
+
+ memcpy(QETH_ULP_ENABLE_PORTNAME_AND_LL(card->send_buf),
+ card->options.portname,9);
+
+ buffer=qeth_send_control_data(card,card->send_buf,
+ ULP_ENABLE_SIZE,MPC_SETUP_STATE);
+
+ if (!buffer) {
+ QETH_DBF_TEXT2(0,trace,"ULE:NOBF");
+ return -EIO;
+ }
+
+ memcpy(&card->token.ulp_filter_r,
+ QETH_ULP_ENABLE_RESP_FILTER_TOKEN(buffer),
+ QETH_MPC_TOKEN_LENGTH);
+
+ /* to be done before qeth_init_ringbuffers and qeth_init_dev */
+ if (qeth_get_mtu_out_of_mpc(card->type)) {
+ memcpy(&framesize,QETH_ULP_ENABLE_RESP_MAX_MTU(buffer),2);
+ mtu=qeth_get_mtu_outof_framesize(framesize);
+
+ sprintf(dbf_text,"ule:%4x",card->irq0);
+ QETH_DBF_TEXT2(0,trace,dbf_text);
+ sprintf(dbf_text,"mtu=%4x",mtu);
+ QETH_DBF_TEXT2(0,trace,dbf_text);
+
+ if (!mtu) return -EINVAL;
+
+ card->max_mtu=mtu;
+ card->initial_mtu=mtu;
+ card->inbound_buffer_size=mtu+2*PAGE_SIZE;
+ } else {
+ card->initial_mtu=qeth_get_initial_mtu_for_card(card);
+ card->max_mtu=qeth_get_max_mtu_for_card(card->type);
+ card->inbound_buffer_size=DEFAULT_BUFFER_SIZE;
+ }
+
+ memcpy(&len,QETH_ULP_ENABLE_RESP_DIFINFO_LEN(buffer),2);
+ if (len>=QETH_MPC_DIFINFO_LEN_INDICATES_LINK_TYPE) {
+ memcpy(&link_type,QETH_ULP_ENABLE_RESP_LINK_TYPE(buffer),1);
+ card->link_type=link_type;
+ sprintf(dbf_text,"link=%2x",link_type);
+ QETH_DBF_TEXT2(0,trace,dbf_text);
+ } else card->link_type=0;
+
+ result=qeth_check_idx_response(buffer);
+
+ sprintf(dbf_text,"ule=%4x",result);
+ QETH_DBF_TEXT3(0,trace,dbf_text);
+
+ return result;
+}
+
+static int qeth_ulp_setup(qeth_card_t *card)
+{
+ unsigned char *buffer;
+ __u16 temp;
+ int result;
+ char dbf_text[15];
+
+ memcpy(card->send_buf,ULP_SETUP,ULP_SETUP_SIZE);
+
+ memcpy(QETH_ULP_SETUP_DEST_ADDR(card->send_buf),
+ &card->token.cm_connection_r,QETH_MPC_TOKEN_LENGTH);
+ memcpy(QETH_ULP_SETUP_CONNECTION_TOKEN(card->send_buf),
+ &card->token.ulp_connection_w,QETH_MPC_TOKEN_LENGTH);
+ memcpy(QETH_ULP_SETUP_FILTER_TOKEN(card->send_buf),
+ &card->token.ulp_filter_r,QETH_MPC_TOKEN_LENGTH);
+
+ temp=card->devno2;
+ memcpy(QETH_ULP_SETUP_CUA(card->send_buf),
+ &temp,2);
+ temp=(card->cula<<8)+card->unit_addr2;
+ memcpy(QETH_ULP_SETUP_REAL_DEVADDR(card->send_buf),
+ &temp,2);
+
+ buffer=qeth_send_control_data(card,card->send_buf,
+ ULP_SETUP_SIZE,MPC_SETUP_STATE);
+
+ if (!buffer) {
+ QETH_DBF_TEXT2(0,trace,"ULS:NOBF");
+ return -EIO;
+ }
+
+ memcpy(&card->token.ulp_connection_r,
+ QETH_ULP_SETUP_RESP_CONNECTION_TOKEN(buffer),
+ QETH_MPC_TOKEN_LENGTH);
+
+ result=qeth_check_idx_response(buffer);
+
+ sprintf(dbf_text,"uls=%4x",result);
+ QETH_DBF_TEXT3(0,trace,dbf_text);
+
+ return result;
+}
+
+static int qeth_qdio_establish(qeth_card_t *card)
+{
+ int result;
+ char adapter_area[15];
+ char dbf_text[15];
+ void **input_array,**output_array,**ptr;
+ int i,j;
+ qdio_initialize_t init_data;
+
+ adapter_area[0]=_ascebc['P'];
+ adapter_area[1]=_ascebc['C'];
+ adapter_area[2]=_ascebc['I'];
+ adapter_area[3]=_ascebc['T'];
+ *((unsigned int*)(&adapter_area[4]))=PCI_THRESHOLD_A;
+ *((unsigned int*)(&adapter_area[8]))=PCI_THRESHOLD_B;
+ *((unsigned int*)(&adapter_area[12]))=PCI_TIMER_VALUE;
+
+ input_array=vmalloc(QDIO_MAX_BUFFERS_PER_Q*sizeof(void*));
+ if (!input_array) return -ENOMEM;
+ ptr=input_array;
+ for (j=0;j<QDIO_MAX_BUFFERS_PER_Q;j++) {
+ *ptr=(void*)virt_to_phys
+ (&card->inbound_qdio_buffers[j]);
+ ptr++;
+ }
+
+ output_array=vmalloc(QDIO_MAX_BUFFERS_PER_Q*sizeof(void*)*
+ card->no_queues);
+ if (!output_array) {
+ vfree(input_array);
+ return -ENOMEM;
+ }
+ ptr=output_array;
+ for (i=0;i<card->no_queues;i++)
+ for (j=0;j<QDIO_MAX_BUFFERS_PER_Q;j++) {
+ *ptr=(void*)virt_to_phys
+ (&card->outbound_ringbuffer[i]->
+ buffer[j]);
+ ptr++;
+ }
+
+ init_data.irq=card->irq2;
+ init_data.q_format=qeth_get_q_format(card->type);
+ init_data.qib_param_field_format=0;
+ init_data.qib_param_field=adapter_area;
+ init_data.input_slib_elements=NULL;
+ init_data.output_slib_elements=NULL;
+ init_data.min_input_threshold=card->options.polltime;
+ init_data.max_input_threshold=card->options.polltime;
+ init_data.min_output_threshold=QETH_MIN_OUTPUT_THRESHOLD;
+ init_data.max_output_threshold=QETH_MAX_OUTPUT_THRESHOLD;
+ init_data.no_input_qs=1;
+ init_data.no_output_qs=card->no_queues;
+ init_data.input_handler=qeth_qdio_input_handler;
+ init_data.output_handler=qeth_qdio_output_handler;
+ init_data.int_parm=(unsigned long)card;
+ init_data.flags=QDIO_INBOUND_0COPY_SBALS|
+ QDIO_OUTBOUND_0COPY_SBALS|
+ QDIO_USE_OUTBOUND_PCIS;
+ if (card->do_pfix)
+ init_data.flags |= QDIO_PFIX;
+ init_data.input_sbal_addr_array=input_array;
+ init_data.output_sbal_addr_array=output_array;
+
+ result=qdio_initialize(&init_data);
+
+ vfree(input_array);
+ vfree(output_array);
+
+ sprintf(dbf_text,"qde=%4i",result);
+ QETH_DBF_TEXT3(0,trace,dbf_text);
+
+ return result;
+}
+
+static int qeth_qdio_activate(qeth_card_t *card)
+{
+ int result;
+ char dbf_text[15];
+
+ result=qdio_activate(card->irq2,0);
+
+ sprintf(dbf_text,"qda=%4x",result);
+ QETH_DBF_TEXT3(0,trace,dbf_text);
+
+ return result;
+}
+
+static int qeth_dm_act(qeth_card_t *card)
+{
+ unsigned char *buffer;
+ int result;
+ char dbf_text[15];
+
+ memcpy(card->send_buf,DM_ACT,DM_ACT_SIZE);
+
+ memcpy(QETH_DM_ACT_DEST_ADDR(card->send_buf),
+ &card->token.cm_connection_r,QETH_MPC_TOKEN_LENGTH);
+ memcpy(QETH_DM_ACT_CONNECTION_TOKEN(card->send_buf),
+ &card->token.ulp_connection_r,QETH_MPC_TOKEN_LENGTH);
+
+ buffer=qeth_send_control_data(card,card->send_buf,
+ DM_ACT_SIZE,MPC_SETUP_STATE);
+
+ if (!buffer) {
+ QETH_DBF_TEXT2(0,trace,"DMA:NOBF");
+ return -EIO;
+ }
+
+ result=qeth_check_idx_response(buffer);
+
+ sprintf(dbf_text,"dma=%4x",result);
+ QETH_DBF_TEXT3(0,trace,dbf_text);
+
+ return result;
+}
+
+#if defined(QETH_VLAN)||defined(QETH_IPV6)
+static int qeth_verify_dev(struct net_device *dev)
+{
+ qeth_card_t *tmp;
+ int result=0;
+#ifdef QETH_VLAN
+ struct vlan_group *vlan_grp;
+ int i;
+#endif
+
+ my_read_lock(&list_lock);
+ tmp=firstcard;
+ for (;tmp&&(!result);tmp=tmp->next) {
+ if (atomic_read(&tmp->shutdown_phase))
+ continue;
+ if (dev==tmp->dev) {
+ result=QETH_VERIFY_IS_REAL_DEV;
+ }
+#ifdef QETH_VLAN
+ /* check all vlan devices */
+ vlan_grp = (struct vlan_group *) tmp->vlangrp;
+ if (vlan_grp) {
+ for (i=0;i<VLAN_GROUP_ARRAY_LEN;i++) {
+ if (vlan_grp->vlan_devices[i]==dev) {
+ result=QETH_VERIFY_IS_VLAN_DEV;
+ }
+ }
+ }
+
+#endif
+ }
+ my_read_unlock(&list_lock);
+ return result;
+}
+#endif /* defined(QETH_VLAN)||defined(QETH_IPV6) */
+
+static int qeth_verify_card(qeth_card_t *card)
+{
+ qeth_card_t *tmp;
+ int result=0;
+
+ my_read_lock(&list_lock);
+ tmp=firstcard;
+ while (tmp) {
+ if ((card==tmp) && (!atomic_read(&card->shutdown_phase))) {
+ result=1;
+ break;
+ }
+ tmp=tmp->next;
+ }
+ my_read_unlock(&list_lock);
+ return result;
+}
+
+#ifdef QETH_IPV6
+extern struct neigh_table arp_tbl;
+int (*qeth_old_arp_constructor)(struct neighbour *);
+static struct neigh_ops arp_direct_ops_template =
+{
+ family: AF_INET,
+ destructor: NULL,
+ solicit: NULL,
+ error_report: NULL,
+ output: dev_queue_xmit,
+ connected_output: dev_queue_xmit,
+ hh_output: dev_queue_xmit,
+ queue_xmit: dev_queue_xmit
+};
+/* as we have neighbour structures point to this structure, even
+ * after our life time, this will stay in memory as a leak */
+static struct neigh_ops *arp_direct_ops;
+
+static int qeth_arp_constructor(struct neighbour *neigh)
+{
+ char dbf_text[15];
+ struct net_device *dev = neigh->dev;
+ struct in_device *in_dev = in_dev_get(dev);
+
+ if (in_dev == NULL)
+ return -EINVAL;
+
+ QETH_DBF_TEXT4(0,trace,"arpconst");
+ if (!qeth_verify_dev(dev)) {
+
+ in_dev_put(in_dev);
+ return qeth_old_arp_constructor(neigh);
+ }
+
+ neigh->type = inet_addr_type(*(u32*)neigh->primary_key);
+ if (in_dev->arp_parms)
+ neigh->parms = in_dev->arp_parms;
+
+ in_dev_put(in_dev);
+
+ sprintf(dbf_text,"%08x",ntohl(*((__u32*)(neigh->primary_key))));
+ QETH_DBF_TEXT4(0,trace,dbf_text);
+ QETH_DBF_HEX4(0,trace,&neigh,sizeof(void*));
+
+ neigh->nud_state = NUD_NOARP;
+ neigh->ops = arp_direct_ops;
+ neigh->output = neigh->ops->queue_xmit;
+ return 0;
+}
+
+static int qeth_hard_header(struct sk_buff *skb,struct net_device *dev,
+ unsigned short type,void *daddr,void *saddr,
+ unsigned len)
+{
+ qeth_card_t *card;
+
+ QETH_DBF_TEXT5(0,trace,"hardhdr");
+
+#ifdef QETH_VLAN
+ if (qeth_verify_dev(dev)==QETH_VERIFY_IS_VLAN_DEV)
+ card = (qeth_card_t *)VLAN_DEV_INFO(dev)->real_dev->priv;
+ else
+#endif
+ card=(qeth_card_t*)dev->priv;
+
+ return card->hard_header(skb,dev,type,daddr,saddr,len);
+}
+
+static void qeth_header_cache_update(struct hh_cache *hh,
+ struct net_device *dev,
+ unsigned char *haddr)
+{
+ qeth_card_t *card;
+
+ card=(qeth_card_t*)dev->priv;
+ QETH_DBF_TEXT5(0,trace,"hdrcheup");
+ return card->header_cache_update(hh,dev,haddr);
+}
+
+static int qeth_rebuild_header(struct sk_buff *skb)
+{
+ qeth_card_t *card;
+ QETH_DBF_TEXT5(0,trace,"rebldhdr");
+ if (skb->protocol==__constant_htons(ETH_P_IP)) return 0;
+
+#ifdef QETH_VLAN
+ if (qeth_verify_dev(skb->dev)==QETH_VERIFY_IS_VLAN_DEV)
+ card = (qeth_card_t *)VLAN_DEV_INFO(skb->dev)->real_dev->priv;
+ else
+#endif
+ card=(qeth_card_t*)skb->dev->priv;
+
+ return card->rebuild_header(skb);
+}
+
+static void qeth_ipv6_init_card(qeth_card_t *card)
+{
+ card->hard_header=qeth_get_hard_header(card->link_type);
+ card->rebuild_header=qeth_get_rebuild_header(card->link_type);
+ card->hard_header_cache=qeth_get_hard_header_cache(card->link_type);
+ card->header_cache_update=
+ qeth_get_header_cache_update(card->link_type);
+ card->type_trans=qeth_get_type_trans(card->link_type);
+}
+#endif /* QETH_IPV6 */
+
+#ifdef QETH_VLAN
+static void qeth_vlan_rx_register(struct net_device *dev,
+ struct vlan_group *grp)
+{
+ qeth_card_t *card;
+ card = (qeth_card_t *)dev->priv;
+ spin_lock_irq(&card->vlan_lock);
+ card->vlangrp = grp;
+ spin_unlock_irq(&card->vlan_lock);
+}
+static void qeth_vlan_rx_kill_vid(struct net_device *dev,
+ unsigned short vid)
+{
+ qeth_card_t *card;
+ card = (qeth_card_t *)dev->priv;
+ spin_lock_irq(&card->vlan_lock);
+ if (card->vlangrp)
+ card->vlangrp->vlan_devices[vid] = NULL;
+ spin_unlock_irq(&card->vlan_lock);
+}
+
+#endif /*QETH_VLAN*/
+
+
+static void qeth_tx_timeout(struct net_device *dev)
+{
+ char dbf_text[15];
+ qeth_card_t *card;
+
+ card=(qeth_card_t *)dev->priv;
+ sprintf(dbf_text,"XMTO%4x",card->irq0);
+ QETH_DBF_TEXT2(1,trace,dbf_text);
+ card->stats->tx_errors++;
+ atomic_set(&card->problem,PROBLEM_TX_TIMEOUT);
+ qeth_schedule_recovery(card);
+}
+
+static int qeth_init_dev(struct net_device *dev)
+{
+ qeth_card_t *card;
+ char dbf_text[15];
+
+ card=(qeth_card_t *)dev->priv;
+
+ sprintf(dbf_text,"inid%4x",card->irq0);
+ QETH_DBF_TEXT3(0,trace,dbf_text);
+
+ dev->tx_timeout=&qeth_tx_timeout;
+ dev->watchdog_timeo=QETH_TX_TIMEOUT;
+ dev->open=qeth_open;
+ dev->stop=qeth_stop;
+ dev->set_config=qeth_set_config;
+ dev->hard_start_xmit=qeth_hard_start_xmit;
+ dev->do_ioctl=qeth_do_ioctl;
+ dev->get_stats=qeth_get_stats;
+ dev->change_mtu=qeth_change_mtu;
+#ifdef QETH_VLAN
+ dev->vlan_rx_register = qeth_vlan_rx_register;
+ dev->vlan_rx_kill_vid = qeth_vlan_rx_kill_vid;
+#endif
+
+ dev->rebuild_header=
+#ifdef QETH_IPV6
+ (!(qeth_get_additional_dev_flags(card->type)&IFF_NOARP))?
+ (qeth_get_rebuild_header(card->link_type)?
+ qeth_rebuild_header:NULL):
+#endif /* QETH_IPV6 */
+ NULL;
+ dev->hard_header=
+#ifdef QETH_IPV6
+ (!(qeth_get_additional_dev_flags(card->type)&IFF_NOARP))?
+ (qeth_get_hard_header(card->link_type)?
+ qeth_hard_header:NULL):
+#endif /* QETH_IPV6 */
+ NULL;
+ dev->header_cache_update=
+#ifdef QETH_IPV6
+ (!(qeth_get_additional_dev_flags(card->type)&IFF_NOARP))?
+ (qeth_get_header_cache_update(card->link_type)?
+ qeth_header_cache_update:NULL):
+#endif /* QETH_IPV6 */
+ NULL;
+ dev->hard_header_cache=
+#ifdef QETH_IPV6
+ (!(qeth_get_additional_dev_flags(card->type)&IFF_NOARP))?
+ qeth_get_hard_header_cache(card->link_type):
+#endif /* QETH_IPV6 */
+ NULL;
+ dev->hard_header_parse=NULL;
+ dev->destructor=qeth_destructor;
+ dev->set_multicast_list=qeth_set_multicast_list;
+ dev->set_mac_address=qeth_set_mac_address;
+ dev->neigh_setup=qeth_neigh_setup;
+
+ dev->flags|=qeth_get_additional_dev_flags(card->type);
+
+ dev->flags|=(
+ (card->options.fake_broadcast==FAKE_BROADCAST)||
+ (card->broadcast_capable)
+ )?
+ IFF_BROADCAST:0;
+
+ /* is done in hardsetup_card... see comment below
+ qeth_send_qipassist(card,4);*/
+
+ /* that was the old place. one id. we need to make sure, that
+ * hydra knows about us going to use the same id again, so we
+ * do that in hardsetup_card every time
+ qeth_get_unique_id(card);*/
+
+#ifdef CONFIG_SHARED_IPV6_CARDS
+ dev->features=(card->unique_id&UNIQUE_ID_NOT_BY_CARD)?
+ 0:NETIF_F_SHARED_IPV6;
+ dev->dev_id=card->unique_id&0xffff;
+#endif /* CONFIG_SHARED_IPV6_CARDS */
+
+ dev->tx_queue_len=qeth_get_device_tx_q_len(card->type);
+ dev->hard_header_len=qeth_get_hlen(card->link_type)+
+ card->options.add_hhlen;
+ dev->addr_len=OSA_ADDR_LEN; /* is ok for eth, tr, atm lane */
+ netif_start_queue(dev);
+
+ dev->mtu=card->initial_mtu;
+
+#ifdef QETH_IPV6
+ qeth_ipv6_init_card(card);
+#endif /* QETH_IPV6 */
+
+ dev_init_buffers(dev);
+
+ return 0;
+}
+
+static int qeth_get_unitaddr(qeth_card_t *card)
+{
+ char *prcd;
+ int result=0;
+ char dbf_text[15];
+ int length;
+
+ sprintf(dbf_text,"gtua%4x",card->irq0);
+ QETH_DBF_TEXT2(0,trace,dbf_text);
+
+ result = read_conf_data(card->irq2, (void **)&prcd, &length, 0);
+ if (result) {
+ sprintf(dbf_text, "rcd%4x", result);
+ QETH_DBF_TEXT3(0, trace, dbf_text);
+ PRINT_ERR("read_conf_data for sch %x returned %i\n",
+ card->irq2, result);
+ goto exit;
+ }
+
+ card->chpid = prcd[30];
+ card->unit_addr2 = prcd[31];
+ card->cula = prcd[63];
+ /* Don't build queues with diag98 for VM guest lan. */
+ card->do_pfix = (MACHINE_HAS_PFIX) ? ((prcd[0x10]!=_ascebc['V']) ||
+ (prcd[0x11]!=_ascebc['M'])):0;
+
+ sprintf(dbf_text,"chpid:%02x",card->chpid);
+ QETH_DBF_TEXT2(0,trace,dbf_text);
+ sprintf(dbf_text,"unad2:%02x",card->unit_addr2);
+ QETH_DBF_TEXT2(0,trace,dbf_text);
+ sprintf(dbf_text,"cula:%02x",card->cula);
+ QETH_DBF_TEXT2(0,trace,dbf_text);
+
+ result=0;
+
+ exit:
+ return result;
+}
+
+static int qeth_send_nops(qeth_card_t *card)
+{
+ int result,result2;
+ char dbf_text[15];
+ int irq;
+ unsigned long saveflags;
+
+ card->dma_stuff->write_ccw.cmd_code=CCW_NOP_CMD;
+ card->dma_stuff->write_ccw.flags=CCW_FLAG_SLI;
+ card->dma_stuff->write_ccw.count=CCW_NOP_COUNT;
+ card->dma_stuff->write_ccw.cda=(unsigned long)NULL;
+
+#define DO_SEND_NOP(subchannel) \
+do { \
+ irq=subchannel; \
+ sprintf(dbf_text,"snnp%4x",irq); \
+ QETH_DBF_TEXT3(0,trace,dbf_text); \
+\
+ s390irq_spin_lock_irqsave(irq,saveflags); \
+ result=do_IO(irq,&card->dma_stuff->write_ccw,NOP_STATE,0,0); \
+ if (result) { \
+ qeth_delay_millis(QETH_WAIT_BEFORE_2ND_DOIO); \
+ result2=do_IO(irq,&card->dma_stuff->write_ccw, \
+ NOP_STATE,0,0); \
+ PRINT_WARN("qeth_send_nops on irq 0x%x: do_IO returned %i, " \
+ "next try returns %i\n", \
+ irq,result,result2); \
+ result=result2; \
+ } \
+ s390irq_spin_unlock_irqrestore(irq,saveflags); \
+\
+ if (result) goto exit; \
+\
+ if (qeth_sleepon(card,QETH_NOP_TIMEOUT)) { \
+ QETH_DBF_TEXT2(0,trace,"snnp:tme"); \
+ result=-EIO; \
+ goto exit; \
+ } \
+} while (0)
+
+ DO_SEND_NOP(card->irq0);
+ DO_SEND_NOP(card->irq1);
+ DO_SEND_NOP(card->irq2);
+
+exit:
+ return result;
+}
+
+static void qeth_clear_card_structures(qeth_card_t *card)
+{
+ int i,j;
+ char dbf_text[15];
+
+ if (!card) {
+ QETH_DBF_TEXT2(0,trace,"clrCRDnc");
+ return;
+ }
+
+ sprintf(dbf_text,"clcs%4x",card->irq0);
+ QETH_DBF_TEXT3(0,trace,dbf_text);
+
+ atomic_set(&card->is_startlaned,0);
+
+ for (i=0;i<QETH_MAX_QUEUES;i++) {
+ card->send_state[i]=SEND_STATE_DONT_PACK;
+ card->outbound_first_free_buffer[i]=0;
+ atomic_set(&card->outbound_used_buffers[i],0);
+ atomic_set(&card->outbound_ringbuffer_lock[i],0);
+
+ for (j=0;j<QDIO_MAX_BUFFERS_PER_Q;j++) {
+ card->outbound_buffer_send_state[i][j]=
+ SEND_STATE_DONT_PACK;
+ card->send_retries[i][j]=0;
+
+ if (i<card->no_queues) {
+ card->outbound_ringbuffer[i]->
+ ringbuf_element[j].
+ next_element_to_fill=0;
+ card->outbound_bytes_in_buffer[i]=0;
+ skb_queue_head_init(&card->
+ outbound_ringbuffer[i]->
+ ringbuf_element[j].
+ skb_list);
+ }
+ }
+ }
+
+ for (i=0;i<card->options.inbound_buffer_count;i++) {
+ xchg((int*)&card->inbound_buffer_pool_entry_used[i],
+ BUFFER_UNUSED);
+ }
+
+ spin_lock_init(&card->requeue_input_lock);
+ atomic_set(&card->requeue_position,0);
+ atomic_set(&card->requeue_counter,0);
+
+ card->seqno.trans_hdr=0;
+ card->seqno.pdu_hdr=0;
+ card->seqno.pdu_hdr_ack=0;
+ card->seqno.ipa=0;
+
+ qeth_clear_ifa4_list(&card->ip_current_state.ip_ifa);
+ qeth_clear_ifa4_list(&card->ip_new_state.ip_ifa);
+ qeth_clear_ifamc_list(&card->ip_mc_current_state.ipm_ifa);
+ qeth_clear_ifamc_list(&card->ip_mc_new_state.ipm_ifa);
+
+#ifdef QETH_IPV6
+ qeth_clear_ifa6_list(&card->ip_current_state.ip6_ifa);
+ qeth_clear_ifa6_list(&card->ip_new_state.ip6_ifa);
+ qeth_clear_ifamc_list(&card->ip_mc_current_state.ipm6_ifa);
+ qeth_clear_ifamc_list(&card->ip_mc_new_state.ipm6_ifa);
+#endif /* QETH_IPV6 */
+}
+
+static void qeth_init_input_buffers(qeth_card_t *card)
+{
+ int i;
+
+ /* slowly, slowly (we don't want to enqueue all buffers
+ * at one time) */
+ for (i=0;i<QDIO_MAX_BUFFERS_PER_Q;i++) {
+ atomic_set(&card->inbound_buffer_refcnt[i],1);
+ }
+ for (i=0;i<QDIO_MAX_BUFFERS_PER_Q;i++) {
+ atomic_set(&card->inbound_buffer_refcnt[i],0);
+ /* only try to queue as many buffers as we have at all */
+ if (i<card->options.inbound_buffer_count) {
+ qeth_queue_input_buffer(card,i,0);
+ }
+ }
+ qdio_synchronize(card->irq2,QDIO_FLAG_SYNC_INPUT,0);
+}
+
+/* initializes all the structures for a card */
+static int qeth_hardsetup_card(qeth_card_t *card,int in_recovery)
+{
+ int result,q,breakout;
+ unsigned long flags;
+ int laps=QETH_HARDSETUP_LAPS;
+ int clear_laps;
+ int cleanup_qdio;
+ char dbf_text[15];
+ int i,r;
+
+ /* setup name and so on */
+ atomic_set(&card->shutdown_phase,0);
+
+ if (atomic_read(&card->is_hardsetup)) {
+ sprintf(dbf_text,"hscd%4x",card->irq0);
+ QETH_DBF_TEXT2(1,trace,dbf_text);
+ PRINT_ALL("card is already hardsetup.\n");
+ return 0;
+ }
+
+ cleanup_qdio=in_recovery; /* if we are in recovery, we clean
+ the qdio stuff up */
+
+ my_spin_lock(&card->hardsetup_lock);
+ atomic_set(&card->write_busy,0);
+
+ do {
+ if (in_recovery) {
+ PRINT_STUPID("qeth: recovery: quiescing %s...\n",
+ card->dev_name);
+ sprintf(dbf_text,"Rqsc%4x",card->irq0);
+ QETH_DBF_TEXT2(0,trace,dbf_text);
+ qeth_wait_nonbusy(QETH_QUIESCE_WAIT_BEFORE_CLEAR);
+ }
+ clear_laps=QETH_HARDSETUP_CLEAR_LAPS;
+ do {
+ if (in_recovery)
+ PRINT_STUPID("clearing card %s\n",
+ card->dev_name);
+ qeth_clear_card(card,cleanup_qdio,
+ (card->type==QETH_CARD_TYPE_OSAE));
+ result=qeth_send_nops(card);
+ breakout=atomic_read(&card->break_out);
+ } while ( (--clear_laps) && (result) );
+ if (result) {
+ goto exit;
+ }
+
+ if (in_recovery) {
+ PRINT_STUPID("qeth: recovery: still quiescing %s...\n",
+ card->dev_name);
+ sprintf(dbf_text,"RQsc%4x",card->irq0);
+ QETH_DBF_TEXT2(0,trace,dbf_text);
+ qeth_wait_nonbusy(QETH_QUIESCE_WAIT_AFTER_CLEAR);
+ } else {
+ atomic_set(&card->shutdown_phase,0);
+ }
+
+ cleanup_qdio=0; /* qdio was cleaned now, if necessary */
+
+ result=qeth_get_unitaddr(card);
+ if (result) goto exit;
+
+ qeth_generate_tokens(card);
+
+#define PRINT_TOKENS do { \
+ sprintf(dbf_text,"stra "); \
+ memcpy(&dbf_text[4],&card->seqno.trans_hdr,4); \
+ QETH_DBF_HEX3(0,trace,dbf_text,QETH_DBF_TRACE_LEN); \
+ sprintf(dbf_text,"spdu "); \
+ memcpy(&dbf_text[4],&card->seqno.pdu_hdr,4); \
+ QETH_DBF_HEX3(0,trace,dbf_text,QETH_DBF_TRACE_LEN); \
+ sprintf(dbf_text,"spda "); \
+ memcpy(&dbf_text[4],&card->seqno.pdu_hdr_ack,4); \
+ QETH_DBF_HEX3(0,trace,dbf_text,QETH_DBF_TRACE_LEN); \
+ sprintf(dbf_text,"sipa "); \
+ memcpy(&dbf_text[4],&card->seqno.ipa,4); \
+ QETH_DBF_HEX3(0,trace,dbf_text,QETH_DBF_TRACE_LEN); \
+ sprintf(dbf_text,"tisw "); \
+ memcpy(&dbf_text[4],&card->token.issuer_rm_w,4); \
+ QETH_DBF_HEX3(0,trace,dbf_text,QETH_DBF_TRACE_LEN); \
+ sprintf(dbf_text,"tisr "); \
+ memcpy(&dbf_text[4],&card->token.issuer_rm_r,4); \
+ QETH_DBF_HEX3(0,trace,dbf_text,QETH_DBF_TRACE_LEN); \
+ sprintf(dbf_text,"tcfw "); \
+ memcpy(&dbf_text[4],&card->token.cm_filter_w,4); \
+ QETH_DBF_HEX3(0,trace,dbf_text,QETH_DBF_TRACE_LEN); \
+ sprintf(dbf_text,"tcfr "); \
+ memcpy(&dbf_text[4],&card->token.cm_filter_r,4); \
+ QETH_DBF_HEX3(0,trace,dbf_text,QETH_DBF_TRACE_LEN); \
+ sprintf(dbf_text,"tccw "); \
+ memcpy(&dbf_text[4],&card->token.cm_connection_w,4); \
+ QETH_DBF_HEX3(0,trace,dbf_text,QETH_DBF_TRACE_LEN); \
+ sprintf(dbf_text,"tccr "); \
+ memcpy(&dbf_text[4],&card->token.cm_connection_r,4); \
+ QETH_DBF_HEX3(0,trace,dbf_text,QETH_DBF_TRACE_LEN); \
+ sprintf(dbf_text,"tufw "); \
+ memcpy(&dbf_text[4],&card->token.ulp_filter_w,4); \
+ QETH_DBF_HEX3(0,trace,dbf_text,QETH_DBF_TRACE_LEN); \
+ sprintf(dbf_text,"tufr "); \
+ memcpy(&dbf_text[4],&card->token.ulp_filter_r,4); \
+ QETH_DBF_HEX3(0,trace,dbf_text,QETH_DBF_TRACE_LEN); \
+ sprintf(dbf_text,"tucw "); \
+ memcpy(&dbf_text[4],&card->token.ulp_connection_w,4); \
+ QETH_DBF_HEX3(0,trace,dbf_text,QETH_DBF_TRACE_LEN); \
+ sprintf(dbf_text,"tucr "); \
+ memcpy(&dbf_text[4],&card->token.ulp_connection_r,4); \
+ QETH_DBF_HEX3(0,trace,dbf_text,QETH_DBF_TRACE_LEN); \
+ } while (0)
+ PRINT_TOKENS;
+
+ /* card->break_out and problem will be set here to 0
+ * (in each lap) (there can't be a problem at this
+ * early time) */
+ atomic_set(&card->problem,0);
+ atomic_set(&card->break_out,0);
+
+#define CHECK_ERRORS \
+ breakout=atomic_read(&card->break_out); \
+ if (breakout==QETH_BREAKOUT_AGAIN) \
+ continue; \
+ else if (breakout==QETH_BREAKOUT_LEAVE) { \
+ result=-EIO; \
+ goto exit; \
+ } \
+ if (result) goto exit
+
+ QETH_DBF_TEXT2(0,trace,"hsidxard");
+ result=qeth_idx_activate_read(card);
+ CHECK_ERRORS;
+
+ PRINT_TOKENS;
+ QETH_DBF_TEXT2(0,trace,"hsidxawr");
+ result=qeth_idx_activate_write(card);
+ CHECK_ERRORS;
+
+ QETH_DBF_TEXT2(0,trace,"hsissurd");
+ /* from here, there will always be an outstanding read */
+ s390irq_spin_lock_irqsave(card->irq0,flags);
+ qeth_issue_next_read(card);
+ s390irq_spin_unlock_irqrestore(card->irq0,flags);
+
+ PRINT_TOKENS;
+ QETH_DBF_TEXT2(0,trace,"hscmenab");
+ result=qeth_cm_enable(card);
+ CHECK_ERRORS;
+
+ PRINT_TOKENS;
+ QETH_DBF_TEXT2(0,trace,"hscmsetu");
+ result=qeth_cm_setup(card);
+ CHECK_ERRORS;
+
+ PRINT_TOKENS;
+ QETH_DBF_TEXT2(0,trace,"hsulpena");
+ result=qeth_ulp_enable(card);
+ CHECK_ERRORS;
+
+ PRINT_TOKENS;
+ QETH_DBF_TEXT2(0,trace,"hsulpset");
+ result=qeth_ulp_setup(card);
+ CHECK_ERRORS;
+
+ cleanup_qdio=1;
+
+ QETH_DBF_TEXT2(0,trace,"hsqdioes");
+ result=qeth_qdio_establish(card);
+ CHECK_ERRORS;
+
+ PRINT_TOKENS;
+ QETH_DBF_TEXT2(0,trace,"hsqdioac");
+ result=qeth_qdio_activate(card);
+ CHECK_ERRORS;
+
+ PRINT_TOKENS;
+ QETH_DBF_TEXT2(0,trace,"hsdmact");
+ result=qeth_dm_act(card);
+ CHECK_ERRORS;
+ } while ( (laps--) && (breakout==QETH_BREAKOUT_AGAIN) );
+ if (breakout==QETH_BREAKOUT_AGAIN) {
+ sprintf(dbf_text,"hsnr%4x",card->irq0);
+ QETH_DBF_TEXT2(0,trace,dbf_text);
+ printk("qeth: recovery not successful on device " \
+ "0x%X/0x%X/0x%X; giving up.\n",
+ card->devno0,card->devno1,card->devno2);
+ result=-EIO;
+ goto exit;
+ }
+
+ qeth_clear_ifa4_list(&card->ip_current_state.ip_ifa);
+ qeth_clear_ifa4_list(&card->ip_new_state.ip_ifa);
+ qeth_clear_ifamc_list(&card->ip_mc_current_state.ipm_ifa);
+ qeth_clear_ifamc_list(&card->ip_mc_new_state.ipm_ifa);
+
+#ifdef QETH_IPV6
+ qeth_clear_ifa6_list(&card->ip_current_state.ip6_ifa);
+ qeth_clear_ifa6_list(&card->ip_new_state.ip6_ifa);
+ qeth_clear_ifamc_list(&card->ip_mc_current_state.ipm6_ifa);
+ qeth_clear_ifamc_list(&card->ip_mc_new_state.ipm6_ifa);
+#endif /* QETH_IPV6 */
+
+ if (!atomic_read(&card->is_registered)) {
+ card->dev->dev_addr[0]=0; /* we don't know the mac addr yet */
+ card->dev->dev_addr[1]=0;
+ card->dev->dev_addr[2]=0;
+ card->dev->dev_addr[3]=0;
+ card->dev->dev_addr[4]=0;
+ card->dev->dev_addr[5]=0;
+ card->dev->broadcast[0]=card->dev->broadcast[1]=0xff;
+ card->dev->broadcast[2]=card->dev->broadcast[3]=0xff;
+ card->dev->broadcast[4]=card->dev->broadcast[5]=0xff;
+
+ card->dev->type=qeth_get_arphrd_type(card->type,
+ card->link_type);
+
+ card->dev->init=qeth_init_dev;
+
+ if (card->options.memusage==MEMUSAGE_CONTIG) {
+ card->easy_copy_cap=
+ qeth_determine_easy_copy_cap(card->type);
+ } else card->easy_copy_cap=0;
+ card->ipa_timeout=qeth_get_ipa_timeout(card->type);
+ }
+
+ atomic_set(&card->is_hardsetup,1);
+ atomic_set(&card->is_softsetup,0);
+ atomic_set(&card->startlan_attempts,1);
+
+ for (q=0;q<card->no_queues;q++)
+ card->send_state[q]=SEND_STATE_DONT_PACK;
+
+ /* here we need to know, whether we should include a value
+ * into eui-64 address generation */
+ QETH_DBF_TEXT2(0,trace,"qipassi4");
+ r=qeth_send_qipassist(card,4);
+ if (r) {
+ PRINT_WARN("couldn't send QIPASSIST4 on %s: " \
+ "0x%x\n",card->dev_name,r);
+ sprintf(dbf_text,"QIP4%4x",r);
+ QETH_DBF_TEXT2(0,trace,dbf_text);
+ }
+
+ sprintf(dbf_text,"%4x%4x",card->ipa_supported,card->ipa_enabled);
+ QETH_DBF_TEXT2(0,trace,dbf_text);
+
+ qeth_get_unique_id(card);
+
+ /* print out status */
+ if (in_recovery) {
+ qeth_clear_card_structures(card);
+ qeth_init_input_buffers(card);
+ QETH_DBF_TEXT1(0,trace,"RECOVSUC");
+ printk("qeth: recovered device 0x%X/0x%X/0x%X (%s) " \
+ "successfully.\n",
+ card->devno0,card->devno1,card->devno2,
+ card->dev_name);
+ } else {
+ QETH_DBF_TEXT2(0,trace,"hrdsetok");
+
+ switch (card->type) {
+ case QETH_CARD_TYPE_OSAE:
+ /* VM will use a non-zero first character to indicate
+ * a HiperSockets like reporting of the level
+ * OSA sets the first character to zero */
+ if (!card->level[0]) {
+ sprintf(card->level,"%02x%02x",card->level[2],
+ card->level[3]);
+ card->level[QETH_MCL_LENGTH]=0;
+ break;
+ } else {
+ /* fallthrough */
+ }
+ case QETH_CARD_TYPE_IQD:
+ card->level[0]=(char)_ebcasc[(__u8)card->level[0]];
+ card->level[1]=(char)_ebcasc[(__u8)card->level[1]];
+ card->level[2]=(char)_ebcasc[(__u8)card->level[2]];
+ card->level[3]=(char)_ebcasc[(__u8)card->level[3]];
+ card->level[QETH_MCL_LENGTH]=0;
+ break;
+ default:
+ memset(&card->level[0],0,QETH_MCL_LENGTH+1);
+ }
+
+ sprintf(dbf_text,"lvl:%s",card->level);
+ QETH_DBF_TEXT2(0,setup,dbf_text);
+
+ if (card->portname_required) {
+ sprintf(dbf_text,"%s",card->options.portname+1);
+ for (i=0;i<8;i++)
+ dbf_text[i]=(char)_ebcasc[(__u8)dbf_text[i]];
+ dbf_text[8]=0;
+ printk("qeth: Device 0x%X/0x%X/0x%X is a%s " \
+ "card%s%s%s\n" \
+ "with link type %s (portname: %s)\n",
+ card->devno0,card->devno1,card->devno2,
+ qeth_get_cardname(card->type),
+ (card->level[0])?" (level: ":"",
+ (card->level[0])?card->level:"",
+ (card->level[0])?")":"",
+ qeth_get_link_type_name(card->type,
+ card->link_type),
+ dbf_text);
+ } else {
+ printk("qeth: Device 0x%X/0x%X/0x%X is a%s " \
+ "card%s%s%s\nwith link type %s " \
+ "(no portname needed by interface)\n",
+ card->devno0,card->devno1,card->devno2,
+ qeth_get_cardname(card->type),
+ (card->level[0])?" (level: ":"",
+ (card->level[0])?card->level:"",
+ (card->level[0])?")":"",
+ qeth_get_link_type_name(card->type,
+ card->link_type));
+ }
+ }
+
+ exit:
+ my_spin_unlock(&card->hardsetup_lock);
+ return result;
+}
+
+static int qeth_reinit_thread(void *param)
+{
+ qeth_card_t *card=(qeth_card_t*)param;
+ int already_registered;
+ int already_hardsetup;
+ int retry=QETH_RECOVERY_HARDSETUP_RETRY;
+ int result;
+ char dbf_text[15];
+ char name[15];
+
+ sprintf(dbf_text,"RINI%4x",card->irq0);
+ QETH_DBF_TEXT1(0,trace,dbf_text);
+
+ daemonize();
+
+ /* set a nice name ... */
+ sprintf(name, "qethrinid%04x", card->irq0);
+ strcpy(current->comm, name);
+
+ if (atomic_read(&card->shutdown_phase)) goto out_wakeup;
+ down_interruptible(&card->reinit_thread_sem);
+ if (atomic_read(&card->shutdown_phase)) goto out_wakeup;
+
+ QETH_DBF_TEXT1(0,trace,"ri-gotin");
+ PRINT_STUPID("entering recovery (reinit) thread for device %s\n",
+ card->dev_name);
+
+ atomic_set(&card->is_startlaned,0);
+ atomic_set(&card->is_softsetup,0);
+
+ my_read_lock(&list_lock);
+ if (!qeth_verify_card(card)) goto out;
+ QETH_DBF_TEXT1(0,trace,"ri-vrfd");
+
+ atomic_set(&card->write_busy,0);
+ qeth_set_dev_flag_norunning(card);
+ already_hardsetup=atomic_read(&card->is_hardsetup);
+ already_registered=atomic_read(&card->is_registered);
+ if (already_hardsetup) {
+ atomic_set(&card->is_hardsetup,0);
+
+ if (-1==my_spin_lock_nonbusy(card,&setup_lock)) goto out;
+ if (atomic_read(&card->shutdown_phase)) goto out_wakeup;
+
+ atomic_set(&card->escape_softsetup,1);
+ if (-1==my_spin_lock_nonbusy(card,&card->softsetup_lock)) {
+ atomic_set(&card->escape_softsetup,0);
+ goto out;
+ }
+ atomic_set(&card->escape_softsetup,0);
+ if (atomic_read(&card->shutdown_phase)) {
+ my_spin_unlock(&card->softsetup_lock);
+ goto out_wakeup;
+ }
+ if (!qeth_verify_card(card)) goto out;
+
+ if (already_registered)
+ netif_stop_queue(card->dev);
+
+ qeth_wait_nonbusy(QETH_QUIESCE_NETDEV_TIME);
+
+ atomic_set(&card->is_startlaned,0);
+
+ QETH_DBF_TEXT1(0,trace,"ri-frskb");
+ qeth_free_all_skbs(card);
+ do {
+ QETH_DBF_TEXT1(0,trace,"ri-hrdst");
+ result=qeth_hardsetup_card(card,1);
+ } while (result&&(retry--));
+
+ /* tries to remove old ips, that's paranoid, but ok */
+ qeth_clear_ifa4_list(&card->ip_new_state.ip_ifa);
+ qeth_clear_ifamc_list(&card->ip_mc_new_state.ipm_ifa);
+
+#ifdef QETH_IPV6
+ qeth_clear_ifa6_list(&card->ip_new_state.ip6_ifa);
+ qeth_clear_ifamc_list(&card->ip_mc_new_state.ipm6_ifa);
+#endif /* QETH_IPV6 */
+
+ if (result) {
+ QETH_DBF_TEXT1(0,trace,"ri-nosuc");
+ printk("qeth: RECOVERY WAS NOT SUCCESSFUL ON %s " \
+ "(devnos 0x%X/0x%X/0x%X), GIVING UP, " \
+ "OUTGOING PACKETS WILL BE DISCARDED!\n",
+ card->dev_name,card->devno0,
+ card->devno1,card->devno2);
+ /* early leave hard_start_xmit! */
+ atomic_set(&card->is_startlaned,0);
+ /* show status in /proc/qeth */
+ atomic_set(&card->is_gone,1);
+ qeth_wakeup_procfile();
+ } else {
+ QETH_DBF_TEXT1(0,trace,"ri-sftst");
+ qeth_softsetup_card(card,QETH_LOCK_ALREADY_HELD);
+ my_spin_unlock(&card->softsetup_lock);
+
+ if (!already_registered) {
+ QETH_DBF_TEXT1(0,trace,"ri-regcd");
+ qeth_register_netdev(card);
+ }
+ qeth_restore_dev_flag_state(card);
+ atomic_set(&card->is_gone,0);
+ netif_wake_queue(card->dev);
+ qeth_wakeup_procfile();
+ }
+ my_spin_unlock(&setup_lock);
+ }
+out:
+ atomic_set(&card->in_recovery,0);
+ my_read_unlock(&list_lock);
+ QETH_DBF_TEXT1(0,trace,"ri-leave");
+out_wakeup:
+ up(&card->reinit_thread_sem);
+ atomic_dec(&card->reinit_counter);
+
+ return 0;
+}
+
+static void qeth_fill_qeth_card_options(qeth_card_t *card)
+{
+ int i;
+
+ card->options.portname[0]=0;
+ for (i=1;i<9;i++)
+ card->options.portname[i]=_ascebc[' '];
+ strcpy(card->options.devname," ");
+ card->options.routing_type4=NO_ROUTER;
+#ifdef QETH_IPV6
+ card->options.routing_type6=NO_ROUTER;
+#endif /* QETH_IPV6 */
+ card->options.portno=0;
+ card->options.checksum_type=QETH_CHECKSUM_DEFAULT;
+ card->options.do_prio_queueing=0;
+ card->options.default_queue=QETH_DEFAULT_QUEUE;
+ card->options.inbound_buffer_count=DEFAULT_BUFFER_COUNT;
+ card->options.polltime=QETH_MAX_INPUT_THRESHOLD;
+ card->options.memusage=MEMUSAGE_DISCONTIG;
+ card->options.macaddr_mode=MACADDR_NONCANONICAL;
+ card->options.broadcast_mode=BROADCAST_ALLRINGS;
+ card->options.fake_broadcast=DONT_FAKE_BROADCAST;
+ card->options.ena_ipat=ENABLE_TAKEOVER;
+ card->options.add_hhlen=DEFAULT_ADD_HHLEN;
+ card->options.fake_ll=DONT_FAKE_LL;
+ card->options.async_iqd=SYNC_IQD;
+}
+
+static qeth_card_t *qeth_alloc_card(void)
+{
+ qeth_card_t *card;
+
+ QETH_DBF_TEXT3(0,trace,"alloccrd");
+ card=(qeth_card_t *)vmalloc(sizeof(qeth_card_t));
+ if (!card) goto exit_card;
+ memset(card,0,sizeof(qeth_card_t));
+ init_waitqueue_head(&card->wait_q);
+ init_waitqueue_head(&card->ioctl_wait_q);
+
+ qeth_fill_qeth_card_options(card);
+
+ card->dma_stuff=(qeth_dma_stuff_t*)kmalloc(sizeof(qeth_dma_stuff_t),
+ GFP_DMA);
+ if (!card->dma_stuff) goto exit_dma;
+ memset(card->dma_stuff,0,sizeof(qeth_dma_stuff_t));
+
+ card->dma_stuff->recbuf=(char*)kmalloc(QETH_BUFSIZE,GFP_DMA);
+ if (!card->dma_stuff->recbuf) goto exit_dma1;
+ memset(card->dma_stuff->recbuf,0,QETH_BUFSIZE);
+
+ card->dma_stuff->sendbuf=(char*)kmalloc(QETH_BUFSIZE,GFP_DMA);
+ if (!card->dma_stuff->sendbuf) goto exit_dma2;
+ memset(card->dma_stuff->sendbuf,0,QETH_BUFSIZE);
+
+ card->dev=(struct net_device *)kmalloc(sizeof(struct net_device),
+ GFP_KERNEL);
+ if (!card->dev) goto exit_dev;
+ memset(card->dev,0,sizeof(struct net_device));
+
+ card->stats=(struct net_device_stats *)kmalloc(
+ sizeof(struct net_device_stats),GFP_KERNEL);
+ if (!card->stats) goto exit_stats;
+ memset(card->stats,0,sizeof(struct net_device_stats));
+
+ card->devstat0=(devstat_t *)kmalloc(sizeof(devstat_t),GFP_KERNEL);
+ if (!card->devstat0) goto exit_stats;
+ memset(card->devstat0,0,sizeof(devstat_t));
+
+ card->devstat1=(devstat_t *)kmalloc(sizeof(devstat_t),GFP_KERNEL);
+ if (!card->devstat1) {
+ kfree(card->devstat0);
+ goto exit_stats;
+ }
+ memset(card->devstat1,0,sizeof(devstat_t));
+
+ card->devstat2=(devstat_t *)kmalloc(sizeof(devstat_t),GFP_KERNEL);
+ if (!card->devstat2) {
+ kfree(card->devstat1);
+ kfree(card->devstat0);
+ goto exit_stats;
+ }
+ memset(card->devstat2,0,sizeof(devstat_t));
+
+ spin_lock_init(&card->wait_q_lock);
+ spin_lock_init(&card->softsetup_lock);
+ spin_lock_init(&card->hardsetup_lock);
+ spin_lock_init(&card->ioctl_lock);
+#ifdef QETH_VLAN
+ spin_lock_init(&card->vlan_lock);
+ card->vlangrp = NULL;
+#endif
+ card->unique_id=0;
+ sema_init(&card->reinit_thread_sem,0);
+ up(&card->reinit_thread_sem);
+
+ /* setup card stuff */
+ card->ip_current_state.ip_ifa=NULL;
+ card->ip_new_state.ip_ifa=NULL;
+ card->ip_mc_current_state.ipm_ifa=NULL;
+ card->ip_mc_new_state.ipm_ifa=NULL;
+
+#ifdef QETH_IPV6
+ card->ip_current_state.ip6_ifa=NULL;
+ card->ip_new_state.ip6_ifa=NULL;
+ card->ip_mc_current_state.ipm6_ifa=NULL;
+ card->ip_mc_new_state.ipm6_ifa=NULL;
+#endif /* QETH_IPV6 */
+
+ /* setup net_device stuff */
+ card->dev->priv=card;
+
+ strncpy(card->dev->name,card->dev_name,IFNAMSIZ);
+
+ /* setup net_device_stats stuff */
+ /* =nothing yet */
+
+ /* and return to the sender */
+ return card;
+
+ /* these are quick exits in case of failures of the kmallocs */
+exit_stats:
+ kfree(card->dev);
+exit_dev:
+ kfree(card->dma_stuff->sendbuf);
+exit_dma2:
+ kfree(card->dma_stuff->recbuf);
+exit_dma1:
+ kfree(card->dma_stuff);
+exit_dma:
+ kfree(card);
+exit_card:
+ return NULL;
+}
+
+static int qeth_init_ringbuffers1(qeth_card_t *card)
+{
+ int i,j;
+ char dbf_text[15];
+
+ sprintf(dbf_text,"irb1%4x",card->irq0);
+ QETH_DBF_TEXT3(0,trace,dbf_text);
+
+ for (i=0;i<card->no_queues;i++) {
+ card->outbound_ringbuffer[i]=
+ vmalloc(sizeof(qeth_ringbuffer_t));
+ if (!card->outbound_ringbuffer[i]) {
+ for (j=i-1;j>=0;j--) {
+ vfree(card->outbound_ringbuffer[j]);
+ card->outbound_ringbuffer[j]=NULL;
+ }
+ return -ENOMEM;
+ }
+ memset(card->outbound_ringbuffer[i],0,
+ sizeof(qeth_ringbuffer_t));
+ for (j=0;j<QDIO_MAX_BUFFERS_PER_Q;j++)
+ skb_queue_head_init(&card->outbound_ringbuffer[i]->
+ ringbuf_element[j].skb_list);
+ }
+
+ return 0;
+}
+
+static int qeth_init_ringbuffers2(qeth_card_t *card)
+{
+ int i,j;
+ int failed=0;
+ char dbf_text[15];
+ int discont_mem,element_count;
+ long alloc_size;
+
+ sprintf(dbf_text,"irb2%4x",card->irq0);
+ QETH_DBF_TEXT3(0,trace,dbf_text);
+
+ discont_mem=(card->options.memusage==MEMUSAGE_DISCONTIG);
+ element_count=(discont_mem)?BUFFER_MAX_ELEMENTS:1;
+ alloc_size=(discont_mem)?PAGE_SIZE:BUFFER_SIZE;
+ if (discont_mem) {
+ for (i=0;i<card->options.inbound_buffer_count;i++) {
+ for (j=0;j<element_count;j++) {
+ card->inbound_buffer_pool_entry[i][j]=
+ kmalloc(alloc_size,GFP_KERNEL);
+ if (!card->inbound_buffer_pool_entry[i][j]) {
+ failed=1;
+ goto out;
+ }
+ }
+ card->inbound_buffer_pool_entry_used[i]=BUFFER_UNUSED;
+ }
+ } else {
+ for (i=0;i<card->options.inbound_buffer_count;i++) {
+ card->inbound_buffer_pool_entry[i][0]=
+ kmalloc(alloc_size,GFP_KERNEL);
+ if (!card->inbound_buffer_pool_entry[i][0]) failed=1;
+ for (j=1;j<element_count;j++)
+ card->inbound_buffer_pool_entry[i][j]=
+ card->inbound_buffer_pool_entry[i][0]+
+ PAGE_SIZE*j;
+ card->inbound_buffer_pool_entry_used[i]=BUFFER_UNUSED;
+ }
+ }
+
+out:
+ if (failed) {
+ for (i=0;i<card->options.inbound_buffer_count;i++) {
+ for (j=0;j<QDIO_MAX_ELEMENTS_PER_BUFFER;j++) {
+ if (card->inbound_buffer_pool_entry[i][j]) {
+ if (j<element_count) kfree(card->
+ inbound_buffer_pool_entry
+ [i][j]);
+ card->inbound_buffer_pool_entry
+ [i][j]=NULL;
+ }
+ }
+ }
+ for (i=0;i<card->no_queues;i++) {
+ vfree(card->outbound_ringbuffer[i]);
+ card->outbound_ringbuffer[i]=NULL;
+ }
+ return -ENOMEM;
+ }
+
+ spin_lock_init(&card->requeue_input_lock);
+
+ return 0;
+}
+
+/* also locked from outside (setup_lock) */
+static void qeth_insert_card_into_list(qeth_card_t *card)
+{
+ char dbf_text[15];
+
+ sprintf(dbf_text,"icil%4x",card->irq0);
+ QETH_DBF_TEXT3(0,trace,dbf_text);
+
+ my_write_lock(&list_lock);
+ card->next=firstcard;
+ firstcard=card;
+ my_write_unlock(&list_lock);
+}
+
+static int qeth_determine_card_type(qeth_card_t *card)
+{
+ int i=0;
+ char dbf_text[15];
+
+ while (known_devices[i][4]) {
+ if ( (card->dev_type==known_devices[i][2]) &&
+ (card->dev_model==known_devices[i][3]) ) {
+ card->type=known_devices[i][4];
+ if (card->options.ena_ipat==ENABLE_TAKEOVER)
+ card->func_level=known_devices[i][6];
+ else
+ card->func_level=known_devices[i][7];
+ card->no_queues=known_devices[i][8];
+ card->is_multicast_different=known_devices[i][9];
+ sprintf(dbf_text,"irq %4x",card->irq0);
+ QETH_DBF_TEXT2(0,setup,dbf_text);
+ sprintf(dbf_text,"ctyp%4x",card->type);
+ QETH_DBF_TEXT2(0,setup,dbf_text);
+ return 0;
+ }
+ i++;
+ }
+ card->type=QETH_CARD_TYPE_UNKNOWN;
+ sprintf(dbf_text,"irq %4x",card->irq0);
+ QETH_DBF_TEXT2(0,setup,dbf_text);
+ sprintf(dbf_text,"ctypUNKN");
+ QETH_DBF_TEXT2(0,setup,dbf_text);
+ PRINT_ERR("unknown card type on irq x%x\n",card->irq0);
+ return -ENOENT;
+}
+
+static int qeth_getint(char *s,int longint)
+{
+ int cnt;
+ int hex;
+ int result;
+ char c;
+
+ if (!s) return -1;
+ hex=((s[0]=='0')&&( (s[1]=='x') || (s[1]=='X') ))?1:0;
+ cnt=(hex)?2:0; /* start from the first real digit */
+ if (!(s[cnt])) return -1;
+ result=0;
+ while ((c=s[cnt++])) {
+ if (hex) {
+ if (isxdigit(c)) result=result*16+qeth_getxdigit(c);
+ else return -1;
+ } else {
+ if (isdigit(c)) result=result*10+c-'0';
+ else return -1;
+ }
+ /* prevent overflow, 0xffff is enough for us */
+ if (longint) {
+ if (result>0xfffffff) return -1;
+ } else {
+ if (result>0xffff) return -1;
+ }
+ }
+ return result;
+}
+
+static int qeth_setvalue_if_possible(qeth_card_t *card,char *option,
+ int parse_category,
+ int *variable,int value)
+{
+ int *a_p=0; /* to shut the compiler up */
+ int routing_already_set_conflict=0;
+
+ a_p= &card->options.already_parsed[parse_category];
+
+ /* reject a general router statement, if router4 or router6
+ * have already been set */
+ if (parse_category==PARSE_ROUTING_TYPE) {
+ if ( (card->options.already_parsed[PARSE_ROUTING_TYPE4]) ||
+ (card->options.already_parsed[PARSE_ROUTING_TYPE6]) )
+ routing_already_set_conflict=1;
+ }
+
+ /* reject a router4 statement, if a general router statement
+ * has already been set */
+ if (parse_category==PARSE_ROUTING_TYPE4) {
+ if ( (card->options.already_parsed[PARSE_ROUTING_TYPE4]) &&
+ (card->options.already_parsed[PARSE_ROUTING_TYPE6]) )
+ routing_already_set_conflict=1;
+ }
+
+ /* reject a router6 statement, if a general router statement
+ * has already been set */
+ if (parse_category==PARSE_ROUTING_TYPE6) {
+ if ( (card->options.already_parsed[PARSE_ROUTING_TYPE4]) &&
+ (card->options.already_parsed[PARSE_ROUTING_TYPE6]) )
+ routing_already_set_conflict=1;
+ }
+
+ if ( (routing_already_set_conflict) ||
+ ((parse_category!=-1) && (*a_p)) ) {
+ PRINT_ERR("parameter %s does not fit to previous " \
+ "parameters\n",option);
+ return 0;
+ }
+ *a_p=1;
+ *variable=value;
+ return 1;
+}
+
+/* um... */
+#define doit1(a,b,c,d) \
+ if (!strcmp(option,a)) \
+ return qeth_setvalue_if_possible(card,option,b,((int*)&c),d)
+
+#define doit2(a,b,c,d,e,f) \
+ doit1(a,b,c,d) | qeth_setvalue_if_possible(card,option,-1,((int*)&e),f)
+
+/* returns 0, if option could notr be parsed alright */
+static int qeth_parse_option(qeth_card_t *card,char *option)
+{
+ int i;
+ int *a_p;
+
+#ifdef QETH_IPV6
+ doit2("no_router",PARSE_ROUTING_TYPE,
+ card->options.routing_type4,NO_ROUTER,
+ card->options.routing_type6,NO_ROUTER);
+ doit2("primary_router",PARSE_ROUTING_TYPE,
+ card->options.routing_type4,PRIMARY_ROUTER,
+ card->options.routing_type6,PRIMARY_ROUTER);
+ doit2("secondary_router",PARSE_ROUTING_TYPE,
+ card->options.routing_type4,SECONDARY_ROUTER,
+ card->options.routing_type6,SECONDARY_ROUTER);
+ doit2("multicast_router",PARSE_ROUTING_TYPE,
+ card->options.routing_type4,MULTICAST_ROUTER,
+ card->options.routing_type6,MULTICAST_ROUTER);
+ doit2("primary_connector",PARSE_ROUTING_TYPE,
+ card->options.routing_type4,PRIMARY_CONNECTOR,
+ card->options.routing_type6,PRIMARY_CONNECTOR);
+ doit2("secondary_connector",PARSE_ROUTING_TYPE,
+ card->options.routing_type4,SECONDARY_CONNECTOR,
+ card->options.routing_type6,SECONDARY_CONNECTOR);
+#else /* QETH_IPV6 */
+ doit1("no_router",PARSE_ROUTING_TYPE,
+ card->options.routing_type4,NO_ROUTER);
+ doit1("primary_router",PARSE_ROUTING_TYPE,
+ card->options.routing_type4,PRIMARY_ROUTER);
+ doit1("secondary_router",PARSE_ROUTING_TYPE,
+ card->options.routing_type4,SECONDARY_ROUTER);
+ doit1("multicast_router",PARSE_ROUTING_TYPE,
+ card->options.routing_type4,MULTICAST_ROUTER);
+ doit1("primary_connector",PARSE_ROUTING_TYPE,
+ card->options.routing_type4,PRIMARY_CONNECTOR);
+ doit1("secondary_connector",PARSE_ROUTING_TYPE,
+ card->options.routing_type4,SECONDARY_CONNECTOR);
+#endif /* QETH_IPV6 */
+
+ doit1("no_router4",PARSE_ROUTING_TYPE4,
+ card->options.routing_type4,NO_ROUTER);
+ doit1("primary_router4",PARSE_ROUTING_TYPE4,
+ card->options.routing_type4,PRIMARY_ROUTER);
+ doit1("secondary_router4",PARSE_ROUTING_TYPE4,
+ card->options.routing_type4,SECONDARY_ROUTER);
+ doit1("multicast_router4",PARSE_ROUTING_TYPE4,
+ card->options.routing_type4,MULTICAST_ROUTER);
+ doit1("primary_connector4",PARSE_ROUTING_TYPE4,
+ card->options.routing_type4,PRIMARY_CONNECTOR);
+ doit1("secondary_connector4",PARSE_ROUTING_TYPE4,
+ card->options.routing_type4,SECONDARY_CONNECTOR);
+
+#ifdef QETH_IPV6
+ doit1("no_router6",PARSE_ROUTING_TYPE6,
+ card->options.routing_type6,NO_ROUTER);
+ doit1("primary_router6",PARSE_ROUTING_TYPE6,
+ card->options.routing_type6,PRIMARY_ROUTER);
+ doit1("secondary_router6",PARSE_ROUTING_TYPE6,
+ card->options.routing_type6,SECONDARY_ROUTER);
+ doit1("multicast_router6",PARSE_ROUTING_TYPE6,
+ card->options.routing_type6,MULTICAST_ROUTER);
+ doit1("primary_connector6",PARSE_ROUTING_TYPE6,
+ card->options.routing_type6,PRIMARY_CONNECTOR);
+ doit1("secondary_connector6",PARSE_ROUTING_TYPE6,
+ card->options.routing_type6,SECONDARY_CONNECTOR);
+#endif /* QETH_IPV6 */
+
+ doit1("sw_checksumming",PARSE_CHECKSUMMING,
+ card->options.checksum_type,SW_CHECKSUMMING);
+ doit1("hw_checksumming",PARSE_CHECKSUMMING,
+ card->options.checksum_type,HW_CHECKSUMMING);
+ doit1("no_checksumming",PARSE_CHECKSUMMING,
+ card->options.checksum_type,NO_CHECKSUMMING);
+
+ doit1("prio_queueing_prec",PARSE_PRIO_QUEUEING,
+ card->options.do_prio_queueing,PRIO_QUEUEING_PREC);
+ doit1("prio_queueing_tos",PARSE_PRIO_QUEUEING,
+ card->options.do_prio_queueing,PRIO_QUEUEING_TOS);
+
+ doit1("mem_discontig",PARSE_MEMUSAGE,
+ card->options.memusage,MEMUSAGE_DISCONTIG);
+ doit1("mem_contig",PARSE_MEMUSAGE,
+ card->options.memusage,MEMUSAGE_CONTIG);
+
+ doit1("broadcast_allrings",PARSE_BROADCAST_MODE,
+ card->options.broadcast_mode,BROADCAST_ALLRINGS);
+ doit1("broadcast_local",PARSE_BROADCAST_MODE,
+ card->options.broadcast_mode,BROADCAST_LOCAL);
+
+ doit1("macaddr_noncanon",PARSE_MACADDR_MODE,
+ card->options.macaddr_mode,MACADDR_NONCANONICAL);
+ doit1("macaddr_canon",PARSE_MACADDR_MODE,
+ card->options.macaddr_mode,MACADDR_CANONICAL);
+
+ doit1("enable_takeover",PARSE_ENA_IPAT,
+ card->options.ena_ipat,ENABLE_TAKEOVER);
+ doit1("disable_takeover",PARSE_ENA_IPAT,
+ card->options.ena_ipat,DISABLE_TAKEOVER);
+
+ doit1("fake_broadcast",PARSE_FAKE_BROADCAST,
+ card->options.fake_broadcast,FAKE_BROADCAST);
+ doit1("dont_fake_broadcast",PARSE_FAKE_BROADCAST,
+ card->options.fake_broadcast,DONT_FAKE_BROADCAST);
+
+ doit1("fake_ll",PARSE_FAKE_LL,
+ card->options.fake_ll,FAKE_LL);
+ doit1("dont_fake_ll",PARSE_FAKE_LL,
+ card->options.fake_ll,DONT_FAKE_LL);
+
+ doit1("sync_hsi",PARSE_ASYNC_IQD,
+ card->options.async_iqd,SYNC_IQD);
+ doit1("async_hsi",PARSE_ASYNC_IQD,
+ card->options.async_iqd,ASYNC_IQD);
+
+ doit2("no_prio_queueing:0",PARSE_PRIO_QUEUEING,
+ card->options.do_prio_queueing,NO_PRIO_QUEUEING,
+ card->options.default_queue,0);
+ doit2("no_prio_queueing:1",PARSE_PRIO_QUEUEING,
+ card->options.do_prio_queueing,NO_PRIO_QUEUEING,
+ card->options.default_queue,1);
+ doit2("no_prio_queueing:2",PARSE_PRIO_QUEUEING,
+ card->options.do_prio_queueing,NO_PRIO_QUEUEING,
+ card->options.default_queue,2);
+ doit2("no_prio_queueing:3",PARSE_PRIO_QUEUEING,
+ card->options.do_prio_queueing,NO_PRIO_QUEUEING,
+ card->options.default_queue,3);
+ doit2("no_prio_queueing",PARSE_PRIO_QUEUEING,
+ card->options.do_prio_queueing,NO_PRIO_QUEUEING,
+ card->options.default_queue,QETH_DEFAULT_QUEUE);
+
+ if (!strncmp(option,"polltime:",9)) {
+ i=qeth_getint(option+9,1);
+ if (i==-1) {
+ PRINT_ERR("parameter %s -- does not contain " \
+ "a valid number.\n",option);
+ return 0;
+ }
+ return qeth_setvalue_if_possible(card,option,PARSE_POLLTIME,
+ &card->options.polltime,i);
+ }
+
+ if (!strncmp(option,"port:",5)) {
+ i=qeth_getint(option+5,0);
+ if (i==-1) {
+ PRINT_ERR("parameter %s -- does not contain " \
+ "a valid number.\n",option);
+ return 0;
+ }
+ if ( (i<0) || (i>MAX_PORTNO) ) {
+ PRINT_ERR("parameter %s -- out of range\n",
+ option);
+ return 0;
+ }
+ return qeth_setvalue_if_possible(card,option,PARSE_PORTNO,
+ &card->options.portno,i);
+ }
+
+ if (!strncmp(option,"add_hhlen:",10)) {
+ i=qeth_getint(option+10,0);
+ if (i==-1) {
+ PRINT_ERR("parameter %s -- does not contain " \
+ "a valid number.\n",option);
+ return 0;
+ }
+ if ( (i<0) || (i>MAX_ADD_HHLEN) ) {
+ PRINT_ERR("parameter %s -- out of range\n",
+ option);
+ return 0;
+ }
+ return qeth_setvalue_if_possible(card,option,PARSE_ADD_HHLEN,
+ &card->options.add_hhlen,i);
+ }
+
+ if (!strncmp(option,"portname:",9)) {
+ a_p=&card->options.already_parsed[PARSE_PORTNAME];
+ if (*a_p) {
+ PRINT_ERR("parameter %s does not fit to " \
+ "previous parameters\n",option);
+ return 0;
+ }
+ if ((strlen(option)-9)>8) {
+ PRINT_ERR("parameter %s -- too long\n",option);
+ return 0;
+ }
+ *a_p=1;
+ card->options.portname[0]=strlen(option)-9;
+ /* for beauty reasons: */
+ for (i=1;i<9;i++)
+ card->options.portname[i]=' ';
+ strcpy(card->options.portname+1,option+9);
+ for (i=1;i<9;i++)
+ card->options.portname[i]=
+ _ascebc[(unsigned char)
+ card->options.portname[i]];
+ return 1;
+ }
+
+ PRINT_ERR("unknown parameter: %s\n",option);
+
+ return 0;
+}
+
+static void qeth_detach_handler(int irq,int status)
+{
+ qeth_card_t *card;
+ int remove_method;
+ char dbf_text[15];
+
+ if (irq>=NR_IRQS) {
+ irq-=NR_IRQS;
+ remove_method=QETH_REMOVE_CARD_PROPER;
+ } else {
+ remove_method=QETH_REMOVE_CARD_QUICK;
+ }
+
+ QETH_DBF_TEXT1(0,trace,"detchhnd");
+ sprintf(dbf_text,"%4x%4x",irq,status);
+ QETH_DBF_TEXT1(0,trace,dbf_text);
+
+ /* try to get the lock, if we didn't get it (hold by recovery),
+ * give up initiative to enable others to release the lock */
+ my_spin_lock_nonbusy(NULL,&setup_lock);
+
+ if ((card=qeth_get_card_by_irq(irq))) {
+ qeth_remove_card(card,remove_method);
+ }
+ qeth_wakeup_procfile();
+ my_spin_unlock(&setup_lock);
+}
+
+static void qeth_chandev_do_unregister_device(struct net_device *dev,
+ int quick)
+{
+ qeth_card_t *card;
+
+ card=(qeth_card_t *)dev->priv;
+
+ if (quick) {
+ qeth_detach_handler(card->irq0,0x1234);
+ } else {
+ qeth_detach_handler(NR_IRQS+card->irq0,0x1234);
+ }
+}
+
+static void qeth_chandev_unregister_device(struct net_device *dev)
+{
+ qeth_chandev_do_unregister_device(dev, 0);
+}
+
+static void qeth_chandev_parse_options(qeth_card_t *card,char *parmstring)
+{
+ /* parse options coming from the chandev layer */
+ char *optionstr;
+ char *tmp;
+
+ QETH_DBF_HEX2(0,misc,parmstring,
+ qeth_min(QETH_DBF_MISC_LEN,strlen(parmstring)));
+ tmp = kmalloc ((strlen(parmstring) + 1) * sizeof (char), GFP_KERNEL);
+ if (tmp) {
+ memset (tmp, 0, strlen(parmstring) + 1);
+ memcpy (tmp, parmstring, strlen(parmstring) + 1);
+ do {
+ char *end;
+ int len;
+ end = strchr (tmp, ',');
+ if (end == NULL) {
+ len = strlen (tmp) + 1;
+ } else {
+ len = (long) end - (long) tmp + 1;
+ *end = '\0';
+ end++;
+ }
+
+ if (len>1) {
+ optionstr = kmalloc (len * sizeof (char),
+ GFP_KERNEL);
+ if (optionstr) {
+ memset(optionstr,0,len*sizeof(char));
+ memcpy(optionstr,tmp,len*sizeof(char));
+
+ qeth_parse_option(card,optionstr);
+ kfree(optionstr);
+ tmp = end;
+ } else {
+ PRINT_ERR("Cannot allocate memory " \
+ "for option parsing!\n");
+ break;
+ }
+ }
+ } while (tmp != NULL && *tmp != '\0');
+ } else {
+ PRINT_ERR("Cannot allocate memory " \
+ "for option parsing!\n");
+ }
+}
+
+static int qeth_get_bufcnt_from_memamnt(qeth_card_t *card,__s32 *memamnt)
+{
+ int cnt = 0;
+ int bufsize = BUFFER_SIZE;
+
+ if (bufsize == 24576)
+ bufsize = 32768;
+ if (bufsize == 40960)
+ bufsize = 65536;
+ cnt = (*memamnt)*1024 / bufsize;
+ cnt = (cnt<BUFCNT_MIN)?BUFCNT_MIN:((cnt>BUFCNT_MAX)?BUFCNT_MAX:cnt);
+ (*memamnt) = cnt * bufsize/1024;
+
+ return cnt;
+}
+
+static void qeth_correct_routing_status(qeth_card_t *card)
+{
+ if (card->type==QETH_CARD_TYPE_IQD) {
+ /* if it's not a mc router, it's no router */
+ if ( (card->options.routing_type4 == PRIMARY_ROUTER) ||
+ (card->options.routing_type4 == SECONDARY_ROUTER)
+#ifdef QETH_IPV6
+ ||
+ (card->options.routing_type6 == PRIMARY_ROUTER) ||
+ (card->options.routing_type6 == SECONDARY_ROUTER)
+#endif /* QETH_IPV6 */
+ ) {
+ PRINT_WARN("routing not applicable, reset " \
+ "routing status.\n");
+ card->options.routing_type4=NO_ROUTER;
+#ifdef QETH_IPV6
+ card->options.routing_type6=NO_ROUTER;
+#endif /* QETH_IPV6 */
+ }
+ card->options.do_prio_queueing=NO_PRIO_QUEUEING;
+ } else {
+ /* if it's a mc router, it's no router */
+ if ( (card->options.routing_type4 == MULTICAST_ROUTER) ||
+ (card->options.routing_type4 == PRIMARY_CONNECTOR) ||
+ (card->options.routing_type4 == SECONDARY_CONNECTOR)
+#ifdef QETH_IPV6
+ ||
+ (card->options.routing_type6 == MULTICAST_ROUTER) ||
+ (card->options.routing_type6 == PRIMARY_CONNECTOR) ||
+ (card->options.routing_type6 == SECONDARY_CONNECTOR)
+#endif /* QETH_IPV6 */
+ ) {
+ PRINT_WARN("routing not applicable, reset " \
+ "routing status. (Did you mean " \
+ "primary_router or secondary_router?)\n");
+ card->options.routing_type4=NO_ROUTER;
+#ifdef QETH_IPV6
+ card->options.routing_type6=NO_ROUTER;
+#endif /* QETH_IPV6 */
+ }
+ }
+}
+
+static int qeth_attach_handler(int irq_to_scan,chandev_probeinfo *probeinfo)
+{
+ int result = 0;
+ int irq1,irq2;
+ unsigned int irq;
+ int nr;
+ __u8 mask;
+ int read_chpid=-1,write_chpid=-2,data_chpid=-3; /* so that it will
+ fail, if getting
+ the chpids fails */
+ qeth_card_t *card;
+ int success = 0;
+ char dbf_text[15];
+ chandev_subchannel_info temp;
+
+ QETH_DBF_TEXT2(0,trace,"athandlr");
+ sprintf(dbf_text,"irq:%4x",irq_to_scan);
+ QETH_DBF_TEXT2(0,trace,dbf_text);
+
+ my_spin_lock_nonbusy(NULL,&setup_lock);
+
+ for (nr=0; nr<8; nr++) {
+ mask = 0x80 >> nr;
+ if (probeinfo->read.pim & mask) {
+ read_chpid=probeinfo->read.chpid[nr];
+ /* we take the first chpid -- well, there's
+ * usually only one... */
+ break;
+ }
+ }
+ for (nr=0; nr<8; nr++) {
+ mask = 0x80 >> nr;
+ if (probeinfo->write.pim & mask) {
+ write_chpid=probeinfo->write.chpid[nr];
+ /* we take the first chpid -- well, there's
+ * usually only one... */
+ break;
+ }
+ }
+ for (nr=0; nr<8; nr++) {
+ mask = 0x80 >> nr;
+ if (probeinfo->data.pim & mask) {
+ data_chpid=probeinfo->data.chpid[nr];
+ /* we take the first chpid -- well, there's
+ * usually only one... */
+ break;
+ }
+ }
+ if ((read_chpid!=write_chpid)||(read_chpid!=data_chpid)) {
+ PRINT_ERR("devices are not on the same CHPID!\n");
+ goto endloop;
+ }
+
+ /* Try to reorder the devices, if neccessary */
+ if (probeinfo->read.dev_model == 0x05)
+ /* No odd/even restr. for IQD */
+ goto correct_order;
+ if ((probeinfo->read.devno %2 == 0) &&
+ (probeinfo->write.devno == probeinfo->read.devno + 1))
+ goto correct_order;
+ if ((probeinfo->write.devno %2 == 0) &&
+ (probeinfo->data.devno == probeinfo->write.devno + 1)) {
+ temp = probeinfo->read;
+ probeinfo->read = probeinfo->write;
+ probeinfo->write = probeinfo->data;
+ probeinfo->data = temp;
+ goto correct_order;
+ }
+ if ((probeinfo->write.devno %2 == 0) &&
+ (probeinfo->read.devno == probeinfo->write.devno + 1)) {
+ temp = probeinfo->read;
+ probeinfo->read = probeinfo->write;
+ probeinfo->write = temp;
+ goto correct_order;
+ }
+ if ((probeinfo->read.devno %2 == 0) &&
+ (probeinfo->data.devno == probeinfo->read.devno + 1)) {
+ temp = probeinfo->write;
+ probeinfo->write = probeinfo->data;
+ probeinfo->data = temp;
+ goto correct_order;
+ }
+ if ((probeinfo->data.devno %2 == 0) &&
+ (probeinfo->write.devno == probeinfo->data.devno + 1)) {
+ temp = probeinfo->read;
+ probeinfo->read = probeinfo->data;
+ probeinfo->data = temp;
+ goto correct_order;
+ }
+ if ((probeinfo->data.devno %2 == 0) &&
+ (probeinfo->read.devno == probeinfo->data.devno + 1)) {
+ temp = probeinfo->read;
+ probeinfo->read = probeinfo->data;
+ probeinfo->data = probeinfo->write;
+ probeinfo->write = temp;
+ goto correct_order;
+ }
+ PRINT_ERR("Failed to reorder devices %04x,%04x,%04x; "
+ "please check your configuration\n",
+ probeinfo->read.devno, probeinfo->write.devno,
+ probeinfo->data.devno);
+ goto endloop;
+
+correct_order:
+ irq = probeinfo->read.irq;
+ irq1 = probeinfo->write.irq;
+ irq2 = probeinfo->data.irq;
+
+ card=qeth_alloc_card();
+ if (card==NULL) {
+ QETH_DBF_TEXT2(0,trace,"nocrdmem");
+ PRINT_ERR("memory structures could not be allocated\n");
+ goto endloop;
+ }
+ card->chpid=read_chpid;
+
+ if (probeinfo->port_protocol_no != -1 )
+ card->options.portno = probeinfo->port_protocol_no;
+ else
+ card->options.portno = 0;
+
+ qeth_chandev_parse_options(card,probeinfo->parmstr);
+
+ card->has_irq=0;
+ card->irq0=irq;
+ card->irq1=irq1;
+ card->irq2=irq2;
+ card->devno0=probeinfo->read.devno;
+ card->devno1=probeinfo->write.devno;
+ card->devno2=probeinfo->data.devno;
+ card->dev_type=probeinfo->read.dev_type;
+ card->dev_model=probeinfo->read.dev_model;
+ atomic_set(&card->is_gone,0);
+ atomic_set(&card->rt4fld,0);
+#ifdef QETH_IPV6
+ atomic_set(&card->rt6fld,0);
+#endif /* QETH_IPV6 */
+
+ sprintf(dbf_text,"atch%4x",card->irq0);
+ QETH_DBF_TEXT1(0,setup,dbf_text);
+ QETH_DBF_HEX1(0,setup,&card,sizeof(void*));
+ QETH_DBF_HEX1(0,setup,&card->dev,sizeof(void*));
+ QETH_DBF_HEX1(0,setup,&card->stats,sizeof(void*));
+ QETH_DBF_HEX1(0,setup,&card->devstat0,sizeof(void*));
+ QETH_DBF_HEX1(0,setup,&card->devstat1,sizeof(void*));
+ QETH_DBF_HEX1(0,setup,&card->devstat2,sizeof(void*));
+
+ QETH_DBF_HEX2(0,misc,&card->options,QETH_DBF_MISC_LEN);
+
+ if (qeth_determine_card_type(card)) {
+ qeth_free_card(card);
+ goto endloop;
+ }
+
+ qeth_correct_routing_status(card);
+ qeth_insert_card_into_list(card);
+
+ QETH_DBF_TEXT3(0,trace,"request0");
+ /* 0 is irqflags. what is SA_SAMPLE_RANDOM? */
+ result=chandev_request_irq(irq,(void*)
+ qeth_interrupt_handler_read,
+ 0,QETH_NAME,card->devstat0);
+ if (result) goto attach_error;
+ card->has_irq++;
+
+ QETH_DBF_TEXT3(0,trace,"request1");
+ result=chandev_request_irq(irq1,(void*)
+ qeth_interrupt_handler_write,
+ 0,QETH_NAME,card->devstat1);
+ if (result) goto attach_error;
+ card->has_irq++;
+
+ QETH_DBF_TEXT3(0,trace,"request2");
+ result=chandev_request_irq(irq2,(void*)
+ qeth_interrupt_handler_qdio,
+ 0,QETH_NAME,card->devstat2);
+ if (result) goto attach_error;
+ card->has_irq++;
+
+ printk("qeth: Trying to use card with devnos 0x%X/0x%X/0x%X\n",
+ card->devno0,card->devno1,card->devno2);
+
+ result=qeth_init_ringbuffers1(card);
+ if (result) goto attach_error;
+
+ result=qeth_hardsetup_card(card,0);
+ if (result) goto attach_error;
+ if (probeinfo->memory_usage_in_k != 0) {
+ card->options.inbound_buffer_count=
+ qeth_get_bufcnt_from_memamnt
+ (card,&probeinfo->memory_usage_in_k);
+ card->options.memory_usage_in_k=
+ probeinfo->memory_usage_in_k;
+ } else {
+ /* sick... */
+ probeinfo->memory_usage_in_k=
+ -card->options.inbound_buffer_count*
+ BUFFER_SIZE/1024;
+ }
+ result=qeth_init_ringbuffers2(card);
+ if (result) goto attach_error;
+
+ success = 1;
+ goto endloop;
+
+ attach_error:
+ sprintf(dbf_text,"ATER%4x",card->irq0);
+ QETH_DBF_TEXT2(0,trace,dbf_text);
+ switch (result) {
+ case 0:
+ break;
+ case -EINVAL:
+ PRINT_ERR("oops... invalid parameter.\n");
+ break;
+ case -EBUSY:
+ PRINT_WARN("Device is busy!\n");
+ break;
+ case -ENODEV:
+ PRINT_WARN("Device became not operational.\n");
+ break;
+ case -ENOMEM:
+ PRINT_ERR("Not enough kernel memory for operation.\n");
+ break;
+ case -EIO:
+ PRINT_ERR("There were problems in hard-setting up " \
+ "the card.\n");
+ break;
+ case -ETIME:
+ PRINT_WARN("Timeout on initializing the card.\n");
+ break;
+ default:
+ PRINT_ERR("Unknown error %d in attach_handler.\n",
+ result);
+ }
+ if (result) {
+ qeth_remove_card(card,QETH_REMOVE_CARD_PROPER);
+
+ qeth_remove_card_from_list(card);
+ QETH_DBF_TEXT4(0,trace,"freecard");
+ qeth_free_card(card);
+ }
+ endloop:
+
+ QETH_DBF_TEXT3(0,trace,"leftloop");
+
+ if (!success) {
+ QETH_DBF_TEXT2(0,trace,"noaddcrd");
+
+ /* we want to return which problem we had */
+ result=result?result:-ENODEV;
+ goto exit;
+ }
+
+
+ exit:
+ my_spin_unlock(&setup_lock);
+
+ return result;
+}
+
+static void qeth_chandev_msck_notfunc(struct net_device *device,
+ int msck_irq,
+ chandev_msck_status prevstatus,
+ chandev_msck_status newstatus )
+{
+
+ if (!(device->priv))
+ return;
+
+ if ((prevstatus != chandev_status_good) ||
+ (prevstatus != chandev_status_all_chans_good)) {
+ if ((newstatus == chandev_status_good) ||
+ (newstatus == chandev_status_all_chans_good)) {
+ qeth_card_t *card = (qeth_card_t *)device->priv;
+
+ atomic_set(&card->problem,PROBLEM_MACHINE_CHECK);
+ atomic_set(&card->write_busy,0);
+ qeth_schedule_recovery(card);
+ }
+ }
+ if ((newstatus == chandev_status_gone) ||
+ (newstatus == chandev_status_no_path) ||
+ (newstatus == chandev_status_not_oper)) {
+ qeth_card_t *card = (qeth_card_t *)device->priv;
+
+ my_read_lock(&list_lock);
+ if (qeth_verify_card(card)) {
+ atomic_set(&card->is_startlaned,0);
+ qeth_set_dev_flag_norunning(card);
+ /*
+ * Unfortunately, the chandev layer does not provide
+ * a possibility to unregister a single device. So
+ * we mark the card as "gone" to avoid internal
+ * mishandling.
+ */
+ atomic_set(&card->is_gone,1);
+ /* means, we prevent looping in
+ * qeth_send_control_data */
+ atomic_set(&card->write_busy,0);
+ qeth_wakeup_procfile();
+ }
+ my_read_unlock(&list_lock);
+ }
+}
+
+struct net_device *qeth_chandev_init_netdev(struct net_device *dev,
+ int sizeof_priv)
+{
+
+ qeth_card_t *card = NULL;
+ int result;
+ char dbf_text[15];
+
+ if (!dev) {
+ PRINT_ERR("qeth_chandev_init_netdev called with no device!\n");
+ goto out;
+ }
+
+ card = (qeth_card_t *)dev->priv;
+ strcpy(card->dev_name,dev->name);
+ result=qeth_register_netdev(card);
+ if (result) {
+ PRINT_ALL(" register_netdev %s -- rc=%i\n",
+ ((qeth_card_t*)firstcard->dev->priv)->
+ dev_name,result);
+ sprintf(dbf_text,"rgnd%4x",(__u16)result);
+ QETH_DBF_TEXT2(1,trace,dbf_text);
+ atomic_set(&card->is_registered,0);
+ goto out;
+ }
+ strcpy(card->dev_name,dev->name);
+ atomic_set(&card->write_busy,0);
+ atomic_set(&card->is_registered,1);
+
+ result=qeth_softsetup_card(card,QETH_WAIT_FOR_LOCK);
+
+ if (!result) {
+ qeth_init_input_buffers(card);
+ } else {
+ QETH_DBF_TEXT2(0,trace,"SSFAILED");
+ PRINT_WARN("soft-setup of card failed!\n");
+ }
+
+ INIT_LIST_HEAD(&card->tqueue_sst.list);
+ card->tqueue_sst.routine=qeth_softsetup_thread_starter;
+ card->tqueue_sst.data=card;
+ card->tqueue_sst.sync=0;
+ schedule_task(&card->tqueue_sst);
+ out:
+ qeth_wakeup_procfile();
+ return dev;
+
+}
+
+static int qeth_probe(chandev_probeinfo *probeinfo)
+{
+ int result;
+ struct net_device *pdevice = NULL;
+ int sizeof_priv;
+ qeth_card_t *card;
+ char *basename = NULL;
+
+ result = qeth_attach_handler(probeinfo->read.irq, probeinfo);
+ if (result)
+ return result;
+
+ sizeof_priv = sizeof(qeth_card_t);
+ card = qeth_get_card_by_irq(probeinfo->read.irq);
+
+ pdevice = card->dev;
+ basename = (char *)qeth_get_dev_basename(card->type, card->link_type);
+
+ pdevice->irq=card->irq0;
+
+ if (probeinfo->memory_usage_in_k>=0)
+ probeinfo->memory_usage_in_k=
+ -card->options.memory_usage_in_k;
+ pdevice = chandev_initnetdevice(probeinfo,
+ card->options.portno,
+ pdevice,
+ sizeof_priv,
+ basename,
+ qeth_chandev_init_netdev,
+ qeth_chandev_unregister_device);
+ if (pdevice) {
+ return 0;
+ } else {
+ qeth_remove_card(card,QETH_REMOVE_CARD_PROPER);
+ qeth_remove_card_from_list(card);
+ QETH_DBF_TEXT4(0,trace,"freecard");
+ qeth_free_card(card);
+ return -ENODEV;
+ }
+
+}
+
+static int qeth_dev_event(struct notifier_block *this,
+ unsigned long event,void *ptr)
+{
+ qeth_card_t *card;
+ struct net_device *dev = (struct net_device *)ptr;
+ char dbf_text[15];
+
+ sprintf(dbf_text,"devevent");
+ QETH_DBF_TEXT3(0,trace,dbf_text);
+ QETH_DBF_HEX3(0,trace,&event,sizeof(unsigned long));
+ QETH_DBF_HEX3(0,trace,&dev,sizeof(void*));
+
+#ifdef QETH_VLAN
+ if (qeth_verify_dev(dev)==QETH_VERIFY_IS_VLAN_DEV)
+ card = (qeth_card_t *)VLAN_DEV_INFO(dev)->real_dev->priv;
+ else
+#endif
+ card=(qeth_card_t *)dev->priv;
+ if (qeth_does_card_exist(card)) {
+ qeth_save_dev_flag_state(card);
+ switch (event) {
+ default:
+ qeth_start_softsetup_thread(card);
+ break;
+ }
+ }
+
+ return NOTIFY_DONE;
+}
+
+static int qeth_ip_event(struct notifier_block *this,
+ unsigned long event,void *ptr)
+{
+ qeth_card_t *card;
+ struct in_ifaddr *ifa=(struct in_ifaddr *)ptr;
+ struct net_device *dev = ifa->ifa_dev->dev;
+ char dbf_text[15];
+
+ sprintf(dbf_text,"ipevent");
+ QETH_DBF_TEXT3(0,trace,dbf_text);
+ QETH_DBF_HEX3(0,trace,&event,sizeof(unsigned long));
+ QETH_DBF_HEX3(0,trace,&dev,sizeof(void*));
+ sprintf(dbf_text,"%08x",ifa->ifa_address);
+ QETH_DBF_TEXT3(0,trace,dbf_text);
+ sprintf(dbf_text,"%08x",ifa->ifa_mask);
+ QETH_DBF_TEXT3(0,trace,dbf_text);
+
+#ifdef QETH_VLAN
+ if (qeth_verify_dev(dev)==QETH_VERIFY_IS_VLAN_DEV)
+ card = (qeth_card_t *)VLAN_DEV_INFO(dev)->real_dev->priv;
+ else
+#endif
+ card=(qeth_card_t *)dev->priv;
+ if (qeth_does_card_exist(card)) {
+ QETH_DBF_HEX3(0,trace,&card,sizeof(void*));
+ qeth_save_dev_flag_state(card);
+ qeth_start_softsetup_thread(card);
+ }
+
+ return NOTIFY_DONE;
+}
+
+#ifdef QETH_IPV6
+static int qeth_ip6_event(struct notifier_block *this,
+ unsigned long event,void *ptr)
+{
+ qeth_card_t *card;
+ struct inet6_ifaddr *ifa=(struct inet6_ifaddr *)ptr;
+ struct net_device *dev = ifa->idev->dev;
+ char dbf_text[15];
+
+ sprintf(dbf_text,"ip6event");
+ QETH_DBF_TEXT3(0,trace,dbf_text);
+ QETH_DBF_HEX3(0,trace,&event,sizeof(unsigned long));
+ QETH_DBF_HEX3(0,trace,&dev,sizeof(void*));
+ QETH_DBF_HEX3(0,trace,ifa->addr.s6_addr,QETH_DBF_TRACE_LEN);
+ QETH_DBF_HEX3(0,trace,ifa->addr.s6_addr+QETH_DBF_TRACE_LEN,
+ QETH_DBF_TRACE_LEN);
+
+#ifdef QETH_VLAN
+ if (qeth_verify_dev(dev)==QETH_VERIFY_IS_VLAN_DEV)
+ card = (qeth_card_t *)VLAN_DEV_INFO(dev)->real_dev->priv;
+ else
+#endif
+ card=(qeth_card_t *)dev->priv;
+
+ if (qeth_does_card_exist(card)) {
+ QETH_DBF_HEX3(0,trace,&card,sizeof(void*));
+ qeth_save_dev_flag_state(card);
+ qeth_start_softsetup_thread(card);
+ }
+
+ return NOTIFY_DONE;
+}
+#endif /* QETH_IPV6 */
+
+static int qeth_reboot_event(struct notifier_block *this,
+ unsigned long event,void *ptr)
+{
+ qeth_card_t *card;
+
+ my_read_lock(&list_lock);
+ if (firstcard) {
+ card = firstcard;
+clear_another_one:
+ if (card->has_irq) {
+ if (card->type==QETH_CARD_TYPE_IQD) {
+ halt_IO(card->irq2,0,0);
+ clear_IO(card->irq0,0,0);
+ clear_IO(card->irq1,0,0);
+ clear_IO(card->irq2,0,0);
+ } else {
+ clear_IO(card->irq2,0,0);
+ clear_IO(card->irq0,0,0);
+ clear_IO(card->irq1,0,0);
+ }
+ }
+ if (card->next) {
+ card = card->next;
+ goto clear_another_one;
+ }
+ }
+ my_read_unlock(&list_lock);
+
+ return 0;
+}
+
+static struct notifier_block qeth_dev_notifier = {
+ qeth_dev_event,
+ 0
+};
+
+static struct notifier_block qeth_ip_notifier = {
+ qeth_ip_event,
+ 0
+};
+
+#ifdef QETH_IPV6
+static struct notifier_block qeth_ip6_notifier = {
+ qeth_ip6_event,
+ 0
+};
+#endif /* QETH_IPV6 */
+
+static struct notifier_block qeth_reboot_notifier = {
+ qeth_reboot_event,
+ 0
+};
+
+static void qeth_register_notifiers(void)
+{
+ int r;
+
+ QETH_DBF_TEXT5(0,trace,"regnotif");
+ /* register to be notified on events */
+ r=register_netdevice_notifier(&qeth_dev_notifier);
+
+ r=register_inetaddr_notifier(&qeth_ip_notifier);
+#ifdef QETH_IPV6
+ r=register_inet6addr_notifier(&qeth_ip6_notifier);
+#endif /* QETH_IPV6 */
+ r=register_reboot_notifier(&qeth_reboot_notifier);
+}
+
+#ifdef MODULE
+static void qeth_unregister_notifiers(void)
+{
+ int r;
+
+ QETH_DBF_TEXT5(0,trace,"unregnot");
+ r=unregister_netdevice_notifier(&qeth_dev_notifier);
+ r=unregister_inetaddr_notifier(&qeth_ip_notifier);
+#ifdef QETH_IPV6
+ r=unregister_inet6addr_notifier(&qeth_ip6_notifier);
+#endif /* QETH_IPV6 */
+ r=unregister_reboot_notifier(&qeth_reboot_notifier);
+}
+#endif /* MODULE */
+
+static int qeth_procfile_open(struct inode *inode, struct file *file)
+{
+ int length=0;
+ qeth_card_t *card;
+ char checksum_str[5],queueing_str[14],router_str[8],bufsize_str[4];
+ char *buffer;
+ int rc=0;
+ int size;
+ tempinfo_t *info;
+
+ info = (tempinfo_t *) vmalloc (sizeof (tempinfo_t));
+ if (info == NULL) {
+ PRINT_WARN("No memory available for data\n");
+ return -ENOMEM;
+ } else {
+ file->private_data = (void *) info;
+ }
+
+ /* lock all the stuff */
+ my_read_lock(&list_lock);
+ card=firstcard;
+ size=200; /* 2 lines plus some sanity space */
+ while (card) {
+ size+=90; /* if device name is > 10 chars, (should never
+ happen...), we'll need that */
+ card=card->next;
+ }
+
+ buffer=info->data = (char *) vmalloc (size);
+ if (info->data == NULL) {
+ PRINT_WARN("No memory available for data\n");
+ vfree (info);
+ rc=-ENOMEM;
+ goto out;
+ }
+
+ QETH_DBF_TEXT2(0,trace,"procread");
+ length+=sprintf(buffer+length,
+ "devnos (hex) CHPID " \
+ "device cardtype port chksum prio-q'ing " \
+ "rtr fsz C cnt\n");
+ length+=sprintf(buffer+length,
+ "-------------- --- ----" \
+ "------ -------------- -- -- ---------- " \
+ "--- --- - ---\n");
+ card=firstcard;
+ while (card) {
+ strcpy(checksum_str,
+ (card->options.checksum_type==SW_CHECKSUMMING)?"SW":
+ (card->options.checksum_type==HW_CHECKSUMMING)?"HW":
+ "no");
+ if (card->options.do_prio_queueing==NO_PRIO_QUEUEING) {
+ sprintf(queueing_str,"always_q_%i",
+ card->options.default_queue);
+ } else {
+ strcpy(queueing_str,(card->options.do_prio_queueing
+ ==PRIO_QUEUEING_PREC)?"by_prec.":
+ "by_ToS");
+ }
+
+#ifdef QETH_IPV6
+ if (atomic_read(&card->rt4fld) &&
+ atomic_read(&card->rt6fld))
+ strcpy(router_str, "no");
+ else if (atomic_read(&card->rt4fld) ||
+ atomic_read(&card->rt6fld))
+ strcpy(router_str, "mix");
+#else /* QETH_IPV6 */
+ if (atomic_read(&card->rt4fld))
+ strcpy(router_str, "no");
+#endif /* QETH_IPV6 */
+ else if ( ((card->options.routing_type4&ROUTER_MASK)==
+ PRIMARY_ROUTER)
+#ifdef QETH_IPV6
+ &&
+ ((card->options.routing_type6&ROUTER_MASK)==
+ PRIMARY_ROUTER)
+#endif /* QETH_IPV6 */
+ ) {
+ strcpy(router_str,"pri");
+ } else
+ if ( ((card->options.routing_type4&ROUTER_MASK)==
+ SECONDARY_ROUTER)
+#ifdef QETH_IPV6
+ &&
+ ((card->options.routing_type6&ROUTER_MASK)==
+ SECONDARY_ROUTER)
+#endif /* QETH_IPV6 */
+ ) {
+ strcpy(router_str,"sec");
+ } else
+ if ( ((card->options.routing_type4&ROUTER_MASK)==
+ MULTICAST_ROUTER)
+#ifdef QETH_IPV6
+ &&
+ ((card->options.routing_type6&ROUTER_MASK)==
+ MULTICAST_ROUTER)
+#endif /* QETH_IPV6 */
+ ) {
+ strcpy(router_str,"mc");
+ } else
+ if ( ((card->options.routing_type4&ROUTER_MASK)==
+ PRIMARY_CONNECTOR)
+#ifdef QETH_IPV6
+ &&
+ ((card->options.routing_type6&ROUTER_MASK)==
+ PRIMARY_CONNECTOR)
+#endif /* QETH_IPV6 */
+ ) {
+ strcpy(router_str,"p.c");
+ } else
+ if ( ((card->options.routing_type4&ROUTER_MASK)==
+ SECONDARY_CONNECTOR)
+#ifdef QETH_IPV6
+ &&
+ ((card->options.routing_type6&ROUTER_MASK)==
+ SECONDARY_CONNECTOR)
+#endif /* QETH_IPV6 */
+ ) {
+ strcpy(router_str,"s.c");
+ } else
+ if ( ((card->options.routing_type4&ROUTER_MASK)==
+ NO_ROUTER)
+#ifdef QETH_IPV6
+ &&
+ ((card->options.routing_type6&ROUTER_MASK)==
+ NO_ROUTER)
+#endif /* QETH_IPV6 */
+ ) {
+ strcpy(router_str,"no");
+ } else {
+ strcpy(router_str,"mix");
+ }
+ strcpy(bufsize_str,
+ (BUFFER_SIZE==16384)?"16k":
+ (BUFFER_SIZE==24576)?"24k":
+ (BUFFER_SIZE==32768)?"32k":
+ (BUFFER_SIZE==40960)?"40k":
+ "64k");
+ if (atomic_read(&card->is_gone)) {
+ length+=sprintf(buffer+length,
+ "%04X/%04X/%04X x%02X %10s %14s %2i"
+ " +++ CARD IS GONE +++\n",
+ card->devno0,card->devno1,card->devno2,
+ card->chpid,
+ card->dev_name,
+ qeth_get_cardname_short
+ (card->type,card->link_type),
+ card->options.portno);
+ } else if (!atomic_read(&card->is_startlaned)) {
+ length+=sprintf(buffer+length,
+ "%04X/%04X/%04X x%02X %10s %14s %2i"
+ " +++ CABLE PULLED +++\n",
+ card->devno0,card->devno1,card->devno2,
+ card->chpid,
+ card->dev_name,
+ qeth_get_cardname_short
+ (card->type,card->link_type),
+ card->options.portno);
+ } else {
+ length+=sprintf(buffer+length,
+ "%04X/%04X/%04X x%02X %10s %14s %2i" \
+ " %2s %10s %3s %3s %c %3i\n",
+ card->devno0,card->devno1,card->devno2,
+ card->chpid,card->dev_name,
+ qeth_get_cardname_short
+ (card->type,card->link_type),
+ card->options.portno,
+ checksum_str,
+ queueing_str,router_str,bufsize_str,
+ (card->options.memusage==
+ MEMUSAGE_CONTIG)?'c':' ',
+ card->options.inbound_buffer_count);
+ }
+ card=card->next;
+ }
+
+out:
+ info->len=length;
+ /* unlock all the stuff */
+ my_read_unlock(&list_lock);
+ return rc;
+}
+
+static qeth_card_t *qeth_find_card(char *buffer,int len)
+{
+ qeth_card_t *card;
+ int devnamelen;
+
+ my_read_lock(&list_lock);
+ card=firstcard;
+ while (card) {
+ devnamelen=0;
+ while ( (devnamelen<DEV_NAME_LEN) &&
+ ( (card->dev_name[devnamelen]!=' ') &&
+ (card->dev_name[devnamelen]!=0) &&
+ (card->dev_name[devnamelen]!='\n') ) ) {
+ devnamelen++;
+ }
+ if ((!strncmp(card->dev_name,buffer,
+ qeth_min(len,DEV_NAME_LEN)))&&
+ (devnamelen==len) &&
+ (!atomic_read(&card->shutdown_phase))) break;
+ card=card->next;
+ }
+ my_read_unlock(&list_lock);
+
+ return card;
+}
+
+static int qeth_get_next_token(char *buffer,int *token_len,
+ int *pos,int *end_pos,int count)
+{
+ *token_len=0;
+ *end_pos=*pos;
+ if (*end_pos>=count) return 0;
+ if (!buffer[*end_pos]) return 0;
+ for (;;) {
+ if ( (buffer[*end_pos]!=' ') &&
+ (buffer[*end_pos]!='\t') &&
+ (buffer[*end_pos]!='\n') &&
+ (buffer[*end_pos]!='\r') &&
+ (buffer[*end_pos]!=0) &&
+ (*end_pos<count) ) {
+ *end_pos=(*end_pos)+1;
+ *token_len=(*token_len)+1;
+ }
+ else break;
+ }
+ return 1;
+}
+
+static void qeth_skip_whitespace(char *buffer,int *pos,int count)
+{
+ for (;;) {
+ if (*pos>=count) return;
+ if ((buffer[*pos]==' ')||
+ (buffer[*pos]=='\t')||
+ (buffer[*pos]=='\n')||
+ (buffer[*pos]=='\r')) *pos=(*pos)+1;
+ else return;
+ }
+}
+
+#define CHECK_MISSING_PARAMETER do { \
+ pos=end_pos; \
+ if (!qeth_get_next_token(buffer,&token_len,&pos,&end_pos,user_len)) { \
+ PRINT_WARN("paramter missing on procfile input, " \
+ "ignoring input!\n"); \
+ goto out; \
+ } \
+ card=qeth_find_card(buffer+pos,token_len); \
+ if (!card) { \
+ PRINT_WARN("paramter invalid on procfile input, " \
+ "ignoring input!\n"); \
+ goto out; \
+ } \
+} while (0)
+
+static ssize_t qeth_procfile_write(struct file *file,
+ const char *user_buffer,
+ size_t user_len,loff_t *offset)
+{
+ qeth_card_t *card;
+ char *buffer;
+ int token_len;
+ int pos=0,end_pos;
+ char dbf_text[15];
+
+ if (*offset>0) return user_len;
+ buffer=vmalloc(__max(user_len+1,QETH_DBF_MISC_LEN));
+ if (buffer == NULL)
+ return -ENOMEM;
+ memset(buffer,0,user_len+1);
+
+ if (copy_from_user(buffer, user_buffer, user_len)) {
+ vfree (buffer);
+ return -EFAULT;
+ }
+
+ QETH_DBF_TEXT2(0,trace,"procwrit");
+ QETH_DBF_TEXT2(0,misc,buffer);
+
+ if (!qeth_get_next_token(buffer,&token_len,&pos,&end_pos,user_len))
+ goto out;
+ qeth_skip_whitespace(buffer,&end_pos,user_len);
+
+ if (!strncmp(buffer+pos,"recover",token_len)) {
+ CHECK_MISSING_PARAMETER;
+ sprintf(dbf_text,"UTRC%4x",card->irq0);
+ QETH_DBF_TEXT2(0,trace,dbf_text);
+ atomic_set(&card->problem,
+ PROBLEM_USER_TRIGGERED_RECOVERY);
+ qeth_schedule_recovery(card);
+
+ } else if (!strncmp(buffer+pos,"remember",token_len)) {
+ CHECK_MISSING_PARAMETER;
+ sprintf(dbf_text,"remb%4x",card->irq0);
+ QETH_DBF_TEXT2(0,trace,dbf_text);
+ card->save_state_flag=1;
+
+ } else if (!strncmp(buffer+pos,"forget",token_len)) {
+ CHECK_MISSING_PARAMETER;
+ sprintf(dbf_text,"forg%4x",card->irq0);
+ QETH_DBF_TEXT2(0,trace,dbf_text);
+ card->save_state_flag=0;
+ } else if (!strncmp(buffer+pos,"primary_router",token_len)) {
+ CHECK_MISSING_PARAMETER;
+ sprintf(dbf_text,"prir%4x",card->irq0);
+ QETH_DBF_TEXT2(0,trace,dbf_text);
+
+ card->options.routing_type4=PRIMARY_ROUTER|
+ RESET_ROUTING_FLAG;
+#ifdef QETH_IPV6
+ card->options.routing_type6=PRIMARY_ROUTER|
+ RESET_ROUTING_FLAG;
+#endif /* QETH_IPV6 */
+ qeth_correct_routing_status(card);
+ atomic_set(&card->enable_routing_attempts4,
+ QETH_ROUTING_ATTEMPTS);
+#ifdef QETH_IPV6
+ atomic_set(&card->enable_routing_attempts6,
+ QETH_ROUTING_ATTEMPTS);
+#endif /* QETH_IPV6 */
+ qeth_start_softsetup_thread(card);
+ } else if (!strncmp(buffer+pos,"primary_router4",token_len)) {
+ CHECK_MISSING_PARAMETER;
+ sprintf(dbf_text,"pri4%4x",card->irq0);
+ QETH_DBF_TEXT2(0,trace,dbf_text);
+
+ card->options.routing_type4=PRIMARY_ROUTER|
+ RESET_ROUTING_FLAG;
+ qeth_correct_routing_status(card);
+ atomic_set(&card->enable_routing_attempts4,
+ QETH_ROUTING_ATTEMPTS);
+ qeth_start_softsetup_thread(card);
+#ifdef QETH_IPV6
+ } else if (!strncmp(buffer+pos,"primary_router6",token_len)) {
+ CHECK_MISSING_PARAMETER;
+ sprintf(dbf_text,"pri6%4x",card->irq0);
+ QETH_DBF_TEXT2(0,trace,dbf_text);
+
+ card->options.routing_type6=PRIMARY_ROUTER|
+ RESET_ROUTING_FLAG;
+ qeth_correct_routing_status(card);
+ atomic_set(&card->enable_routing_attempts6,
+ QETH_ROUTING_ATTEMPTS);
+ qeth_start_softsetup_thread(card);
+#endif /* QETH_IPV6 */
+ } else if (!strncmp(buffer+pos,"secondary_router",token_len)) {
+ CHECK_MISSING_PARAMETER;
+ sprintf(dbf_text,"secr%4x",card->irq0);
+ QETH_DBF_TEXT2(0,trace,dbf_text);
+
+ card->options.routing_type4=SECONDARY_ROUTER|
+ RESET_ROUTING_FLAG;
+#ifdef QETH_IPV6
+ card->options.routing_type6=SECONDARY_ROUTER|
+ RESET_ROUTING_FLAG;
+#endif /* QETH_IPV6 */
+ qeth_correct_routing_status(card);
+ atomic_set(&card->enable_routing_attempts4,
+ QETH_ROUTING_ATTEMPTS);
+#ifdef QETH_IPV6
+ atomic_set(&card->enable_routing_attempts6,
+ QETH_ROUTING_ATTEMPTS);
+#endif /* QETH_IPV6 */
+ qeth_start_softsetup_thread(card);
+ } else if (!strncmp(buffer+pos,"secondary_router4",token_len)) {
+ CHECK_MISSING_PARAMETER;
+ sprintf(dbf_text,"sec4%4x",card->irq0);
+ QETH_DBF_TEXT2(0,trace,dbf_text);
+
+ card->options.routing_type4=SECONDARY_ROUTER|
+ RESET_ROUTING_FLAG;
+ qeth_correct_routing_status(card);
+ atomic_set(&card->enable_routing_attempts4,
+ QETH_ROUTING_ATTEMPTS);
+ qeth_start_softsetup_thread(card);
+#ifdef QETH_IPV6
+ } else if (!strncmp(buffer+pos,"secondary_router6",token_len)) {
+ CHECK_MISSING_PARAMETER;
+ sprintf(dbf_text,"sec6%4x",card->irq0);
+ QETH_DBF_TEXT2(0,trace,dbf_text);
+
+ card->options.routing_type6=SECONDARY_ROUTER|
+ RESET_ROUTING_FLAG;
+ qeth_correct_routing_status(card);
+ atomic_set(&card->enable_routing_attempts6,
+ QETH_ROUTING_ATTEMPTS);
+ qeth_start_softsetup_thread(card);
+#endif /* QETH_IPV6 */
+ } else if (!strncmp(buffer+pos,"multicast_router",token_len)) {
+ CHECK_MISSING_PARAMETER;
+ sprintf(dbf_text,"mcr %4x",card->irq0);
+ QETH_DBF_TEXT2(0,trace,dbf_text);
+
+ card->options.routing_type4=MULTICAST_ROUTER|
+ RESET_ROUTING_FLAG;
+#ifdef QETH_IPV6
+ card->options.routing_type6=MULTICAST_ROUTER|
+ RESET_ROUTING_FLAG;
+#endif /* QETH_IPV6 */
+ qeth_correct_routing_status(card);
+ atomic_set(&card->enable_routing_attempts4,
+ QETH_ROUTING_ATTEMPTS);
+#ifdef QETH_IPV6
+ atomic_set(&card->enable_routing_attempts6,
+ QETH_ROUTING_ATTEMPTS);
+#endif /* QETH_IPV6 */
+ qeth_start_softsetup_thread(card);
+ } else if (!strncmp(buffer+pos,"multicast_router4",token_len)) {
+ CHECK_MISSING_PARAMETER;
+ sprintf(dbf_text,"mcr4%4x",card->irq0);
+ QETH_DBF_TEXT2(0,trace,dbf_text);
+
+ card->options.routing_type4=MULTICAST_ROUTER|
+ RESET_ROUTING_FLAG;
+ qeth_correct_routing_status(card);
+ atomic_set(&card->enable_routing_attempts4,
+ QETH_ROUTING_ATTEMPTS);
+ qeth_start_softsetup_thread(card);
+#ifdef QETH_IPV6
+ } else if (!strncmp(buffer+pos,"multicast_router6",token_len)) {
+ CHECK_MISSING_PARAMETER;
+ sprintf(dbf_text,"mcr6%4x",card->irq0);
+ QETH_DBF_TEXT2(0,trace,dbf_text);
+
+ card->options.routing_type6=MULTICAST_ROUTER|
+ RESET_ROUTING_FLAG;
+ qeth_correct_routing_status(card);
+ atomic_set(&card->enable_routing_attempts6,
+ QETH_ROUTING_ATTEMPTS);
+ qeth_start_softsetup_thread(card);
+#endif /* QETH_IPV6 */
+ } else if (!strncmp(buffer+pos,"primary_connector",token_len)) {
+ CHECK_MISSING_PARAMETER;
+ sprintf(dbf_text,"prc %4x",card->irq0);
+ QETH_DBF_TEXT2(0,trace,dbf_text);
+
+ card->options.routing_type4=PRIMARY_CONNECTOR|
+ RESET_ROUTING_FLAG;
+#ifdef QETH_IPV6
+ card->options.routing_type6=PRIMARY_CONNECTOR|
+ RESET_ROUTING_FLAG;
+#endif /* QETH_IPV6 */
+ qeth_correct_routing_status(card);
+ atomic_set(&card->enable_routing_attempts4,
+ QETH_ROUTING_ATTEMPTS);
+#ifdef QETH_IPV6
+ atomic_set(&card->enable_routing_attempts6,
+ QETH_ROUTING_ATTEMPTS);
+#endif /* QETH_IPV6 */
+ qeth_start_softsetup_thread(card);
+ } else if (!strncmp(buffer+pos,"primary_connector4",token_len)) {
+ CHECK_MISSING_PARAMETER;
+ sprintf(dbf_text,"prc4%4x",card->irq0);
+ QETH_DBF_TEXT2(0,trace,dbf_text);
+
+ card->options.routing_type4=PRIMARY_CONNECTOR|
+ RESET_ROUTING_FLAG;
+ qeth_correct_routing_status(card);
+ atomic_set(&card->enable_routing_attempts4,
+ QETH_ROUTING_ATTEMPTS);
+ qeth_start_softsetup_thread(card);
+#ifdef QETH_IPV6
+ } else if (!strncmp(buffer+pos,"primary_connector6",token_len)) {
+ CHECK_MISSING_PARAMETER;
+ sprintf(dbf_text,"prc6%4x",card->irq0);
+ QETH_DBF_TEXT2(0,trace,dbf_text);
+
+ card->options.routing_type6=PRIMARY_CONNECTOR|
+ RESET_ROUTING_FLAG;
+ qeth_correct_routing_status(card);
+ atomic_set(&card->enable_routing_attempts6,
+ QETH_ROUTING_ATTEMPTS);
+ qeth_start_softsetup_thread(card);
+#endif /* QETH_IPV6 */
+ } else if (!strncmp(buffer+pos,"secondary_connector",token_len)) {
+ CHECK_MISSING_PARAMETER;
+ sprintf(dbf_text,"scc %4x",card->irq0);
+ QETH_DBF_TEXT2(0,trace,dbf_text);
+
+ card->options.routing_type4=SECONDARY_CONNECTOR|
+ RESET_ROUTING_FLAG;
+#ifdef QETH_IPV6
+ card->options.routing_type6=SECONDARY_CONNECTOR|
+ RESET_ROUTING_FLAG;
+#endif /* QETH_IPV6 */
+ qeth_correct_routing_status(card);
+ atomic_set(&card->enable_routing_attempts4,
+ QETH_ROUTING_ATTEMPTS);
+#ifdef QETH_IPV6
+ atomic_set(&card->enable_routing_attempts6,
+ QETH_ROUTING_ATTEMPTS);
+#endif /* QETH_IPV6 */
+ qeth_start_softsetup_thread(card);
+ } else if (!strncmp(buffer+pos,"secondary_connector4",token_len)) {
+ CHECK_MISSING_PARAMETER;
+ sprintf(dbf_text,"scc4%4x",card->irq0);
+ QETH_DBF_TEXT2(0,trace,dbf_text);
+
+ card->options.routing_type4=SECONDARY_CONNECTOR|
+ RESET_ROUTING_FLAG;
+ qeth_correct_routing_status(card);
+ atomic_set(&card->enable_routing_attempts4,
+ QETH_ROUTING_ATTEMPTS);
+ qeth_start_softsetup_thread(card);
+#ifdef QETH_IPV6
+ } else if (!strncmp(buffer+pos,"secondary_connector6",token_len)) {
+ CHECK_MISSING_PARAMETER;
+ sprintf(dbf_text,"scc6%4x",card->irq0);
+ QETH_DBF_TEXT2(0,trace,dbf_text);
+
+ card->options.routing_type6=SECONDARY_CONNECTOR|
+ RESET_ROUTING_FLAG;
+ qeth_correct_routing_status(card);
+ atomic_set(&card->enable_routing_attempts6,
+ QETH_ROUTING_ATTEMPTS);
+ qeth_start_softsetup_thread(card);
+#endif /* QETH_IPV6 */
+ } else if (!strncmp(buffer+pos,"no_router",token_len)) {
+ CHECK_MISSING_PARAMETER;
+ sprintf(dbf_text,"nor %4x",card->irq0);
+ QETH_DBF_TEXT2(0,trace,dbf_text);
+
+ card->options.routing_type4=NO_ROUTER|
+ RESET_ROUTING_FLAG;
+#ifdef QETH_IPV6
+ card->options.routing_type6=NO_ROUTER|
+ RESET_ROUTING_FLAG;
+#endif /* QETH_IPV6 */
+ atomic_set(&card->enable_routing_attempts4,
+ QETH_ROUTING_ATTEMPTS);
+#ifdef QETH_IPV6
+ atomic_set(&card->enable_routing_attempts6,
+ QETH_ROUTING_ATTEMPTS);
+#endif /* QETH_IPV6 */
+ qeth_start_softsetup_thread(card);
+ } else if (!strncmp(buffer+pos,"no_router4",token_len)) {
+ CHECK_MISSING_PARAMETER;
+ sprintf(dbf_text,"nor4%4x",card->irq0);
+ QETH_DBF_TEXT2(0,trace,dbf_text);
+
+ card->options.routing_type4=NO_ROUTER|
+ RESET_ROUTING_FLAG;
+ atomic_set(&card->enable_routing_attempts4,
+ QETH_ROUTING_ATTEMPTS);
+ qeth_start_softsetup_thread(card);
+#ifdef QETH_IPV6
+ } else if (!strncmp(buffer+pos,"no_router6",token_len)) {
+ CHECK_MISSING_PARAMETER;
+ sprintf(dbf_text,"nor6%4x",card->irq0);
+ QETH_DBF_TEXT2(0,trace,dbf_text);
+
+ card->options.routing_type6=NO_ROUTER|
+ RESET_ROUTING_FLAG;
+ atomic_set(&card->enable_routing_attempts6,
+ QETH_ROUTING_ATTEMPTS);
+ qeth_start_softsetup_thread(card);
+#endif /* QETH_IPV6 */
+ } else {
+ PRINT_WARN("unknown command input in procfile\n");
+ }
+#undef CHECK_MISSING_PARAMETER
+out:
+
+ *offset = *offset + user_len;
+ vfree(buffer);
+
+ return user_len;
+}
+
+#define _OUTP_IT(x...) c+=sprintf(buffer+c,x)
+
+#ifdef QETH_PERFORMANCE_STATS
+static int qeth_perf_procfile_read(char *buffer,char **buffer_location,
+ off_t offset,int buffer_length,int *eof,
+ void *data)
+{
+ int c=0;
+ qeth_card_t *card;
+ /* we are always called with buffer_length=4k, so we all
+ deliver on the first read */
+ if (offset>0) return 0;
+
+ QETH_DBF_TEXT2(0,trace,"perfpfrd");
+
+ card=firstcard;
+
+ while (card) {
+ _OUTP_IT("For card with devnos 0x%X/0x%X/0x%X (%s):\n",
+ card->devno0,card->devno1,card->devno2,
+ card->dev_name);
+ _OUTP_IT(" Skb's/buffers received : %i/%i\n",
+ card->perf_stats.skbs_rec,
+ card->perf_stats.bufs_rec);
+ _OUTP_IT(" Skb's/buffers sent : %i/%i\n",
+ card->perf_stats.skbs_sent,
+ card->perf_stats.bufs_sent);
+ _OUTP_IT("\n");
+ _OUTP_IT(" Skb's/buffers sent without packing : %i/%i\n",
+ card->perf_stats.skbs_sent_dont_pack,
+ card->perf_stats.bufs_sent_dont_pack);
+ _OUTP_IT(" Skb's/buffers sent with packing : %i/%i\n",
+ card->perf_stats.skbs_sent_pack,
+ card->perf_stats.bufs_sent_pack);
+ _OUTP_IT("\n");
+ _OUTP_IT(" Packing state changes no pkg.->packing : %i/%i\n",
+ card->perf_stats.sc_dp_p,
+ card->perf_stats.sc_p_dp);
+ _OUTP_IT(" Current buffer usage (outbound q's) : " \
+ "%i/%i/%i/%i\n",
+ atomic_read(&card->outbound_used_buffers[0]),
+ atomic_read(&card->outbound_used_buffers[1]),
+ atomic_read(&card->outbound_used_buffers[2]),
+ atomic_read(&card->outbound_used_buffers[3]));
+ _OUTP_IT("\n");
+ _OUTP_IT(" Inbound time (in us) : %i\n",
+ card->perf_stats.inbound_time);
+ _OUTP_IT(" Inbound cnt : %i\n",
+ card->perf_stats.inbound_cnt);
+ _OUTP_IT(" Outbound time (in us, incl QDIO) : %i\n",
+ card->perf_stats.outbound_time);
+ _OUTP_IT(" Outbound cnt : %i\n",
+ card->perf_stats.outbound_cnt);
+ _OUTP_IT(" Watermarks: L/H=%i/%i\n",
+ LOW_WATERMARK_PACK,HIGH_WATERMARK_PACK);
+ _OUTP_IT("\n");
+
+ card=card->next;
+ }
+
+ return c;
+}
+
+static struct proc_dir_entry *qeth_perf_proc_file;
+
+#endif /* QETH_PERFORMANCE_STATS */
+
+static int qeth_ipato_procfile_open(struct inode *inode, struct file *file)
+{
+ char text[33];
+ ipato_entry_t *ipato_entry;
+ qeth_card_t *card;
+ qeth_vipa_entry_t *vipa_entry;
+ int rc=0;
+ tempinfo_t *info;
+ int size;
+ char entry_type[5];
+
+ info = (tempinfo_t *) vmalloc (sizeof (tempinfo_t));
+ if (info == NULL) {
+ PRINT_WARN("No memory available for data\n");
+ return -ENOMEM;
+ } else {
+ file->private_data = (void *) info;
+ }
+ info->len=0;
+
+ QETH_DBF_TEXT2(0,trace,"ipatorea");
+ /* lock all the stuff */
+ my_spin_lock(&ipato_list_lock);
+ my_read_lock(&list_lock);
+
+ size=64; /* for inv4/6 etc. */
+
+ ipato_entry=ipato_entries;
+ while (ipato_entry) {
+ ipato_entry=ipato_entry->next;
+ size+=64;
+ }
+ card=firstcard;
+ while (card) {
+ my_read_lock(&card->vipa_list_lock);
+ vipa_entry=card->vipa_list;
+ while (vipa_entry) {
+ vipa_entry=vipa_entry->next;
+ size+=64;
+ }
+ /*my_read_unlock(&card->vipa_list_lock); don't unlock it here*/
+ card=card->next;
+ }
+ info->data = (char *) vmalloc (size);
+ if (info->data == NULL) {
+ PRINT_WARN("No memory available for data\n");
+ vfree (info);
+ rc=-ENOMEM;
+ goto out;
+ }
+
+#define _IOUTP_IT(x...) info->len+=sprintf(info->data+info->len,x)
+ if (ipato_inv4)
+ _IOUTP_IT("inv4\n");
+ ipato_entry=ipato_entries;
+ text[8]=0;
+ while (ipato_entry) {
+ if (ipato_entry->version==4) {
+ qeth_convert_addr_to_text(4,ipato_entry->addr,text);
+ _IOUTP_IT("add4 %s/%i%s%s\n",text,
+ ipato_entry->mask_bits,
+ ipato_entry->dev_name[0]?":":"",
+ ipato_entry->dev_name[0]?
+ ipato_entry->dev_name:"");
+ }
+ ipato_entry=ipato_entry->next;
+ }
+
+ if (ipato_inv6)
+ _IOUTP_IT("inv6\n");
+ ipato_entry=ipato_entries;
+ text[32]=0;
+ while (ipato_entry) {
+ if (ipato_entry->version==6) {
+ qeth_convert_addr_to_text(6,ipato_entry->addr,text);
+ _IOUTP_IT("add6 %s/%i%s%s\n",text,
+ ipato_entry->mask_bits,
+ ipato_entry->dev_name[0]?":":"",
+ ipato_entry->dev_name[0]?
+ ipato_entry->dev_name:"");
+ }
+ ipato_entry=ipato_entry->next;
+ }
+ card=firstcard;
+ while (card) {
+ vipa_entry=card->vipa_list;
+ while (vipa_entry) {
+ strcpy(entry_type,(vipa_entry->flag==
+ IPA_SETIP_VIPA_FLAGS)?
+ "vipa":"rxip");
+ if (vipa_entry->version==4) {
+ _IOUTP_IT("add_%s4 %02x%02x%02x%02x:%s\n",
+ entry_type,
+ vipa_entry->ip[0],
+ vipa_entry->ip[1],
+ vipa_entry->ip[2],
+ vipa_entry->ip[3],
+ card->dev_name);
+ } else {
+ _IOUTP_IT("add_%s6 %02x%02x%02x%02x" \
+ "%02x%02x%02x%02x" \
+ "%02x%02x%02x%02x" \
+ "%02x%02x%02x%02x:%s\n",
+ entry_type,
+ vipa_entry->ip[0],
+ vipa_entry->ip[1],
+ vipa_entry->ip[2],
+ vipa_entry->ip[3],
+ vipa_entry->ip[4],
+ vipa_entry->ip[5],
+ vipa_entry->ip[6],
+ vipa_entry->ip[7],
+ vipa_entry->ip[8],
+ vipa_entry->ip[9],
+ vipa_entry->ip[10],
+ vipa_entry->ip[11],
+ vipa_entry->ip[12],
+ vipa_entry->ip[13],
+ vipa_entry->ip[14],
+ vipa_entry->ip[15],
+ card->dev_name);
+ }
+ vipa_entry=vipa_entry->next;
+ }
+ card=card->next;
+ }
+out:
+ /* unlock all the stuff */
+ card=firstcard;
+ while (card) {
+ /*my_read_lock(&card->vipa_list_lock); don't lock it here */
+ my_read_unlock(&card->vipa_list_lock);
+ card=card->next;
+ }
+ my_read_unlock(&list_lock);
+ my_spin_unlock(&ipato_list_lock);
+
+ return rc;
+}
+
+static ssize_t qeth_procfile_read(struct file *file,char *user_buf,
+ size_t user_len,loff_t * offset)
+{
+ loff_t len;
+ tempinfo_t *p_info = (tempinfo_t *) file->private_data;
+
+ if (*offset >= p_info->len) {
+ return 0;
+ } else {
+ len = __min(user_len, (p_info->len - *offset));
+ if (copy_to_user (user_buf, &(p_info->data[*offset]), len))
+ return -EFAULT;
+ (*offset) += len;
+ return len;
+ }
+}
+
+/* ATT: this is also the procfile release function for the ipato
+ * procfs entry */
+static int qeth_procfile_release(struct inode *inode,struct file *file)
+{
+ tempinfo_t *p_info = (tempinfo_t *) file->private_data;
+
+ if (p_info) {
+ if (p_info->data)
+ vfree (p_info->data);
+ vfree (p_info);
+ }
+
+ return 0;
+}
+
+static ssize_t qeth_ipato_procfile_write(struct file *file,
+ const char *user_buffer,
+ size_t user_len,loff_t *offset)
+{
+ int add,version;
+ char text[33];
+ __u8 addr[16];
+ int len,i,flag;
+ int mask_bits;
+ char *buffer;
+ int dev_name_there;
+ char *dev_name_ptr;
+ qeth_card_t *card;
+#define BUFFER_LEN (10+32+1+5+1+DEV_NAME_LEN+1)
+
+ if (*offset>0) return user_len;
+ buffer=vmalloc(__max(__max(user_len+1,BUFFER_LEN),QETH_DBF_MISC_LEN));
+
+ if (buffer == NULL)
+ return -ENOMEM;
+ /* BUFFER_LEN=command incl. blank+addr+slash+mask_bits+
+ * colon+DEV_NAME_LEN+zero */
+ memset(buffer,0,BUFFER_LEN);
+
+ if (copy_from_user(buffer, user_buffer, user_len)) {
+ vfree (buffer);
+ return -EFAULT;
+ }
+
+ QETH_DBF_TEXT2(0,trace,"ipatowri");
+ QETH_DBF_TEXT2(0,misc,buffer);
+ if (!strncmp(buffer,"inv4",4)) {
+ ipato_inv4=1-ipato_inv4;
+ goto out;
+ }
+ if (!strncmp(buffer,"inv6",4)) {
+ ipato_inv6=1-ipato_inv6;
+ goto out;
+ }
+ if ( (!strncmp(buffer,"add4 ",5)) ||
+ (!strncmp(buffer,"add6 ",5)) ||
+ (!strncmp(buffer,"del4 ",5)) ||
+ (!strncmp(buffer,"del6 ",5)) ) {
+ text[8]=0;
+ text[32]=0;
+ add=!strncmp(buffer,"add",3);
+ version=(buffer[3]=='4')?4:6;
+ len=(version==4)?8:32;
+ strncpy(text,buffer+5,len);
+ if (qeth_convert_text_to_addr(version,text,addr)) {
+ PRINT_ERR("error in parsing ipato information " \
+ "(addr)\n");
+ goto out;
+ }
+ strncpy(text,buffer+5+len+1,10);
+ /* we prepare mask_bits for qeth_getints */
+ dev_name_there=0;
+ for (i=5+len+1;i<BUFFER_LEN;i++) {
+ if (*(buffer+i)=='\n') {
+ *(buffer+i)=0;
+ break;
+ }
+ if (*(buffer+i)==':') {
+ *(buffer+i)=0; /* so that qeth_getint works */
+ dev_name_there=i;
+ break;
+ }
+ if (*(buffer+i)==0)
+ break;
+ }
+ mask_bits=qeth_getint(buffer+5+len+1,0);
+ if ((mask_bits<0)||(mask_bits>((version==4)?32:128))) {
+ PRINT_ERR("error in parsing ipato information " \
+ "(mask bits)\n");
+ goto out;
+ }
+ if (dev_name_there) {
+ dev_name_ptr=buffer+dev_name_there+1;
+ /* wipe out the linefeed */
+ for (i=dev_name_there+1;
+ i<dev_name_there+1+DEV_NAME_LEN+1;i++)
+ if (*(buffer+i)=='\n') *(buffer+i)=0;
+ } else
+ dev_name_ptr=NULL;
+
+ if (add)
+ qeth_add_ipato_entry(version,addr,mask_bits,
+ dev_name_ptr);
+ else
+ qeth_del_ipato_entry(version,addr,mask_bits,
+ dev_name_ptr);
+ goto out;
+ }
+ if ( (!strncmp(buffer,"add_vipa4 ",10)) ||
+ (!strncmp(buffer,"add_rxip4 ",10)) ||
+ (!strncmp(buffer,"add_vipa6 ",10)) ||
+ (!strncmp(buffer,"add_rxip6 ",10)) ||
+ (!strncmp(buffer,"del_vipa4 ",10)) ||
+ (!strncmp(buffer,"del_rxip4 ",10)) ||
+ (!strncmp(buffer,"del_vipa6 ",10)) ||
+ (!strncmp(buffer,"del_rxip6 ",10)) ) {
+ text[8]=0;
+ text[32]=0;
+ add=!strncmp(buffer,"add",3);
+ flag=(!strncmp(buffer+4,"vipa",4))?IPA_SETIP_VIPA_FLAGS:
+ IPA_SETIP_TAKEOVER_FLAGS;
+ version=(buffer[8]=='4')?4:6;
+ len=(version==4)?8:32;
+ strncpy(text,buffer+10,len);
+ if (qeth_convert_text_to_addr(version,text,addr)) {
+ PRINT_ERR("error in parsing vipa/rxip information " \
+ "(addr)\n");
+ goto out;
+ }
+ if (*(buffer+10+len)!=':') {
+ PRINT_ERR("error in parsing vipa/rxip information " \
+ "(no interface)\n");
+ goto out;
+ }
+ /* interface name is at buffer+10+len+1 */
+ /* wipe out the \n */
+ for (i=10+len+1;i<10+len+1+DEV_NAME_LEN+1;i++)
+ if (*(buffer+i)=='\n') *(buffer+i)=0;
+ card=qeth_get_card_by_name(buffer+10+len+1);
+ if (!card) {
+ PRINT_ERR("error in parsing vipa/rxip information " \
+ "(unknown interface)\n");
+ goto out;
+ }
+ if (add)
+ i=qeth_add_vipa_entry(card,version,addr,flag);
+ else
+ i=qeth_del_vipa_entry(card,version,addr,flag);
+ if (!i) qeth_start_softsetup_thread(card);
+ goto out;
+ }
+ PRINT_ERR("unknown ipato information command\n");
+out:
+ vfree(buffer);
+ *offset = *offset + user_len;
+#undef BUFFER_LEN
+ return user_len;
+}
+
+static int qeth_procfile_getinterfaces(unsigned long arg)
+{
+ qeth_card_t *card;
+
+ char parms[16];
+ char *buffer;
+ char *buffer_pointer;
+ __u32 version,valid_fields,qeth_version,number_of_devices,if_index;
+ __u32 data_size,data_len;
+ unsigned long ioctl_flags;
+ int result=0;
+
+ /* the struct of version 0 is:
+typedef struct dev_list
+{
+ char device_name[IFNAME_MAXLEN]; // OSA-Exp device name (e.g. eth0)
+ __u32 if_index; // interface index from kernel
+ __u32 flags; // device charateristics
+} __attribute__((packed)) DEV_LIST;
+
+typedef struct osaexp_dev_ver0
+{
+ __u32 version; // structure version
+ __u32 valid_fields; // bitmask of fields that are really filled
+ __u32 qeth_version; // qeth driver version
+ __u32 number_of_devices; // number of OSA Express devices
+ struct dev_list devices[0]; // list of OSA Express devices
+} __attribute__((packed)) OSAEXP_DEV_VER0;
+ */
+
+ version = 0;
+ valid_fields = 0;
+ qeth_version = 0;
+ number_of_devices= 0;
+
+ copy_from_user((void*)parms,(void*)arg,sizeof(parms));
+ memcpy(&data_size,parms,sizeof(__u32));
+
+ if ( !(data_size > 0) )
+ return -EFAULT;
+ if ( data_size > IOCTL_MAX_TRANSFER_SIZE )
+ return -EFAULT;
+ if ( !access_ok(VERIFY_WRITE, (void *)arg, data_size) )
+ return -EFAULT;
+
+ my_read_lock(&list_lock);
+ card = firstcard;
+#define IOCTL_USER_STRUCT_SIZE (DEV_NAME_LEN*sizeof(char)) + \
+ sizeof(__u32) + sizeof(__u32)
+ while (card) {
+ if (card->type == QETH_CARD_TYPE_OSAE)
+ number_of_devices=number_of_devices + IOCTL_USER_STRUCT_SIZE;
+ card = card->next;
+ }
+#undef IOCTL_USER_STRUCT_SIZE
+ if ((number_of_devices + 4*sizeof(__u32)) >= data_size) {
+ result=-ENOMEM;
+ goto out;
+ }
+
+ number_of_devices=0;
+ card = firstcard;
+ buffer = (char *)vmalloc(data_size);
+ if (!buffer) {
+ result=-EFAULT;
+ goto out;
+ }
+ buffer_pointer = ((char *)(buffer)) + (4*sizeof(__u32)) ;
+ while (card) {
+ if ((card->type == QETH_CARD_TYPE_OSAE)&&
+ (!atomic_read(&card->is_gone))&&
+ (atomic_read(&card->is_hardsetup))&&
+ (atomic_read(&card->is_registered))) {
+
+ memcpy(buffer_pointer,card->dev_name,DEV_NAME_LEN);
+ buffer_pointer = buffer_pointer + DEV_NAME_LEN;
+ if_index=card->dev->ifindex;
+ memcpy(buffer_pointer,&if_index,sizeof(__u32));
+ buffer_pointer = buffer_pointer + sizeof(__u32);
+ memcpy(buffer_pointer,&ioctl_flags,sizeof(__u32));
+ buffer_pointer = buffer_pointer + sizeof(__u32);
+ number_of_devices=number_of_devices+1;
+ }
+ card = card->next;
+ }
+
+ /* we copy the real size */
+ data_len=buffer_pointer-buffer;
+
+ buffer_pointer = buffer;
+ /* copy the header information at the beginning of the buffer */
+ memcpy(buffer_pointer,&version,sizeof(__u32));
+ memcpy(((char *)buffer_pointer)+sizeof(__u32),&valid_fields,
+ sizeof(__u32));
+ memcpy(((char *)buffer_pointer)+(2*sizeof(__u32)),&qeth_version,
+ sizeof(__u32));
+ memcpy(((char *)buffer_pointer)+(3*sizeof(__u32)),&number_of_devices,
+ sizeof(__u32));
+ copy_to_user((char *)arg,buffer,data_len);
+ vfree(buffer);
+out:
+ my_read_unlock(&list_lock);
+ return result;
+
+#undef PARMS_BUFFERLENGTH
+
+};
+
+static int qeth_procfile_interfacechanges(unsigned long arg)
+{
+ return qeth_sleepon_procfile();
+
+}
+
+static int qeth_procfile_ioctl(struct inode *inode, struct file *file,
+ unsigned int cmd, unsigned long arg)
+{
+
+ int result;
+ down_interruptible(&qeth_procfile_ioctl_lock);
+ switch (cmd) {
+
+ case QETH_IOCPROC_OSAEINTERFACES:
+ result = qeth_procfile_getinterfaces(arg);
+ break;
+ case QETH_IOCPROC_INTERFACECHANGES:
+ result = qeth_procfile_interfacechanges(arg);
+ break;
+ default:
+ result = -EOPNOTSUPP;
+ }
+ up(&qeth_procfile_ioctl_lock);
+ return result;
+};
+
+static struct file_operations qeth_procfile_fops =
+{
+ ioctl:qeth_procfile_ioctl,
+ read:qeth_procfile_read,
+ write:qeth_procfile_write,
+ open:qeth_procfile_open,
+ release:qeth_procfile_release,
+};
+
+static struct proc_dir_entry *qeth_proc_file;
+
+static struct file_operations qeth_ipato_procfile_fops =
+{
+ read:qeth_procfile_read, /* same as above! */
+ write:qeth_ipato_procfile_write,
+ open:qeth_ipato_procfile_open,
+ release:qeth_procfile_release /* same as above! */
+};
+
+static struct proc_dir_entry *qeth_ipato_proc_file;
+
+static void qeth_add_procfs_entries(void)
+{
+ proc_file_registration=0;
+ qeth_proc_file=create_proc_entry(QETH_PROCFILE_NAME,
+ S_IFREG|0644,&proc_root);
+ if (qeth_proc_file) {
+ qeth_proc_file->proc_fops = &qeth_procfile_fops;
+ sema_init(&qeth_procfile_ioctl_sem,
+ PROCFILE_SLEEP_SEM_MAX_VALUE);
+ sema_init(&qeth_procfile_ioctl_lock,
+ PROCFILE_IOCTL_SEM_MAX_VALUE);
+ } else proc_file_registration=-1;
+
+ if (proc_file_registration)
+ PRINT_WARN("was not able to register proc-file (%i).\n",
+ proc_file_registration);
+ proc_ipato_file_registration=0;
+ qeth_ipato_proc_file=create_proc_entry(QETH_IPA_PROCFILE_NAME,
+ S_IFREG|0644,&proc_root);
+ if (qeth_ipato_proc_file) {
+ qeth_ipato_proc_file->proc_fops = &qeth_ipato_procfile_fops;
+ } else proc_ipato_file_registration=-1;
+
+ if (proc_ipato_file_registration)
+ PRINT_WARN("was not able to register ipato-proc-file (%i).\n",
+ proc_ipato_file_registration);
+
+#ifdef QETH_PERFORMANCE_STATS
+ proc_perf_file_registration=0;
+ qeth_perf_proc_file=create_proc_entry(QETH_PERF_PROCFILE_NAME,
+ S_IFREG|0444,&proc_root);
+ if (qeth_perf_proc_file) {
+ qeth_perf_proc_file->read_proc=&qeth_perf_procfile_read;
+ } else proc_perf_file_registration=-1;
+
+ if (proc_perf_file_registration)
+ PRINT_WARN("was not able to register perf. proc-file (%i).\n",
+ proc_perf_file_registration);
+#endif /* QETH_PERFORMANCE_STATS */
+}
+
+#ifdef MODULE
+static void qeth_remove_procfs_entries(void)
+{
+ if (!proc_file_registration) /* means if it went ok earlier */
+ remove_proc_entry(QETH_PROCFILE_NAME,&proc_root);
+
+ if (!proc_ipato_file_registration) /* means if it went ok earlier */
+ remove_proc_entry(QETH_IPA_PROCFILE_NAME,&proc_root);
+
+#ifdef QETH_PERFORMANCE_STATS
+ if (!proc_perf_file_registration) /* means if it went ok earlier */
+ remove_proc_entry(QETH_PERF_PROCFILE_NAME,&proc_root);
+#endif /* QETH_PERFORMANCE_STATS */
+}
+#endif /* MODULE */
+
+static void qeth_unregister_dbf_views(void)
+{
+ if (qeth_dbf_setup)
+ debug_unregister(qeth_dbf_setup);
+ if (qeth_dbf_qerr)
+ debug_unregister(qeth_dbf_qerr);
+ if (qeth_dbf_sense)
+ debug_unregister(qeth_dbf_sense);
+ if (qeth_dbf_misc)
+ debug_unregister(qeth_dbf_misc);
+ if (qeth_dbf_data)
+ debug_unregister(qeth_dbf_data);
+ if (qeth_dbf_control)
+ debug_unregister(qeth_dbf_control);
+ if (qeth_dbf_trace)
+ debug_unregister(qeth_dbf_trace);
+}
+
+static int qeth_chandev_shutdown(struct net_device *dev)
+{
+
+ qeth_card_t* card;
+
+ card = (qeth_card_t *)dev->priv;
+
+ my_spin_lock(&setup_lock);
+
+ qeth_remove_card_from_list(card);
+ QETH_DBF_TEXT4(0,trace,"freecard");
+ qeth_free_card(card);
+
+ my_spin_unlock(&setup_lock);
+
+ return 0;
+
+}
+
+#ifdef QETH_IPV6
+static int qeth_ipv6_init(void)
+{
+ qeth_old_arp_constructor=arp_tbl.constructor;
+ write_lock(&arp_tbl.lock);
+ arp_tbl.constructor=qeth_arp_constructor;
+ write_unlock(&arp_tbl.lock);
+
+ /* generate the memory leak here */
+ arp_direct_ops=(struct neigh_ops*)
+ kmalloc(sizeof(struct neigh_ops),GFP_KERNEL);
+ if (!arp_direct_ops)
+ return -ENOMEM;
+
+ memcpy(arp_direct_ops,&arp_direct_ops_template,
+ sizeof(struct neigh_ops));
+ return 0;
+}
+
+static void qeth_ipv6_uninit(void)
+{
+ write_lock(&arp_tbl.lock);
+ arp_tbl.constructor=qeth_old_arp_constructor;
+ write_unlock(&arp_tbl.lock);
+}
+#endif /* QETH_IPV6 */
+
+static void qeth_get_internal_functions(void)
+{
+ struct net_device dev;
+ ether_setup(&dev);
+ qeth_my_eth_header=dev.hard_header;
+ qeth_my_eth_rebuild_header=dev.rebuild_header;
+ qeth_my_eth_header_cache=dev.hard_header_cache;
+ qeth_my_eth_header_cache_update=dev.header_cache_update;
+ qeth_my_eth_header=dev.hard_header;
+#ifdef CONFIG_TR
+ tr_setup(&dev);
+ qeth_my_tr_header=dev.hard_header;
+ qeth_my_tr_rebuild_header=dev.rebuild_header;
+#endif /* CONFIG_TR */
+}
+
+#ifdef MODULE
+int init_module(void)
+#else /* MODULE */
+static int __init qeth_init(void)
+#endif /* MODULE */
+{
+ int result;
+#ifdef MODULE
+ void *ptr;
+#endif /* MODULE */
+
+ int unregister_from_chandev=0;
+ int cards_found;
+
+ qeth_eyecatcher();
+
+ printk("qeth: loading %s\n",version);
+
+#ifdef MODULE
+ global_stay_in_mem = chandev_persist(chandev_type_qeth);
+#endif /* MODULE */
+
+ spin_lock_init(&setup_lock);
+
+ spin_lock_init(&ipato_list_lock);
+
+ qeth_get_internal_functions();
+
+ qeth_alloc_spare_bufs();
+
+#ifdef QETH_IPV6
+ if (qeth_ipv6_init()) goto oom;
+#endif /* QETH_IPV6 */
+
+ qeth_dbf_setup=debug_register(QETH_DBF_SETUP_NAME,
+ QETH_DBF_SETUP_INDEX,
+ QETH_DBF_SETUP_NR_AREAS,
+ QETH_DBF_SETUP_LEN);
+ if (!qeth_dbf_setup) goto oom;
+
+ debug_register_view(qeth_dbf_setup,&debug_hex_ascii_view);
+ debug_set_level(qeth_dbf_setup,QETH_DBF_SETUP_LEVEL);
+
+ qeth_dbf_misc=debug_register(QETH_DBF_MISC_NAME,
+ QETH_DBF_MISC_INDEX,
+ QETH_DBF_MISC_NR_AREAS,
+ QETH_DBF_MISC_LEN);
+ if (!qeth_dbf_misc) goto oom;
+
+ debug_register_view(qeth_dbf_misc,&debug_hex_ascii_view);
+ debug_set_level(qeth_dbf_misc,QETH_DBF_MISC_LEVEL);
+
+ qeth_dbf_data=debug_register(QETH_DBF_DATA_NAME,
+ QETH_DBF_DATA_INDEX,
+ QETH_DBF_DATA_NR_AREAS,
+ QETH_DBF_DATA_LEN);
+ if (!qeth_dbf_data) goto oom;
+
+ debug_register_view(qeth_dbf_data,&debug_hex_ascii_view);
+ debug_set_level(qeth_dbf_data,QETH_DBF_DATA_LEVEL);
+
+ qeth_dbf_control=debug_register(QETH_DBF_CONTROL_NAME,
+ QETH_DBF_CONTROL_INDEX,
+ QETH_DBF_CONTROL_NR_AREAS,
+ QETH_DBF_CONTROL_LEN);
+ if (!qeth_dbf_control) goto oom;
+
+ debug_register_view(qeth_dbf_control,&debug_hex_ascii_view);
+ debug_set_level(qeth_dbf_control,QETH_DBF_CONTROL_LEVEL);
+
+ qeth_dbf_sense=debug_register(QETH_DBF_SENSE_NAME,
+ QETH_DBF_SENSE_INDEX,
+ QETH_DBF_SENSE_NR_AREAS,
+ QETH_DBF_SENSE_LEN);
+ if (!qeth_dbf_sense) goto oom;
+
+ debug_register_view(qeth_dbf_sense,&debug_hex_ascii_view);
+ debug_set_level(qeth_dbf_sense,QETH_DBF_SENSE_LEVEL);
+
+ qeth_dbf_qerr=debug_register(QETH_DBF_QERR_NAME,
+ QETH_DBF_QERR_INDEX,
+ QETH_DBF_QERR_NR_AREAS,
+ QETH_DBF_QERR_LEN);
+ if (!qeth_dbf_qerr) goto oom;
+
+ debug_register_view(qeth_dbf_qerr,&debug_hex_ascii_view);
+ debug_set_level(qeth_dbf_qerr,QETH_DBF_QERR_LEVEL);
+
+ qeth_dbf_trace=debug_register(QETH_DBF_TRACE_NAME,
+ QETH_DBF_TRACE_INDEX,
+ QETH_DBF_TRACE_NR_AREAS,
+ QETH_DBF_TRACE_LEN);
+ if (!qeth_dbf_trace) goto oom;
+
+ debug_register_view(qeth_dbf_trace,&debug_hex_ascii_view);
+ debug_set_level(qeth_dbf_trace,QETH_DBF_TRACE_LEVEL);
+
+ cards_found = chandev_register_and_probe
+ (qeth_probe,(chandev_shutdownfunc)qeth_chandev_shutdown,
+ (chandev_msck_notification_func)qeth_chandev_msck_notfunc,
+ chandev_type_qeth);
+ if (cards_found>0)
+ result=0;
+ else if (cards_found<0) {
+ result=cards_found;
+ global_stay_in_mem=0;
+ } else result=-ENODEV;
+
+ unregister_from_chandev=(cards_found>=0);
+ if ((result)&&(global_stay_in_mem)) {
+ result=0;
+ }
+
+#ifdef MODULE
+ QETH_DBF_TEXT0(0,setup,"initmodl");
+ ptr=&init_module;
+ QETH_DBF_HEX0(0,setup,&ptr,sizeof(void*));
+#endif /* MODULE */
+
+ if (!result) {
+ qeth_register_notifiers();
+ qeth_add_procfs_entries();
+ } else {
+ /* don't call it with shutdown -- there was not device
+ * initialized or an internal problem in chandev, better
+ * have him not try to call us */
+ if (unregister_from_chandev)
+ chandev_unregister(qeth_probe,0);
+ qeth_unregister_dbf_views();
+#ifdef QETH_IPV6
+ qeth_ipv6_uninit();
+#endif /* QETH_IPV6 */
+ qeth_free_all_spare_bufs();
+ }
+
+ return result;
+oom:
+ PRINT_ERR("not enough memory for dbf. Will not load module.\n");
+ result=-ENOMEM;
+#ifdef QETH_IPV6
+ qeth_ipv6_uninit();
+#endif /* QETH_IPV6 */
+ qeth_unregister_dbf_views();
+ qeth_free_all_spare_bufs();
+ return result;
+}
+
+#ifdef MODULE
+void cleanup_module(void)
+{
+#ifdef QETH_IPV6
+ qeth_ipv6_uninit();
+#endif /* QETH_IPV6 */
+ qeth_unregister_notifiers();
+
+ qeth_remove_procfs_entries();
+
+ QETH_DBF_TEXT1(0,trace,"cleanup.");
+
+ chandev_unregister(qeth_probe, 1 );
+
+ qeth_free_all_spare_bufs();
+
+ qeth_unregister_dbf_views();
+
+ printk("qeth: %s: module removed\n",version);
+}
+EXPORT_SYMBOL(qeth_eyecatcher); /* yeah, i know, could be outside of
+ the ifdef */
+#else /* MODULE */
+__initcall(qeth_init);
+#endif /* MODULE */
FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen (who was at: slshen@lbl.gov)