/* * * linux/drivers/s390/net/qeth.c ($Revision: 1.337.4.24 $) * * Linux on zSeries OSA Express and HiperSockets support * * Copyright 2000,2003 IBM Corporation * * Author(s): Utz Bacher * Cornelia Huck (chandev stuff, * numerous bugfixes) * Frank Pavlic (query/purge ARP, SNMP, fixes) * Andreas Herrmann (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. */ /* * 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 /[:] * add6 /[:] * del4 /[:] * del6 /[:] * 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). * is a 8 or 32byte hexadecimal view of the IP address. * specifies the number of bits which are set in the network mask. * 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 : * add_vipa6 : * del_vipa4 : * del_vipa6 : * * 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 : * add_rxip6 : * del_rxip4 : * del_rxip6 : * * 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 #ifndef CONFIG_CHANDEV #error "qeth can only be compiled with chandev support" #endif /* CONFIG_CHANDEV */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef CONFIG_PROC_FS #include #endif /* CONFIG_PROC_FS */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #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.4.24 $" 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 "); MODULE_DESCRIPTION("Linux on zSeries OSA Express and HiperSockets support\n" \ "Copyright 2000,2003 IBM Corporation\n"); MODULE_LICENSE("GPL"); /******************** HERE WE GO ***********************************/ 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 inline void qeth_schedule_recovery(qeth_card_t *card); static int qeth_fake_header(struct sk_buff *skb, struct net_device *dev, unsigned short type, void *daddr, void *saddr, unsigned len); static inline 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>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()-starttimeout) { goto out; } schedule_timeout(((start+timeout-qeth_get_millis())>>10)*HZ); } out: set_task_state(current,TASK_RUNNING); } static inline 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 inline 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=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=RTN_UNSPEC; qeth_card_t *card = (qeth_card_t *)skb->dev->priv; 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; } if (!memcmp(skb->nh.raw,skb->dev->broadcast,6)) { i=RTN_BROADCAST; } else { __u16 hdr_mac; hdr_mac=*((__u16*)skb->nh.raw); /* tr multicast? */ switch (card->link_type) { case QETH_MPC_LINK_TYPE_HSTR: case QETH_MPC_LINK_TYPE_LANE_TR: if ( (hdr_mac==QETH_TR_MAC_NC) || (hdr_mac==QETH_TR_MAC_C) ) i = RTN_MULTICAST; break; /* eth or so multicast? */ default: if ( (hdr_mac==QETH_ETH_MAC_V4) || (hdr_mac==QETH_ETH_MAC_V6) ) i = RTN_MULTICAST; } } return ((i==RTN_BROADCAST)|| (i==RTN_MULTICAST)|| (i==RTN_ANYCAST))?i:0; } static int qeth_get_prioqueue(qeth_card_t *card,struct sk_buff *skb, int multicast,int version) { if (!version && (card->type==QETH_CARD_TYPE_OSAE)) 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 inline int qeth_get_spare_buf(void) { int i=0; char dbf_text[15]; while (iinbound_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;iinbound_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;ielement[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 inline 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])) && (countdev_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;iirq2, 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;iirq2, 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 inline 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_lenirq0); 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_lenirq0); 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); } 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 inline void qeth_transform_outbound_addrs(qeth_card_t *card, qdio_buffer_t *buffer) { int i; void *ptr; if (card->do_pfix) { for (i=0;ielement[i].addr; buffer->element[i].addr=(void *)pfix_get_addr(ptr); } } } static inline 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;iinbound_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 inline 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;ielement[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_SRC_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; } if (card->options.checksum_type==HW_CHECKSUMMING) { /* do we have a checksummed packet? */ /* we only check for TCP/UDP checksums when the * pseudo header was also checked sucessfully -- for * the rest of the packets, it's not clear, whether * the upper layer csum is alright. And they * shouldn't occur too often anyway in real life */ if ( (*(__u8*)(hdr_ptr+11)& (QETH_EXT_HEADER_CSUM_HDR_REQ| QETH_EXT_HEADER_CSUM_TRANSP_REQ)) == (QETH_EXT_HEADER_CSUM_HDR_REQ| QETH_EXT_HEADER_CSUM_TRANSP_REQ) ) { /* csum does not need to be set * inbound anyway * * 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]); } */ skb->ip_summed=CHECKSUM_UNNECESSARY; } else { /* make the stack check it */ skb->ip_summed=CHECKSUM_NONE; } } else { skb->ip_summed=card->options.checksum_type; } #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 inline 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. * v6 uses passthrough, v4 sets the tag in the QDIO header */ card = (qeth_card_t *)skb->dev->priv; if ((card->vlangrp != NULL) && vlan_tx_tag_present(skb)) { if (version == 4) { hdr->ext_flags = QETH_EXT_HEADER_VLAN_FRAME; } else { hdr->ext_flags = QETH_EXT_HEADER_INCLUDE_VLAN_TAG; } 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 { if (multicast==RTN_MULTICAST) { hdr->flags=QETH_CAST_MULTICAST| 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 inline int 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 (lengthelement[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 inline 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: /* only request a PCI, if the fill level of the queue * is close to the high watermark, so that we don't * loose initiative during packing */ if (atomic_read(&card->outbound_used_buffers[queue]) last_pci_pos[queue]); /* compensate queues that wrapped around */ if (position_for_do_qdiooutbound_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_qdiooutbound_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 inline 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"); card->stats->tx_dropped++; card->stats->tx_errors++; 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 inline void qeth_free_all_skbs(qeth_card_t *card) { int q,b; for (q=0;qno_queues;q++) for (b=0;boutbound_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 static inline 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 inline 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)realloc_message)) { card->realloc_message=1; PRINT_WARN("%s: not enough headroom in skb. " \ "Increasing the " \ "add_hhlen parameter by %i may help.\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 inline 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)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;iirq0); 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; unsigned long stackptr; card=(qeth_card_t*)(dev->priv); if (skb==NULL) return 0; #ifdef CONFIG_ARCH_S390X asm volatile ("lgr %0,15" : "=d" (stackptr)); #else /* CONFIG_ARCH_S390X */ asm volatile ("lr %0,15" : "=d" (stackptr)); #endif /* CONFIG_ARCH_S390X */ /* prevent stack overflows */ /* normal and async stack is both 8k on s390 and 16k on s390x, * so it doesn't matter whether we're in an interrupt */ if ( (stackptr & STACK_PTR_MASK)< (sizeof(struct task_struct) + WORST_CASE_STACK_USAGE) ) { PRINT_ERR("delaying packet transmission " \ "due to potential stack overflow\n"); sprintf(dbf_text,"STOF%4x",card->irq0); QETH_DBF_TEXT1(1,trace,dbf_text); PRINT_ERR("Backtrace follows:\n"); show_trace((unsigned long *)stackptr); return -EBUSY; } #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"); card->stats->tx_dropped++; card->stats->tx_errors++; 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); card->stats->tx_dropped++; card->stats->tx_errors++; dev_kfree_skb_irq(skb); return 0; } if (dev->hard_header == qeth_fake_header) { /* * in theory, if we run in undef-ed QETH_IPV6, we should * always unshare, because we do skb_push, then overwrite * that place with OSA header in qeth_send_packet_fast(). * But it is only visible to one application - tcpdump. * Nobody else cares if (fake) MAC header gets smashed. * So, we only do it if fake_ll is in effect. */ if ((skb = qeth_pskb_unshare(skb, GFP_ATOMIC)) == NULL) { card->stats->tx_dropped++; dev_kfree_skb_irq(skb); return 0; } skb_pull(skb, QETH_FAKE_LL_LEN); } result=qeth_do_send_packet(card,skb,dev); if (!result) netif_wake_queue(card->dev); return result; } /* * This function is needed to tell af_packet.c to process headers. * It is not called from there, but only from the transmit path, * when we do not need any actual header. * * N.B. Why do we insist on kludging here instead of fixing tcpdump? * Because tcpdump is shared among gazillions of platforms, and * there is a) no reliable way to identify qeth or its packets * in pcap-linux.c (sll->sll_halen is the only hope); b) no easy * way to pass this information from libpcap to tcpdump proper. * * XXX This fails with TR: traffic flows ok, but tcpdump remains confused. */ int qeth_fake_header(struct sk_buff *skb, struct net_device *dev, unsigned short type, void *daddr, void *saddr, unsigned len) { unsigned char *hdr; hdr = skb_push(skb, QETH_FAKE_LL_LEN); memcpy(hdr, "FAKELLFAKELL", ETH_ALEN*2); if (type != ETH_P_802_3) *(u16 *)(hdr + ETH_ALEN*2) = htons(type); else *(u16 *)(hdr + ETH_ALEN*2) = htons(len); /* XXX Maybe dev->hard_header_len here? Then skb_pull by same size. */ return QETH_FAKE_LL_LEN; } 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_snmp_notify(void) { /*notify all registered processes */ struct list_head *l; struct qeth_notify_list *n_entry; QETH_DBF_TEXT5(0,trace,"snmpnoti"); spin_lock(¬ify_lock); list_for_each(l, ¬ify_list) { n_entry = list_entry(l, struct qeth_notify_list, list); send_sig(n_entry->signum, n_entry->task, 1); } spin_unlock(¬ify_lock); } 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); /* re-enable qeth_send_control_data again */ atomic_set(&card->write_busy,0); return NULL; } if (intparam==IPA_IOCTL_STATE) { if (qeth_sleepon_ioctl(card,QETH_IPA_TIMEOUT)) { QETH_DBF_TEXT2(0,trace,"scd:ioct"); /* re-enable qeth_send_control_data again */ atomic_set(&card->write_busy,0); return NULL; } rec_buf=card->dma_stuff->recbuf; 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; } if ( (ipa_cmd==IPA_CMD_SETASSPARMS) && (result==0) && (reply->data.setassparms.assist_no== IPA_INBOUND_CHECKSUM) && (reply->data.setassparms.command_code== IPA_CMD_ASS_START) ) { card->csum_enable_mask= reply->data.setassparms.data.flags_32bit; } } 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.command_code== IPA_CMD_ASS_ARP_SET_NO_ENTRIES) || (reply->data.setassparms.command_code== IPA_CMD_ASS_ARP_ADD_ENTRY) || (reply->data.setassparms.command_code== IPA_CMD_ASS_ARP_REMOVE_ENTRY) ) { if (reply->data.setassparms.return_code) { return ARP_RETURNCODE_ERROR; } else { return ARP_RETURNCODE_LASTREPLY; } } 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_is_arp_command(int cmd) { switch (cmd) { case IPA_CMD_ASS_ARP_SET_NO_ENTRIES: case IPA_CMD_ASS_ARP_QUERY_CACHE: case IPA_CMD_ASS_ARP_ADD_ENTRY: case IPA_CMD_ASS_ARP_REMOVE_ENTRY: case IPA_CMD_ASS_ARP_FLUSH_CACHE: case IPA_CMD_ASS_ARP_QUERY_INFO: case IPA_CMD_ASS_ARP_QUERY_STATS: return 1; default: return 0; } } 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) && (qeth_is_arp_command(reply->data.setassparms.command_code)) ) { 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)); if (copy_to_user(req->ifr_ifru.ifru_data, card->ioctl_data_buffer,data_size)) result =-EFAULT; } 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 ) result = IPA_REPLY_FAILED; else result = IPA_REPLY_SUCCESS; if (copy_to_user(req->ifr_ifru.ifru_data + SNMP_REQUEST_DATA_OFFSET, card->ioctl_data_buffer, card->ioctl_buffersize)) result = -EFAULT; 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==0xf012)?"unicast IP address invalid": \ (result==0xf013)?"multicast router limit reached": \ (result==0xf014)?"stop assist not supported": \ (result==0xf015)?"multicast assist not set": \ (result==0xf080)?"VM: 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)||(result==0xf080))&& (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]={0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}; 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; ne->flag=e->flag; 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; ne->flag=e->flag; 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 x%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==-EFAULT) ret_val=-EFAULT; \ 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 ((cmdSIOCDEVPRIVATE+5)) return -EOPNOTSUPP; if (copy_from_user(buff,rq->ifr_ifru.ifru_data,sizeof(buff))) return -EFAULT; data=buff; if ((cmdSIOCDEVPRIVATE+5)) return -EOPNOTSUPP; if (copy_from_user(buff,rq->ifr_ifru.ifru_data,sizeof(buff))) return -EFAULT; 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; if (down_interruptible ( &card->ioctl_sem ) ) return -ERESTARTSYS; if (atomic_read(&card->shutdown_phase)) { ret_val=-ENODEV; goto out; } 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=12;i<24;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; case SIOCDEVPRIVATE+6: if (!card->is_guest_lan && (card->type == QETH_CARD_TYPE_OSAE)) ret_val = 1; else ret_val = 0; break; default: ret_val=-EOPNOTSUPP; goto out; } out: up (&card->ioctl_sem); 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;ivlan_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;ivlan_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;idev))) { 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;ivlan_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": (result==0xf080)? "startlan disabled (VM: LAN " \ "is offline for functions " \ "requiring LAN access.": "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==0xf080)) { 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==0xf080)) { 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,"%8x",card->ipa6_supported); QETH_DBF_TEXT2(0,trace,dbf_text); sprintf(dbf_text,"%8x",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=BROADCAST_WITH_ECHO; result=qeth_send_setassparms_simple_with_data( card,IPA_FILTERING,IPA_CMD_ASS_ENABLE,1); sprintf(dbf_text,"Flt3%4x",result); QETH_DBF_TEXT2(0,trace,dbf_text); QETH_DBF_TEXT2(0,setup,dbf_text); if (!result) { PRINT_INFO("Broadcast packets will not be " \ "echoed back on %s.\n", card->dev_name); card->broadcast_capable=BROADCAST_WITHOUT_ECHO; } } 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, card->csum_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)) { /* for OSAs that can't do v6 multicast routing, we don't try */ if ( (card->type==QETH_CARD_TYPE_OSAE) && ( (card->options.routing_type6&ROUTER_MASK) == MULTICAST_ROUTER) && (!qeth_is_supported6(IPA_OSA_MC_ROUTER_AVAIL)) ) { atomic_set(&card->enable_routing_attempts6,0); atomic_set(&card->rt6fld,0); } else 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); if (i!=PROBLEM_TX_TIMEOUT) { 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 inline 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) { sbalf15=(card->outbound_ringbuffer[queue]->buffer[ (first_element+count-1)& QDIO_MAX_BUFFERS_PER_Q].element[15].flags)&0xff; 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; } if ( (siga_error==0x01) || (siga_error==(0x02|QDIO_SIGA_ERROR_B_BIT_SET)) || (siga_error==0x03) ) { sprintf(dbf_text,"BS%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", first_element+count-1, 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); goto out; } 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 OSA */ 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; /* reset the last_pci position to avoid * races, when we get close to packing again * immediately (in order to never loose * a PCI) */ atomic_set(&card->last_pci_pos[queue], (-2*QDIO_MAX_BUFFERS_PER_Q)); 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_useddev); } 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(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;ioptions.inbound_buffer_count;i++) { for (j=0;jinbound_buffer_pool_entry[i][j]) { kfree(card->inbound_buffer_pool_entry[i][j]); card->inbound_buffer_pool_entry[i][j]=NULL; } } } for (i=0;ino_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)); /* however, as the portname indication of OSA is wrong, we have to * do this: */ card->portname_required=(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; char dbf_text[15]; void **input_array,**output_array,**ptr; int i,j; qdio_initialize_t init_data; adapter_area=vmalloc(QDIO_MAX_BUFFERS_PER_Q*sizeof(char)); if (!adapter_area) return -ENOMEM; memset(adapter_area,0,QDIO_MAX_BUFFERS_PER_Q*sizeof(char)); 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) { vfree(adapter_area); return -ENOMEM; } ptr=input_array; for (j=0;jinbound_qdio_buffers[j]); ptr++; } output_array=vmalloc(QDIO_MAX_BUFFERS_PER_Q*sizeof(void*)* card->no_queues); if (!output_array) { vfree(input_array); vfree(adapter_area); return -ENOMEM; } ptr=output_array; for (i=0;ino_queues;i++) for (j=0;joutbound_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); vfree(adapter_area); 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;ivlan_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; } 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 ( (((!qeth_is_supported(IPA_OSA_MC_ROUTER_AVAIL))&& card->options.routing_type4 == MULTICAST_ROUTER)) || (card->options.routing_type4 == PRIMARY_CONNECTOR) || (card->options.routing_type4 == SECONDARY_CONNECTOR) #ifdef QETH_IPV6 || (((!qeth_is_supported(IPA_OSA_MC_ROUTER_AVAIL))&& 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 */ } } } #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): #else /* QETH_IPV6 */ (card->options.fake_ll==FAKE_LL)?qeth_fake_header: #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 * OSA 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->is_guest_lan= ((prcd[0x10]==_ascebc['V']) && (prcd[0x11]==_ascebc['M'])); card->do_pfix = (MACHINE_HAS_PFIX) ? (!(card->is_guest_lan)):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;isend_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;joutbound_buffer_send_state[i][j]= SEND_STATE_DONT_PACK; card->send_retries[i][j]=0; if (ino_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;ioptions.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;iinbound_buffer_refcnt[i],1); } for (i=0;iinbound_buffer_refcnt[i],0); /* only try to queue as many buffers as we have at all */ if (ioptions.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;qno_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,"%8x",card->ipa_supported); QETH_DBF_TEXT2(0,trace,dbf_text); sprintf(dbf_text,"%8x",card->ipa_enabled); QETH_DBF_TEXT2(0,trace,dbf_text); qeth_correct_routing_status(card); 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->is_guest_lan), (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 { if (card->options.portname[0]) { 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->is_guest_lan), (card->level[0])?" (level: ":"", (card->level[0])?card->level:"", (card->level[0])?")":"", qeth_get_link_type_name(card->type, card-> link_type)); } else { printk("qeth: Device 0x%X/0x%X/0x%X is a%s " \ "card%s%s%s\nwith link type %s\n", card->devno0,card->devno1,card->devno2, qeth_get_cardname(card->type, card->is_guest_lan), (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_snmp_notify(); } 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_snmp_notify(); } 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); sema_init(&card->ioctl_sem, 1); #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 */ card->csum_enable_mask=IPA_CHECKSUM_DEFAULT_ENABLE_MASK; /* 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;ino_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;joutbound_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;ioptions.inbound_buffer_count;i++) { for (j=0;jinbound_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;ioptions.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;jinbound_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;ioptions.inbound_buffer_count;i++) { for (j=0;jinbound_buffer_pool_entry[i][j]) { if (j inbound_buffer_pool_entry [i][j]); card->inbound_buffer_pool_entry [i][j]=NULL; } } } for (i=0;ino_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_snmp_notify(); 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 = (cntBUFCNT_MAX)?BUFCNT_MAX:cnt); (*memamnt) = cnt * bufsize/1024; return cnt; } 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_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_snmp_notify(); } 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_snmp_notify(); 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)); 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,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; } static int qeth_multicast6_event(struct notifier_block *this, unsigned long event, void *ptr) { qeth_card_t *card; struct ifmcaddr6 *mc = (struct ifmcaddr6 *) ptr; struct net_device *dev = mc->idev->dev; char dbf_text[15]; sprintf(dbf_text,"mc6event"); QETH_DBF_TEXT3(0,trace,dbf_text); QETH_DBF_HEX3(0,trace,&event,sizeof(unsigned long)); #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_multicast_event(struct notifier_block *this, unsigned long event, void *ptr) { qeth_card_t *card; struct ip_mc_list *mc = (struct ip_mc_list *) ptr; struct net_device *dev = mc->interface->dev; char dbf_text[15]; sprintf(dbf_text,"mc4event"); QETH_DBF_TEXT3(0,trace,dbf_text); QETH_DBF_HEX3(0,trace,&event,sizeof(unsigned long)); #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; } 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; MOD_INC_USE_COUNT; info = (tempinfo_t *) vmalloc (sizeof (tempinfo_t)); if (info == NULL) { PRINT_WARN("No memory available for data\n"); MOD_DEC_USE_COUNT; 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; MOD_DEC_USE_COUNT; 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"); } /* a '+' in the routing indicator means, that broadcast * packets are not echoed back to the sender */ #ifdef QETH_IPV6 if (atomic_read(&card->rt4fld) || atomic_read(&card->rt6fld)) strcpy(router_str, "FLD"); #else /* QETH_IPV6 */ if (atomic_read(&card->rt4fld)) strcpy(router_str, "FLD"); #endif /* QETH_IPV6 */ else if ( ((card->options.routing_type4&ROUTER_MASK)== PRIMARY_ROUTER) #ifdef QETH_IPV6 && ( ((card->options.routing_type6&ROUTER_MASK)== PRIMARY_ROUTER)|| (!qeth_is_supported(IPA_IPv6)) ) #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)|| (!qeth_is_supported(IPA_IPv6)) ) #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)|| (!qeth_is_supported(IPA_IPv6)) ) #endif /* QETH_IPV6 */ ) { if (card->broadcast_capable==BROADCAST_WITHOUT_ECHO) strcpy(router_str,"mc+"); else 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)|| (!qeth_is_supported(IPA_IPv6)) ) #endif /* QETH_IPV6 */ ) { if (card->broadcast_capable==BROADCAST_WITHOUT_ECHO) strcpy(router_str,"p+c"); else 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)|| (!qeth_is_supported(IPA_IPv6)) ) #endif /* QETH_IPV6 */ ) { if (card->broadcast_capable==BROADCAST_WITHOUT_ECHO) strcpy(router_str,"s+c"); else 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)|| (!qeth_is_supported(IPA_IPv6)) ) #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->is_guest_lan), 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->is_guest_lan), 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->is_guest_lan), 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 ( (devnamelendev_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) 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]; MOD_INC_USE_COUNT; 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; struct list_head *l,*n; struct qeth_notify_list *n_entry; if (p_info) { if (p_info->data) vfree (p_info->data); vfree (p_info); } /*remove task-entry to notify from list */ spin_lock(¬ify_lock); list_for_each_safe(l, n, ¬ify_list) { n_entry = list_entry(l, struct qeth_notify_list, list); if (n_entry->task == current) { list_del(&n_entry->list); kfree(n_entry); } } spin_unlock(¬ify_lock); MOD_DEC_USE_COUNT; 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((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; itask == p) { n_entry->signum = (int) arg; goto reg_out; } } spin_unlock(¬ify_lock); n_entry = (struct qeth_notify_list *) kmalloc(sizeof(struct qeth_notify_list),GFP_KERNEL); if (!n_entry) return -ENOMEM; n_entry->task = p; n_entry->signum = (int) arg; spin_lock(¬ify_lock); list_add(&n_entry->list,¬ify_list); reg_out: spin_unlock(¬ify_lock); return 0; } static int qeth_procfile_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg) { int result; switch (cmd) { case QETH_IOCPROC_REGISTER: if ( (arg > 0) && (arg < 32) ) result = qeth_snmp_register(current,arg); else result = -EINVAL; break; default: result = -EOPNOTSUPP; } 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; } 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 */ /*SNMP init stuff*/ spin_lock_init(¬ify_lock); INIT_LIST_HEAD(¬ify_list); 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 */