/*
*
* 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) 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;
loff_t n = *offset;
unsigned long pos = n;
if (pos != n || pos >= p_info->len) {
return 0;
} else {
len = __min(user_len, (p_info->len - pos));
if (copy_to_user (user_buf, &(p_info->data[pos]), len))
return -EFAULT;
*offset = pos + 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) 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 = 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 */