From: Martin Schwidefsky The rewritten qeth network driver. --- /dev/null |10872 ----------------------------------- 25-akpm/drivers/s390/net/Kconfig | 2 25-akpm/drivers/s390/net/Makefile | 6 25-akpm/drivers/s390/net/qeth.h | 1823 ++--- 25-akpm/drivers/s390/net/qeth_fs.h | 156 25-akpm/drivers/s390/net/qeth_main.c | 6820 +++++++++++++++++++++ 25-akpm/drivers/s390/net/qeth_mpc.c | 22 25-akpm/drivers/s390/net/qeth_mpc.h | 766 +- 25-akpm/drivers/s390/net/qeth_proc.c | 468 + 25-akpm/drivers/s390/net/qeth_sys.c | 1479 ++++ 25-akpm/include/asm-s390/qeth.h | 60 11 files changed, 10152 insertions(+), 12322 deletions(-) diff -puN drivers/s390/net/Kconfig~s390-12-12-rewritten-qeth-driver drivers/s390/net/Kconfig --- 25/drivers/s390/net/Kconfig~s390-12-12-rewritten-qeth-driver 2004-04-08 13:55:46.658930936 -0700 +++ 25-akpm/drivers/s390/net/Kconfig 2004-04-08 13:55:46.672928808 -0700 @@ -63,7 +63,7 @@ config QETH To compile this driver as a module, choose M here: the - module will be called qeth. + module will be called qeth.ko. comment "Gigabit Ethernet default settings" diff -puN drivers/s390/net/Makefile~s390-12-12-rewritten-qeth-driver drivers/s390/net/Makefile --- 25/drivers/s390/net/Makefile~s390-12-12-rewritten-qeth-driver 2004-04-08 13:55:46.659930784 -0700 +++ 25-akpm/drivers/s390/net/Makefile 2004-04-08 13:55:46.672928808 -0700 @@ -9,6 +9,6 @@ obj-$(CONFIG_NETIUCV) += netiucv.o fsm.o obj-$(CONFIG_SMSGIUCV) += smsgiucv.o obj-$(CONFIG_CTC) += ctc.o fsm.o cu3088.o obj-$(CONFIG_LCS) += lcs.o cu3088.o -qeth_mod-objs := qeth.o qeth_mpc.o -obj-$(CONFIG_QETH) += qeth_mod.o - +qeth-y := qeth_main.o qeth_mpc.o qeth_sys.o +qeth-$(CONFIG_PROC_FS) += qeth_proc.o +obj-$(CONFIG_QETH) += qeth.o diff -L drivers/s390/net/qeth.c -puN drivers/s390/net/qeth.c~s390-12-12-rewritten-qeth-driver /dev/null --- 25/drivers/s390/net/qeth.c +++ /dev/null 2003-09-15 06:40:47.000000000 -0700 @@ -1,10872 +0,0 @@ -/* - * - * linux/drivers/s390/net/qeth.c ($Revision: 1.177 $) - * - * Linux on zSeries OSA Express and HiperSockets support - * - * Copyright 2000,2003 IBM Corporation - * - * Author(s): Utz Bacher - * Cornelia Huck (2.5 integration, - * numerous bugfixes) - * Frank Pavlic (query/purge ARP, SNMP, fixes) - * Andreas Herrmann (bugfixes) - * Thomas Spatzier (bugfixes) - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2, or (at your option) - * any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. - */ - -/* - * The driver supports in general all QDIO driven network devices on the - * Hydra card. - * - * For all devices, three channels must be available to the driver. One - * channel is the read channel, one is the write channel and the third - * one is the channel used to control QDIO. - * - * There are several stages from the channel recognition to the running - * network device: - * - The channels are scanned and ordered due to the parameters (see - * MODULE_PARM_DESC) - * - The card is hardsetup: this means, that the communication channels - * are prepared - * - The card is softsetup: this means, that commands are issued - * to activate the network parameters - * - After that, data can flow through the card (transported by QDIO) - * - *IPA Takeover: - * /proc/qeth_ipa_takeover provides the possibility to add and remove - * certain ranges of IP addresses to the driver. As soon as these - * addresses have to be set by the driver, the driver uses the OSA - * Address Takeover mechanism. - * reading out of the proc-file displays the registered addresses; - * writing into it changes the information. Only one command at one - * time must be written into the file. Subsequent commands are ignored. - * The following commands are available: - * inv4 - * inv6 - * add4 /[:] - * 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. - */ - -static void volatile -qeth_eyecatcher(void) -{ - return; -} - -#undef DEBUG - -#include -#include -#include - -#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 "qeth_mpc.h" -#include "qeth.h" - -/****************** MODULE PARAMETER VARIABLES ********************/ -static int qeth_sparebufs = 0; -module_param(qeth_sparebufs, int, 0); -MODULE_PARM_DESC(qeth_sparebufs, "the number of pre-allocated spare buffers " - "reserved for low memory situations"); - -/****************** MODULE STUFF **********************************/ -#define VERSION_QETH_C "$Revision: 1.177 $" -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 ***********************************/ - -#define PROCFILE_SLEEP_SEM_MAX_VALUE 0 -#define PROCFILE_IOCTL_SEM_MAX_VALUE 3 -static struct semaphore qeth_procfile_ioctl_lock; -static struct semaphore qeth_procfile_ioctl_sem; -static struct qeth_card *firstcard = NULL; - -static struct sparebufs sparebufs[MAX_SPARE_BUFFERS]; -static int sparebuffer_count; - -static unsigned int known_devices[][10] = QETH_MODELLIST_ARRAY; - -static spinlock_t setup_lock = SPIN_LOCK_UNLOCKED; -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 struct ipato_entry *ipato_entries = NULL; -static spinlock_t ipato_list_lock = SPIN_LOCK_UNLOCKED; - -struct tempinfo{ - char *data; - int len; -}; - -/* thought I could get along without forward declarations... - * just lazyness here */ -static int qeth_reinit_thread(void *); -static inline void qeth_schedule_recovery(struct qeth_card *card); - -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 < b) - return a; - else - return b; -} - -static inline unsigned int -qeth_get_millis(void) -{ - return (int) (get_clock() >> 22); /* time>>12 is microseconds, we - divide it by 1024 */ -} - -#ifdef QETH_PERFORMANCE_STATS -static inline unsigned int -qeth_get_micros(void) -{ - return (int) (get_clock() >> 12); -} -#endif /* QETH_PERFORMANCE_STATS */ - -static void -qeth_delay_millis(unsigned long msecs) -{ - unsigned int start; - - start = qeth_get_millis(); - while (qeth_get_millis() - start < msecs) ; -} - -static void -qeth_wait_nonbusy(unsigned int timeout) -{ - unsigned int start; - char dbf_text[15]; - - sprintf(dbf_text, "wtnb%4x", timeout); - QETH_DBF_TEXT3(0, trace, dbf_text); - - start = qeth_get_millis(); - for (;;) { - set_task_state(current, TASK_INTERRUPTIBLE); - if (qeth_get_millis() - start > timeout) { - goto out; - } - schedule_timeout(((start + timeout - - qeth_get_millis()) >> 10) * HZ); - } -out: - set_task_state(current, TASK_RUNNING); -} - -static void -qeth_get_mac_for_ipm(__u32 ipm, char *mac, struct net_device *dev) -{ - if (dev->type == ARPHRD_IEEE802_TR) - ip_tr_mc_map(ipm, mac); - else - ip_eth_mc_map(ipm, mac); -} - -#define atomic_swap(a,b) xchg((int*)a.counter,b) - -static int inline -my_spin_lock_nonbusy(struct qeth_card *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); - } -} - -static int inline -my_down_trylock_nonbusy(struct qeth_card *card, struct semaphore *sema) -{ - for (;;) { - if (card) { - if (atomic_read(&card->shutdown_phase)) - return -1; - } - if (down_trylock(sema)) - return 0; - qeth_wait_nonbusy(QETH_IDLE_WAIT_TIME); - } -} - - -#ifdef CONFIG_ARCH_S390X -#define QETH_GET_ADDR(x) ((__u32)(unsigned long)x) -#else /* CONFIG_ARCH_S390X */ -#define QETH_GET_ADDR(x) ((__u32)x) -#endif /* CONFIG_ARCH_S390X */ - -static int -qeth_does_card_exist(struct qeth_card *card) -{ - struct qeth_card *c = firstcard; - int rc = 0; - - read_lock(&list_lock); - while (c) { - if (c == card) { - rc = 1; - break; - } - c = c->next; - } - read_unlock(&list_lock); - return rc; -} - -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 struct qeth_card * -qeth_get_card_by_name(char *name) -{ - struct qeth_card *card; - - read_lock(&list_lock); - card = firstcard; - while (card) { - if (!strncmp(name, card->dev_name, DEV_NAME_LEN)) - break; - card = card->next; - } - 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) -{ - struct ipato_entry *entry, *e; - int len = (version == 4) ? 4 : 16; - - entry = - (struct ipato_entry *) kmalloc(sizeof (struct ipato_entry), - 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; - - 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: - spin_unlock(&ipato_list_lock); -} - -static void -qeth_del_ipato_entry(int version, __u8 * addr, int mask_bits, char *dev_name) -{ - struct ipato_entry *e, *e_before; - int len = (version == 4) ? 4 : 16; - int found = 0; - - 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"); - spin_unlock(&ipato_list_lock); -} - -static void -qeth_convert_addr_to_bits(__u8 * addr, char *bits, int len) -{ - int i, j; - __u8 octet; - - for (i = 0; i < len; i++) { - octet = addr[i]; - for (j = 7; j >= 0; j--) { - bits[i * 8 + j] = (octet & 1) ? 1 : 0; - octet >>= 1; - } - } -} - -static int -qeth_is_ipa_covered_by_ipato_entries(int version, __u8 * addr, - struct qeth_card *card) -{ - char *memarea, *addr_bits, *entry_bits; - int len = (version == 4) ? 4 : 16; - int invert = (version == 4) ? ipato_inv4 : ipato_inv6; - int result = 0; - struct ipato_entry *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(struct qeth_card *card) -{ - if (card) { - card->dev->flags |= IFF_RUNNING; - } -} - -static void -qeth_set_dev_flag_norunning(struct qeth_card *card) -{ - if (card) { - card->dev->flags &= ~IFF_RUNNING; - } -} - -static void -qeth_restore_dev_flag_state(struct qeth_card *card) -{ - if (card) { - if (card->saved_dev_flags & IFF_RUNNING) - card->dev->flags |= IFF_RUNNING; - else - card->dev->flags &= ~IFF_RUNNING; - } -} - -static void -qeth_save_dev_flag_state(struct qeth_card *card) -{ - if (card) { - card->saved_dev_flags = card->dev->flags & IFF_RUNNING; - } -} - -static int -qeth_open(struct net_device *dev) -{ - struct qeth_card *card; - - card = (struct qeth_card *) dev->priv; - QETH_DBF_CARD2(0, trace, "open", card); - QETH_DBF_CARD2(0, setup, "open", card); - - qeth_save_dev_flag_state(card); - - netif_start_queue(dev); - atomic_set(&((struct qeth_card *) dev->priv)->is_open, 1); - - return 0; -} - -static int -qeth_set_config(struct net_device *dev, struct ifmap *map) -{ - struct qeth_card *card; - - card = (struct qeth_card *)dev->priv; - QETH_DBF_CARD3(0, trace, "nscf", card); - - return -EOPNOTSUPP; -} - -static int -qeth_is_multicast_skb_at_all(struct sk_buff *skb, int version) -{ - int i; - struct qeth_card *card; - - i = RTN_UNSPEC; - card = (struct qeth_card *)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(struct qeth_card *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->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(struct qeth_card *card) -{ - QETH_DBF_CARD5(0, trace, "wkup", card); - - atomic_set(&card->data_has_arrived, 1); - wake_up(&card->wait_q); -} - -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(struct ccw_device *cdev, unsigned char *buffer, - int dstat, int cstat, int rqparam, - char *irb, char *sense) -{ - char dbf_text[15]; - int problem = 0; - struct qeth_card *card; - - card = CARD_FROM_CDEV(cdev); - - if (atomic_read(&card->shutdown_phase)) - return 0; - if (dstat & DEV_STAT_UNIT_CHECK) { - if (sense[SENSE_RESETTING_EVENT_BYTE] & - SENSE_RESETTING_EVENT_FLAG) { - QETH_DBF_CARD1(0, trace, "REVN", card); - problem = PROBLEM_RESETTING_EVENT_INDICATOR; - goto out; - } - if (sense[SENSE_COMMAND_REJECT_BYTE] & - SENSE_COMMAND_REJECT_FLAG) { - QETH_DBF_CARD1(0, trace, "CREJ", card); - problem = PROBLEM_COMMAND_REJECT; - goto out; - } - if ((sense[2] == 0xaf) && (sense[3] == 0xfe)) { - QETH_DBF_CARD1(0, trace, "AFFE", card); - problem = PROBLEM_AFFE; - goto out; - } - if ((!sense[0]) && (!sense[1]) && (!sense[2]) && (!sense[3])) { - QETH_DBF_CARD1(0, trace, "ZSNS", card); - problem = PROBLEM_ZERO_SENSE_DATA; - goto out; - } - QETH_DBF_CARD1(0, trace, "GCHK", card); - 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)) { - QETH_DBF_TEXT1(0, trace, "GCHK"); - QETH_DBF_TEXT1(0, trace, cdev->dev.bus_id); - QETH_DBF_HEX1(0, misc, irb, __max(QETH_DBF_MISC_LEN, 64)); - PRINT_WARN("check on device %s, dstat=x%x, cstat=x%x, " - "rqparam=x%x\n", - cdev->dev.bus_id, 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 device %s " - "with cause code 0x%02x%s\n", - CARD_BUS_ID(card), buffer[4], - (buffer[4] == - 0x22) ? " -- try another portname" : ""); - QETH_DBF_CARD1(0, trace, "RTRM", card); - 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); - QETH_DBF_CARD1(0, trace, "CBOT", card); - qeth_set_dev_flag_norunning(card); - problem = 0; - goto out; - } - 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) - QETH_DBF_CARD3(0, trace, "irla", card); - if (*(PDU_ENCAPSULATION(buffer)) == - IPA_CMD_UNREGISTER_LOCAL_ADDR) - QETH_DBF_CARD3(0, trace, "irla", card); - 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)); - QETH_DBF_CARD1(0, trace, "INRP", card); - goto out; - } - /* no probs */ -out: - if (problem) { - QETH_DBF_CARD3(0, trace, "gcpr", card); - 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(struct qeth_card *card) -{ - int result, result2; - char dbf_text[15]; - - QETH_DBF_CARD5(0, trace, "isnr", card); - - /* set up next read ccw */ - memcpy(&card->dma_stuff->read_ccw, READ_CCW, sizeof (struct ccw1)); - 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 spin_lock_irqsave(get_ccwdev_lock(CARD_RDEV(card)),flags), as - * we are only called in the interrupt handler - */ - result = ccw_device_start(CARD_RDEV(card), &card->dma_stuff->read_ccw, - MPC_SETUP_STATE, 0, 0); - if (result) { - qeth_delay_millis(QETH_WAIT_BEFORE_2ND_DOIO); - result2 = - ccw_device_start(CARD_RDEV(card), &card->dma_stuff->read_ccw, - MPC_SETUP_STATE, 0, 0); - PRINT_WARN("read handler on device %s, read: ccw_device_start " - "returned %i, next try returns %i\n", - CARD_BUS_ID(card), result, result2); - QETH_DBF_CARD1(0, trace, "IsNR", card); - sprintf(dbf_text, "%04x%04x", (__s16) result, (__s16) result2); - QETH_DBF_TEXT1(0, trace, dbf_text); - } -} - -static int -qeth_is_to_recover(struct qeth_card *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_get_spare_buf(void) -{ - int i = 0; - char dbf_text[15]; - - while (i < sparebuffer_count) { - if (!atomic_compare_and_swap(SPAREBUF_FREE, SPAREBUF_USED, - &sparebufs[i].status)) { - sprintf(dbf_text, "gtspb%3x", i); - QETH_DBF_TEXT4(0, trace, dbf_text); - return i; - } - i++; - } - QETH_DBF_TEXT3(0, trace, "nospbuf"); - - return -1; -} - -static void -qeth_put_spare_buf(int no) -{ - char dbf_text[15]; - - sprintf(dbf_text, "ptspb%3x", no); - QETH_DBF_TEXT4(0, trace, dbf_text); - atomic_set(&sparebufs[no].status, SPAREBUF_FREE); -} - -static inline void -qeth_put_buffer_pool_entry(struct qeth_card *card, int entry_no) -{ - if (entry_no & SPAREBUF_MASK) - qeth_put_spare_buf(entry_no & (~SPAREBUF_MASK)); - else - card->inbound_buffer_pool_entry_used[entry_no] = BUFFER_UNUSED; -} - -static inline int -qeth_get_empty_buffer_pool_entry(struct qeth_card *card) -{ - int i; - int max_buffers = card->options.inbound_buffer_count; - - for (i = 0; i < max_buffers; i++) { - if (xchg((int *) &card->inbound_buffer_pool_entry_used[i], - BUFFER_USED) == BUFFER_UNUSED) - return i; - } - return -1; -} - -static inline void -qeth_clear_input_buffer(struct qeth_card *card, int bufno) -{ - struct qdio_buffer *buffer; - int i; - int elements, el_m_1; - char dbf_text[15]; - - QETH_DBF_CARD6(0, trace, "clib", card); - sprintf(dbf_text, "bufno%3x", bufno); - QETH_DBF_TEXT6(0, trace, dbf_text); - - buffer = &card->inbound_qdio_buffers[bufno]; - elements = BUFFER_MAX_ELEMENTS; - el_m_1 = elements - 1; - - for (i = 0; i < elements; i++) { - if (i == el_m_1) - buffer->element[i].flags = SBAL_FLAGS_LAST_ENTRY; - else - buffer->element[i].flags = 0; - - buffer->element[i].length = PAGE_SIZE; - buffer->element[i].addr = INBOUND_BUFFER_POS(card, bufno, i); - } -} - -static void -qeth_queue_input_buffer(struct qeth_card *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; - - QETH_DBF_CARD5(0, trace, "qibf", card); - sprintf(dbf_text, "%4x%4x", under_int, bufno); - QETH_DBF_TEXT5(0, trace, dbf_text); - atomic_inc(&card->requeue_counter); - if (atomic_read(&card->requeue_counter) <= QETH_REQUEUE_THRESHOLD) - return; - - if (!spin_trylock(&card->requeue_input_lock)) { - QETH_DBF_CARD5(0, trace, "qibl", card); - return; - } - requeue_counter = atomic_read(&card->requeue_counter); - pos = atomic_read(&card->requeue_position); - - start = pos; - /* - * omit the situation with 128 simultaneously - * enqueued buffers, as then we can't benefit from PCI - * avoidance anymore -- therefore we let count not grow as - * big as requeue_counter - */ - while ((!atomic_read(&card->inbound_buffer_refcnt[pos])) && - (count < requeue_counter - 1)) { - no = qeth_get_empty_buffer_pool_entry(card); - if (no == -1) { - if (count) - break; - no = qeth_get_spare_buf(); - if (no == -1) { - PRINT_ERR("%s: no more input buffers " - "available! Inbound traffic could " - "be lost! Try increasing the bufcnt " - "parameter\n", - card->dev_name); - QETH_DBF_CARD2(1, trace, "QINB", card); - 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; - - QETH_DBF_CARD3(0, trace, "qibi", card); - 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); - - if (wrapped) { - cnt1 = QDIO_MAX_BUFFERS_PER_Q - start; - cnt2 = stop; - } else { - cnt1 = count; - /* cnt2 is already set to 0 */ - } - - atomic_sub(count, &card->requeue_counter); - /* - * this is the only place where card->requeue_position is - * written to, so that's ok (as it is in a lock) - */ - atomic_set(&card->requeue_position, - (atomic_read(&card->requeue_position) + count) - & (QDIO_MAX_BUFFERS_PER_Q - 1)); - - if (cnt1) { - for (i = start; i < start + cnt1; i++) { - qeth_clear_input_buffer(card, i); - } - result = do_QDIO(CARD_DDEV(card), - QDIO_FLAG_SYNC_INPUT | under_int, - 0, start, cnt1, NULL); - if (result) { - PRINT_WARN("qeth_queue_input_buffer's " - "do_QDIO returnd %i (device %s)\n", - result, CARD_DDEV_ID(card)); - QETH_DBF_CARD1(0, trace, "QIDQ", card); - sprintf(dbf_text, "%4x%4x", result, requeue_counter); - QETH_DBF_TEXT1(0, trace, dbf_text); - sprintf(dbf_text, "%4x%4x", start, cnt1); - QETH_DBF_TEXT1(1, trace, dbf_text); - } - } - if (cnt2) { - for (i = 0; i < cnt2; i++) { - qeth_clear_input_buffer(card, i); - } - result = do_QDIO(CARD_DDEV(card), - QDIO_FLAG_SYNC_INPUT | under_int, 0, - 0, cnt2, NULL); - if (result) { - PRINT_WARN("qeth_queue_input_buffer's " - "do_QDIO returnd %i (device %s)\n", - result, CARD_DDEV_ID(card)); - QETH_DBF_CARD1(0, trace, "QIDQ", card); - 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: - 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(struct qeth_card *card, - int *element_ptr, int *pos_in_el_ptr, - void **hdr_ptr, struct qdio_buffer *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("device %s: error in interpreting buffer (data " - "too long), %i elements.\n", - CARD_BUS_ID(card), element); - QETH_DBF_CARD0(0, trace, "IEDL", card); - 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("device %s: bad element length in element %i: " - "0x%x\n", CARD_BUS_ID(card), element, curr_len); - QETH_DBF_CARD0(0, trace, "BELN", card); - sprintf(dbf_text, "%4x", curr_len); - QETH_DBF_TEXT0(0, trace, dbf_text); - sprintf(dbf_text, "%4x%4x", *element_ptr, *pos_in_el_ptr); - QETH_DBF_TEXT0(1, trace, dbf_text); - QETH_DBF_HEX0(0, misc, buffer, QETH_DBF_MISC_LEN); - QETH_DBF_HEX0(0, misc, buffer + QETH_DBF_MISC_LEN, - QETH_DBF_MISC_LEN); - return NULL; - } - /* header fits in current element? */ - if (curr_len < pos_in_el + QETH_HEADER_SIZE) { - if (!pos_in_el) { - QETH_DBF_CARD6(0, trace, "gnmh", card); - return NULL; /* no more data in buffer */ - } - /* set hdr to next element */ - element++; - pos_in_el = 0; - curr_len = SBALE_LEN(element); - /* does it fit in there? */ - if (curr_len < QETH_HEADER_SIZE) { - QETH_DBF_CARD6(0, trace, "gdnf", card); - return NULL; - } - } - - *hdr_ptr = SBALE_ADDR(element) + pos_in_el; - - length = *(__u16 *) ((char *) (*hdr_ptr) + QETH_HEADER_LEN_POS); - - QETH_DBF_CARD6(0, trace, "gdHd", card); - QETH_DBF_HEX6(0, trace, hdr_ptr, sizeof (void *)); - - 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("device %s: inb. buffer with more headers " - "than data areas (%i elements).\n", - CARD_BUS_ID(card), element); - QETH_DBF_CARD0(0, trace, "IEMH", card); - 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; - } - - QETH_DBF_HEX6(0, trace, &data_ptr, sizeof (void *)); - QETH_DBF_HEX6(0, trace, &skb, sizeof (void *)); - - len_togo = length; - while (1) { - step = qeth_min(len_togo, curr_len - pos_in_el); - if (!step) { - PRINT_WARN("device %s: unexpected end of buffer, " - "length of element %i is 0. Discarding " - "packet.\n", - CARD_BUS_ID(card), element); - QETH_DBF_CARD0(0, trace, "IEUE", card); - 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; - } - 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; - } - } - - sprintf(dbf_text, "%4x%4x", element, pos_in_el); - QETH_DBF_TEXT6(0, trace, dbf_text); - - *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); - } - QETH_DBF_CARD0(0, trace, "NOMM", card); - return NULL; -} - -static inline void -__qeth_rebuild_skb_fake_ll(struct qeth_card *card, struct sk_buff *skb, - void *hdr_ptr) -{ - skb->mac.raw = skb->data - QETH_FAKE_LL_LEN; - switch (skb->pkt_type) { - case 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); - } - break; - case PACKET_BROADCAST: - memset(skb->mac.raw + QETH_FAKE_LL_DEST_MAC_POS, - 0xff, QETH_FAKE_LL_ADDR_LEN); - break; - default: - 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); - -} - -static inline void -__qeth_rebuild_skb_vlan(struct qeth_card *card, struct sk_buff *skb, - void *hdr_ptr) -{ -#ifdef QETH_VLAN - __u16 *vlan_tag; - - 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 -} - -static inline void -__qeth_rebuild_skb(struct qeth_card *card, struct sk_buff *skb, void *hdr_ptr) -{ - char dbf_text[15]; - int version; - unsigned short cast_type; - - 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); - switch (cast_type) { - case QETH_CAST_UNICAST: - skb->pkt_type = PACKET_HOST; - break; - case QETH_CAST_MULTICAST: - skb->pkt_type = PACKET_MULTICAST; - break; - case QETH_CAST_BROADCAST: - skb->pkt_type = PACKET_BROADCAST; - break; - case QETH_CAST_ANYCAST: - case QETH_CAST_NOCAST: - QETH_DBF_CARD2(0, trace, "ribf", card); - sprintf(dbf_text, "castan%2x", cast_type); - QETH_DBF_TEXT2(1, trace, dbf_text); - skb->pkt_type = PACKET_HOST; - break; - default: - PRINT_WARN("adapter is using an unknown casting value " - "of 0x%x. Using unicasting instead.\n", - cast_type); - skb->pkt_type = PACKET_HOST; - QETH_DBF_CARD2(0, trace, "ribf", card); - sprintf(dbf_text, "castun%2x", cast_type); - QETH_DBF_TEXT2(1, trace, dbf_text); - } - - if (card->options.fake_ll == FAKE_LL) - __qeth_rebuild_skb_fake_ll(card, skb, hdr_ptr); - else - skb->mac.raw = skb->data; - - skb->ip_summed = card->options.checksum_type; - if (card->options.checksum_type == HW_CHECKSUMMING) { - /* do we have a checksummed packet? */ - - /* - * we only check for TCP/UDP checksums when the pseudo - * header was also checked successfully -- 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)) { -#if 0 - /* 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]); - } -#endif /* 0 */ - skb->ip_summed=CHECKSUM_UNNECESSARY; - } else { - /* make the stack check it */ - skb->ip_summed = SW_CHECKSUMMING; - } - } else - skb->ip_summed=card->options.checksum_type; - - __qeth_rebuild_skb_vlan(card, skb, hdr_ptr); -} - -static void -qeth_read_in_buffer(struct qeth_card *card, int buffer_no) -{ - struct sk_buff *skb; - void *hdr_ptr; - int element = 0, pos_in_el = 0; - struct qdio_buffer *buffer; - 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 device %s: incoming SBALF 15 on buffer " - "0x%x are 0x%x\n", - CARD_BUS_ID(card), buffer_no, - buffer->element[15].flags & 0xff); - sprintf(dbf_text, "SF%s%2x%2x", - CARD_BUS_ID(card), buffer_no, - buffer->element[15].flags & 0xff); - QETH_DBF_HEX1(1, trace, dbf_text, QETH_DBF_TRACE_LEN); - } - - for (i = 0; i < max_elements - 1; i++) { - if (buffer->element[i].flags & SBAL_FLAGS_LAST_ENTRY) { - buffer->element[i + 1].length = 0; - break; - } - } -#ifdef QETH_PERFORMANCE_STATS - card->perf_stats.bufs_rec++; -#endif /* QETH_PERFORMANCE_STATS */ - - sprintf(dbf_text, "ribX%s", CARD_BUS_ID(card)); - dbf_text[3] = buffer_no; - QETH_DBF_HEX6(0, trace, dbf_text, QETH_DBF_TRACE_LEN); - - 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 */ - __qeth_rebuild_skb(card, skb, hdr_ptr); - -#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 */ - - QETH_DBF_CARD6(0, trace, "rxpk", card); - - netif_rx(skb); - dev->last_rx = jiffies; - card->stats->rx_packets++; - card->stats->rx_bytes += skb->len; - } else { - PRINT_WARN("%s: dropped packet, no buffers " - "available.\n", card->dev_name); - QETH_DBF_CARD2(1, trace, "DROP", card); - 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_add_vlan(struct qeth_hdr *hdr, struct sk_buff *skb, - int version) -{ -#ifdef QETH_VLAN - struct qeth_card *card; - - /* - * before we're going to overwrite this location with next hop ip. - * v6 uses passthrough, v4 sets the tag in the QDIO header. - */ - card = (struct qeth_card *) skb->dev->priv; - if ((card->vlangrp != NULL) && vlan_tx_tag_present(skb)) { - hdr->ext_flags = (version == 4) ? QETH_EXT_HEADER_VLAN_FRAME : - QETH_EXT_HEADER_INCLUDE_VLAN_TAG; - hdr->vlan_id = vlan_tx_tag_get(skb); - } -#endif -} - -static inline __u8 -__qeth_get_flags_v4(int multicast) -{ - if (multicast == RTN_MULTICAST) - return QETH_CAST_MULTICAST; - if (multicast == RTN_BROADCAST) - return QETH_CAST_BROADCAST; - return QETH_CAST_UNICAST; -} - -static inline __u8 -__qeth_get_flags_v6(int multicast) -{ - if (multicast == RTN_MULTICAST) - return QETH_CAST_MULTICAST | - QETH_HEADER_PASSTHRU | QETH_HEADER_IPV6; - if (multicast == RTN_ANYCAST) - return QETH_CAST_ANYCAST | - QETH_HEADER_PASSTHRU | QETH_HEADER_IPV6; - if (multicast == RTN_BROADCAST) - return QETH_CAST_BROADCAST | - QETH_HEADER_PASSTHRU | QETH_HEADER_IPV6; - return QETH_CAST_UNICAST | -#ifdef QETH_IPV6 - QETH_HEADER_PASSTHRU | -#endif /* QETH_IPV6 */ - QETH_HEADER_IPV6; -} - -static inline void -qeth_fill_header(struct qeth_hdr *hdr, struct sk_buff *skb, - int version, int multicast) -{ - char dbf_text[15]; - - hdr->id = 1; - hdr->ext_flags = 0; - - __qeth_fill_header_add_vlan(hdr, skb, version); - - 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 */ - hdr->flags = __qeth_get_flags_v4(multicast); - *((__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 */ - hdr->flags = __qeth_get_flags_v6(multicast); - if ((skb->dst) && (skb->dst->neighbour)) { - memcpy(hdr->dest_addr, - skb->dst->neighbour->primary_key, 16); - } else { - /* fill in destination address used in ip header */ - memcpy(hdr->dest_addr, &skb->nh.ipv6h->daddr, 16); - } - } else { /* passthrough */ - if (!memcmp(skb->data + QETH_HEADER_SIZE, - skb->dev->broadcast, 6)) { /* broadcast? */ - hdr->flags = QETH_CAST_BROADCAST | QETH_HEADER_PASSTHRU; - } else { - hdr->flags = (multicast == RTN_MULTICAST) ? - QETH_CAST_MULTICAST | QETH_HEADER_PASSTHRU : - QETH_CAST_UNICAST | QETH_HEADER_PASSTHRU; - } - } - 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)); -} - -static inline int -qeth_fill_buffer(struct qdio_buffer *buffer, char *dataptr, - int length, int element) -{ - int length_here; - int first_lap = 1; - char dbf_text[15]; - int first_element = element; - - while (length > 0) { - /* length_here is the remaining amount of data in this page */ - length_here = - PAGE_SIZE - ((unsigned long) dataptr & (PAGE_SIZE - 1)); - if (length < length_here) - length_here = length; - - buffer->element[element].addr = dataptr; - buffer->element[element].length = length_here; - length -= length_here; - if (!length) { - if (first_lap) { - buffer->element[element].flags = 0; - } else { - buffer->element[element].flags = - SBAL_FLAGS_LAST_FRAG; - } - } else { - if (first_lap) { - buffer->element[element].flags = - SBAL_FLAGS_FIRST_FRAG; - } else { - buffer->element[element].flags = - SBAL_FLAGS_MIDDLE_FRAG; - } - } - dataptr = dataptr + length_here; - element++; - if (element > QDIO_MAX_ELEMENTS_PER_BUFFER) { - PRINT_ERR("qeth_fill_buffer: IP packet too big!\n"); - QETH_DBF_TEXT1(0, trace, "IPpktobg"); - QETH_DBF_HEX1(1, trace, &dataptr, sizeof (void *)); - buffer->element[first_element].length = 0; - break; - } - first_lap = 0; - } - 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); - - return element; -} - -static inline void -qeth_flush_packed_packets(struct qeth_card *card, int queue, int under_int) -{ - struct qdio_buffer *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]); - - QETH_DBF_CARD5(0, trace, "flsp", card); - 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); - - /* - * we always set the outbound pci flag, don't care, whether the - * adapter honors it or not - */ - switch (card->send_state[queue]) { - case SEND_STATE_DONT_PACK: - if (atomic_read(&card->outbound_used_buffers[queue]) - < HIGH_WATERMARK_PACK - WATERMARK_FUZZ) - break; - /* set the PCI bit */ - card->outbound_ringbuffer[queue]-> - buffer[position_for_do_qdio].element[0].flags |= 0x40; - atomic_set(&card->last_pci_pos[queue], position_for_do_qdio); - break; - case SEND_STATE_PACK: - last_pci = atomic_read(&card->last_pci_pos[queue]); - if (position_for_do_qdio < last_pci) - last_pci -= QDIO_MAX_BUFFERS_PER_Q; - /* so: - * last_pci is the position of the last pci we've set - * position_for_do_qdio is the position we will send out now - * outbound_used_buffers is the number of buffers used (means - * all buffers hydra has, inclusive position_for_do_qdio) - * - * we have to request a pci, if we have got the buffer of the - * last_pci position back. - * - * position_for_do_qdio-outbound_used_buffers is the newest - * buffer that we got back from hydra - * - * if this is greater or equal than the last_pci position, - * we should request a pci, as no pci request is - * outstanding anymore - */ - if (position_for_do_qdio - - atomic_read(&card->outbound_used_buffers[queue]) >= - last_pci) { - /* set the PCI bit */ - card->outbound_ringbuffer[queue]-> - buffer[position_for_do_qdio]. - element[0].flags |= 0x40; - atomic_set(&card->last_pci_pos[queue], - position_for_do_qdio); - } - } - - /* - * this has to be at the end, otherwise a buffer could be flushed - * twice (see comment in qeth_do_send_packet) - */ - result = do_QDIO(CARD_DDEV(card), QDIO_FLAG_SYNC_OUTPUT | under_int, queue, - position_for_do_qdio, 1, NULL); - - if (result) { - PRINT_WARN("Outbound do_QDIO returned %i " - "(device %s)\n", result, CARD_DDEV_ID(card)); - QETH_DBF_CARD5(0, trace, "FLSP", card); - 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(struct qeth_card *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]; - struct qdio_buffer *buffer; - - switch (card->outbound_buffer_send_state[queue][bufno]) { - case SEND_STATE_DONT_PACK: /* fallthrough */ - case SEND_STATE_PACK: - QETH_DBF_CARD5(0, trace, "frbf", card); - sprintf(dbf_text, "%2x%2x%4x", queue, bufno, - card->outbound_buffer_send_state[queue][bufno]); - QETH_DBF_TEXT5(0, trace, dbf_text); - - 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%s%2x", - CARD_BUS_ID(card), 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 device %s (%s); " - "errs: siga: x%x, qdio: x%x, flags15: " - "x%x. The device will be taken down.\n", - queue, CARD_BUS_ID(card), 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%s%2x", - CARD_BUS_ID(card), 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 */ - QETH_DBF_CARD3(1, trace, "Fail", card); - 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!!!"); - atomic_dec(&skb->users); - /* 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"); - 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]); - QETH_DBF_CARD0(1, trace, "UPSf", card); - QETH_DBF_CARD0(1, qerr, "UPSf", card); - 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(struct qeth_card *card) -{ - int q, b; - - for (q = 0; q < card->no_queues; q++) - for (b = 0; b < QDIO_MAX_BUFFERS_PER_Q; b++) - if (card->outbound_buffer_send_state[q][b] != - SEND_STATE_INACTIVE) - qeth_free_buffer(card, q, b, 0, 0); -} - -static inline void -qeth_flush_buffer(struct qeth_card *card, int queue, int under_int) -{ - char dbf_text[15]; - QETH_DBF_CARD5(0, trace, "flsb", card); - 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); - - 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_add_vlan_tag(struct qeth_card *card, struct sk_buff *skb, int version) -{ -#ifdef QETH_VLAN - if ((card->vlangrp != NULL) && - vlan_tx_tag_present(skb) && (version == 6)) { - qeth_insert_ipv6_vlan_tag(skb); - } -#endif -} - -static inline void -qeth_send_packet_fast(struct qeth_card *card, struct sk_buff *skb, - struct net_device *dev, - int queue, int version, int multicast) -{ - struct qeth_ringbuffer_element *mybuffer; - int position; - struct qeth_hdr *hdr; - char *dataptr; - char dbf_text[15]; - struct sk_buff *nskb; - - position = card->outbound_first_free_buffer[queue]; - - card->outbound_buffer_send_state[queue][position] = - SEND_STATE_DONT_PACK; - - mybuffer = &card->outbound_ringbuffer[queue]->ringbuf_element[position]; - if (skb_headroom(skb) < QETH_HEADER_SIZE) { - if ((version) && (!card->realloc_message)) { - card->realloc_message = 1; - PRINT_WARN("%s: not enough headroom in skb. " - "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)); - QETH_DBF_CARD3(0, trace, "NHRf", card); - 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); - QETH_DBF_CARD2(0, trace, "CNRf", card); - dev_kfree_skb_irq(skb); - return; - } - dev_kfree_skb_irq(skb); - skb = nskb; - } - __qeth_add_vlan_tag(card, skb, version); - hdr = (struct qeth_hdr *) (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); - QETH_DBF_CARD1(0, trace, "PMAf", card); - 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(struct qeth_card *card, struct sk_buff *skb, - struct net_device *dev, - int queue, int version, int multicast) -{ - struct qeth_ringbuffer_element *mybuffer; - int elements_needed; - int element_to_fill; - int buffer_no; - int length; - char *dataptr; - struct qeth_hdr *hdr; - char dbf_text[15]; - struct sk_buff *nskb; - - /* sanity check, dev->hard_header_len should prevent this */ - if (skb_headroom(skb) < QETH_HEADER_SIZE) { - if ((version) && (!card->realloc_message)) { - card->realloc_message = 1; - PRINT_WARN("%s: not enough headroom in skb. " - "Try increasing the " - "add_hhlen parameter by %i.\n", - card->dev_name, - QETH_HEADER_SIZE - skb_headroom(skb)); - } - PRINT_STUPID("%s: not enough headroom in skb (missing: %i)\n", - card->dev_name, - QETH_HEADER_SIZE - skb_headroom(skb)); - QETH_DBF_CARD3(0, trace, "NHRp", card); - 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); - QETH_DBF_CARD2(0, trace, "CNRp", card); - dev_kfree_skb_irq(skb); - return; - } - dev_kfree_skb_irq(skb); - skb = nskb; - } - __qeth_add_vlan_tag(card, skb, version); - hdr = (struct qeth_hdr *) (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); - QETH_DBF_CARD1(0, trace, "PMAp", card); - sprintf(dbf_text, "%2x%2x%2x%2x", skb_headroom(skb), - version, multicast, queue); - QETH_DBF_TEXT1(0, trace, dbf_text); - QETH_DBF_HEX1(0, trace, &skb->head, sizeof (void *)); - QETH_DBF_HEX1(1, trace, &skb->data, sizeof (void *)); - dev_kfree_skb_irq(skb); - return; - } - - buffer_no = card->outbound_first_free_buffer[queue]; - - element_to_fill = card->outbound_ringbuffer[queue]-> - ringbuf_element[buffer_no].next_element_to_fill; - - elements_needed = 1 + (((((unsigned long) dataptr) & (PAGE_SIZE - 1)) + - length) >> PAGE_SHIFT); - if ((elements_needed > (QDIO_MAX_ELEMENTS_PER_BUFFER - element_to_fill)) - || - ((elements_needed == - (QDIO_MAX_ELEMENTS_PER_BUFFER - element_to_fill)) - && ((element_to_fill >> PAGE_SHIFT) == - card->outbound_bytes_in_buffer[queue]))) { - qeth_flush_packed_packets(card, queue, 0); - element_to_fill = 0; - card->outbound_bytes_in_buffer[queue] = 0; - buffer_no = (buffer_no + 1) & (QDIO_MAX_BUFFERS_PER_Q - 1); - } - - if (!element_to_fill) - card->outbound_buffer_send_state[queue][buffer_no] - = SEND_STATE_PACK; - -#ifdef QETH_PERFORMANCE_STATS - card->perf_stats.skbs_sent_pack++; -#endif /* QETH_PERFORMANCE_STATS */ - - mybuffer = - &card->outbound_ringbuffer[queue]->ringbuf_element[buffer_no]; - atomic_inc(&skb->users); - skb_queue_tail(&mybuffer->skb_list, skb); - qeth_fill_header(hdr, skb, version, multicast); - card->outbound_bytes_in_buffer[queue] += length + QETH_HEADER_SIZE; - card->outbound_ringbuffer[queue]->ringbuf_element[buffer_no]. - next_element_to_fill = - qeth_fill_buffer(&card->outbound_ringbuffer[queue]-> - buffer[buffer_no], - dataptr, length, element_to_fill); -} - -static void -qeth_alloc_spare_bufs(void) -{ - int i; - int dont_alloc_more = 0; - char dbf_text[15]; - - sparebuffer_count = 0; - for (i = 0; i < qeth_sparebufs; i++) { - if (!dont_alloc_more) { - sparebufs[i].buf = (char *) - kmalloc(DEFAULT_BUFFER_SIZE, GFP_KERNEL); - if (sparebufs[i].buf) - sparebuffer_count++; - else - dont_alloc_more = 1; - } - atomic_set(&sparebufs[i].status, (dont_alloc_more) ? - SPAREBUF_UNAVAIL : SPAREBUF_FREE); - } - sprintf(dbf_text, "alspb%3x", sparebuffer_count); - QETH_DBF_TEXT2(0, trace, dbf_text); - - PRINT_INFO("allocated %i spare buffers\n", sparebuffer_count); -} - -static void -qeth_free_all_spare_bufs(void) -{ - int i; - - QETH_DBF_TEXT2(0, trace, "frealspb"); - - for (i = 0; i < qeth_sparebufs; i++) - if (atomic_read(&sparebufs[i].status) != SPAREBUF_UNAVAIL) { - kfree(sparebufs[i].buf); - atomic_set(&sparebufs[i].status, SPAREBUF_UNAVAIL); - } -} - -static inline void -__qeth_dump_packet_info(struct qeth_card *card, int version, int multicast, - int queue) -{ - char dbf_text[15]; - - QETH_DBF_CARD6(0, trace, "dsp:", card); - 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"); - } -} - -static inline void -__qeth_switch_state_if_needed(struct qeth_card *card, int queue) -{ - if (atomic_read(&card->outbound_used_buffers[queue]) - >= HIGH_WATERMARK_PACK) { - card->send_state[queue] = SEND_STATE_PACK; - QETH_DBF_CARD3(0, trace, "stchup", card); -#ifdef QETH_PERFORMANCE_STATS - card->perf_stats.sc_dp_p++; -#endif /* QETH_PERFORMANCE_STATS */ - } -} - -static inline int -qeth_do_send_packet(struct qeth_card *card, struct sk_buff *skb, - struct net_device *dev) -{ - int queue, result = 0; - int multicast, version; - - version = QETH_IP_VERSION(skb); - multicast = qeth_is_multicast_skb_at_all(skb, version); - queue = qeth_get_prioqueue(card, skb, multicast, version); - - __qeth_dump_packet_info(card, version, multicast, queue); - - if (atomic_read(&card->outbound_used_buffers[queue]) - >= QDIO_MAX_BUFFERS_PER_Q - 1) { - QETH_DBF_CARD2(1, trace, "cdbs", card); - 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])) { - QETH_DBF_CARD2(0, trace, "SPIN", card); - while (atomic_compare_and_swap - (QETH_LOCK_UNLOCKED, QETH_LOCK_NORMAL, - &card->outbound_ringbuffer_lock[queue])) ; - QETH_DBF_CARD2(0, trace, "spin", card); - } -#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); - __qeth_switch_state_if_needed(card, queue); - break; - case SEND_STATE_PACK: - qeth_send_packet_packed(card, skb, dev, queue, - version, multicast); - break; - default: - result = -EBUSY; - QETH_DBF_CARD0(1, trace, "UPSs", card); - 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); - } - - /* ATOMIC: (NORMAL->UNLOCKED, FLUSH->NORMAL) */ - while (atomic_dec_return(&card->outbound_ringbuffer_lock[queue])) { - qeth_flush_buffer(card, queue, 0); - card->send_state[queue] = SEND_STATE_DONT_PACK; - } -#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) -{ - struct qeth_card *card; - int result; - - card = (struct qeth_card *) (dev->priv); - - if (skb == NULL) - return 0; - - QETH_DBF_HEX4(0, data, skb->data, __max(QETH_DBF_DATA_LEN, skb->len)); - - netif_stop_queue(dev); - - if (!card) { - QETH_DBF_TEXT2(0, trace, "XMNSNOCD"); - 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++; - QETH_DBF_CARD2(0, trace, "XMNS", card); - dev_kfree_skb_irq(skb); - return 0; - } - - result = qeth_do_send_packet(card, skb, dev); - - if (!result) - netif_wake_queue(card->dev); - - return result; -} - -static struct net_device_stats * -qeth_get_stats(struct net_device *dev) -{ - struct qeth_card *card; - - card = (struct qeth_card *) (dev->priv); - - QETH_DBF_CARD3(0, trace, "gtst", card); - - return card->stats; -} - -static int -qeth_change_mtu(struct net_device *dev, int new_mtu) -{ - struct qeth_card *card; - char dbf_text[15]; - - card = (struct qeth_card *) (dev->priv); - - QETH_DBF_CARD2(0, trace, "mtu", card); - 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(struct qeth_card *card) -{ - if (!atomic_read(&card->shutdown_phase)) { - QETH_DBF_CARD2(0, trace, "stss", card); - up(&card->softsetup_thread_sem); - } -} - -static int -qeth_sleepon(struct qeth_card *card, int timeout) -{ - char dbf_text[15]; - - QETH_DBF_CARD5(0, trace, "slpn", card); - sprintf(dbf_text, "%08x", timeout); - QETH_DBF_TEXT5(0, trace, dbf_text); - - wait_event_interruptible_timeout(card->wait_q, - atomic_read(&card->data_has_arrived), - timeout * HZ); - if (atomic_read(&card->data_has_arrived)) { - atomic_set(&card->data_has_arrived, 0); - return 0; - } - return -ETIME; -} - -static void -qeth_wakeup_ioctl(struct qeth_card *card) -{ - - QETH_DBF_CARD5(0, trace, "wkup", card); - - atomic_set(&card->ioctl_data_has_arrived, 1); - wake_up(&card->ioctl_wait_q); -} - -static int -qeth_sleepon_ioctl(struct qeth_card *card, int timeout) -{ - char dbf_text[15]; - - QETH_DBF_CARD5(0, trace, "ioctlslpn", card); - sprintf(dbf_text, "%08x", timeout); - QETH_DBF_TEXT5(0, trace, dbf_text); - - wait_event_interruptible_timeout(card->ioctl_wait_q, - atomic_read(&card-> - ioctl_data_has_arrived), - timeout * HZ); - if (atomic_read(&card->ioctl_data_has_arrived)) { - atomic_set(&card->ioctl_data_has_arrived, 0); - return 0; - } - return -ETIME; -} - -/*SNMP IOCTL on Procfile */ - -static void -qeth_wakeup_procfile(void) -{ - QETH_DBF_TEXT5(0, trace, "procwkup"); - /* is this if statement correct? */ - if (atomic_read(&qeth_procfile_ioctl_sem.count) <= - PROCFILE_SLEEP_SEM_MAX_VALUE) - up(&qeth_procfile_ioctl_sem); -} - -static int -qeth_sleepon_procfile(void) -{ - QETH_DBF_TEXT5(0, trace, "procslp"); - if (down_interruptible(&qeth_procfile_ioctl_sem)) { - return -ERESTARTSYS; - } - return 0; -} - -/* SNMP END */ - -static char * -qeth_send_control_data(struct qeth_card *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); - QETH_DBF_CARD2(0, trace, "LSCD", card); - 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 (struct ccw1)); - card->dma_stuff->write_ccw.count = len; - card->dma_stuff->write_ccw.cda = - QETH_GET_ADDR(card->dma_stuff->sendbuf); - - QETH_DBF_CARD2(0, trace, "scdw", card); - 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); - - spin_lock_irqsave(get_ccwdev_lock(CARD_WDEV(card)), flags); - result = ccw_device_start(CARD_WDEV(card), &card->dma_stuff->write_ccw, - intparam, 0, 0); - if (result) { - qeth_delay_millis(QETH_WAIT_BEFORE_2ND_DOIO); - result2 = ccw_device_start(CARD_WDEV(card), - &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; - } - spin_unlock_irqrestore(get_ccwdev_lock(CARD_WDEV(card)), 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:ioctime"); - /* re-enable qeth_send_control_data again */ - atomic_set(&card->write_busy, 0); - return NULL; - } - rec_buf = card->dma_stuff->recbuf; - QETH_DBF_CARD2(0, trace, "scro", card); - } 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; - QETH_DBF_CARD2(0, trace, "scri", card); - } - 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(struct qeth_card *card, struct ipa_cmd *cmd, int update_cmd, - int ipatype) -{ - unsigned char *buffer; - struct ipa_cmd *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 (struct ipa_cmd)); - - buffer = qeth_send_control_data(card, card->send_buf, - IPA_PDU_HEADER_SIZE + - sizeof (struct ipa_cmd), ipatype); - - if (!buffer) { - if (atomic_read(&card->escape_softsetup)) - return 0; - else - return -1; - } - reply = (struct ipa_cmd *) PDU_ENCAPSULATION(buffer); - if ((update_cmd) && (reply)) - memcpy(cmd, reply, sizeof (struct ipa_cmd)); - result = reply->return_code; - - /* some special sausages: */ - if ((ipa_cmd == IPA_CMD_SETASSPARMS) && (result == 0)) { - result = reply->data.setassparms.return_code; - if ((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; - } - if ((ipa_cmd == IPA_CMD_SETADAPTERPARMS) && (result == 0)) { - result = reply->data.setadapterparms.return_code; - } - - return result; -} - -static void -qeth_fill_ipa_cmd(struct qeth_card *card, struct ipa_cmd *cmd, - __u8 command, int ip_vers) -{ - memset(cmd, 0, sizeof (struct ipa_cmd)); - 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(struct qeth_card *card, __u8 ipacmd, __u16 ip_vers) -{ - struct ipa_cmd 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(struct qeth_card *card, __u16 ip_vers) -{ - int result; - char dbf_text[15]; - - QETH_DBF_CARD4(0, trace, "stln", card); - - result = qeth_send_startstoplan(card, IPA_CMD_STARTLAN, ip_vers); - if (!result) - atomic_set(&card->is_startlaned, 1); - - if (result) { - QETH_DBF_CARD2(0, trace, "STRTLNFL", card); - sprintf(dbf_text, "%4x", result); - QETH_DBF_TEXT2(0, trace, dbf_text); - } - - return result; -} - -static int -qeth_send_stoplan(struct qeth_card *card) -{ -#ifdef QETH_SEND_STOPLAN_ON_SHUTDOWN - int result; - char dbf_text[15]; - - atomic_set(&card->is_startlaned, 0); - - QETH_DBF_CARD4(0, trace, "spln", card); - - result = qeth_send_startstoplan(card, IPA_CMD_STOPLAN, 4); - - if (result) { - QETH_DBF_CARD2(0, trace, "STPLNFLD", card); - sprintf(dbf_text, "%4x", 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(struct qeth_card *card, short ip_vers) -{ - struct ipa_cmd 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; -} - -/* QUERY ARP FUNCTIONS */ - -static int -qeth_send_ipa_arpcmd(struct qeth_card *card, struct arp_cmd *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 (struct arp_cmd)); - - 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 (struct arp_cmd), ipatype); - if (!buffer) - result = -ENODATA; - else - result = card->ioctl_returncode; - return result; -} - -static int -qeth_ioctl_handle_snmp_data(struct qeth_card *card, struct arp_cmd *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(struct qeth_card *card, struct arp_cmd *reply) -{ - - if (reply->data.setassparms.seq_no == 1) { - if (card->ioctl_buffersize <= - (sizeof (__u16) + sizeof (int) + - reply->data.setassparms.number_of_replies * - ARP_DATA_SIZE)) { - - card->ioctl_returncode = ARP_RETURNCODE_ERROR; - - } else { - card->ioctl_returncode = ARP_RETURNCODE_SUCCESS; - card->number_of_entries = 0; - card->ioctl_buffer_pointer = - card->ioctl_data_buffer + sizeof (__u16) + - sizeof (int); - } - } - - if (card->ioctl_returncode != ARP_RETURNCODE_ERROR && - reply->data.setassparms.seq_no <= - reply->data.setassparms.number_of_replies) { - - if (reply->data.setassparms.return_code == IPA_REPLY_SUCCESS) { - - card->number_of_entries = card->number_of_entries + - reply->data.setassparms. - data.queryarp_data.number_of_entries; - memcpy(card->ioctl_buffer_pointer, - reply->data.setassparms.data.queryarp_data. - arp_data, ARP_DATA_SIZE); - card->ioctl_buffer_pointer = card-> - ioctl_buffer_pointer + ARP_DATA_SIZE; - card->ioctl_returncode = ARP_RETURNCODE_SUCCESS; - if (reply->data.setassparms.seq_no == - reply->data.setassparms.number_of_replies) { - memcpy(card->ioctl_data_buffer, - &reply->data.setassparms.data. - queryarp_data.osa_setbitmask, - sizeof (__u16)); - card->ioctl_returncode = - ARP_RETURNCODE_LASTREPLY; - } - } else { - - card->ioctl_returncode = ARP_RETURNCODE_ERROR; - memset(card->ioctl_data_buffer, 0, - card->ioctl_buffersize); - } - } - return card->ioctl_returncode; -} - -static int -qeth_look_for_arp_data(struct qeth_card *card) -{ - struct arp_cmd *reply; - int result; - - reply = (struct arp_cmd *) PDU_ENCAPSULATION(card->dma_stuff->recbuf); - - if ((reply->command == IPA_CMD_SETASSPARMS) && - (reply->data.setassparms.assist_no == IPA_ARP_PROCESSING) && - (reply->data.setassparms.command_code == - IPA_CMD_ASS_ARP_FLUSH_CACHE)) { - result = ARP_FLUSH; - } else if ((reply->command == IPA_CMD_SETASSPARMS) && - (reply->data.setassparms.assist_no == IPA_ARP_PROCESSING) && - (reply->data.setassparms.command_code == - IPA_CMD_ASS_ARP_QUERY_INFO) && - (card->ioctl_returncode == ARP_RETURNCODE_SUCCESS)) { - - result = qeth_ioctl_handle_arp_data(card, reply); - - } else if ((reply->command == IPA_CMD_SETADAPTERPARMS) && - (reply->data.setadapterparms.command_code == - IPA_SETADP_SET_SNMP_CONTROL) && - (card->ioctl_returncode == ARP_RETURNCODE_SUCCESS)) { - - result = qeth_ioctl_handle_snmp_data(card, reply); - } else - result = ARP_RETURNCODE_NOARPDATA; - - return result; -} - -static int -qeth_queryarp(struct qeth_card *card, struct ifreq *req, int version, - __u32 assist_no, __u16 command_code, char *c_data, __u16 len) -{ - int data_size; - struct arp_cmd *cmd; - int result; - - cmd = (struct arp_cmd *) kmalloc(sizeof (struct arp_cmd), GFP_KERNEL); - if (!cmd) { - return IPA_REPLY_FAILED; - } - - memcpy(&data_size, c_data, sizeof (int)); - - qeth_fill_ipa_cmd(card, (struct ipa_cmd *) 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(struct qeth_card *card, - struct arp_cmd *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(struct qeth_card *card, struct ifreq *req, - __u32 command, __u16 command_code, - char *c_data, __u16 len) -{ - struct arp_cmd *cmd; - __u32 result, req_size; - - cmd = (struct arp_cmd *) kmalloc(sizeof (struct arp_cmd), GFP_KERNEL); - if (!cmd) { - return IPA_REPLY_FAILED; - } - - qeth_fill_ipa_cmd(card, (struct ipa_cmd *) 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; - if (copy_to_user(req->ifr_ifru.ifru_data + - SNMP_REQUEST_DATA_OFFSET, card->ioctl_data_buffer, - card->ioctl_buffersize)) - result = -EFAULT; - } 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(struct qeth_card *card, int version, __u32 assist_no, - __u16 command_code, long data, __u16 len) -{ - struct ipa_cmd 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, - 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(struct qeth_card *card) -{ - struct ipa_cmd 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(struct qeth_card *card, __u32 command, - __u32 mode) -{ - - struct ipa_cmd 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(struct qeth_card *card, - __u32 command, - __u32 subcmd, __u8 * mac_addr, - int addr_len) -{ - struct ipa_cmd 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(struct qeth_card *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(struct qeth_card *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(struct qeth_card *card, - __u32 assist_no, __u16 command_code) -{ - return qeth_send_setassparms(card, 6, assist_no, command_code, 0, 0); -} - -static int -qeth_send_setdelip(struct qeth_card *card, __u8 * ip, __u8 * netmask, - int ipacmd, short ip_vers, unsigned int flags) -{ - struct ipa_cmd 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(struct qeth_card *card, __u8 * ip, __u8 * mac, - int ipacmd, short ip_vers) -{ - struct ipa_cmd 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(struct qeth_card *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)) { - QETH_DBF_CARD2(0, trace, "ipto", card); - 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; - } else { - } -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_CARD2(0, trace, "SETIPFLD", card); - sprintf(dbf_text, "%4x", result); - QETH_DBF_TEXT2(0, trace, dbf_text); - } - - if (((result == -1) || (result == 0xe080) ||(result==0xf080)) && - (retries--)) { - QETH_DBF_CARD2(0, trace, "sipr", card); - 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(struct qeth_card *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(struct qeth_card *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)) { - QETH_DBF_CARD2(0, trace, "imto", card); - 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_CARD2(0, trace, "SETIMFLD", card); - sprintf(dbf_text, "%4x", result); - QETH_DBF_TEXT2(0, trace, dbf_text); - } - - if ((result == -1) && (retries--)) { - QETH_DBF_CARD2(0, trace, "simr", card); - 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(struct qeth_card *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(struct qeth_card *card, int version, __u8 * addr, int flag) -{ - struct qeth_vipa_entry *entry, *e; - int result = 0; - - entry = - (struct qeth_vipa_entry *) kmalloc(sizeof (struct qeth_vipa_entry), - 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; - - 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: - write_unlock(&card->vipa_list_lock); - return result; -} - -static int -qeth_del_vipa_entry(struct qeth_card *card, int version, __u8 * addr, int flag) -{ - struct qeth_vipa_entry *e; - int result = 0; - - 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: - write_unlock(&card->vipa_list_lock); - return result; -} - -static void -qeth_set_vipas(struct qeth_card *card, int set_only) -{ - struct qeth_vipa_entry *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 - }; - struct qeth_vipa_entry *priv_add_list = NULL; - struct qeth_vipa_entry *priv_del_list = NULL; - - 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 = (struct qeth_vipa_entry *) - kmalloc(sizeof (struct qeth_vipa_entry), - GFP_ATOMIC); - 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 = (struct qeth_vipa_entry *) - kmalloc(sizeof (struct qeth_vipa_entry), - 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; - } - 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_CARD2(0, trace, "SETSVFLD", card); - sprintf(dbf_text, "%4x", 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_CARD2(0, trace, "DELSVFLD", card); - sprintf(dbf_text, "%4x", 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); - } -/* in case of problems, it's better if we just display a message and - * don't requeue the entry back... - write_lock(&card->vipa_list_lock); - e=card->vipa_list; - card->vipa_list=priv_del_list; - priv_del_list=priv_del_list->next; - card->vipa_list->next=e; - card->vipa_list->state=VIPA_ESTABLISHED; - write_unlock(&card->vipa_list_lock); - continue; -*/ - } - e = priv_del_list; - priv_del_list = priv_del_list->next; - kfree(e); - } -} - -static void -qeth_refresh_vipa_states(struct qeth_card *card) -{ - struct qeth_vipa_entry *e; - - 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; - } - write_unlock(&card->vipa_list_lock); -} - -static inline int -qeth_send_setrtg(struct qeth_card *card, int routing_type, short ip_vers) -{ - struct ipa_cmd 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 void -__qeth_setips_ipv6(struct qeth_card *card, int use_setip_retries) -{ -#ifdef QETH_IPV6 - int result; - char dbf_text[15]; - struct inet6_ifaddr *addr6; - __u8 netmask[16]; - -#define FILL_NETMASK(len) { \ - int i,j; \ - for (i=0;i<16;i++) { \ - j=(len)-(i*8); \ - 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)) { - addr6 = addr6->if_next; - continue; - } - 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 device %s " - "(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_BUS_ID(card), 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)) { - addr6 = addr6->if_next; - continue; - } - 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) { - addr6 = addr6->if_next; - continue; - } - PRINT_ERR("was not able to set ip " - "%04x:%04x:%04x:%04x:%04x:%04x:" - "%04x:%04x/%u on device %s " - "(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_BUS_ID(card), result); - sprintf(dbf_text, "sts6%4x", result); - QETH_DBF_TEXT3(0, trace, dbf_text); - addr6 = addr6->if_next; - } -#endif /* QETH_IPV6 */ -} - -static int -qeth_setips(struct qeth_card *card, int use_setip_retries) -{ - struct in_ifaddr *addr; - int result; - char dbf_text[15]; - - QETH_DBF_CARD3(0, trace, "stip", card); - - 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 device %s " - "(result: 0x%x), " - "trying to continue\n", - addr->ifa_address, addr->ifa_mask, - CARD_BUS_ID(card), 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)) { - addr = addr->ifa_next; - continue; - } - 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) { - addr = addr->ifa_next; - continue; - } - PRINT_ERR("was not able to set ip " - "%08x/%08x on device %s, trying to continue\n", - addr->ifa_address, addr->ifa_mask, - CARD_BUS_ID(card)); - sprintf(dbf_text, "stst%4x", result); - QETH_DBF_TEXT3(0, trace, dbf_text); - addr = addr->ifa_next; - } - - __qeth_setips_ipv6(card, use_setip_retries); - - 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 void -__qeth_setipms_ipv6(struct qeth_card *card, int use_setipm_retries) -{ -#ifdef QETH_IPV6 - struct qeth_ipm_mac *addr; - int result; - char dbf_text[15]; - - /* 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 device %s (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_BUS_ID(card), 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_remove_mc_ifa_from_list( - &card->ip_mc_new_state.ipm6_ifa, - addr); - addr = addr->next; - continue; - } - 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 device %s (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_BUS_ID(card), result); - sprintf(dbf_text, "sms6%4x", result); - QETH_DBF_TEXT3(0, trace, dbf_text); - } else { - qeth_remove_mc_ifa_from_list( - &card->ip_mc_new_state.ipm6_ifa, - addr); - qeth_add_mc_ifa_to_list( - &card->ip_mc_current_state.ipm6_ifa, - addr); - } - addr = addr->next; - } -#endif /* QETH_IPV6 */ -} - -static int -qeth_setipms(struct qeth_card *card, int use_setipm_retries) -{ - struct qeth_ipm_mac *addr; - int result; - char dbf_text[15]; - - QETH_DBF_CARD3(0, trace, "stim", card); - - if (!qeth_is_supported(IPA_MULTICASTING)) - return 0; - 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 device %s " - "(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_BUS_ID(card), 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)) { - addr = addr->next; - continue; - } - 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 device %s (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_BUS_ID(card), 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; - } - __qeth_setipms_ipv6(card, use_setipm_retries); - 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 == -EFAULT) ret_val = -EFAULT; \ - if (result==IPA_REPLY_SUCCESS) ret_val=0; \ - if (result==IPA_REPLY_FAILED) ret_val=-EIO; \ - if (result==IPA_REPLY_OPNOTSUPP) ret_val=-EOPNOTSUPP - -static int -qeth_do_ioctl(struct net_device *dev, struct ifreq *rq, int cmd) -{ - char *data; - int result, i, ret_val; - int version = 4; - struct qeth_card *card; - char dbf_text[15]; - char buff[100]; - - card = (struct qeth_card *) dev->priv; - - PRINT_STUPID("CALL: qeth_do_ioctl called with cmd %i (=0x%x).\n", cmd, - cmd); - QETH_DBF_CARD2(0, trace, "ioct", card); - sprintf(dbf_text, "cmd=%4x", cmd); - QETH_DBF_TEXT2(0, trace, dbf_text); - QETH_DBF_HEX2(0, trace, &rq, sizeof (void *)); - - if ((cmd < SIOCDEVPRIVATE) || (cmd > SIOCDEVPRIVATE + 5)) - return -EOPNOTSUPP; - 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))) - return -ENODEV; - - if (atomic_read(&card->shutdown_phase)) - return -ENODEV; - - spin_lock(&card->ioctl_lock); - - if (atomic_read(&card->shutdown_phase)) { - ret_val = -ENODEV; - goto out; - } - if ((!atomic_read(&card->is_registered)) || - (!atomic_read(&card->is_hardsetup))) { - ret_val = -ENODEV; - goto out; - } - - switch (cmd) { - case SIOCDEVPRIVATE + 0: - if (!capable(CAP_NET_ADMIN)) { - ret_val = -EPERM; - break; - } - result = - qeth_send_setassparms(card, version, IPA_ARP_PROCESSING, - IPA_CMD_ASS_ARP_SET_NO_ENTRIES, - rq->ifr_ifru.ifru_ivalue, 4); - QETH_STANDARD_RETVALS; - if (result == 3) - ret_val = -EINVAL; - break; - case SIOCDEVPRIVATE + 1: - if (!capable(CAP_NET_ADMIN)) { - ret_val = -EPERM; - break; - } - result = qeth_queryarp(card, rq, version, IPA_ARP_PROCESSING, - IPA_CMD_ASS_ARP_QUERY_INFO, data, 4); - - QETH_STANDARD_RETVALS; - break; - case SIOCDEVPRIVATE + 2: - if (!capable(CAP_NET_ADMIN)) { - ret_val = -EPERM; - break; - } - for (i = 12; i < 24; i++) - if (data[i]) - version = 6; - result = - qeth_send_setassparms(card, version, IPA_ARP_PROCESSING, - IPA_CMD_ASS_ARP_ADD_ENTRY, - (long) data, 56); - QETH_STANDARD_RETVALS; - break; - case SIOCDEVPRIVATE + 3: - if (!capable(CAP_NET_ADMIN)) { - ret_val = -EPERM; - break; - } - for (i = 4; i < 12; i++) - if (data[i]) - version = 6; - result = - qeth_send_setassparms(card, version, IPA_ARP_PROCESSING, - IPA_CMD_ASS_ARP_REMOVE_ENTRY, - (long) data, 16); - QETH_STANDARD_RETVALS; - break; - case SIOCDEVPRIVATE + 4: - if (!capable(CAP_NET_ADMIN)) { - ret_val = -EPERM; - break; - } - result = - qeth_send_setassparms(card, version, IPA_ARP_PROCESSING, - IPA_CMD_ASS_ARP_FLUSH_CACHE, 0, 0); - QETH_STANDARD_RETVALS; - break; - case SIOCDEVPRIVATE + 5: - - result = - qeth_send_snmp_control(card, rq, IPA_CMD_SETADAPTERPARMS, - IPA_SETADP_SET_SNMP_CONTROL, data, - 4); - QETH_STANDARD_RETVALS; - break; - - default: - ret_val = -EOPNOTSUPP; - goto out; - } -out: - spin_unlock(&card->ioctl_lock); - - sprintf(dbf_text, "ret=%4x", ret_val); - QETH_DBF_TEXT2(0, trace, dbf_text); - - return ret_val; -} - -static void -qeth_clear_ifamc_list(struct qeth_ipm_mac **ifa_list) -{ - struct qeth_ipm_mac *ifa; - while (*ifa_list) { - ifa = *ifa_list; - *ifa_list = ifa->next; - kfree(ifa); - } -} - -#ifdef QETH_IPV6 -static void -qeth_clear_ifa6_list(struct inet6_ifaddr **ifa_list) -{ - struct inet6_ifaddr *ifa; - while (*ifa_list) { - ifa = *ifa_list; - *ifa_list = ifa->if_next; - kfree(ifa); - } -} - -static inline void -__qeth_append_vlan_ipas_v6(struct qeth_card *card) -{ -#ifdef QETH_VLAN - char dbf_text[15]; - struct vlan_group *card_group; - int i; - int remove; - struct inet6_ifaddr *ifa, *ifanew; - - /* - * 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))) - return; - - card_group = (struct vlan_group *) card->vlangrp; - - if (!card_group) - return; - for (i = 0; i < VLAN_GROUP_ARRAY_LEN; i++) { - if (!card_group->vlan_devices[i] || - !(card_group->vlan_devices[i]->flags & IFF_UP) || - !(struct inet6_dev *) card_group->vlan_devices[i]->ip6_ptr) - continue; - 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 -} - -static inline void -__qeth_append_vlan_ipas_v6_mc(struct qeth_card *card) -{ -#ifdef QETH_VLAN - struct vlan_group *card_group; - int i; - int remove; - struct inet6_dev *in6_vdev; - char buf[MAX_ADDR_LEN]; - struct qeth_ipm_mac *ipmanew; - struct ifmcaddr6 *im6; - - QETH_DBF_TEXT4(0, trace, "tovipm6s"); - if (!qeth_is_supported(IPA_FULL_VLAN) || !atomic_read(&card->is_open)) - return; - - card_group = (struct vlan_group *) card->vlangrp; - if (!card_group) - return; - - for (i = 0; i < VLAN_GROUP_ARRAY_LEN; i++) { - if (!card_group->vlan_devices[i] || - !(card_group->vlan_devices[i]->flags & IFF_UP)) - continue; - - in6_vdev = in6_dev_get(card_group->vlan_devices[i]); - if (!in6_vdev) { - QETH_DBF_CARD2(0, trace, "id26", card); - continue; - } - - 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); - } -#endif -} - -static struct inet6_dev * -__qeth_get_mc_lock_v6(struct qeth_card *card) -{ - struct inet6_dev *in6_dev; - - in6_dev = in6_dev_get(card->dev); - - if (!in6_dev) { - QETH_DBF_CARD2(0, trace, "id16", card); - return ERR_PTR(-ENODEV); - } - read_lock(&in6_dev->lock); - return in6_dev; -} - -static void -__qeth_takeover_ip_ipms6_mc(struct qeth_card *card, struct inet6_dev *in6_dev) -{ - int remove; - struct qeth_ipm_mac *ipmanew; - struct ifmcaddr6 *im6; - char buf[MAX_ADDR_LEN]; - - 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_ATOMIC); - 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); - } - } - } - __qeth_append_vlan_ipas_v6_mc(card); - - read_unlock(&in6_dev->lock); - in6_dev_put(in6_dev); -} -#endif /* QETH_IPV6 */ - -static void -qeth_takeover_ip_ipms6(struct qeth_card *card) -{ -#ifdef QETH_IPV6 - struct inet6_ifaddr *ifa, *ifanew; - char dbf_text[15]; - int remove; - struct inet6_dev *in6_dev; - - QETH_DBF_CARD3(0, trace, "tip6", card); - /* 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; - - in6_dev = __qeth_get_mc_lock_v6(card); - if (PTR_ERR(in6_dev) == -ENODEV) - return; - /* 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_ATOMIC); - 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; - } - } - - __qeth_append_vlan_ipas_v6(card); - - __qeth_takeover_ip_ipms6_mc(card, in6_dev); -#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 inline void -__qeth_append_vlan_ipas_v4(struct qeth_card *card) -{ -#ifdef QETH_VLAN - struct in_ifaddr *ifa, *ifanew; - char dbf_text[15]; - struct vlan_group *card_group; - int i; - int remove; - struct in_device *vin4_dev; - - /* - * 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)) - return; - - card_group = (struct vlan_group *) card->vlangrp; - if (!card_group) - return; - - for (i = 0; i < VLAN_GROUP_ARRAY_LEN; i++) { - vin4_dev = in_dev_get(card->dev); - if (!vin4_dev) { - QETH_DBF_TEXT2(0, trace, "nodvhol2"); - QETH_DBF_TEXT2(0, trace, card->dev_name); - continue; - } - 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); - } -#endif /* QETH_VLAN */ - -} - -static inline void -__qeth_append_vlan_ipas_v4_mc(struct qeth_card *card) -{ -#ifdef QETH_VLAN - char dbf_text[15]; - int i; - int remove; - struct vlan_group *card_group; - struct in_device *vin4_dev; - struct qeth_ipm_mac *ipmanew; - struct ip_mc_list *im4; - char buf[MAX_ADDR_LEN]; - __u32 maddr; - - QETH_DBF_TEXT4(0, trace, "to-vipms"); - if (!qeth_is_supported(IPA_FULL_VLAN) || !atomic_read(&card->is_open)) - return; - - card_group = (struct vlan_group *) card->vlangrp; - if (!card_group) - return; - - for (i = 0; i < VLAN_GROUP_ARRAY_LEN; i++) { - if (!card_group->vlan_devices[i] || - !(card_group->vlan_devices[i]->flags & IFF_UP)) - continue; - - vin4_dev = in_dev_get(card_group->vlan_devices[i]); - if (!vin4_dev) { - 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); - continue; - } - 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); - } -#endif /* QETH_VLAN */ - -} - -static struct in_device * -__qeth_get_mc_lock(struct qeth_card *card) -{ - struct in_device *in4_dev; - - /* 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; - - in4_dev = in_dev_get(card->dev); - if (!in4_dev) { - QETH_DBF_TEXT2(0, trace, "nodvhol1"); - QETH_DBF_TEXT2(0, trace, card->dev_name); - return ERR_PTR(-ENODEV); - } - read_lock(&in4_dev->lock); - return in4_dev; -} - -static void -__qeth_takeover_ip_ipms_mc(struct qeth_card *card, struct in_device *in4_dev) -{ - char dbf_text[15]; - int remove; - struct qeth_ipm_mac *ipmanew; - struct ip_mc_list *im4; - char buf[MAX_ADDR_LEN]; - __u32 maddr; - - 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_ATOMIC); - 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); - } - } - } - __qeth_append_vlan_ipas_v4(card); - - read_unlock(&in4_dev->lock); - in_dev_put(in4_dev); - -} - -static void -qeth_takeover_ip_ipms(struct qeth_card *card) -{ - struct in_ifaddr *ifa, *ifanew; - char dbf_text[15]; - int remove; - struct in_device *in4_dev; - - QETH_DBF_CARD3(0, trace, "tips", card); - /* 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; - - in4_dev = __qeth_get_mc_lock(card); - if (PTR_ERR(in4_dev) == -ENODEV) - return; - - /* 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_ATOMIC); - 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; - } - } - __qeth_append_vlan_ipas_v4(card); - - __qeth_takeover_ip_ipms_mc(card, in4_dev); -} - -static void -qeth_get_unique_id(struct qeth_card *card) -{ -#ifdef QETH_IPV6 - struct ipa_cmd 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 device " - "%s (result=x%x), using default id. ipv6 " - "autoconfig on other lpars may lead to duplicate " - "ip addresses. please use manually " - "configured ones.\n", - CARD_BUS_ID(card), result); - QETH_DBF_CARD2(0, trace, "unid fld", card); - sprintf(dbf_text, "%4x", result); - QETH_DBF_TEXT2(0, trace, dbf_text); - } else { - card->unique_id = - *((__u16 *) & cmd.data.create_destroy_addr.unique_id[6]); - QETH_DBF_CARD2(0, setup, "uniqueid", card); - sprintf(dbf_text, "%4x", card->unique_id); - QETH_DBF_TEXT2(0, setup, dbf_text); - } -#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(struct qeth_card *card) -{ -#ifdef QETH_IPV6 - struct ipa_cmd 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_CARD2(0, trace, "unibkfld", card); - sprintf(dbf_text, "%4x", result); - QETH_DBF_TEXT2(0, trace, dbf_text); - } -#else /* QETH_IPV6 */ - card->unique_id = - UNIQUE_ID_IF_CREATE_ADDR_FAILED | UNIQUE_ID_NOT_BY_CARD; -#endif /* QETH_IPV6 */ -} - -static inline void -__qeth_setparms_hstr(struct qeth_card *card) -{ - char dbf_text[15]; - int result; - - if ((card->link_type != QETH_MPC_LINK_TYPE_HSTR) && - (card->link_type != QETH_MPC_LINK_TYPE_LANE_TR)) - return; - - QETH_DBF_CARD3(0, trace, "hstr", card); - - 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 " - "device %s: x%x\n", - CARD_BUS_ID(card), result); - QETH_DBF_CARD1(0, trace, "STBRDCST", card); - sprintf(dbf_text, "%4x", 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 device %s:\n", CARD_BUS_ID(card)); - QETH_DBF_CARD1(0, trace, "NOBC", card); - } - - 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 " - "device %s: x%x\n", CARD_BUS_ID(card), - result); - QETH_DBF_CARD1(0, trace, "STMACMOD", card); - sprintf(dbf_text, "%4x", 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 device %s:\n", CARD_BUS_ID(card)); - QETH_DBF_CARD1(0, trace, "NOMA", card); - } -} - -static void -qeth_do_setadapterparms_stuff(struct qeth_card *card) -{ - int result; - char dbf_text[15]; - - if (!qeth_is_supported(IPA_SETADAPTERPARMS)) { - return; - } - - QETH_DBF_CARD4(0, trace, "stap", card); - - result = qeth_send_setadapterparms_query(card); - - if (result) { - PRINT_WARN("couldn't set adapter parameters on device %s: " - "x%x\n", CARD_BUS_ID(card), result); - QETH_DBF_CARD1(0, trace, "SETADPFL", card); - sprintf(dbf_text, "%4x", 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)) { - QETH_DBF_CARD3(0, trace, "rdmc", card); - QETH_DBF_CARD2(0, setup, "rdmc", card); - - 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 " - "device %s: x%x\n", - CARD_BUS_ID(card), result); - QETH_DBF_CARD1(0, trace, "NOMACADD", card); - sprintf(dbf_text, "%4x", 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)); - } - } - __qeth_setparms_hstr(card); -} - -static inline void -__qeth_start_vlan_assist(struct qeth_card *card) -{ -#ifdef QETH_VLAN - char dbf_text[15]; - int result; - - if (!qeth_is_supported(IPA_FULL_VLAN)) { - PRINT_WARN("VLAN not supported on %s\n", - card->dev_name); - QETH_DBF_TEXT2(0, trace, "vlnotsup"); - return; - } - 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); - return; - } - card->dev->features |= NETIF_F_HW_VLAN_TX | NETIF_F_HW_VLAN_RX; -#endif /* QETH_VLAN */ -} - -static inline void -__qeth_start_mc_assist(struct qeth_card *card) -{ - char dbf_text[15]; - int result; - - if (!qeth_is_supported(IPA_MULTICASTING)) { - PRINT_WARN("multicasting not supported on %s\n", - card->dev_name); - QETH_DBF_TEXT2(0, trace, "mcnotsup"); - return; - } - 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); - return; - } - card->dev->flags |= IFF_MULTICAST; -} - -static int -__qeth_softsetup_enable_ipv6(struct qeth_card *card, int do_a_startlan6) -{ - int result; - char dbf_text[15]; - - 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; - return result; - } - } - 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); - return result; - } - - sprintf(dbf_text, "%4x%4x", card->ipa6_supported, card->ipa6_enabled); - QETH_DBF_TEXT2(0, trace, dbf_text); - - QETH_DBF_TEXT2(0, trace, "enaipv46"); - result = qeth_send_setassparms_simple_with_data(card, IPA_IPv6, - IPA_CMD_ASS_START, 3); - if (result) { - PRINT_WARN("Could not enable IPv4&6 assist " - "on %s: 0x%x, continuing\n", - card->dev_name, result); - sprintf(dbf_text, "I46A%4x", result); - QETH_DBF_TEXT2(0, trace, dbf_text); - /* go on */ - } - - QETH_DBF_TEXT2(0, trace, "enaipv6"); - result = qeth_send_setassparms_simple_without_data6(card, IPA_IPv6, - IPA_CMD_ASS_START); - if (result) { - PRINT_WARN("Could not start IPv6 assist " - "on %s: 0x%x, continuing\n", - card->dev_name, result); - sprintf(dbf_text, "I6AS%4x", result); - QETH_DBF_TEXT2(0, trace, dbf_text); - /* go on */ - } - - QETH_DBF_TEXT2(0, trace, "enapstr6"); - result = qeth_send_setassparms_simple_without_data6(card, IPA_PASSTHRU, - IPA_CMD_ASS_START); - if (result) { - PRINT_WARN("Could not enable passthrough " - "on %s: 0x%x, continuing\n", - card->dev_name, result); - sprintf(dbf_text, "PSTR%4x", result); - QETH_DBF_TEXT2(0, trace, dbf_text); - /* go on */ - } - return 0; -} - -static int -__qeth_softsetup_start_assists(struct qeth_card *card) -{ - int result; - char dbf_text[15]; - int do_a_startlan6 = 0; - - if (atomic_read(&card->is_softsetup)) - return 0; - - 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; - } - return result; - } - 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); - return result; - } - } - - if (qeth_is_supported(IPA_IP_FRAGMENTATION)) { - PRINT_INFO("IP fragmentation supported on " - "%s... :-)\n", card->dev_name); - /* start it */ - 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)) { - /* start it */ - 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); - } - } - __qeth_start_vlan_assist(card); - __qeth_start_mc_assist(card); - - if (!qeth_is_supported(IPA_IPv6)) { - QETH_DBF_TEXT2(0, trace, "ipv6ntsp"); - PRINT_WARN("IPv6 not supported on %s\n", card->dev_name); - } else { - result = __qeth_softsetup_enable_ipv6(card, do_a_startlan6); - if (result != 0) - return result; - } - - card->broadcast_capable = 0; - if (!qeth_is_supported(IPA_FILTERING)) { - QETH_DBF_TEXT2(0, trace, "filtntsp"); - PRINT_WARN("Broadcasting not supported on %s\n", - card->dev_name); - } else { - QETH_DBF_TEXT2(0, trace, "enafiltr"); - result = qeth_send_setassparms_simple_without_data - (card, IPA_FILTERING, IPA_CMD_ASS_START); - if (result) { - PRINT_WARN("Could not enable broadcast " - "filtering on %s: " - "0x%x, continuing\n", - card->dev_name, result); - sprintf(dbf_text, "FLT1%4x", result); - QETH_DBF_TEXT2(0, trace, dbf_text); - goto go_on_filt; - } - result = qeth_send_setassparms_simple_with_data - (card, IPA_FILTERING, IPA_CMD_ASS_CONFIGURE, 1); - if (result) { - PRINT_WARN("Could not set up broadcast " - "filtering on %s: " - "0x%x, continuing\n", - card->dev_name, result); - sprintf(dbf_text, "FLT2%4x", result); - QETH_DBF_TEXT2(0, trace, dbf_text); - goto go_on_filt; - } - card->dev->flags |= IFF_BROADCAST; - card->broadcast_capable = 1; - } -go_on_filt: - if (card->options.checksum_type == HW_CHECKSUMMING) { - if (!qeth_is_supported(IPA_INBOUND_CHECKSUM)) { - PRINT_WARN("Inbound HW checksumming not " - "supported on %s, continuing " - "using inbound sw checksumming\n", - card->dev_name); - QETH_DBF_TEXT2(0, trace, "ibckntsp"); - card->options.checksum_type = SW_CHECKSUMMING; - } else { - QETH_DBF_TEXT2(0, trace, "ibcksupp"); - result = qeth_send_setassparms_simple_without_data - (card, IPA_INBOUND_CHECKSUM, - IPA_CMD_ASS_START); - if (result) { - PRINT_WARN("Could not start inbound " - "checksumming on %s: 0x%x, " - "continuing using " - "inbound sw checksumming\n", - card->dev_name, result); - sprintf(dbf_text, "SIBC%4x", result); - QETH_DBF_TEXT2(0, trace, dbf_text); - card->options.checksum_type = SW_CHECKSUMMING; - goto go_on_checksum; - } - result=qeth_send_setassparms_simple_with_data - (card,IPA_INBOUND_CHECKSUM, - IPA_CMD_ASS_ENABLE, 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); - return 0; -} - -static inline void -__qeth_softsetup_routingv4(struct qeth_card *card) -{ - int result; - char dbf_text[15]; - - if (!atomic_read(&card->enable_routing_attempts4)) - return; - - if (!card->options.routing_type4) { - atomic_set(&card->enable_routing_attempts4, 0); - atomic_set(&card->rt4fld, 0); - return; - } - - 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) { /* routing set correctly */ - atomic_set(&card->enable_routing_attempts4, 0); - atomic_set(&card->rt4fld, 0); - return; - } - 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); - atomic_set(&card->rt4fld, 1); - QETH_DBF_TEXT2(0, trace, dbf_text); - } 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); - } -} - -static void -__qeth_softsetup_routingv6(struct qeth_card *card) -{ -#ifdef QETH_IPV6 - int result; - char dbf_text[15]; - - if (!atomic_read(&card->enable_routing_attempts6)) - return; - - if (!card->options.routing_type6 || - ((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); - return; - } - 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) { /* routing set correctly */ - atomic_set(&card->enable_routing_attempts6, 0); - atomic_set(&card->rt6fld, 0); - return; - } - 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); - } -#endif /* QETH_IPV6 */ -} - -static int -qeth_softsetup_card(struct qeth_card *card, int wait_for_lock) -{ - int result; - char dbf_text[15]; - int use_setip_retries = 1; - - if (wait_for_lock == QETH_WAIT_FOR_LOCK) { - down(&card->softsetup_sema); - } else if (wait_for_lock == QETH_DONT_WAIT_FOR_LOCK) { - if (!down_trylock(&card->softsetup_sema)) { - 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); - - QETH_DBF_CARD1(0, trace, wait_for_lock?"sscw":"sscn", card); - - result = __qeth_softsetup_start_assists(card); - if (result) - goto out; - - __qeth_softsetup_routingv4(card); - __qeth_softsetup_routingv6(card); - - QETH_DBF_TEXT2(0, trace, "delvipa"); - qeth_set_vipas(card, 0); - QETH_DBF_TEXT2(0, trace, "toip/ms"); - qeth_takeover_ip_ipms(card); - qeth_takeover_ip_ipms6(card); - 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) - up(&card->softsetup_sema); - return result; -} - -static int -qeth_softsetup_thread(void *param) -{ - char name[15]; - struct qeth_card *card = (struct qeth_card *) param; - - /* set a nice name ... */ - sprintf(name, "qethsoftd%s", CARD_BUS_ID(card)); - daemonize(name); - - QETH_DBF_CARD2(0, trace, "ssth", card); - - atomic_set(&card->softsetup_thread_is_running, 1); - for (;;) { - if (atomic_read(&card->shutdown_phase)) - goto out; - down_interruptible(&card->softsetup_thread_sem); - QETH_DBF_CARD2(0, trace, "ssst", card); - 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); - } - QETH_DBF_CARD2(0, trace, "sssd", card); - netif_wake_queue(card->dev); - } -out: - atomic_set(&card->softsetup_thread_is_running, 0); - - QETH_DBF_CARD2(0, trace, "lsst", card); - - return 0; -} - -static void -qeth_softsetup_thread_starter(void *data) -{ - struct qeth_card *card = (struct qeth_card *) data; - - QETH_DBF_CARD4(0, trace, "ssts", card); - sema_init(&card->softsetup_thread_sem, 0); - kernel_thread(qeth_softsetup_thread, card, SIGCHLD); -} - -static void -qeth_start_reinit_thread(struct qeth_card *card) -{ - /* 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; - } - QETH_DBF_CARD2(0, trace, "stri", card); - PRINT_STUPID("starting reinit-thread\n"); - kernel_thread(qeth_reinit_thread, card, SIGCHLD); - } -} - -static void -qeth_recover(void *data) -{ - struct qeth_card *card; - int i; - char dbf_text[15]; - - card = (struct qeth_card *) data; - - QETH_DBF_CARD2(0, trace, "recv", card); - - 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 device %s (%s) with " - "problem 0x%x\n", - CARD_BUS_ID(card), 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(struct qeth_card *card) -{ - if (card) { - INIT_WORK(&card->tqueue, qeth_recover, card); - schedule_work(&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(struct ccw_device *cdev, 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; - struct qeth_card *card; - int problem; - int sbalf15; - char dbf_text[15]; - - sprintf(dbf_text, "qibhn%s", cdev->dev.bus_id); - QETH_DBF_HEX6(0, trace, dbf_text, QETH_DBF_TRACE_LEN); - - card = (struct qeth_card *) 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; - 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); - QETH_DBF_CARD1(1, trace, "qscd", card); - 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 device %s. " - "qdio_error=0x%x (more than one: %c), " - "siga_error=0x%x (more than one: %c), " - "sbalf15=x%x, bufno=x%x\n", cdev->dev.bus_id, - 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); - QETH_DBF_CARD1(0, trace, "IQTI", card); - QETH_DBF_CARD1(0, qerr, "IQTI", card); - 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_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_try_to_flush_packets(struct qeth_card *card, int last_pci_hit, - unsigned int queue) -{ - int switch_state; - - switch_state = (atomic_read(&card->outbound_used_buffers[queue]) <= - LOW_WATERMARK_PACK); - /* first_element is the last buffer that we got back from hydra */ - if (!switch_state && !last_pci_hit) - return; - QETH_DBF_CARD3(0, trace, "stchcw", card); - if (atomic_swap(&card->outbound_ringbuffer_lock[queue], QETH_LOCK_FLUSH) - == QETH_LOCK_UNLOCKED) { - /* - * we stop the queue as we try to not run onto the - * outbound_ringbuffer_lock -- this will not prevent it totally, - * but reduce it. in high traffic situations, it saves around - * 20us per second, hopefully this is amortized by calling - * netif_... - */ - netif_stop_queue(card->dev); - qeth_flush_packed_packets(card, queue, - QDIO_FLAG_UNDER_INTERRUPT); - /* - * only switch state to non-packing, if the amount of used - * buffers decreased - */ - if (switch_state) - card->send_state[queue] = SEND_STATE_DONT_PACK; - netif_wake_queue(card->dev); - atomic_set(&card->outbound_ringbuffer_lock[queue], - QETH_LOCK_UNLOCKED); - } - /* - * if the lock was UNLOCKED, we flush ourselves, otherwise this is done - * in do_send_packet when the lock is released - */ -#ifdef QETH_PERFORMANCE_STATS - card->perf_stats.sc_p_dp++; -#endif /* QETH_PERFORMANCE_STATS */ -} - -static void -qeth_qdio_output_handler(struct ccw_device *cdev, - unsigned int status, - unsigned int qdio_error, - unsigned int siga_error, - unsigned int queue, - int first_element, int count, unsigned long card_ptr) -{ - struct qeth_card *card; - int mycnt, problem, buffers_used; - int sbalf15; - char dbf_text[15]; - int last_pci_hit = 0; - int last_pci; - - sprintf(dbf_text, "qouthn%s", cdev->dev.bus_id); - QETH_DBF_HEX6(0, trace, dbf_text, QETH_DBF_TRACE_LEN); - - mycnt = count; - card = (struct qeth_card *) card_ptr; - - if (status & QDIO_STATUS_LOOK_FOR_ERROR) { - if (status & QDIO_STATUS_ACTIVATE_CHECK_CONDITION) { - problem = PROBLEM_ACTIVATE_CHECK_CONDITION; - 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); - QETH_DBF_CARD1(1, trace, "qscd", card); - qeth_schedule_recovery(card); - goto out; - } - sbalf15 = (card->outbound_ringbuffer[queue]-> - buffer[(first_element + count - 1) & QDIO_MAX_BUFFERS_PER_Q]. - element[15].flags) & 0xff; - PRINT_STUPID("outbound qdio transfer error on device %s, " - "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", - cdev->dev.bus_id, 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); - QETH_DBF_CARD1(0, trace, "IQTO", card); - QETH_DBF_CARD1(0, qerr, "IQTO", card); - 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_add_return(-count, - &card->outbound_used_buffers[queue]) - + count; - - switch (card->send_state[queue]) { - case SEND_STATE_PACK: - __qeth_try_to_flush_packets(card, last_pci_hit, queue); - break; - default: - break; - } - - /* we don't have to start the queue, if it was started already */ - if (buffers_used < QDIO_MAX_BUFFERS_PER_Q - 1) - return; - -out: - netif_wake_queue(card->dev); -} - -static long -__qeth_check_irb_error(struct ccw_device *cdev, struct irb *irb) -{ - if (!IS_ERR(irb)) - return 0; - - switch (PTR_ERR(irb)) { - case -EIO: - PRINT_WARN("i/o-error on device %s\n", cdev->dev.bus_id); - break; - case -ETIMEDOUT: - PRINT_WARN("timeout on device %s\n", cdev->dev.bus_id); - break; - default: - PRINT_WARN("unknown error %ld on device %s\n", PTR_ERR(irb), - cdev->dev.bus_id); - } - return PTR_ERR(irb); -} - -static void -qeth_interrupt_handler_read(struct ccw_device *cdev, unsigned long intparm, - struct irb *irb) -{ - int cstat, dstat; - int problem; - struct qeth_card *card; - int rqparam; - char dbf_text[15]; - int result; - - if (__qeth_check_irb_error(cdev, irb)) - return; - - cstat = irb->scsw.cstat; - dstat = irb->scsw.dstat; - rqparam = intparm; - - sprintf(dbf_text, "rint%s", cdev->dev.bus_id); - 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); - - card = CARD_FROM_CDEV(cdev); - if (!card) - return; - - if (irb->scsw.fctl & (SCSW_FCTL_CLEAR_FUNC | SCSW_FCTL_HALT_FUNC)) { - atomic_set(&card->clear_succeeded0, 1); - wake_up(&card->wait_q); - return; - } - - if (!rqparam) { - PRINT_STUPID("got unsolicited interrupt in read handler " - "for %s\n", cdev->dev.bus_id); - return; - } - - if ((dstat == 0) && (cstat == 0)) - return; - - if (irb->esw.esw0.erw.cons) { - PRINT_WARN("sense data available on read channel.\n"); - HEXDUMP16(WARN, "irb: ", irb); - HEXDUMP16(WARN, "sense data: ", irb->ecw); - sprintf(dbf_text, "RSNS%s", cdev->dev.bus_id); - QETH_DBF_TEXT1(0, trace, dbf_text); - QETH_DBF_HEX0(0, sense, irb, QETH_DBF_SENSE_LEN); - } - - if (cstat != 0) { - PRINT_WARN("got nonzero-nonpci channel status in read_" - "handler (device %s, devstat 0x%02x, schstat " - "0x%02x, rqparam 0x%x)\n", cdev->dev.bus_id, - dstat, cstat, rqparam); - } - - problem = qeth_get_cards_problem(cdev, card->dma_stuff->recbuf, - dstat, cstat, rqparam, - (char *) irb, (char *) irb->ecw); - - /* detect errors in dstat here */ - if ((dstat & DEV_STAT_UNIT_EXCEP) || (dstat & DEV_STAT_UNIT_CHECK)) { - PRINT_WARN("unit check/exception in read_handler " - "(device %s, devstat 0x%02x, schstat 0x%02x, " - "rqparam 0x%x)\n", - cdev->dev.bus_id, 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 " - "(device %s, devstat 0x%02x, schstat 0x%02x, " - "rqparam 0x%x)\n", - cdev->dev.bus_id, 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)) { - QETH_DBF_CARD2(1, trace, "rscd", card); - qeth_schedule_recovery(card); - goto wakeup_out; - } - - if (!IS_IPA(card->dma_stuff->recbuf) || - IS_IPA_REPLY(card->dma_stuff->recbuf)) { - /* setup or unknown data */ - result = qeth_look_for_arp_data(card); - switch (result) { - case ARP_RETURNCODE_ERROR: - case ARP_RETURNCODE_LASTREPLY: - qeth_wakeup_ioctl(card); - return; - default: - break; - } - } - -wakeup_out: - memcpy(card->ipa_buf, card->dma_stuff->recbuf, QETH_BUFSIZE); - qeth_wakeup(card); -} - -static void -qeth_interrupt_handler_write(struct ccw_device *cdev, unsigned long intparm, - struct irb *irb) -{ - int cstat, dstat, rqparam; - struct qeth_card *card; - int problem; - char dbf_text[15]; - - if (__qeth_check_irb_error(cdev, irb)) - return; - - cstat = irb->scsw.cstat; - dstat = irb->scsw.dstat; - rqparam = intparm; - - sprintf(dbf_text, "wint%s", cdev->dev.bus_id); - 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); - - card = CARD_FROM_CDEV(cdev); - if (!card) - return; - - if (irb->scsw.fctl & (SCSW_FCTL_CLEAR_FUNC | SCSW_FCTL_HALT_FUNC)) { - atomic_set(&card->clear_succeeded1, 1); - wake_up(&card->wait_q); - goto out; - } - - if (!rqparam) { - PRINT_STUPID("got unsolicited interrupt in write handler " - "for %s\n", cdev->dev.bus_id); - return; - } - - if ((dstat == 0) && (cstat == 0)) - goto out; - - if (irb->esw.esw0.erw.cons) { - PRINT_WARN("sense data available on write channel.\n"); - HEXDUMP16(WARN, "irb: ", irb); - HEXDUMP16(WARN, "sense data: ", irb->ecw); - sprintf(dbf_text, "WSNS%s", cdev->dev.bus_id); - QETH_DBF_TEXT1(0, trace, dbf_text); - QETH_DBF_HEX0(0, sense, irb, QETH_DBF_SENSE_LEN); - } - - if (cstat != 0) { - PRINT_WARN("got nonzero channel status in write_handler " - "(device %s, devstat 0x%02x, schstat 0x%02x, " - "rqparam 0x%x)\n", - cdev->dev.bus_id, dstat, cstat, rqparam); - } - - problem = qeth_get_cards_problem(cdev, NULL, - dstat, cstat, rqparam, - (char *) irb, (char *) irb->ecw); - - /* detect errors in dstat here */ - if ((dstat & DEV_STAT_UNIT_EXCEP) || (dstat & DEV_STAT_UNIT_CHECK)) { - PRINT_WARN("unit check/exception in write_handler " - "(device %s, devstat 0x%02x, schstat 0x%02x, " - "rqparam 0x%x)\n", - cdev->dev.bus_id, 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 " - "(device %s, devstat 0x%02x, schstat 0x%02x, " - "rqparam 0x%x)\n", - cdev->dev.bus_id, dstat, cstat, rqparam); - goto out; - } - -recover: - if (qeth_is_to_recover(card, problem)) { - QETH_DBF_CARD2(1, trace, "wscd", card); - 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(struct ccw_device *cdev, unsigned long intparm, - struct irb *irb) -{ - int cstat, dstat, rqparam; - char dbf_text[15]; - struct qeth_card *card; - - if (__qeth_check_irb_error(cdev, irb)) - return; - - cstat = irb->scsw.cstat; - dstat = irb->scsw.dstat; - rqparam = intparm; - - sprintf(dbf_text, "qint%s", cdev->dev.bus_id); - 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); - - card = CARD_FROM_CDEV(cdev); - if (!card) - return; - - if (irb->scsw.fctl & (SCSW_FCTL_CLEAR_FUNC | SCSW_FCTL_HALT_FUNC)) { - atomic_set(&card->clear_succeeded2, 1); - wake_up(&card->wait_q); - return; - } - - if (!rqparam) { - PRINT_STUPID("got unsolicited interrupt in qdio handler, " - "device%s\n", cdev->dev.bus_id); - return; - } - - if ((dstat == 0) && (cstat == 0)) - return; - - if (irb->esw.esw0.erw.cons) { - PRINT_WARN("sense data available on qdio channel.\n"); - HEXDUMP16(WARN, "irb: ", irb); - HEXDUMP16(WARN, "sense data: ", irb->ecw); - sprintf(dbf_text, "QSNS%s", cdev->dev.bus_id); - QETH_DBF_TEXT1(0, trace, dbf_text); - QETH_DBF_HEX0(0, sense, irb, QETH_DBF_SENSE_LEN); - } - - if (rqparam == NOP_STATE) { - qeth_wakeup(card); - return; - } - - if (cstat != 0) { - sprintf(dbf_text, "qchk%s", cdev->dev.bus_id); - 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 " - "(device %s, devstat 0x%02x, schstat 0x%02x)\n", - cdev->dev.bus_id, dstat, cstat); - } - - if (dstat & ~(DEV_STAT_CHN_END | DEV_STAT_DEV_END)) { - PRINT_WARN("got the following dstat on the qdio channel: " - "device %s, dstat 0x%02x, cstat 0x%02x, " - "rqparam=%i\n", - cdev->dev.bus_id, dstat, cstat, rqparam); - } - -} - -static int -qeth_register_netdev(struct qeth_card *card) -{ - int result; - - QETH_DBF_CARD3(0, trace, "rgnd", card); - - /* sysfs magic */ - SET_NETDEV_DEV(card->dev, &card->gdev->dev); - result = register_netdev(card->dev); - - return result; -} - -static void -qeth_unregister_netdev(struct qeth_card *card) -{ - QETH_DBF_CARD3(0, trace, "nrgn", card); - - unregister_netdev(card->dev); -} - -static int -qeth_stop(struct net_device *dev) -{ - struct qeth_card *card; - - card = (struct qeth_card *) dev->priv; - QETH_DBF_CARD2(0, trace, "stop", card); - QETH_DBF_CARD2(0, setup, "stop", card); - - qeth_save_dev_flag_state(card); - - netif_stop_queue(dev); - atomic_set(&card->is_open, 0); - - return 0; -} - -static void -qeth_softshutdown(struct qeth_card *card) -{ - QETH_DBF_CARD3(0, trace, "ssht", card); - - qeth_send_stoplan(card); -} - -static void -__qeth_clear_card_halt_clear(struct qeth_card *card, int halt) -{ - unsigned long flags0, flags1, flags2; - int ret0, ret1, ret2; - - atomic_set(&card->clear_succeeded0, 0); - atomic_set(&card->clear_succeeded1, 0); - atomic_set(&card->clear_succeeded2, 0); - - spin_lock_irqsave(get_ccwdev_lock(CARD_RDEV(card)), flags0); - if (halt) - ret0 = ccw_device_halt(CARD_RDEV(card), CLEAR_STATE); - else - ret0 = ccw_device_clear(CARD_RDEV(card), CLEAR_STATE); - spin_unlock_irqrestore(get_ccwdev_lock(CARD_RDEV(card)), flags0); - - spin_lock_irqsave(get_ccwdev_lock(CARD_WDEV(card)), flags1); - if (halt) - ret1 = ccw_device_halt(CARD_WDEV(card), CLEAR_STATE); - else - ret1 = ccw_device_clear(CARD_WDEV(card), CLEAR_STATE); - spin_unlock_irqrestore(get_ccwdev_lock(CARD_WDEV(card)), flags1); - - spin_lock_irqsave(get_ccwdev_lock(CARD_DDEV(card)), flags2); - if (halt) - ret2 = ccw_device_halt(CARD_DDEV(card), CLEAR_STATE); - else - ret2 = ccw_device_clear(CARD_DDEV(card), CLEAR_STATE); - spin_unlock_irqrestore(get_ccwdev_lock(CARD_DDEV(card)), flags2); - - /* The device owns us an interrupt. */ - if ((ret0 == 0) && (atomic_read(&card->clear_succeeded0) == 0)) - wait_event(card->wait_q, - atomic_read(&card->clear_succeeded0) == 1); - if ((ret1 == 0) && (atomic_read(&card->clear_succeeded1) == 0)) - wait_event(card->wait_q, - atomic_read(&card->clear_succeeded1) == 1); - if ((ret2 == 0) && (atomic_read(&card->clear_succeeded2) == 0)) - wait_event(card->wait_q, - atomic_read(&card->clear_succeeded2) == 1); -} - -static void -qeth_clear_card(struct qeth_card *card, int qdio_clean, int use_halt) -{ - QETH_DBF_CARD3(0, trace, qdio_clean?"clrq":"clr", card); - QETH_DBF_CARD1(0, setup, qdio_clean?"clrq":"clr", card); - - atomic_set(&card->write_busy, 0); - if (qdio_clean) - qdio_cleanup(CARD_DDEV(card), - (card->type == QETH_CARD_TYPE_IQD) ? - QDIO_FLAG_CLEANUP_USING_HALT : - QDIO_FLAG_CLEANUP_USING_CLEAR); - - if (use_halt) - __qeth_clear_card_halt_clear(card, 1); - - __qeth_clear_card_halt_clear(card, 0); -} - -static void -qeth_free_card_stuff(struct qeth_card *card) -{ - int i, j; - struct qeth_vipa_entry *e, *e2; - - if (!card) - return; - - QETH_DBF_CARD3(0, trace, "freest", card); - QETH_DBF_CARD1(0, setup, "freest", card); - - write_lock(&card->vipa_list_lock); - e = card->vipa_list; - while (e) { - e2 = e->next; - kfree(e); - e = e2; - } - write_unlock(&card->vipa_list_lock); - - for (i = 0; i < card->options.inbound_buffer_count; i++) { - for (j = 0; j < BUFFER_MAX_ELEMENTS; j++) { - if (card->inbound_buffer_pool_entry[i][j]) { - kfree(card->inbound_buffer_pool_entry[i][j]); - card->inbound_buffer_pool_entry[i][j] = NULL; - } - } - } - for (i = 0; i < card->no_queues; i++) - if (card->outbound_ringbuffer[i]) - vfree(card->outbound_ringbuffer[i]); - - if (card->stats) - kfree(card->stats); - if (card->dma_stuff) - kfree(card->dma_stuff); - if (card->dev) - free_netdev(card->dev); - -} - -static void -qeth_free_card(struct qeth_card *card) -{ - - if (!card) - return; - - QETH_DBF_CARD3(0, trace, "free", card); - QETH_DBF_CARD1(0, setup, "free", card); - - vfree(card); /* we checked against NULL already */ -} - -/* also locked from outside (setup_lock) */ -static void -qeth_remove_card_from_list(struct qeth_card *card) -{ - struct qeth_card *cn; - unsigned long flags0, flags1, flags2; - - write_lock(&list_lock); - if (!card) { - QETH_DBF_TEXT2(0, trace, "RMCWNOCD"); - PRINT_WARN("qeth_remove_card_from_list call with no card!\n"); - write_unlock(&list_lock); - return; - } - - QETH_DBF_CARD3(0, trace, "rmcl", card); - - /* 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"); - write_unlock(&list_lock); - return; - } - - spin_lock_irqsave(get_ccwdev_lock(CARD_RDEV(card)), flags0); - spin_lock_irqsave(get_ccwdev_lock(CARD_WDEV(card)), flags1); - spin_lock_irqsave(get_ccwdev_lock(CARD_DDEV(card)), 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; - } - } - - spin_unlock_irqrestore(get_ccwdev_lock(CARD_DDEV(card)), flags2); - spin_unlock_irqrestore(get_ccwdev_lock(CARD_WDEV(card)), flags1); - spin_unlock_irqrestore(get_ccwdev_lock(CARD_RDEV(card)), flags0); - - write_unlock(&list_lock); - -} - -static void -qeth_delete_all_ips(struct qeth_card *card) -{ - struct qeth_vipa_entry *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 */ - - write_lock(&card->vipa_list_lock); - e = card->vipa_list; - while (e) { - e->state = VIPA_2_B_REMOVED; - e = e->next; - } - write_unlock(&card->vipa_list_lock); - qeth_start_softsetup_thread(card); - } -} - -static void -qeth_remove_card(struct qeth_card *card, int method) -{ - if (!card) - return; - - QETH_DBF_CARD2(0, trace, "rmcd", card); - QETH_DBF_CARD1(0, setup, "rmcd", card); - - 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); - - QETH_DBF_TEXT2(0, trace, "cardrmvd"); - -} - -static void -qeth_set_multicast_list(struct net_device *dev) -{ - struct qeth_card *card = dev->priv; - - QETH_DBF_CARD2(0, trace, "smcl", card); - - qeth_start_softsetup_thread(card); -} - -static int -qeth_set_mac_address(struct net_device *dev, void *addr) -{ - struct qeth_card *card; - - card = (struct qeth_card *) dev->priv; - QETH_DBF_CARD2(0, trace, "stmc", card); - - return -EOPNOTSUPP; -} - -static int -qeth_neigh_setup(struct net_device *dev, struct neigh_parms *np) -{ - struct qeth_card *card; - - card = (struct qeth_card *) dev->priv; - QETH_DBF_CARD2(0, trace, "ngst", card); - - return 0; -} - -static void -qeth_generate_tokens(struct qeth_card *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. */ -} - -/* returns last four digits of bus_id */ -/* FIXME: device driver shouldn't be aware of bus_id format - but don't know - what else to use... (CH) */ -static inline __u16 -__raw_devno_from_bus_id(char *id) -{ - id += (strlen(id) - 4); - return (__u16) simple_strtoul(id, &id, 16); -} - -static int -qeth_idx_activate_read(struct qeth_card *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 (struct ccw1)); - 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 = __raw_devno_from_bus_id(CARD_DDEV_ID(card)); - 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); - - QETH_DBF_TEXT2(0, trace, "iarw"); - QETH_DBF_TEXT2(0, trace, CARD_RDEV_ID(card)); - QETH_DBF_HEX2(0, control, card->dma_stuff->sendbuf, - QETH_DBF_CONTROL_LEN); - - spin_lock_irqsave(get_ccwdev_lock(CARD_RDEV(card)), flags); - result = ccw_device_start(CARD_RDEV(card), &card->dma_stuff->write_ccw, - IDX_ACTIVATE_WRITE_STATE, 0, 0); - if (result) { - qeth_delay_millis(QETH_WAIT_BEFORE_2ND_DOIO); - result2 = ccw_device_start(CARD_RDEV(card), - &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); - } - spin_unlock_irqrestore(get_ccwdev_lock(CARD_RDEV(card)), flags); - - if (atomic_read(&card->break_out)) { - QETH_DBF_TEXT3(0, trace, "IARWBRKO"); - return -EIO; - } - - if (qeth_sleepon(card, QETH_MPC_TIMEOUT)) { - QETH_DBF_TEXT1(0, trace, "IRWT"); - QETH_DBF_TEXT1(0, trace, CARD_RDEV_ID(card)); - PRINT_ERR("IDX_ACTIVATE(wr) on read channel device %s: " - "timeout\n", CARD_RDEV_ID(card)); - return -EIO; - } - -/* start reading on read channel, card->read_ccw is not yet used */ - memcpy(&card->dma_stuff->read_ccw, READ_CCW, sizeof (struct ccw1)); - card->dma_stuff->read_ccw.count = QETH_BUFSIZE; - card->dma_stuff->read_ccw.cda = QETH_GET_ADDR(card->dma_stuff->recbuf); - - spin_lock_irqsave(get_ccwdev_lock(CARD_RDEV(card)), flags); - result2 = 0; - result = ccw_device_start(CARD_RDEV(card), &card->dma_stuff->read_ccw, - IDX_ACTIVATE_READ_STATE, 0, 0); - if (result) { - qeth_delay_millis(QETH_WAIT_BEFORE_2ND_DOIO); - result2 = ccw_device_start(CARD_RDEV(card), - &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); - } - spin_unlock_irqrestore(get_ccwdev_lock(CARD_RDEV(card)), flags); - - if (result2) { - result = result2; - if (result) - return result; - } - - if (qeth_sleepon(card, QETH_MPC_TIMEOUT)) { - QETH_DBF_TEXT1(0, trace, "IRRT"); - QETH_DBF_TEXT1(0, trace, CARD_RDEV_ID(card)); - PRINT_ERR("IDX_ACTIVATE(rd) on read channel device %s: " - "timeout\n", CARD_RDEV_ID(card)); - return -EIO; - } - QETH_DBF_TEXT2(0, trace, "iarr"); - QETH_DBF_TEXT2(0, trace, CARD_RDEV_ID(card)); - QETH_DBF_HEX2(0, control, card->dma_stuff->recbuf, - QETH_DBF_CONTROL_LEN); - - if (!(QETH_IS_IDX_ACT_POS_REPLY(card->dma_stuff->recbuf))) { - QETH_DBF_TEXT1(0, trace, "IRNR"); - QETH_DBF_TEXT1(0, trace, CARD_RDEV_ID(card)); - PRINT_ERR("IDX_ACTIVATE on read channel device %s: negative " - "reply\n", CARD_RDEV_ID(card)); - return -EIO; - } - - 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)) { - QETH_DBF_TEXT1(0, trace, "IRFL"); - QETH_DBF_TEXT1(0, trace, CARD_RDEV_ID(card)); - sprintf(dbf_text, "%4x%4x", card->func_level, temp); - QETH_DBF_TEXT1(0, trace, dbf_text); - PRINT_WARN("IDX_ACTIVATE on read channel device %s: function " - "level mismatch (sent: 0x%x, received: 0x%x)\n", - CARD_RDEV_ID(card), 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); - - return result; -} - -static int -qeth_idx_activate_write(struct qeth_card *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 (struct ccw1)); - 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 = __raw_devno_from_bus_id(CARD_DDEV_ID(card)); - 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); - - QETH_DBF_TEXT2(0, trace, "iaww"); - QETH_DBF_TEXT2(0, trace, CARD_WDEV_ID(card)); - QETH_DBF_HEX2(0, control, card->dma_stuff->sendbuf, - QETH_DBF_CONTROL_LEN); - - spin_lock_irqsave(get_ccwdev_lock(CARD_WDEV(card)), flags); - result = ccw_device_start(CARD_WDEV(card), &card->dma_stuff->write_ccw, - IDX_ACTIVATE_WRITE_STATE, 0, 0); - if (result) { - qeth_delay_millis(QETH_WAIT_BEFORE_2ND_DOIO); - result2 = ccw_device_start(CARD_WDEV(card), - &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); - } - spin_unlock_irqrestore(get_ccwdev_lock(CARD_WDEV(card)), flags); - - if (atomic_read(&card->break_out)) { - QETH_DBF_TEXT3(0, trace, "IAWWBRKO"); - return -EIO; - } - - if (qeth_sleepon(card, QETH_MPC_TIMEOUT)) { - QETH_DBF_TEXT1(0, trace, "IWWT"); - QETH_DBF_TEXT1(0, trace, CARD_WDEV_ID(card)); - PRINT_ERR("IDX_ACTIVATE(wr) on write channel device %s: " - "timeout\n", CARD_WDEV_ID(card)); - return -EIO; - } - - QETH_DBF_TEXT3(0, trace, "idxawrrd"); - /* start one read on write channel */ - memcpy(&card->dma_stuff->read_ccw, READ_CCW, sizeof (struct ccw1)); - 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); - - spin_lock_irqsave(get_ccwdev_lock(CARD_WDEV(card)), flags); - result2 = 0; - result = ccw_device_start(CARD_WDEV(card), &card->dma_stuff->read_ccw, - IDX_ACTIVATE_READ_STATE, 0, 0); - if (result) { - qeth_delay_millis(QETH_WAIT_BEFORE_2ND_DOIO); - result2 = ccw_device_start(CARD_WDEV(card), - &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); - } - - spin_unlock_irqrestore(get_ccwdev_lock(CARD_WDEV(card)), flags); - - if (result2) { - result = result2; - if (result) - return result; - } - - if (qeth_sleepon(card, QETH_MPC_TIMEOUT)) { - QETH_DBF_TEXT1(0, trace, "IWRT"); - QETH_DBF_TEXT1(0, trace, CARD_WDEV_ID(card)); - PRINT_ERR("IDX_ACTIVATE(rd) on write channel device %s: " - "timeout\n", CARD_WDEV_ID(card)); - return -EIO; - } - QETH_DBF_TEXT2(0, trace, "iawr"); - QETH_DBF_TEXT2(0, trace, CARD_WDEV_ID(card)); - QETH_DBF_HEX2(0, control, card->dma_stuff->recbuf, - QETH_DBF_CONTROL_LEN); - - if (!(QETH_IS_IDX_ACT_POS_REPLY(card->dma_stuff->recbuf))) { - QETH_DBF_TEXT1(0, trace, "IWNR"); - QETH_DBF_TEXT1(0, trace, CARD_WDEV_ID(card)); - PRINT_ERR("IDX_ACTIVATE on write channel device %s: negative " - "reply\n", CARD_WDEV_ID(card)); - return -EIO; - } - - memcpy(&temp, QETH_IDX_ACT_FUNC_LEVEL(card->dma_stuff->recbuf), 2); - if ((temp & ~0x0100) != qeth_peer_func_level(card->func_level)) { - QETH_DBF_TEXT1(0, trace, "IWFM"); - QETH_DBF_TEXT1(0, trace, CARD_WDEV_ID(card)); - sprintf(dbf_text, "%4x%4x", card->func_level, temp); - QETH_DBF_TEXT1(0, trace, dbf_text); - PRINT_WARN("IDX_ACTIVATE on write channel device %s: function " - "level mismatch (sent: 0x%x, received: 0x%x)\n", - CARD_WDEV_ID(card), card->func_level, temp); - result = -EIO; - } - - return result; -} - -static int -qeth_cm_enable(struct qeth_card *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(struct qeth_card *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(struct qeth_card *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); - - QETH_DBF_CARD2(0, trace, "ule", card); - 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(struct qeth_card *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 = __raw_devno_from_bus_id(CARD_DDEV_ID(card)); - 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(struct qeth_card *card) -{ - int result; - char *adapter_area; - char dbf_text[15]; - void **input_array, **output_array, **ptr; - int i, j; - struct qdio_initialize 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; j < QDIO_MAX_BUFFERS_PER_Q; j++) { - *ptr = (void *) virt_to_phys(&card->inbound_qdio_buffers[j]); - ptr++; - } - - output_array = vmalloc(QDIO_MAX_BUFFERS_PER_Q * sizeof (void *) * - card->no_queues); - if (!output_array) { - vfree(input_array); - vfree(adapter_area); - return -ENOMEM; - } - ptr = output_array; - for (i = 0; i < card->no_queues; i++) - for (j = 0; j < QDIO_MAX_BUFFERS_PER_Q; j++) { - *ptr = (void *) virt_to_phys - (&card->outbound_ringbuffer[i]->buffer[j]); - ptr++; - } - - init_data.cdev = CARD_DDEV(card); - 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; - 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(struct qeth_card *card) -{ - int result; - char dbf_text[15]; - - result = qdio_activate(CARD_DDEV(card), 0); - - sprintf(dbf_text, "qda=%4x", result); - QETH_DBF_TEXT3(0, trace, dbf_text); - - return result; -} - -static int -qeth_dm_act(struct qeth_card *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; -} - -static inline int -__qeth_verify_dev_vlan(struct net_device *dev,struct qeth_card *card) -{ -#ifdef QETH_VLAN - struct vlan_group *vlan_grp; - int i; - int result = 0; - - /* check all vlan devices */ - vlan_grp = (struct vlan_group *) card->vlangrp; - if (vlan_grp) { - for (i = 0; i < VLAN_GROUP_ARRAY_LEN; i++) { - if (vlan_grp->vlan_devices[i] == dev) { - result = QETH_VERIFY_IS_VLAN_DEV; - } - } - } - return result; -#endif - return 0; -} - -#if defined(QETH_VLAN)||defined(QETH_IPV6) -static int -qeth_verify_dev(struct net_device *dev) -{ - struct qeth_card *tmp; - int result = 0; - - read_lock(&list_lock); - tmp = firstcard; - for (; tmp && (!result); tmp = tmp->next) { - if (atomic_read(&tmp->shutdown_phase)) - continue; - result = (dev == tmp->dev)? - QETH_VERIFY_IS_REAL_DEV:__qeth_verify_dev_vlan(dev, tmp); - } - read_unlock(&list_lock); - return result; -} -#endif /* defined(QETH_VLAN)||defined(QETH_IPV6) */ - -static int -qeth_verify_card(struct qeth_card *card) -{ - struct qeth_card *tmp; - int result = 0; - - read_lock(&list_lock); - tmp = firstcard; - while (tmp) { - if ((card == tmp) && (!atomic_read(&card->shutdown_phase))) { - result = 1; - break; - } - tmp = tmp->next; - } - read_unlock(&list_lock); - return result; -} - -static inline struct qeth_card * -__qeth_get_card_from_dev(struct net_device *dev) -{ -#ifdef QETH_VLAN - if (qeth_verify_dev(dev) == QETH_VERIFY_IS_VLAN_DEV) - return (struct qeth_card *) VLAN_DEV_INFO(dev)->real_dev->priv; - else -#endif - return (struct qeth_card *) dev->priv; -} - -#ifdef QETH_IPV6 -/* FIXME: don't put extern declarations in a c file, use a header that's - * shared with the definition for this! */ -extern struct neigh_table arp_tbl; -static 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 -}; - -/* - * FIXME: - * 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) -{ - struct qeth_card *card; - - QETH_DBF_TEXT5(0, trace, "hardhdr"); - - card = __qeth_get_card_from_dev(dev); - 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) -{ - struct qeth_card *card; - - card = (struct qeth_card *) 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) -{ - struct qeth_card *card; - QETH_DBF_TEXT5(0, trace, "rebldhdr"); - if (skb->protocol == __constant_htons(ETH_P_IP)) - return 0; - - card = __qeth_get_card_from_dev(skb->dev); - - return card->rebuild_header(skb); -} - -int -qeth_ipv6_generate_eui64(u8 * eui, struct net_device *dev) -{ - switch (dev->type) { - case ARPHRD_ETHER: - case ARPHRD_FDDI: - case ARPHRD_IEEE802_TR: - if (dev->addr_len != ETH_ALEN) - return -1; - memcpy(eui, dev->dev_addr, 3); - memcpy(eui + 5, dev->dev_addr + 3, 3); - eui[3] = (dev->dev_id >> 8) & 0xff; - eui[4] = dev->dev_id & 0xff; - return 0; - } - return -1; - -} -#endif /* QETH_IPV6 */ - -static void -qeth_ipv6_init_card(struct qeth_card *card) -{ -#ifdef QETH_IPV6 - 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); - card->dev->dev_id = card->unique_id & 0xffff; - if (!(card->unique_id & UNIQUE_ID_NOT_BY_CARD)) - card->dev->generate_eui64 = qeth_ipv6_generate_eui64; -#endif /* QETH_IPV6 */ -} - -#ifdef QETH_VLAN -static void -qeth_vlan_rx_register(struct net_device *dev, struct vlan_group *grp) -{ - struct qeth_card *card; - card = (struct qeth_card *) 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) -{ - struct qeth_card *card; - card = (struct qeth_card *) dev->priv; - spin_lock_irq(&card->vlan_lock); - if (card->vlangrp) - card->vlangrp->vlan_devices[vid] = NULL; - spin_unlock_irq(&card->vlan_lock); -} -#endif - -static void -qeth_tx_timeout(struct net_device *dev) -{ - struct qeth_card *card; - - card = (struct qeth_card *) dev->priv; - QETH_DBF_CARD2(1, trace, "XMTO", card); - card->stats->tx_errors++; - atomic_set(&card->problem, PROBLEM_TX_TIMEOUT); - qeth_schedule_recovery(card); -} - -static void* -__qeth_rebuild_header_func(struct qeth_card *card) -{ -#ifdef QETH_IPV6 - return (!(qeth_get_additional_dev_flags(card->type) & IFF_NOARP)) ? - (qeth_get_rebuild_header(card->link_type) ? - qeth_rebuild_header : NULL) : NULL; -#endif /* QETH_IPV6 */ - return NULL; -} - -static void* -__qeth_hard_header_func(struct qeth_card *card) -{ -#ifdef QETH_IPV6 - return (!(qeth_get_additional_dev_flags(card->type) & IFF_NOARP)) ? - (qeth_get_hard_header(card->link_type) ? - qeth_hard_header : NULL) : NULL; -#endif /* QETH_IPV6 */ - return NULL; -} - -static void* -__qeth_header_cache_update_func(struct qeth_card *card) -{ -#ifdef QETH_IPV6 - return (!(qeth_get_additional_dev_flags(card->type) & IFF_NOARP)) ? - (qeth_get_header_cache_update(card->link_type) ? - qeth_header_cache_update : NULL) : NULL; -#endif /* QETH_IPV6 */ - return NULL; -} - -static void* -__qeth_hard_header_cache_func(struct qeth_card *card) -{ -#ifdef QETH_IPV6 - return (!(qeth_get_additional_dev_flags(card->type) & IFF_NOARP)) ? - qeth_get_hard_header_cache(card->link_type) : NULL; -#endif /* QETH_IPV6 */ - return NULL; -} - -static int -qeth_init_dev(struct net_device *dev) -{ - struct qeth_card *card; - - card = (struct qeth_card *) dev->priv; - - QETH_DBF_CARD3(0, trace, "inid", card); - - dev->rebuild_header = __qeth_rebuild_header_func(card); - dev->hard_header = __qeth_hard_header_func(card); - dev->header_cache_update = __qeth_header_cache_update_func(card); - dev->hard_header_cache = __qeth_hard_header_cache_func(card); - dev->hard_header_parse = NULL; - - dev->flags |= qeth_get_additional_dev_flags(card->type); - - dev->flags |= ((card->options.fake_broadcast == FAKE_BROADCAST) || - (card->broadcast_capable)) ? IFF_BROADCAST : 0; - - /* is done in hardsetup_card... see comment below - qeth_send_qipassist(card,4);*/ - - /* that was the old place. one id. we need to make sure, that - * hydra knows about us going to use the same id again, so we - * do that in hardsetup_card every time - qeth_get_unique_id(card);*/ - - 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; - netif_start_queue(dev); - - dev->mtu = card->initial_mtu; - - qeth_ipv6_init_card(card); - - return 0; -} - -static int -qeth_get_unitaddr(struct qeth_card *card) -{ - char *prcd; - int result = 0; - char dbf_text[15]; - int length; - - QETH_DBF_CARD3(0, trace, "gtua", card); - - result = read_conf_data(CARD_DDEV(card), (void **) &prcd, &length); - if (result) { - sprintf(dbf_text, "rcd%4x", result); - QETH_DBF_TEXT3(0, trace, dbf_text); - PRINT_ERR("read_conf_data for device %s returned %i\n", - CARD_DDEV_ID(card), result); - return result; - } - - card->chpid = prcd[30]; - card->unit_addr2 = prcd[31]; - card->cula = prcd[63]; - card->is_guest_lan= ((prcd[0x10] == _ascebc['V']) && - (prcd[0x11] == _ascebc['M'])); - - 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); - - return 0; -} - -static int -qeth_send_nops(struct qeth_card *card) -{ - int result, result2; - 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(cdev) \ -do { \ - QETH_DBF_TEXT3(0, trace, "snnp"); \ - QETH_DBF_TEXT3(0, trace, cdev->dev.bus_id); \ -\ - spin_lock_irqsave(get_ccwdev_lock(cdev),saveflags); \ - ccw_device_set_options(cdev, 0); \ - result=ccw_device_start(cdev,&card->dma_stuff->write_ccw, \ - NOP_STATE,0,0); \ - if (result) { \ - qeth_delay_millis(QETH_WAIT_BEFORE_2ND_DOIO); \ - result2=ccw_device_start(cdev,&card->dma_stuff->write_ccw, \ - NOP_STATE,0,0); \ - PRINT_WARN("qeth_send_nops on device %s: do_IO returned %i, " \ - "next try returns %i\n", \ - cdev->dev.bus_id,result,result2); \ - result=result2; \ - } \ - spin_unlock_irqrestore(get_ccwdev_lock(cdev),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_RDEV(card)); - DO_SEND_NOP(CARD_WDEV(card)); - DO_SEND_NOP(CARD_DDEV(card)); - -exit: - return result; -} - -static void -qeth_clear_card_structures(struct qeth_card *card) -{ - int i, j; - - if (!card) { - QETH_DBF_TEXT2(0, trace, "clrCRDnc"); - return; - } - - QETH_DBF_CARD3(0, trace, "clcs", card); - - atomic_set(&card->is_startlaned, 0); - - for (i = 0; i < QETH_MAX_QUEUES; i++) { - card->send_state[i] = SEND_STATE_DONT_PACK; - card->outbound_first_free_buffer[i] = 0; - atomic_set(&card->outbound_used_buffers[i], 0); - atomic_set(&card->outbound_ringbuffer_lock[i], 0); - - for (j = 0; j < QDIO_MAX_BUFFERS_PER_Q; j++) { - card->outbound_buffer_send_state[i][j] = - SEND_STATE_DONT_PACK; - card->send_retries[i][j] = 0; - - if (i < card->no_queues) { - card->outbound_ringbuffer[i]-> - ringbuf_element[j].next_element_to_fill = 0; - card->outbound_bytes_in_buffer[i] = 0; - skb_queue_head_init(&card-> - outbound_ringbuffer[i]-> - ringbuf_element[j]. - skb_list); - } - } - } - - for (i = 0; i < card->options.inbound_buffer_count; i++) { - xchg((int *) &card->inbound_buffer_pool_entry_used[i], - BUFFER_UNUSED); - } - - spin_lock_init(&card->requeue_input_lock); - atomic_set(&card->requeue_position, 0); - atomic_set(&card->requeue_counter, 0); - - card->seqno.trans_hdr = 0; - card->seqno.pdu_hdr = 0; - card->seqno.pdu_hdr_ack = 0; - card->seqno.ipa = 0; - - qeth_clear_ifa4_list(&card->ip_current_state.ip_ifa); - qeth_clear_ifa4_list(&card->ip_new_state.ip_ifa); - qeth_clear_ifamc_list(&card->ip_mc_current_state.ipm_ifa); - qeth_clear_ifamc_list(&card->ip_mc_new_state.ipm_ifa); - -#ifdef QETH_IPV6 - qeth_clear_ifa6_list(&card->ip_current_state.ip6_ifa); - qeth_clear_ifa6_list(&card->ip_new_state.ip6_ifa); - qeth_clear_ifamc_list(&card->ip_mc_current_state.ipm6_ifa); - qeth_clear_ifamc_list(&card->ip_mc_new_state.ipm6_ifa); -#endif /* QETH_IPV6 */ -} - -static void -qeth_init_input_buffers(struct qeth_card *card) -{ - int i; - - /* slowly, slowly (we don't want to enqueue all buffers - * at one time) */ - for (i = 0; i < QDIO_MAX_BUFFERS_PER_Q; i++) { - atomic_set(&card->inbound_buffer_refcnt[i], 1); - } - for (i = 0; i < QDIO_MAX_BUFFERS_PER_Q; i++) { - atomic_set(&card->inbound_buffer_refcnt[i], 0); - /* only try to queue as many buffers as we have at all */ - if (i < card->options.inbound_buffer_count) - qeth_queue_input_buffer(card,i,0); - } - qdio_synchronize(CARD_DDEV(card), QDIO_FLAG_SYNC_INPUT, 0); -} - -/* initializes all the structures for a card */ -static int -qeth_hardsetup_card(struct qeth_card *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)) { - QETH_DBF_CARD2(1, trace, "hscd", card); - PRINT_ALL("card is already hardsetup.\n"); - return 0; - } - - cleanup_qdio = in_recovery; /* if we are in recovery, we clean - the qdio stuff up */ - - down(&card->hardsetup_sema); - atomic_set(&card->write_busy, 0); - - do { - if (in_recovery) { - PRINT_STUPID("qeth: recovery: quiescing %s...\n", - card->dev_name); - QETH_DBF_CARD2(0, trace, "Rqsc", card); - 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); - QETH_DBF_CARD2(0, trace, "RQsc", card); - 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 */ - spin_lock_irqsave(get_ccwdev_lock(CARD_RDEV(card)), flags); - qeth_issue_next_read(card); - spin_unlock_irqrestore(get_ccwdev_lock(CARD_RDEV(card)), 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) { - QETH_DBF_CARD2(0, trace, "hsnr", card); - PRINT_ERR("qeth: recovery not successful on device " - "%s/%s/%s; giving up.\n", - CARD_RDEV_ID(card), - CARD_WDEV_ID(card), CARD_DDEV_ID(card)); - 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; - - card->ipa_timeout = qeth_get_ipa_timeout(card->type); - } - - atomic_set(&card->is_hardsetup, 1); - atomic_set(&card->is_softsetup, 0); - atomic_set(&card->startlan_attempts, 1); - - for (q = 0; q < card->no_queues; q++) - card->send_state[q] = SEND_STATE_DONT_PACK; - - /* we need to know first, whether we should include a value - * into eui-64 address generation */ - QETH_DBF_TEXT2(0, trace, "qipassi4"); - r = qeth_send_qipassist(card, 4); - if (r) { - PRINT_WARN("couldn't send QIPASSIST4 on %s: " - "0x%x\n", card->dev_name, r); - sprintf(dbf_text, "QIP4%4x", r); - QETH_DBF_TEXT2(0, trace, dbf_text); - } - - sprintf(dbf_text, "%4x%4x", card->ipa_supported, card->ipa_enabled); - QETH_DBF_TEXT2(0, trace, dbf_text); - - qeth_get_unique_id(card); - - /* print out status */ - if (in_recovery) { - qeth_clear_card_structures(card); - qeth_init_input_buffers(card); - QETH_DBF_TEXT1(0, trace, "RECOVSUC"); - PRINT_INFO("qeth: recovered device %s/%s/%s (%s) " - "successfully.\n", - CARD_RDEV_ID(card), - CARD_WDEV_ID(card), - CARD_DDEV_ID(card), 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; - } - /* 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 %s/%s/%s is a%s card%s%s%s\n" - "with link type %s (portname: %s)\n", - CARD_RDEV_ID(card), - CARD_WDEV_ID(card), - CARD_DDEV_ID(card), - 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 %s/%s/%s is a%s " - "card%s%s%s\nwith link type %s " - "(no portname needed by interface).\n", - CARD_RDEV_ID(card), - CARD_WDEV_ID(card), - CARD_DDEV_ID(card), - 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 %s/%s/%s is a%s " - "card%s%s%s\nwith link type %s.\n", - CARD_RDEV_ID(card), - CARD_WDEV_ID(card), - CARD_DDEV_ID(card), - 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: - up(&card->hardsetup_sema); - return result; -} - -static int -qeth_reinit_thread(void *param) -{ - struct qeth_card *card = (struct qeth_card *) param; - int already_registered; - int already_hardsetup; - int retry = QETH_RECOVERY_HARDSETUP_RETRY; - int result; - char name[15]; - - QETH_DBF_CARD1(0, trace, "RINI", card); - - /* set a nice name ... */ - sprintf(name, "qethrinid%s", CARD_BUS_ID(card)); - daemonize(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); - - 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_down_trylock_nonbusy(card, &card->softsetup_sema)) { - atomic_set(&card->escape_softsetup, 0); - goto out; - } - atomic_set(&card->escape_softsetup, 0); - if (atomic_read(&card->shutdown_phase)) { - up(&card->softsetup_sema); - 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"); - PRINT_ERR("qeth: RECOVERY WAS NOT SUCCESSFUL ON %s " - "(%s/%s/%s), GIVING UP, " - "OUTGOING PACKETS WILL BE DISCARDED!\n", - card->dev_name, - CARD_RDEV_ID(card), - CARD_WDEV_ID(card), - CARD_DDEV_ID(card)); - /* early leave hard_start_xmit! */ - atomic_set(&card->is_startlaned, 0); - qeth_wakeup_procfile(); - } else { - QETH_DBF_TEXT1(0, trace, "ri-sftst"); - qeth_softsetup_card(card, QETH_LOCK_ALREADY_HELD); - up(&card->softsetup_sema); - - if (!already_registered) { - QETH_DBF_TEXT1(0, trace, "ri-regcd"); - qeth_register_netdev(card); - } - qeth_restore_dev_flag_state(card); - netif_wake_queue(card->dev); - qeth_wakeup_procfile(); - } - spin_unlock(&setup_lock); - } -out: - atomic_set(&card->in_recovery, 0); - 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(struct qeth_card *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 = QETH_PRIOQ_DEFAULT; - card->options.default_queue = QETH_DEFAULT_QUEUE; - card->options.inbound_buffer_count = DEFAULT_BUFFER_COUNT; - card->options.polltime = QETH_MAX_INPUT_THRESHOLD; - 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; -} - -static void qeth_setup(struct net_device *dev) -{ - 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->set_multicast_list = qeth_set_multicast_list; - dev->set_mac_address = qeth_set_mac_address; - dev->neigh_setup = qeth_neigh_setup; - dev->addr_len = OSA_ADDR_LEN; /* is ok for eth, tr, atm lane */ - SET_MODULE_OWNER(dev); -} - -static int -qeth_alloc_card_stuff(struct qeth_card *card) -{ - if (!card) - return -EINVAL; - - QETH_DBF_TEXT3(0, trace, "alccrdst"); - - card->dma_stuff = - (struct qeth_dma_stuff *) kmalloc(sizeof (struct qeth_dma_stuff), - GFP_KERNEL | GFP_DMA); - if (!card->dma_stuff) - goto exit_dma; - memset(card->dma_stuff, 0, sizeof (struct qeth_dma_stuff)); - - card->dma_stuff->recbuf = (char *) kmalloc(QETH_BUFSIZE, - GFP_KERNEL | 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_KERNEL | GFP_DMA); - if (!card->dma_stuff->sendbuf) - goto exit_dma2; - memset(card->dma_stuff->sendbuf, 0, QETH_BUFSIZE); - - card->dev = alloc_netdev(0, "", qeth_setup); - if (!card->dev) - goto exit_dev; - - 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)); - - /* setup net_device stuff */ - card->dev->priv = card; - - /* setup net_device_stats stuff */ - /* =nothing yet */ - - return 0; - - /* these are quick exits in case of failures of the kmallocs */ -exit_stats: - free_netdev(card->dev); -exit_dev: - kfree(card->dma_stuff->sendbuf); -exit_dma2: - kfree(card->dma_stuff->recbuf); -exit_dma1: - kfree(card->dma_stuff); -exit_dma: - return -ENOMEM; -} - -static struct qeth_card * -qeth_alloc_card(void) -{ - struct qeth_card *card; - - QETH_DBF_TEXT3(0, trace, "alloccrd"); - card = (struct qeth_card *) vmalloc(sizeof (struct qeth_card)); - if (!card) - return NULL; - memset(card, 0, sizeof (struct qeth_card)); - init_waitqueue_head(&card->wait_q); - init_waitqueue_head(&card->ioctl_wait_q); - - qeth_fill_qeth_card_options(card); - - init_MUTEX(&card->softsetup_sema); - init_MUTEX(&card->hardsetup_sema); - spin_lock_init(&card->ioctl_lock); -#ifdef QETH_VLAN - spin_lock_init(&card->vlan_lock); - card->vlangrp = NULL; -#endif - card->unique_id = 0; - sema_init(&card->reinit_thread_sem, 0); - up(&card->reinit_thread_sem); - - /* setup card stuff */ - card->ip_current_state.ip_ifa = NULL; - card->ip_new_state.ip_ifa = NULL; - card->ip_mc_current_state.ipm_ifa = NULL; - card->ip_mc_new_state.ipm_ifa = NULL; - -#ifdef QETH_IPV6 - card->ip_current_state.ip6_ifa = NULL; - card->ip_new_state.ip6_ifa = NULL; - card->ip_mc_current_state.ipm6_ifa = NULL; - card->ip_mc_new_state.ipm6_ifa = NULL; -#endif /* QETH_IPV6 */ - - card->csum_enable_mask = IPA_CHECKSUM_DEFAULT_ENABLE_MASK; - - /* and return to the sender */ - return card; - -} - -static int -qeth_init_ringbuffers1(struct qeth_card *card) -{ - int i, j; - - QETH_DBF_CARD3(0, trace, "irb1", card); - - for (i = 0; i < card->no_queues; i++) { - card->outbound_ringbuffer[i] = - vmalloc(sizeof (struct qeth_ringbuffer)); - 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 (struct qeth_ringbuffer)); - for (j = 0; j < QDIO_MAX_BUFFERS_PER_Q; j++) - skb_queue_head_init(&card->outbound_ringbuffer[i]-> - ringbuf_element[j].skb_list); - } - - return 0; -} - -static int -qeth_init_ringbuffers2(struct qeth_card *card) -{ - int i, j; - - QETH_DBF_CARD3(0, trace, "irb2", card); - - for (i = 0; i < card->options.inbound_buffer_count; i++) { - for (j = 0; j < BUFFER_MAX_ELEMENTS; j++) { - card->inbound_buffer_pool_entry[i][j] = - kmalloc(PAGE_SIZE, GFP_KERNEL); - if (!card->inbound_buffer_pool_entry[i][j]) { - goto out; - } - } - card->inbound_buffer_pool_entry_used[i] = BUFFER_UNUSED; - } - - spin_lock_init(&card->requeue_input_lock); - - return 0; -out: - for (i = 0; i < card->options.inbound_buffer_count; i++) { - for (j = 0; j < QDIO_MAX_ELEMENTS_PER_BUFFER; j++) { - if (card->inbound_buffer_pool_entry[i][j]) { - if (j < BUFFER_MAX_ELEMENTS) - kfree(card-> - inbound_buffer_pool_entry[i][j]); - card->inbound_buffer_pool_entry[i][j] = NULL; - } - } - } - for (i = 0; i < card->no_queues; i++) { - vfree(card->outbound_ringbuffer[i]); - card->outbound_ringbuffer[i] = NULL; - } - return -ENOMEM; - -} - -/* also locked from outside (setup_lock) */ -static void -qeth_insert_card_into_list(struct qeth_card *card) -{ - QETH_DBF_CARD3(0, trace, "icil", card); - - write_lock(&list_lock); - card->next = firstcard; - firstcard = card; - write_unlock(&list_lock); -} - -static int -qeth_determine_card_type(struct qeth_card *card) -{ - int i = 0; - char dbf_text[15]; - - while (known_devices[i][4]) { - if ((CARD_RDEV(card)->id.dev_type == known_devices[i][2]) && - (CARD_RDEV(card)->id.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]; - QETH_DBF_TEXT2(0, setup, CARD_BUS_ID(card)); - sprintf(dbf_text, "ctyp%4x", card->type); - QETH_DBF_TEXT2(0, setup, dbf_text); - return 0; - } - i++; - } - card->type = QETH_CARD_TYPE_UNKNOWN; - QETH_DBF_TEXT2(0, setup, CARD_BUS_ID(card)); - sprintf(dbf_text, "ctypUNKN"); - QETH_DBF_TEXT2(0, setup, dbf_text); - PRINT_ERR("unknown card type on device %s\n", CARD_BUS_ID(card)); - 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 void -__qeth_correct_routing_status_v4(struct qeth_card *card) -{ - if (card->options.routing_type4 == NO_ROUTER) - return; - - 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)) { - PRINT_WARN("routing not applicable, reset " - "routing status for ipv4. \n"); - card->options.routing_type4 = NO_ROUTER; - } - 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)) { - PRINT_WARN("routing not applicable, reset " - "routing status for ipv4. (Did you mean " - "primary_router or secondary_router?)\n"); - card->options.routing_type4 = NO_ROUTER; - } - } -} - -static void -__qeth_correct_routing_status_v6(struct qeth_card *card) -{ -#ifdef QETH_IPV6 - if (card->options.routing_type6 == NO_ROUTER) - return; - if (card->type == QETH_CARD_TYPE_IQD) { - /* if it's not a mc router, it's no router */ - if ((card->options.routing_type6 == PRIMARY_ROUTER) || - (card->options.routing_type6 == SECONDARY_ROUTER)) { - PRINT_WARN("routing not applicable, reset " - "routing status for ipv6. \n"); - card->options.routing_type6 = NO_ROUTER; - } - 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_type6 == MULTICAST_ROUTER)) || - (card->options.routing_type6 == PRIMARY_CONNECTOR) || - (card->options.routing_type6 == SECONDARY_CONNECTOR)) { - PRINT_WARN("routing not applicable, reset " - "routing status for ipv6. (Did you mean " - "primary_router or secondary_router?)\n"); - card->options.routing_type6 = NO_ROUTER; - } - } -#endif /* QETH_IPV6 */ -} - -static void -qeth_correct_routing_status(struct qeth_card *card) -{ - __qeth_correct_routing_status_v4(card); - __qeth_correct_routing_status_v6(card); -} - -static int -qeth_init_netdev(struct qeth_card *card) -{ - - int result; - char dbf_text[15]; - - result = qeth_register_netdev(card); - if (result) { - PRINT_ALL(" register_netdev %s -- rc=%i\n", - card->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, card->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_WORK(&card->tqueue, qeth_softsetup_thread_starter, card); - schedule_work(&card->tqueue); -out: - qeth_wakeup_procfile(); - return result; - -} - -static int -qeth_dev_event(struct notifier_block *this, unsigned long event, void *ptr) -{ - struct qeth_card *card; - struct net_device *dev = (struct net_device *) ptr; - - QETH_DBF_TEXT3(0, trace, "devevent"); - QETH_DBF_HEX3(0, trace, &event, sizeof (unsigned long)); - QETH_DBF_HEX3(0, trace, &dev, sizeof (void *)); - - card = __qeth_get_card_from_dev(dev); - 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) -{ - struct qeth_card *card; - struct in_ifaddr *ifa = (struct in_ifaddr *) ptr; - struct net_device *dev = ifa->ifa_dev->dev; - char dbf_text[15]; - - QETH_DBF_TEXT3(0, trace, "ipevent"); - QETH_DBF_HEX3(0, trace, &event, sizeof (unsigned long)); - QETH_DBF_HEX3(0, trace, &dev, sizeof (void *)); - sprintf(dbf_text, "%08x", ifa->ifa_address); - QETH_DBF_TEXT3(0, trace, dbf_text); - sprintf(dbf_text, "%08x", ifa->ifa_mask); - QETH_DBF_TEXT3(0, trace, dbf_text); - - card = __qeth_get_card_from_dev(dev); - 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) -{ - struct qeth_card *card; - struct inet6_ifaddr *ifa = (struct inet6_ifaddr *) ptr; - struct net_device *dev = ifa->idev->dev; - - QETH_DBF_TEXT3(0, trace, "ip6event"); - QETH_DBF_HEX3(0, trace, &event, sizeof (unsigned long)); - QETH_DBF_HEX3(0, trace, &dev, sizeof (void *)); - QETH_DBF_HEX3(0, trace, ifa->addr.s6_addr, QETH_DBF_TRACE_LEN); - QETH_DBF_HEX3(0, trace, ifa->addr.s6_addr + QETH_DBF_TRACE_LEN, - QETH_DBF_TRACE_LEN); - - card = __qeth_get_card_from_dev(dev); - if (qeth_does_card_exist(card)) { - QETH_DBF_HEX3(0, trace, &card, sizeof (void *)); - qeth_save_dev_flag_state(card); - qeth_start_softsetup_thread(card); - } - - return NOTIFY_DONE; -} -#endif /* QETH_IPV6 */ - -static int -qeth_reboot_event(struct notifier_block *this, unsigned long event, void *ptr) -{ - struct qeth_card *card; - - read_lock(&list_lock); - if (firstcard) { - card = firstcard; - clear_another_one: - if (card->type == QETH_CARD_TYPE_IQD) { - ccw_device_halt(CARD_DDEV(card), 0); - ccw_device_clear(CARD_RDEV(card), 0); - ccw_device_clear(CARD_WDEV(card), 0); - ccw_device_clear(CARD_DDEV(card), 0); - } else { - ccw_device_clear(CARD_DDEV(card), 0); - ccw_device_clear(CARD_RDEV(card), 0); - ccw_device_clear(CARD_WDEV(card), 0); - } - if (card->next) { - card = card->next; - goto clear_another_one; - } - } - 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); -} - -static void __exit -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); - -} - -static int -qeth_procfile_open(struct inode *inode, struct file *file) -{ - int length = 0; - struct qeth_card *card; - char checksum_str[5], queueing_str[14], router_str[8], bufsize_str[4]; - char *buffer; - int rc = 0; - int size; - struct tempinfo *info; - - info = (struct tempinfo *) vmalloc(sizeof (struct tempinfo)); - if (info == NULL) { - PRINT_WARN("No memory available for data\n"); - return -ENOMEM; - } else { - file->private_data = (void *) info; - } - - /* lock all the stuff */ - read_lock(&list_lock); - card = firstcard; - size = 200; /* 2 lines plus some sanity space */ - while (card) { - size += 90; /* if device name is > 10 chars, (should never - happen...), we'll need that */ - card = card->next; - } - - buffer = info->data = (char *) vmalloc(size); - if (info->data == NULL) { - PRINT_WARN("No memory available for data\n"); - vfree(info); - rc = -ENOMEM; - goto out; - } - - QETH_DBF_TEXT2(0, trace, "procread"); - length += sprintf(buffer + length, - "devices CHPID " - "device cardtype port chksum prio-q'ing " - "rtr fsz 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"); - } - - /* FIXME: this is really a mess... */ - -#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 */ - ) { - 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 */ - ) { - 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 */ - ) { - 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_startlaned)) { - length += sprintf(buffer + length, - "%s/%s/%s x%02X %10s %14s %2i" - " +++ CABLE PULLED +++\n", - CARD_RDEV_ID(card), - CARD_WDEV_ID(card), - CARD_DDEV_ID(card), - 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, - "%s/%s/%s x%02X %10s %14s %2i" - " %2s %10s %3s %3s %3i\n", - CARD_RDEV_ID(card), - CARD_WDEV_ID(card), - CARD_DDEV_ID(card), - 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.inbound_buffer_count); - } - card = card->next; - } - -out: - info->len = length; - /* unlock all the stuff */ - read_unlock(&list_lock); - return rc; -} - -#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; - struct qeth_card *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 %s/%s/%s (%s):\n", - CARD_RDEV_ID(card), - CARD_WDEV_ID(card), - CARD_DDEV_ID(card), 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]; - struct ipato_entry *ipato_entry; - struct qeth_card *card; - struct qeth_vipa_entry *vipa_entry; - int rc = 0; - struct tempinfo *info; - int size; - char entry_type[5]; - - info = (struct tempinfo *) vmalloc(sizeof (struct tempinfo)); - 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 */ - spin_lock(&ipato_list_lock); - 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) { - read_lock(&card->vipa_list_lock); - vipa_entry = card->vipa_list; - while (vipa_entry) { - vipa_entry = vipa_entry->next; - size += 64; - } - /*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) { - /*read_lock(&card->vipa_list_lock); don't lock it here */ - read_unlock(&card->vipa_list_lock); - card = card->next; - } - read_unlock(&list_lock); - 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; - struct tempinfo *p_info = (struct tempinfo *) file->private_data; - - if (*offset >= p_info->len) { - return 0; - } else { - len = __min(user_len, (p_info->len - *offset)); - if (copy_to_user(user_buf, &(p_info->data[*offset]), len)) - return -EFAULT; - (*offset) += len; - return len; - } -} - -/* ATT: this is also the procfile release function for the ipato - * procfs entry */ -static int -qeth_procfile_release(struct inode *inode, struct file *file) -{ - struct tempinfo *p_info = (struct tempinfo *) file->private_data; - - if (p_info) { - if (p_info->data) - vfree(p_info->data); - vfree(p_info); - } - - return 0; -} - -static ssize_t -qeth_ipato_procfile_write(struct file *file, - const char *user_buffer, - size_t user_len, loff_t * offset) -{ - int add, version; - char text[33]; - __u8 addr[16]; - int len, i, flag; - int mask_bits; - char *buffer; - int dev_name_there; - char *dev_name_ptr; - struct qeth_card *card; -#define BUFFER_LEN (10+32+1+5+1+DEV_NAME_LEN+1) - - if (*offset > 0) - return user_len; - buffer = - vmalloc(__max(__max(user_len + 1, BUFFER_LEN), QETH_DBF_MISC_LEN)); - - if (buffer == NULL) - return -ENOMEM; - /* BUFFER_LEN=command incl. blank+addr+slash+mask_bits+ - * colon+DEV_NAME_LEN+zero */ - memset(buffer, 0, BUFFER_LEN); - - if (copy_from_user(buffer, user_buffer, user_len)) { - vfree(buffer); - return -EFAULT; - } - - QETH_DBF_TEXT2(0, trace, "ipatowri"); - QETH_DBF_TEXT2(0, misc, buffer); - if (!strncmp(buffer, "inv4", 4)) { - ipato_inv4 = 1 - ipato_inv4; - goto out; - } - if (!strncmp(buffer, "inv6", 4)) { - ipato_inv6 = 1 - ipato_inv6; - goto out; - } - if ((!strncmp(buffer, "add4 ", 5)) || - (!strncmp(buffer, "add6 ", 5)) || - (!strncmp(buffer, "del4 ", 5)) || (!strncmp(buffer, "del6 ", 5))) { - text[8] = 0; - text[32] = 0; - add = !strncmp(buffer, "add", 3); - version = (buffer[3] == '4') ? 4 : 6; - len = (version == 4) ? 8 : 32; - strncpy(text, buffer + 5, len); - if (qeth_convert_text_to_addr(version, text, addr)) { - PRINT_ERR("error in parsing ipato information " - "(addr)\n"); - goto out; - } - strncpy(text, buffer + 5 + len + 1, 10); - /* we prepare mask_bits for qeth_getints */ - dev_name_there = 0; - for (i = 5 + len + 1; i < BUFFER_LEN; i++) { - if (*(buffer + i) == '\n') { - *(buffer + i) = 0; - break; - } - if (*(buffer + i) == ':') { - *(buffer + i) = 0; /* so that qeth_getint works */ - dev_name_there = i; - break; - } - if (*(buffer + i) == 0) - break; - } - mask_bits = qeth_getint(buffer + 5 + len + 1, 0); - if ((mask_bits < 0) - || (mask_bits > ((version == 4) ? 32 : 128))) { - PRINT_ERR("error in parsing ipato information " - "(mask bits)\n"); - goto out; - } - if (dev_name_there) { - dev_name_ptr = buffer + dev_name_there + 1; - /* wipe out the linefeed */ - for (i = dev_name_there + 1; - i < dev_name_there + 1 + DEV_NAME_LEN + 1; i++) - if (*(buffer + i) == '\n') - *(buffer + i) = 0; - } else - dev_name_ptr = NULL; - - if (add) - qeth_add_ipato_entry(version, addr, mask_bits, - dev_name_ptr); - else - qeth_del_ipato_entry(version, addr, mask_bits, - dev_name_ptr); - goto out; - } - if ((!strncmp(buffer, "add_vipa4 ", 10)) || - (!strncmp(buffer, "add_rxip4 ", 10)) || - (!strncmp(buffer, "add_vipa6 ", 10)) || - (!strncmp(buffer, "add_rxip6 ", 10)) || - (!strncmp(buffer, "del_vipa4 ", 10)) || - (!strncmp(buffer, "del_rxip4 ", 10)) || - (!strncmp(buffer, "del_vipa6 ", 10)) || - (!strncmp(buffer, "del_rxip6 ", 10))) { - text[8] = 0; - text[32] = 0; - add = !strncmp(buffer, "add", 3); - flag = - (!strncmp(buffer + 4, "vipa", 4)) ? IPA_SETIP_VIPA_FLAGS : - IPA_SETIP_TAKEOVER_FLAGS; - version = (buffer[8] == '4') ? 4 : 6; - len = (version == 4) ? 8 : 32; - strncpy(text, buffer + 10, len); - if (qeth_convert_text_to_addr(version, text, addr)) { - PRINT_ERR("error in parsing vipa/rxip information " - "(addr)\n"); - goto out; - } - if (*(buffer + 10 + len) != ':') { - PRINT_ERR("error in parsing vipa/rxip information " - "(no interface)\n"); - goto out; - } - /* interface name is at buffer+10+len+1 */ - /* wipe out the \n */ - for (i = 10 + len + 1; i < 10 + len + 1 + DEV_NAME_LEN + 1; i++) - if (*(buffer + i) == '\n') - *(buffer + i) = 0; - card = qeth_get_card_by_name(buffer + 10 + len + 1); - if (!card) { - PRINT_ERR("error in parsing vipa/rxip information " - "(unknown interface)\n"); - goto out; - } - if (add) - i = qeth_add_vipa_entry(card, version, addr, flag); - else - i = qeth_del_vipa_entry(card, version, addr, flag); - if (!i) - qeth_start_softsetup_thread(card); - goto out; - } - PRINT_ERR("unknown ipato information command\n"); -out: - vfree(buffer); - *offset = *offset + user_len; -#undef BUFFER_LEN - return user_len; -} - -static int -qeth_procfile_getinterfaces(unsigned long arg) -{ - struct qeth_card *card; - - char parms[16]; - char *buffer; - char *buffer_pointer; - __u32 version, valid_fields, qeth_version, number_of_devices, if_index; - __u32 data_size, data_len; - unsigned long ioctl_flags; - int result = 0; - - /* the struct of version 0 is: - typedef struct dev_list - { - char device_name[IFNAME_MAXLEN]; // OSA-Exp device name (e.g. eth0) - __u32 if_index; // interface index from kernel - __u32 flags; // device charateristics - } __attribute__((packed)) DEV_LIST; - - typedef struct osaexp_dev_ver0 - { - __u32 version; // structure version - __u32 valid_fields; // bitmask of fields that are really filled - __u32 qeth_version; // qeth driver version - __u32 number_of_devices; // number of OSA Express devices - struct dev_list devices[0]; // list of OSA Express devices - } __attribute__((packed)) OSAEXP_DEV_VER0; - */ - - version = 0; - valid_fields = 0; - qeth_version = 0; - number_of_devices = 0; - - if (copy_from_user((void *) parms, (void *) arg, sizeof (parms))) - return -EFAULT; - memcpy(&data_size, parms, sizeof (__u32)); - - if (!(data_size > 0)) - return -EFAULT; - if (data_size > IOCTL_MAX_TRANSFER_SIZE) - return -EFAULT; - if (!access_ok(VERIFY_WRITE, (void *) arg, data_size)) - return -EFAULT; - - read_lock(&list_lock); - card = firstcard; -#define IOCTL_USER_STRUCT_SIZE (DEV_NAME_LEN*sizeof(char)) + \ - sizeof(__u32) + sizeof(__u32) - while (card) { - if (card->type == QETH_CARD_TYPE_OSAE) - number_of_devices = - number_of_devices + IOCTL_USER_STRUCT_SIZE; - card = card->next; - } -#undef IOCTL_USER_STRUCT_SIZE - if ((number_of_devices + 4 * sizeof (__u32)) >= data_size) { - result = -ENOMEM; - goto out; - } - - number_of_devices = 0; - card = firstcard; - buffer = (char *) vmalloc(data_size); - if (!buffer) { - result = -EFAULT; - goto out; - } - buffer_pointer = ((char *) (buffer)) + (4 * sizeof (__u32)); - while (card) { - if ((card->type == QETH_CARD_TYPE_OSAE) && - (atomic_read(&card->is_hardsetup)) && - (atomic_read(&card->is_registered))) { - - memcpy(buffer_pointer, card->dev_name, DEV_NAME_LEN); - buffer_pointer = buffer_pointer + DEV_NAME_LEN; - if_index = card->dev->ifindex; - memcpy(buffer_pointer, &if_index, sizeof (__u32)); - buffer_pointer = buffer_pointer + sizeof (__u32); - memcpy(buffer_pointer, &ioctl_flags, sizeof (__u32)); - buffer_pointer = buffer_pointer + sizeof (__u32); - number_of_devices = number_of_devices + 1; - } - card = card->next; - } - - /* we copy the real size */ - data_len = buffer_pointer - buffer; - - buffer_pointer = buffer; - /* copy the header information at the beginning of the buffer */ - memcpy(buffer_pointer, &version, sizeof (__u32)); - memcpy(((char *) buffer_pointer) + sizeof (__u32), &valid_fields, - sizeof (__u32)); - memcpy(((char *) buffer_pointer) + (2 * sizeof (__u32)), &qeth_version, - sizeof (__u32)); - memcpy(((char *) buffer_pointer) + (3 * sizeof (__u32)), - &number_of_devices, sizeof (__u32)); - if (copy_to_user((char *) arg, buffer, data_len)) - result = -EFAULT; - vfree(buffer); -out: - read_unlock(&list_lock); - return result; - -#undef PARMS_BUFFERLENGTH - -}; - -static int -qeth_procfile_interfacechanges(unsigned long arg) -{ - return qeth_sleepon_procfile(); - -} - -static int -qeth_procfile_ioctl(struct inode *inode, struct file *file, - unsigned int cmd, unsigned long arg) -{ - - int result; - if (!down_interruptible(&qeth_procfile_ioctl_lock)) { - switch (cmd) { - case QETH_IOCPROC_OSAEINTERFACES: - result = qeth_procfile_getinterfaces(arg); - break; - case QETH_IOCPROC_INTERFACECHANGES: - result = qeth_procfile_interfacechanges(arg); - break; - default: - result = -EOPNOTSUPP; - } - up(&qeth_procfile_ioctl_lock); - } else - result = -ERESTARTSYS; - return result; -}; - -static struct file_operations qeth_procfile_fops = { - .owner = THIS_MODULE, - .ioctl = qeth_procfile_ioctl, - .read = qeth_procfile_read, - .open = qeth_procfile_open, - .release = qeth_procfile_release, -}; - -static struct proc_dir_entry *qeth_proc_file; - -static struct file_operations qeth_ipato_procfile_fops = { - .owner = THIS_MODULE, - .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 inline void -__qeth_add_procfs_perf(void) -{ -#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 */ -} - -static void -qeth_add_procfs_entries(void) -{ - proc_file_registration = 0; - qeth_proc_file = create_proc_entry(QETH_PROCFILE_NAME, - S_IFREG | 0444, &proc_root); - if (qeth_proc_file) { - qeth_proc_file->proc_fops = &qeth_procfile_fops; - sema_init(&qeth_procfile_ioctl_sem, - PROCFILE_SLEEP_SEM_MAX_VALUE); - sema_init(&qeth_procfile_ioctl_lock, - PROCFILE_IOCTL_SEM_MAX_VALUE); - } else - proc_file_registration = -1; - - if (proc_file_registration) - PRINT_WARN("was not able to register proc-file (%i).\n", - proc_file_registration); - proc_ipato_file_registration = 0; - qeth_ipato_proc_file = create_proc_entry(QETH_IPA_PROCFILE_NAME, - S_IFREG | 0644, &proc_root); - if (qeth_ipato_proc_file) { - qeth_ipato_proc_file->proc_fops = &qeth_ipato_procfile_fops; - } else - proc_ipato_file_registration = -1; - - if (proc_ipato_file_registration) - PRINT_WARN("was not able to register ipato-proc-file (%i).\n", - proc_ipato_file_registration); - __qeth_add_procfs_perf(); -} - -static void __exit -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 */ -} - -static int -qeth_register_dbf_views(void) -{ - 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) - return -ENOMEM; - - 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) - return -ENOMEM; - - 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) - return -ENOMEM; - - 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) - return -ENOMEM; - - 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) - return -ENOMEM; - - 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) - return -ENOMEM; - - 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) - return -ENOMEM; - - debug_register_view(qeth_dbf_trace, &debug_hex_ascii_view); - debug_set_level(qeth_dbf_trace, QETH_DBF_TRACE_LEVEL); - - return 0; -} - -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); -} - -#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 - FIXME*/ - 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 int -qeth_get_internal_functions(void) -{ - struct net_device *dev; -#ifdef CONFIG_NET_ETHERNET - dev = alloc_etherdev(0); - if (!dev) { - PRINT_ERR("Not enough memory for internal functions.\n"); - return -ENOMEM; - } - 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; - free_netdev(dev); -#endif -#ifdef CONFIG_TR - dev = alloc_trdev(0); - if (!dev) { - PRINT_ERR("Not enough memory for internal functions.\n"); - return -ENOMEM; - } - qeth_my_tr_header = dev->hard_header; - qeth_my_tr_rebuild_header = dev->rebuild_header; - free_netdev(dev); -#endif - return 0; -} - -static struct ccw_device_id qeth_ids[] = { - {CCW_DEVICE(0x1731, 0x01), driver_info:QETH_CARD_TYPE_OSAE}, - {CCW_DEVICE(0x1731, 0x05), driver_info:QETH_CARD_TYPE_IQD}, - {}, -}; - -MODULE_DEVICE_TABLE(ccw, qeth_ids); - -static struct ccw_driver qeth_ccw_driver = { - .name = "qeth", - .ids = qeth_ids, - .probe = ccwgroup_probe_ccwdev, - .remove = ccwgroup_remove_ccwdev, -}; - -static struct device *qeth_root_dev; - -static struct ccwgroup_driver qeth_ccwgroup_driver; -static ssize_t -qeth_group_store(struct device_driver *drv, const char *buf, size_t count) -{ - const char *start, *end; - char bus_ids[3][BUS_ID_SIZE], *argv[3]; - int i; - - pr_debug("group_store %s\n", buf); - start = buf; - for (i = 0; i < 3; i++) { - static const char delim[] = { ',', ',', '\n' }; - int len; - - if (!(end = strchr(start, delim[i]))) - return count; - len = min_t(ptrdiff_t, BUS_ID_SIZE, end - start); - strncpy(bus_ids[i], start, len); - bus_ids[i][len] = '\0'; - start = end + 1; - argv[i] = bus_ids[i]; - } - pr_debug("creating qeth group device from '%s', '%s' and '%s'\n", - bus_ids[0], bus_ids[1], bus_ids[2]); - ccwgroup_create(qeth_root_dev, qeth_ccwgroup_driver.driver_id, - &qeth_ccw_driver, 3, argv); - return count; -} - -static DRIVER_ATTR(group, 0200, 0, qeth_group_store); - -static ssize_t -qeth_bufcnt_show(struct device *dev, char *buf) -{ - struct qeth_card *card = dev->driver_data; - - if (!card) - return -EINVAL; - - return sprintf(buf, "%i\n", card->options.inbound_buffer_count); -} - -static ssize_t -qeth_bufcnt_store(struct device *dev, const char *buf, size_t count) -{ - struct qeth_card *card = dev->driver_data; - unsigned long cnt; - char *tmp; - - if (!card) - return count; - - if (atomic_read(&card->is_softsetup)) - return -EPERM; - - cnt = simple_strtoul(buf, &tmp, 16); - cnt = (cnt < BUFCNT_MIN) ? BUFCNT_MIN : - ((cnt > BUFCNT_MAX) ? BUFCNT_MAX : cnt); - card->options.inbound_buffer_count = cnt; - - return count; -} - -static DEVICE_ATTR(bufcnt, 0644, qeth_bufcnt_show, qeth_bufcnt_store); - -static ssize_t -qeth_portname_show(struct device *dev, char *buf) -{ - struct qeth_card *card = dev->driver_data; - char tmp[9]; - int i; - - if (!card) - return -EINVAL; - - if (card->portname_required) { - sprintf(tmp, "%s", card->options.portname + 1); - for (i = 0; i < 8; i++) - tmp[i] = (char) _ebcasc[(__u8) tmp[i]]; - tmp[8] = 0; - return sprintf(buf, "%s\n", tmp); - } else - return sprintf(buf, "%s\n", "no portname required"); -} - -static ssize_t -qeth_portname_store(struct device *dev, const char *buf, size_t count) -{ - struct qeth_card *card = dev->driver_data; - char *tmp; - int i; - - if (!card) - return count; - - if (atomic_read(&card->is_hardsetup)) - return -EPERM; - - /* Remove trailing '\n'. */ - tmp = strsep((char **) &buf, "\n"); - if ((strlen(tmp) > 8) || (strlen(tmp) < 2)) - return -EINVAL; - - card->options.portname[0] = strlen(tmp); - /* for beauty reasons: */ - for (i = 1; i < 9; i++) - card->options.portname[i] = ' '; - strcpy(card->options.portname + 1, tmp); - for (i = 1; i < 9; i++) - card->options.portname[i] = - _ascebc[(unsigned char)card->options.portname[i]]; - - return count; -} - -static DEVICE_ATTR(portname, 0644, qeth_portname_show, qeth_portname_store); - -static ssize_t -qeth_route4_show(struct device *dev, char *buf) -{ - struct qeth_card *card = dev->driver_data; - - if (!card) - return -EINVAL; - - if (atomic_read(&card->rt4fld)) - return sprintf(buf, "%s\n", "FLD"); - - switch (card->options.routing_type4 & ROUTER_MASK) { - case PRIMARY_ROUTER: - return sprintf(buf, "%s\n", "primary router"); - case SECONDARY_ROUTER: - return sprintf(buf, "%s\n", "secondary router"); - case MULTICAST_ROUTER: - return sprintf(buf, "%s\n", "multicast router"); - case PRIMARY_CONNECTOR: - return sprintf(buf, "%s\n", "primary connector"); - case SECONDARY_CONNECTOR: - return sprintf(buf, "%s\n", "secondary connector"); - default: - return sprintf(buf, "%s\n", "no"); - } -} - -static ssize_t -qeth_route4_store(struct device *dev, const char *buf, size_t count) -{ - struct qeth_card *card = dev->driver_data; - int cnt; - char *tmp; - - if (!card) - return count; - - /* Remove trailing '\n'. */ - tmp = strsep((char **) &buf, "\n"); - cnt = strlen(tmp); - if (!strncmp(tmp, "primary_router", cnt)) { - QETH_DBF_CARD2(0, trace, "pri4", card); - card->options.routing_type4 = - PRIMARY_ROUTER | RESET_ROUTING_FLAG; - } else if (!strncmp(tmp, "secondary_router", cnt)) { - QETH_DBF_CARD2(0, trace, "sec4", card); - card->options.routing_type4 = - SECONDARY_ROUTER | RESET_ROUTING_FLAG; - } else if (!strncmp(tmp, "multicast_router", cnt)) { - QETH_DBF_CARD2(0, trace, "mcr4", card); - card->options.routing_type4 = - MULTICAST_ROUTER | RESET_ROUTING_FLAG; - } else if (!strncmp(tmp, "primary_connector", cnt)) { - QETH_DBF_CARD2(0, trace, "prc4", card); - card->options.routing_type4 = - PRIMARY_CONNECTOR | RESET_ROUTING_FLAG; - } else if (!strncmp(tmp, "secondary_connector", cnt)) { - QETH_DBF_CARD2(0, trace, "scc4", card); - card->options.routing_type4 = - SECONDARY_CONNECTOR | RESET_ROUTING_FLAG; - } else if (!strncmp(tmp, "no_router", cnt)) { - QETH_DBF_CARD2(0, trace, "nor4", card); - card->options.routing_type4 = NO_ROUTER | RESET_ROUTING_FLAG; - } else { - PRINT_WARN("unknown command input in route4 attribute\n"); - return -EINVAL; - } - __qeth_correct_routing_status_v4(card); - atomic_set(&card->enable_routing_attempts4, QETH_ROUTING_ATTEMPTS); - if (atomic_read(&card->is_softsetup)) - qeth_start_softsetup_thread(card); - return count; -} - -static DEVICE_ATTR(route4, 0644, qeth_route4_show, qeth_route4_store); - -static ssize_t -qeth_route6_show(struct device *dev, char *buf) -{ -#ifdef QETH_IPV6 - struct qeth_card *card = dev->driver_data; - - if (!card) - return -EINVAL; - - if (atomic_read(&card->rt6fld)) - return sprintf(buf, "%s\n", "FLD"); - - if (!qeth_is_supported(IPA_IPv6)) - return sprintf(buf, "%s\n", "n/a"); - - switch (card->options.routing_type6 & ROUTER_MASK) { - case PRIMARY_ROUTER: - return sprintf(buf, "%s\n", "primary router"); - case SECONDARY_ROUTER: - return sprintf(buf, "%s\n", "secondary router"); - case MULTICAST_ROUTER: - return sprintf(buf, "%s\n", "multicast router"); - case PRIMARY_CONNECTOR: - return sprintf(buf, "%s\n", "primary connector"); - case SECONDARY_CONNECTOR: - return sprintf(buf, "%s\n", "secondary connector"); - default: - return sprintf(buf, "%s\n", "no"); - } -#endif /* QETH_IPV6 */ - return sprintf(buf, "%s\n", "n/a"); -} - -static ssize_t -qeth_route6_store(struct device *dev, const char *buf, size_t count) -{ -#ifdef QETH_IPV6 - struct qeth_card *card = dev->driver_data; - int cnt; - char *tmp; - - if (!card) - return count; - - /* Remove trailing '\n'. */ - tmp = strsep((char **) &buf, "\n"); - cnt = strlen(tmp); - if (!strncmp(tmp, "primary_router", cnt)) { - QETH_DBF_CARD2(0, trace, "pri6", card); - card->options.routing_type6 = - PRIMARY_ROUTER | RESET_ROUTING_FLAG; - } else if (!strncmp(tmp, "secondary_router", cnt)) { - QETH_DBF_TEXT2(0, trace, "sec6"); - QETH_DBF_CARD2(0, trace, "sec6", card); - card->options.routing_type6 = - SECONDARY_ROUTER | RESET_ROUTING_FLAG; - } else if (!strncmp(tmp, "multicast_router", cnt)) { - QETH_DBF_CARD2(0, trace, "mcr6", card); - card->options.routing_type6 = - MULTICAST_ROUTER | RESET_ROUTING_FLAG; - } else if (!strncmp(tmp, "primary_connector", cnt)) { - QETH_DBF_CARD2(0, trace, "prc6", card); - card->options.routing_type6 = - PRIMARY_CONNECTOR | RESET_ROUTING_FLAG; - } else if (!strncmp(tmp, "secondary_connector", cnt)) { - QETH_DBF_CARD2(0, trace, "scc6", card); - card->options.routing_type6 = - SECONDARY_CONNECTOR | RESET_ROUTING_FLAG; - } else if (!strncmp(tmp, "no_router", cnt)) { - QETH_DBF_CARD2(0, trace, "nor6", card); - card->options.routing_type6 = NO_ROUTER | RESET_ROUTING_FLAG; - } else { - PRINT_WARN("unknown command input in route6 attribute\n"); - return -EINVAL; - } - __qeth_correct_routing_status_v6(card); - atomic_set(&card->enable_routing_attempts6, QETH_ROUTING_ATTEMPTS); - if (atomic_read(&card->is_softsetup)) - qeth_start_softsetup_thread(card); - return count; -#endif /* QETH_IPV6 */ - return -EINVAL; -} - -static DEVICE_ATTR(route6, 0644, qeth_route6_show, qeth_route6_store); - - -static ssize_t -qeth_checksum_show(struct device *dev, char *buf) -{ - struct qeth_card *card = dev->driver_data; - - if (!card) - return -EINVAL; - - switch (card->options.checksum_type) { - case SW_CHECKSUMMING: - return sprintf(buf, "%s\n", "sw"); - case HW_CHECKSUMMING: - return sprintf(buf, "%s\n", "hw"); - default: - return sprintf(buf, "%s\n", "no"); - } -} - -static ssize_t -qeth_checksum_store(struct device *dev, const char *buf, size_t count) -{ - struct qeth_card *card = dev->driver_data; - char *tmp; - int cnt; - - if (!card) - return count; - - if (atomic_read(&card->is_hardsetup)) - return -EPERM; - - /* Remove trailing '\n'. */ - tmp = strsep((char **) &buf, "\n"); - cnt = strlen(tmp); - if (!strncmp(tmp, "sw_checksumming", cnt)) - card->options.checksum_type = SW_CHECKSUMMING; - else if (!strncmp(tmp, "hw_checksumming", cnt)) - card->options.checksum_type = HW_CHECKSUMMING; - else if (!strncmp(tmp, "no_checksumming", cnt)) - card->options.checksum_type = NO_CHECKSUMMING; - else - PRINT_WARN("unknown checksumming type '%s'\n", tmp); - - return count; -} - -static DEVICE_ATTR(checksumming, 0644, qeth_checksum_show, qeth_checksum_store); - -static ssize_t -qeth_prioq_show(struct device *dev, char *buf) -{ - struct qeth_card *card = dev->driver_data; - - if (!card) - return -EINVAL; - - switch (card->options.do_prio_queueing) { - case PRIO_QUEUEING_PREC: - return sprintf(buf, "%s\n", "by precedence"); - case PRIO_QUEUEING_TOS: - return sprintf(buf, "%s\n", "by type of service"); - default: - return sprintf(buf, "always queue %i\n", - card->options.default_queue); - } -} - -static ssize_t -qeth_prioq_store(struct device *dev, const char *buf, size_t count) -{ - struct qeth_card *card = dev->driver_data; - char *tmp; - int cnt; - - if (!card) - return count; - - if (atomic_read(&card->is_hardsetup)) - return -EPERM; - - /* Remove trailing '\n'. */ - tmp = strsep((char **) &buf, "\n"); - cnt = strlen(tmp); - if (!strncmp(tmp, "prio_queueing_prec", cnt)) - card->options.do_prio_queueing = PRIO_QUEUEING_PREC; - else if (!strncmp(tmp, "prio_queueing_tos", cnt)) - card->options.do_prio_queueing = PRIO_QUEUEING_TOS; - else if (!strncmp(tmp, "no_prio_queueing:0", cnt)) { - card->options.do_prio_queueing = NO_PRIO_QUEUEING; - card->options.default_queue = 0; - } else if (!strncmp(tmp, "no_prio_queueing:1", cnt)) { - card->options.do_prio_queueing = NO_PRIO_QUEUEING; - card->options.default_queue = 1; - } else if (!strncmp(tmp, "no_prio_queueing:2", cnt)) { - card->options.do_prio_queueing = NO_PRIO_QUEUEING; - card->options.default_queue = 2; - } else if (!strncmp(tmp, "no_prio_queueing:3", cnt)) { - card->options.do_prio_queueing = NO_PRIO_QUEUEING; - card->options.default_queue = 3; - } else if (!strncmp(tmp, "no_prio_queueing", cnt)) { - card->options.do_prio_queueing = NO_PRIO_QUEUEING; - card->options.default_queue = QETH_DEFAULT_QUEUE; - } else - PRINT_WARN("unknown queueing type '%s'\n", tmp); - - return count; -} - -static DEVICE_ATTR(priority_queueing, 0644, qeth_prioq_show, qeth_prioq_store); - -static ssize_t -qeth_portno_show(struct device *dev, char *buf) -{ - struct qeth_card *card = dev->driver_data; - - if (!card) - return -EINVAL; - - return sprintf(buf, "%i\n", card->options.portno); -} - -static ssize_t -qeth_portno_store(struct device *dev, const char *buf, size_t count) -{ - struct qeth_card *card = dev->driver_data; - char *tmp; - int i; - - if (!card) - return count; - - if (atomic_read(&card->is_hardsetup)) - return -EPERM; - - i = simple_strtoul(buf, &tmp, 16); - if ((i < 0) || (i > MAX_PORTNO)) { - PRINT_ERR("portno %i out of range\n", i); - return -EINVAL; - } - card->options.portno = i; - - return count; -} - -static DEVICE_ATTR(portno, 0644, qeth_portno_show, qeth_portno_store); - -static ssize_t -qeth_polltime_show(struct device *dev, char *buf) -{ - struct qeth_card *card = dev->driver_data; - - if (!card) - return -EINVAL; - - return sprintf(buf, "%i\n", card->options.polltime); -} - -static ssize_t -qeth_polltime_store(struct device *dev, const char *buf, size_t count) -{ - struct qeth_card *card = dev->driver_data; - char *tmp; - int i; - - if (!card) - return count; - - if (atomic_read(&card->is_hardsetup)) - return -EPERM; - - i = simple_strtoul(buf, &tmp, 16); - if (i < 0) { - PRINT_ERR("polltime %i invalid\n", i); - return -EINVAL; - } - card->options.polltime = i; - - return count; -} - -static DEVICE_ATTR(polltime, 0644, qeth_polltime_show, qeth_polltime_store); - -static ssize_t -qeth_hhlen_show(struct device *dev, char *buf) -{ - struct qeth_card *card = dev->driver_data; - - if (!card) - return -EINVAL; - - return sprintf(buf, "%i\n", card->options.add_hhlen); -} - -static ssize_t -qeth_hhlen_store(struct device *dev, const char *buf, size_t count) -{ - struct qeth_card *card = dev->driver_data; - char *tmp; - int i; - - if (!card) - return count; - - if (atomic_read(&card->is_hardsetup)) - return -EPERM; - - i = simple_strtoul(buf, &tmp, 16); - if ((i < 0) || (i > MAX_ADD_HHLEN)) { - PRINT_ERR("add_hhlen out of range\n"); - return -EINVAL; - } - card->options.add_hhlen = i; - - return count; -} - -static DEVICE_ATTR(add_hhlen, 0644, qeth_hhlen_show, qeth_hhlen_store); - -static ssize_t -qeth_takeover_show(struct device *dev, char *buf) -{ - struct qeth_card *card = dev->driver_data; - - if (!card) - return -EINVAL; - - return sprintf(buf, "%s\n", - (card->options.ena_ipat == ENABLE_TAKEOVER)?"1":"0"); -} - -static ssize_t -qeth_takeover_store(struct device *dev, const char *buf, size_t count) -{ - struct qeth_card *card = dev->driver_data; - int i; - char *tmp; - - if (!card) - return count; - - if (atomic_read(&card->is_hardsetup)) - return -EPERM; - - i = simple_strtoul(buf, &tmp, 16); - if (i == 1) - card->options.ena_ipat = ENABLE_TAKEOVER; - else if (i == 0) - card->options.ena_ipat = DISABLE_TAKEOVER; - else - return -EINVAL; - - return count; -} - -static DEVICE_ATTR(enable_takeover, 0644, qeth_takeover_show, qeth_takeover_store); - -static ssize_t -qeth_macaddr_show(struct device *dev, char *buf) -{ - struct qeth_card *card = dev->driver_data; - - if (!card) - return -EINVAL; - - return sprintf(buf, "%s\n", - (card->options.macaddr_mode == MACADDR_CANONICAL)?"1":"0"); -} - -static ssize_t -qeth_macaddr_store(struct device *dev, const char *buf, size_t count) -{ - struct qeth_card *card = dev->driver_data; - int i; - char *tmp; - - if (!card) - return count; - - if (atomic_read(&card->is_hardsetup)) - return -EPERM; - - i = simple_strtoul(buf, &tmp, 16); - if (i == 0) - card->options.macaddr_mode = MACADDR_NONCANONICAL; - else if (i == 1) - card->options.macaddr_mode = MACADDR_CANONICAL; - else - return -EINVAL; - - return count; -} - -static DEVICE_ATTR(canonical_macaddr, 0644, qeth_macaddr_show, qeth_macaddr_store); - -static ssize_t -qeth_fakebr_show(struct device *dev, char *buf) -{ - struct qeth_card *card = dev->driver_data; - - if (!card) - return -EINVAL; - - return sprintf(buf, "%s\n", - (card->options.fake_broadcast == FAKE_BROADCAST)?"1":"0"); -} - -static ssize_t -qeth_fakebr_store(struct device *dev, const char *buf, size_t count) -{ - struct qeth_card *card = dev->driver_data; - int i; - char *tmp; - - if (!card) - return count; - - if (atomic_read(&card->is_hardsetup)) - return -EPERM; - - i = simple_strtoul(buf, &tmp, 16); - if (i == 0) - card->options.fake_broadcast = DONT_FAKE_BROADCAST; - else if (i == 1) - card->options.fake_broadcast = FAKE_BROADCAST; - else - return -EINVAL; - - return count; -} - -static DEVICE_ATTR(fake_broadcast, 0644, qeth_fakebr_show, qeth_fakebr_store); - -static ssize_t -qeth_fakell_show(struct device *dev, char *buf) -{ - struct qeth_card *card = dev->driver_data; - - if (!card) - return -EINVAL; - - return sprintf(buf, "%s\n", - (card->options.fake_ll == FAKE_LL)?"1":"0"); -} - -static ssize_t -qeth_fakell_store(struct device *dev, const char *buf, size_t count) -{ - struct qeth_card *card = dev->driver_data; - int i; - char *tmp; - - if (!card) - return count; - - if (atomic_read(&card->is_hardsetup)) - return -EPERM; - - i = simple_strtoul(buf, &tmp, 16); - if (i == 0) - card->options.fake_ll = DONT_FAKE_LL; - else if (i == 1) - card->options.fake_ll = FAKE_LL; - else - return -EINVAL; - - return count; -} - -static DEVICE_ATTR(fake_ll, 0644, qeth_fakell_show, qeth_fakell_store); - -static ssize_t -qeth_broadcast_show(struct device *dev, char *buf) -{ - struct qeth_card *card = dev->driver_data; - - if (!card) - return -EINVAL; - - return sprintf(buf, "%s\n", - (card->options.broadcast_mode == BROADCAST_ALLRINGS) - ?"allrings":"local"); -} - -static ssize_t -qeth_broadcast_store(struct device *dev, const char *buf, size_t count) -{ - struct qeth_card *card = dev->driver_data; - char *tmp; - int cnt; - - if (!card) - return count; - - if (atomic_read(&card->is_hardsetup)) - return -EPERM; - - /* Remove trailing '\n'. */ - tmp = strsep((char **) &buf, "\n"); - cnt = strlen(tmp); - if (!strncmp(tmp, "broadcast_allrings", cnt)) - card->options.broadcast_mode = BROADCAST_ALLRINGS; - else if (!strncmp(tmp, "broadcast_local", cnt)) - card->options.broadcast_mode = BROADCAST_LOCAL; - else - PRINT_WARN("unknown broadcast type '%s'\n", tmp); - - return count; -} - -static DEVICE_ATTR(broadcast_mode, 0644, qeth_broadcast_show, qeth_broadcast_store); - -static ssize_t -qeth_recover_store(struct device *dev, const char *buf, size_t count) -{ - struct qeth_card *card = dev->driver_data; - int i; - char *tmp; - - if (!card) - return count; - - i = simple_strtoul(buf, &tmp, 16); - if (i == 1) { - QETH_DBF_CARD2(0, trace, "UTRC", card); - atomic_set(&card->problem, PROBLEM_USER_TRIGGERED_RECOVERY); - qeth_schedule_recovery(card); - return count; - } else - return -EINVAL; -} - -static DEVICE_ATTR(recover, 0200, 0, qeth_recover_store); - -static ssize_t -qeth_card_type_show(struct device *dev, char *buf) -{ - struct qeth_card *card = dev->driver_data; - - if (!card) - return -EINVAL; - - if (!atomic_read(&card->is_softsetup)) - return sprintf(buf, "n/a\n"); - - return sprintf(buf, "%s\n", - qeth_get_cardname_short(card->type, card->link_type, - card->is_guest_lan)); -} - -static DEVICE_ATTR(card_type, 0444, qeth_card_type_show, NULL); - -static struct attribute * qeth_attrs[] = { - &dev_attr_bufcnt.attr, - &dev_attr_portname.attr, - &dev_attr_route4.attr, - &dev_attr_route6.attr, - &dev_attr_checksumming.attr, - &dev_attr_priority_queueing.attr, - &dev_attr_portno.attr, - &dev_attr_polltime.attr, - &dev_attr_add_hhlen.attr, - &dev_attr_enable_takeover.attr, - &dev_attr_canonical_macaddr.attr, - &dev_attr_fake_broadcast.attr, - &dev_attr_fake_ll.attr, - &dev_attr_broadcast_mode.attr, - &dev_attr_recover.attr, - &dev_attr_card_type.attr, - NULL, -}; - -static struct attribute_group qeth_attr_group = { - .attrs = qeth_attrs, -}; - -static inline int -__qeth_create_attributes(struct device *dev) -{ - return sysfs_create_group(&dev->kobj, &qeth_attr_group); -} - -static inline void -__qeth_remove_attributes(struct device *dev) -{ - sysfs_remove_group(&dev->kobj, &qeth_attr_group); -} - -static int -qeth_probe_device(struct ccwgroup_device *gdev) -{ - struct qeth_card *card; - int ret; - - if (!get_device(&gdev->dev)) - return -ENODEV; - - card = qeth_alloc_card(); - if (!card) { - put_device(&gdev->dev); - return -ENOMEM; - } - - gdev->dev.driver_data = card; - card->gdev = gdev; - - gdev->cdev[0]->handler = qeth_interrupt_handler_read; - - gdev->cdev[1]->handler = qeth_interrupt_handler_write; - - gdev->cdev[2]->handler = qeth_interrupt_handler_qdio; - - ret = __qeth_create_attributes(&gdev->dev); - if (ret != 0) - goto out; - - return 0; -out: - put_device(&gdev->dev); - qeth_free_card(card); - return ret; -} - -/* - * Replaces qeth_probe and qeth_attach_handler. - * This is called after piping to the 'online' attribute, - * when all parameters are ready. - */ -static int -qeth_activate(struct qeth_card *card) -{ - int result; - - ccw_device_set_online(CARD_RDEV(card)); - ccw_device_set_online(CARD_WDEV(card)); - ccw_device_set_online(CARD_DDEV(card)); - - QETH_DBF_CARD1(0, setup, "activ", card); - 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_HEX2(0, misc, &card->options, QETH_DBF_MISC_LEN); - - if (qeth_determine_card_type(card)) { - PRINT_WARN("%s: not a valid card type\n", __func__); - goto out; - } - - qeth_insert_card_into_list(card); - - qeth_correct_routing_status(card); - - result = qeth_init_ringbuffers1(card); - if (result) { - PRINT_WARN("%s: could not init ringbuffers1\n", __func__); - goto out_remove; - } - - result = qeth_hardsetup_card(card, 0); - if (result) { - goto out_remove; - } - - result = qeth_init_ringbuffers2(card); - if (result) { - PRINT_WARN("%s: could not init ringbuffers2\n", __func__); - goto out_remove; - } - - /* this was previously done in chandev_initnetdevice */ - snprintf(card->dev->name, 8, "%s%%d", - qeth_get_dev_basename(card->type, card->link_type)); - if (qeth_init_netdev(card)) - goto out_remove; - - return 0; /* success */ - -out_remove: - qeth_remove_card(card, QETH_REMOVE_CARD_QUICK); - qeth_remove_card_from_list(card); - -out: - QETH_DBF_TEXT4(0, trace, "freecard"); - - ccw_device_set_offline(CARD_DDEV(card)); - ccw_device_set_offline(CARD_WDEV(card)); - ccw_device_set_offline(CARD_RDEV(card)); - - return -ENODEV; -} - -static int -qeth_set_online(struct ccwgroup_device *gdev) -{ - int rc; - struct qeth_card *card = gdev->dev.driver_data; - - BUG_ON(!card); - - rc = qeth_alloc_card_stuff(card); - - return rc ? rc : qeth_activate(card); - -} - -static int -qeth_set_offline(struct ccwgroup_device *gdev) -{ - struct qeth_card *card = gdev->dev.driver_data; - - if (!card) - return -ENODEV; - - qeth_remove_card(card, QETH_REMOVE_CARD_PROPER); - qeth_remove_card_from_list(card); - - QETH_DBF_TEXT4(0, trace, "freecard"); - - ccw_device_set_offline(CARD_DDEV(card)); - ccw_device_set_offline(CARD_WDEV(card)); - ccw_device_set_offline(CARD_RDEV(card)); - - qeth_free_card_stuff(card); - - return 0; -} - -static void -qeth_remove_device(struct ccwgroup_device *gdev) -{ - struct qeth_card *card = gdev->dev.driver_data; - - if (card && qeth_does_card_exist(card)) - /* Means that card is already in list. */ - qeth_set_offline(gdev); - __qeth_remove_attributes(&gdev->dev); - gdev->dev.driver_data = NULL; - if (card) - qeth_free_card(card); - put_device(&gdev->dev); -} - -static struct ccwgroup_driver qeth_ccwgroup_driver = { - .owner = THIS_MODULE, - .name = "qeth", - .driver_id = 0xD8C5E3C8, - .probe = qeth_probe_device, - .remove = qeth_remove_device, - .set_online = qeth_set_online, - .set_offline = qeth_set_offline, -}; - -static int __init -qeth_init(void) -{ - int result; - - qeth_eyecatcher(); - - printk(KERN_INFO "qeth: loading %s\n", version); - - result = qeth_get_internal_functions(); - if (result) - goto out; - - qeth_alloc_spare_bufs(); - -#ifdef QETH_IPV6 - if (qeth_ipv6_init()) { - PRINT_ERR("Out of memory during ipv6 init.\n"); - goto out_sparebufs; - } -#endif /* QETH_IPV6 */ - - result = qeth_register_dbf_views(); - if (result) { - PRINT_ERR("not enough memory for dbf. Will not load module.\n"); - goto out_ipv6; - } - - result = ccwgroup_driver_register(&qeth_ccwgroup_driver); - if (result) - goto out_dbf; - - result = ccw_driver_register(&qeth_ccw_driver); - if (result) - goto out_gdrv; - - result = driver_create_file(&qeth_ccwgroup_driver.driver, - &driver_attr_group); - if (result) - goto out_cdrv; - - qeth_root_dev = s390_root_dev_register("qeth"); - if (IS_ERR(qeth_root_dev)) { - result = PTR_ERR(qeth_root_dev); - goto out_file; - } - qeth_register_notifiers(); - qeth_add_procfs_entries(); - - return 0; - -out_file: - driver_remove_file(&qeth_ccwgroup_driver.driver, &driver_attr_group); -out_cdrv: - ccw_driver_unregister(&qeth_ccw_driver); -out_gdrv: - ccwgroup_driver_unregister(&qeth_ccwgroup_driver); -out_dbf: - qeth_unregister_dbf_views(); -out_ipv6: -#ifdef QETH_IPV6 - qeth_ipv6_uninit(); -out_sparebufs: -#endif /* QETH_IPV6 */ - qeth_free_all_spare_bufs(); -out: - return result; -} - -static void __exit -qeth_exit(void) -{ -#ifdef QETH_IPV6 - qeth_ipv6_uninit(); -#endif /* QETH_IPV6 */ - qeth_unregister_notifiers(); - - qeth_remove_procfs_entries(); - - QETH_DBF_TEXT1(0, trace, "cleanup."); - - driver_remove_file(&qeth_ccwgroup_driver.driver, &driver_attr_group); - ccw_driver_unregister(&qeth_ccw_driver); - ccwgroup_driver_unregister(&qeth_ccwgroup_driver); - s390_root_dev_unregister(qeth_root_dev); - - while (firstcard) { - struct qeth_card *card = firstcard; - qeth_remove_card(card, QETH_REMOVE_CARD_QUICK); - qeth_remove_card_from_list(card); - qeth_free_card(card); - } - - qeth_free_all_spare_bufs(); - - qeth_unregister_dbf_views(); - - printk("qeth: %s: module removed\n", version); -} - -EXPORT_SYMBOL(qeth_eyecatcher); - -module_init(qeth_init); -module_exit(qeth_exit); diff -puN /dev/null drivers/s390/net/qeth_fs.h --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ 25-akpm/drivers/s390/net/qeth_fs.h 2004-04-08 13:55:46.731919840 -0700 @@ -0,0 +1,156 @@ +/* + * linux/drivers/s390/net/qeth_fs.h + * + * Linux on zSeries OSA Express and HiperSockets support. + * + * This header file contains definitions related to sysfs and procfs. + * + * Copyright 2000,2003 IBM Corporation + * Author(s): Thomas Spatzier + * + */ +#ifndef __QETH_FS_H__ +#define __QETH_FS_H__ + +#ifdef CONFIG_PROC_FS +extern int +qeth_create_procfs_entries(void); + +extern void +qeth_remove_procfs_entries(void); +#else +static inline int +qeth_create_procfs_entries(void) +{ + return 0; +} + +static inline void +qeth_remove_procfs_entries(void) +{ +} +#endif /* CONFIG_PROC_FS */ + +extern int +qeth_create_device_attributes(struct device *dev); + +extern void +qeth_remove_device_attributes(struct device *dev); + +extern int +qeth_create_driver_attributes(void); + +extern void +qeth_remove_driver_attributes(void); + +/* + * utility functions used in qeth_proc.c and qeth_sys.c + */ + +static inline const char * +qeth_get_checksum_str(struct qeth_card *card) +{ + if (card->options.checksum_type == SW_CHECKSUMMING) + return "sw"; + else if (card->options.checksum_type == HW_CHECKSUMMING) + return "hw"; + else + return "no"; +} + +static inline const char * +qeth_get_prioq_str(struct qeth_card *card, char *buf) +{ + if (card->qdio.do_prio_queueing == QETH_NO_PRIO_QUEUEING) + sprintf(buf, "always_q_%i", card->qdio.default_out_queue); + else + strcpy(buf, (card->qdio.do_prio_queueing == + QETH_PRIO_Q_ING_PREC)? + "by_prec." : "by_ToS"); + return buf; +} + +static inline const char * +qeth_get_bufsize_str(struct qeth_card *card) +{ + if (card->qdio.in_buf_size == 16384) + return "16k"; + else if (card->qdio.in_buf_size == 24576) + return "24k"; + else if (card->qdio.in_buf_size == 32768) + return "32k"; + else if (card->qdio.in_buf_size == 40960) + return "40k"; + else + return "64k"; +} + +static inline const char * +qeth_get_cardname(struct qeth_card *card) +{ + if (card->info.guestlan) { + switch (card->info.type) { + case QETH_CARD_TYPE_OSAE: + return " Guest LAN QDIO"; + case QETH_CARD_TYPE_IQD: + return " Guest LAN Hiper"; + default: + return " unknown"; + } + } else { + switch (card->info.type) { + case QETH_CARD_TYPE_OSAE: + return " OSD Express"; + case QETH_CARD_TYPE_IQD: + return " HiperSockets"; + default: + return " unknown"; + } + } + return " n/a"; +} + +/* max length to be returned: 14 */ +static inline const char * +qeth_get_cardname_short(struct qeth_card *card) +{ + if (card->info.guestlan){ + switch (card->info.type){ + case QETH_CARD_TYPE_OSAE: + return "GuestLAN QDIO"; + case QETH_CARD_TYPE_IQD: + return "GuestLAN Hiper"; + default: + return "unknown"; + } + } else { + switch (card->info.type) { + case QETH_CARD_TYPE_OSAE: + switch (card->info.link_type) { + case QETH_LINK_TYPE_FAST_ETH: + return "OSD_100"; + case QETH_LINK_TYPE_HSTR: + return "HSTR"; + case QETH_LINK_TYPE_GBIT_ETH: + return "OSD_1000"; + case QETH_LINK_TYPE_LANE_ETH100: + return "OSD_FE_LANE"; + case QETH_LINK_TYPE_LANE_TR: + return "OSD_TR_LANE"; + case QETH_LINK_TYPE_LANE_ETH1000: + return "OSD_GbE_LANE"; + case QETH_LINK_TYPE_LANE: + return "OSD_ATM_LANE"; + default: + return "OSD_Express"; + } + case QETH_CARD_TYPE_IQD: + return "HiperSockets"; + default: + return "unknown"; + } + } + return "n/a"; +} + +#endif /* __QETH_FS_H__ */ diff -puN drivers/s390/net/qeth.h~s390-12-12-rewritten-qeth-driver drivers/s390/net/qeth.h --- 25/drivers/s390/net/qeth.h~s390-12-12-rewritten-qeth-driver 2004-04-08 13:55:46.664930024 -0700 +++ 25-akpm/drivers/s390/net/qeth.h 2004-04-08 13:55:46.730919992 -0700 @@ -1,1147 +1,789 @@ -/* - * linux/drivers/s390/net/qeth.h - * - * Linux on zSeries OSA Express and HiperSockets support - * - * Copyright 2000,2003 IBM Corporation - * Author(s): Utz Bacher - * - */ #ifndef __QETH_H__ #define __QETH_H__ -#include +#include +#include + +#include +#include +#include +#include + +#include +#include +#include +#include -#define QETH_NAME " qeth" -#define VERSION_QETH_H "$Revision: 1.60 $" +#include +#include +#include +#include +#include + +#include "qeth_mpc.h" -/******************** CONFIG STUFF ***********************/ -//#define QETH_DBF_LIKE_HELL +#define VERSION_QETH_H "$Revision: 1.98 $" #ifdef CONFIG_QETH_IPV6 -#define QETH_IPV6 -#define QETH_VERSION_IPV6 ":IPv6" +#define QETH_VERSION_IPV6 ":IPv6" #else -#define QETH_VERSION_IPV6 "" -#endif /* CONFIG_QETH_IPV6 */ - +#define QETH_VERSION_IPV6 "" +#endif #ifdef CONFIG_QETH_VLAN -#define QETH_VLAN -#define QETH_VERSION_VLAN ":VLAN" +#define QETH_VERSION_VLAN ":VLAN" #else -#define QETH_VERSION_VLAN "" -#endif /* CONFIG_QETH_VLAN */ - -/* these values match CHECKSUM_* in include/linux/skbuff.h */ -#define SW_CHECKSUMMING 0 -#define HW_CHECKSUMMING 1 -#define NO_CHECKSUMMING 2 - -#define QETH_CHECKSUM_DEFAULT NO_CHECKSUMMING - -#define QETH_PRIOQ_DEFAULT NO_PRIO_QUEUEING -#define QETH_DEFAULT_QUEUE 2 - -/******************** CONFIG STUFF END ***********************/ -/********************* TUNING STUFF **************************/ -#define HIGH_WATERMARK_PACK 5 -#define LOW_WATERMARK_PACK 2 -#define WATERMARK_FUZZ 2 - -#define QETH_MAX_INPUT_THRESHOLD 500 -#define QETH_MAX_OUTPUT_THRESHOLD 300 /* ? */ - -/* only the MAX values are used */ -#define QETH_MIN_INPUT_THRESHOLD 1 -#define QETH_MIN_OUTPUT_THRESHOLD 1 - -#define QETH_REQUEUE_THRESHOLD (card->options.inbound_buffer_count/4) - -#ifdef CONFIG_QETH_PERF_STATS -#define QETH_PERFORMANCE_STATS -#endif /* CONFIG_QETH_PERF_STATS */ - -#ifdef QETH_DBF_LIKE_HELL -#define QETH_VERBOSE_LEVEL 8 -#else /* QETH_DBF_LIKE_HELL */ -#define QETH_VERBOSE_LEVEL 5 -#endif /* QETH_DBF_LIKE_HELL */ - -#define PCI_THRESHOLD_A (card->options.inbound_buffer_count+1) -/* buffers we have to be behind before we get a PCI */ -#define PCI_THRESHOLD_B 0 /* enqueued free buffers left before we get a PCI */ -#define PCI_TIMER_VALUE 3 /* not used, unless the microcode gets patched */ - -#define DEFAULT_SPARE_BUFFERS 0 -#define MAX_SPARE_BUFFERS 1024 -#define SPAREBUF_MASK 65536 -#define MAX_PORTNO 15 - -#define QETH_PROCFILE_NAME "qeth" -#define QETH_PERF_PROCFILE_NAME "qeth_perf" -#define QETH_IPA_PROCFILE_NAME "qeth_ipa_takeover" - -#define SEND_RETRIES_ALLOWED 5 -#define QETH_ROUTING_ATTEMPTS 2 - -#define QETH_HARDSETUP_LAPS 5 -#define QETH_HARDSETUP_CLEAR_LAPS 3 -#define QETH_RECOVERY_HARDSETUP_RETRY 2 - -/************************* DEBUG FACILITY STUFF *********************/ - -#define QETH_DBF_HEX(ex,name,level,addr,len) \ - do { \ - if (ex) \ - debug_exception(qeth_dbf_##name,level,(void*)addr,len); \ - else \ - debug_event(qeth_dbf_##name,level,(void*)addr,len); \ - } while (0) -#define QETH_DBF_TEXT(ex,name,level,text) \ - do { \ - if (ex) \ - debug_text_exception(qeth_dbf_##name,level,text); \ - else \ - debug_text_event(qeth_dbf_##name,level,text); \ - } while (0) - -#define QETH_DBF_CARD(ex,name,level,text,card) \ - do { \ - QETH_DBF_TEXT(ex,name,level,text); \ - QETH_DBF_TEXT(ex,name,level,card->gdev->dev.bus_id); \ - } while (0) - -#define QETH_DBF_HEX0(ex,name,addr,len) QETH_DBF_HEX(ex,name,0,addr,len) -#define QETH_DBF_HEX1(ex,name,addr,len) QETH_DBF_HEX(ex,name,1,addr,len) -#define QETH_DBF_HEX2(ex,name,addr,len) QETH_DBF_HEX(ex,name,2,addr,len) -#ifdef QETH_DBF_LIKE_HELL -#define QETH_DBF_HEX3(ex,name,addr,len) QETH_DBF_HEX(ex,name,3,addr,len) -#define QETH_DBF_HEX4(ex,name,addr,len) QETH_DBF_HEX(ex,name,4,addr,len) -#define QETH_DBF_HEX5(ex,name,addr,len) QETH_DBF_HEX(ex,name,5,addr,len) -#define QETH_DBF_HEX6(ex,name,addr,len) QETH_DBF_HEX(ex,name,6,addr,len) -#else /* QETH_DBF_LIKE_HELL */ -#define QETH_DBF_HEX3(ex,name,addr,len) do {} while (0) -#define QETH_DBF_HEX4(ex,name,addr,len) do {} while (0) -#define QETH_DBF_HEX5(ex,name,addr,len) do {} while (0) -#define QETH_DBF_HEX6(ex,name,addr,len) do {} while (0) -#endif /* QETH_DBF_LIKE_HELL */ - -#define QETH_DBF_TEXT0(ex,name,text) QETH_DBF_TEXT(ex,name,0,text) -#define QETH_DBF_TEXT1(ex,name,text) QETH_DBF_TEXT(ex,name,1,text) -#define QETH_DBF_TEXT2(ex,name,text) QETH_DBF_TEXT(ex,name,2,text) -#ifdef QETH_DBF_LIKE_HELL -#define QETH_DBF_TEXT3(ex,name,text) QETH_DBF_TEXT(ex,name,3,text) -#define QETH_DBF_TEXT4(ex,name,text) QETH_DBF_TEXT(ex,name,4,text) -#define QETH_DBF_TEXT5(ex,name,text) QETH_DBF_TEXT(ex,name,5,text) -#define QETH_DBF_TEXT6(ex,name,text) QETH_DBF_TEXT(ex,name,6,text) -#else /* QETH_DBF_LIKE_HELL */ -#define QETH_DBF_TEXT3(ex,name,text) do {} while (0) -#define QETH_DBF_TEXT4(ex,name,text) do {} while (0) -#define QETH_DBF_TEXT5(ex,name,text) do {} while (0) -#define QETH_DBF_TEXT6(ex,name,text) do {} while (0) -#endif /* QETH_DBF_LIKE_HELL */ - -#define QETH_DBF_CARD0(ex,name,text,card) QETH_DBF_CARD(ex,name,0,text,card) -#define QETH_DBF_CARD1(ex,name,text,card) QETH_DBF_CARD(ex,name,1,text,card) -#define QETH_DBF_CARD2(ex,name,text,card) QETH_DBF_CARD(ex,name,2,text,card) -#ifdef QETH_DBF_LIKE_HELL -#define QETH_DBF_CARD3(ex,name,text,card) QETH_DBF_CARD(ex,name,3,text,card) -#define QETH_DBF_CARD4(ex,name,text,card) QETH_DBF_CARD(ex,name,4,text,card) -#define QETH_DBF_CARD5(ex,name,text,card) QETH_DBF_CARD(ex,name,5,text,card) -#define QETH_DBF_CARD6(ex,name,text,card) QETH_DBF_CARD(ex,name,6,text,card) -#else /* QETH_DBF_LIKE_HELL */ -#define QETH_DBF_CARD3(ex,name,text,card) do {} while (0) -#define QETH_DBF_CARD4(ex,name,text,card) do {} while (0) -#define QETH_DBF_CARD5(ex,name,text,card) do {} while (0) -#define QETH_DBF_CARD6(ex,name,text,card) do {} while (0) -#endif /* QETH_DBF_LIKE_HELL */ +#define QETH_VERSION_VLAN "" +#endif +/** + * Debug Facility stuff + */ #define QETH_DBF_SETUP_NAME "qeth_setup" #define QETH_DBF_SETUP_LEN 8 #define QETH_DBF_SETUP_INDEX 3 #define QETH_DBF_SETUP_NR_AREAS 1 -#ifdef QETH_DBF_LIKE_HELL -#define QETH_DBF_SETUP_LEVEL 6 -#else /* QETH_DBF_LIKE_HELL */ #define QETH_DBF_SETUP_LEVEL 3 -#endif /* QETH_DBF_LIKE_HELL */ #define QETH_DBF_MISC_NAME "qeth_misc" #define QETH_DBF_MISC_LEN 128 #define QETH_DBF_MISC_INDEX 1 #define QETH_DBF_MISC_NR_AREAS 1 -#ifdef QETH_DBF_LIKE_HELL -#define QETH_DBF_MISC_LEVEL 6 -#else /* QETH_DBF_LIKE_HELL */ #define QETH_DBF_MISC_LEVEL 2 -#endif /* QETH_DBF_LIKE_HELL */ #define QETH_DBF_DATA_NAME "qeth_data" #define QETH_DBF_DATA_LEN 96 #define QETH_DBF_DATA_INDEX 3 #define QETH_DBF_DATA_NR_AREAS 1 -#ifdef QETH_DBF_LIKE_HELL -#define QETH_DBF_DATA_LEVEL 6 -#else /* QETH_DBF_LIKE_HELL */ #define QETH_DBF_DATA_LEVEL 2 -#endif /* QETH_DBF_LIKE_HELL */ #define QETH_DBF_CONTROL_NAME "qeth_control" -/* buffers are 255 bytes long, but no prob */ #define QETH_DBF_CONTROL_LEN 256 #define QETH_DBF_CONTROL_INDEX 3 #define QETH_DBF_CONTROL_NR_AREAS 2 -#ifdef QETH_DBF_LIKE_HELL -#define QETH_DBF_CONTROL_LEVEL 6 -#else /* QETH_DBF_LIKE_HELL */ #define QETH_DBF_CONTROL_LEVEL 2 -#endif /* QETH_DBF_LIKE_HELL */ #define QETH_DBF_TRACE_NAME "qeth_trace" #define QETH_DBF_TRACE_LEN 8 -#ifdef QETH_DBF_LIKE_HELL -#define QETH_DBF_TRACE_INDEX 3 -#else /* QETH_DBF_LIKE_HELL */ #define QETH_DBF_TRACE_INDEX 2 -#endif /* QETH_DBF_LIKE_HELL */ #define QETH_DBF_TRACE_NR_AREAS 2 -#ifdef QETH_DBF_LIKE_HELL -#define QETH_DBF_TRACE_LEVEL 6 -#else /* QETH_DBF_LIKE_HELL */ -#define QETH_DBF_TRACE_LEVEL 2 -#endif /* QETH_DBF_LIKE_HELL */ +#define QETH_DBF_TRACE_LEVEL 3 #define QETH_DBF_SENSE_NAME "qeth_sense" #define QETH_DBF_SENSE_LEN 64 #define QETH_DBF_SENSE_INDEX 1 #define QETH_DBF_SENSE_NR_AREAS 1 -#ifdef QETH_DBF_LIKE_HELL -#define QETH_DBF_SENSE_LEVEL 6 -#else /* QETH_DBF_LIKE_HELL */ #define QETH_DBF_SENSE_LEVEL 2 -#endif /* QETH_DBF_LIKE_HELL */ #define QETH_DBF_QERR_NAME "qeth_qerr" #define QETH_DBF_QERR_LEN 8 #define QETH_DBF_QERR_INDEX 1 #define QETH_DBF_QERR_NR_AREAS 2 -#ifdef QETH_DBF_LIKE_HELL -#define QETH_DBF_QERR_LEVEL 6 -#else /* QETH_DBF_LIKE_HELL */ #define QETH_DBF_QERR_LEVEL 2 -#endif /* QETH_DBF_LIKE_HELL */ -/****************** END OF DEBUG FACILITY STUFF *********************/ -/********************* CARD DATA STUFF **************************/ - -#define QETH_MAX_PARAMS 150 - -#define QETH_CARD_TYPE_UNKNOWN 0 -#define QETH_CARD_TYPE_OSAE 10 -#define QETH_CARD_TYPE_IQD 1234 +#define QETH_DBF_TEXT(name,level,text) \ + do { \ + debug_text_event(qeth_dbf_##name,level,text); \ + } while (0) -#define QETH_IDX_FUNC_LEVEL_OSAE_ENA_IPAT 0x0101 -#define QETH_IDX_FUNC_LEVEL_OSAE_DIS_IPAT 0x0101 -#define QETH_IDX_FUNC_LEVEL_IQD_ENA_IPAT 0x4108 -#define QETH_IDX_FUNC_LEVEL_IQD_DIS_IPAT 0x5108 +#define QETH_DBF_HEX(name,level,addr,len) \ + do { \ + debug_event(qeth_dbf_##name,level,(void*)(addr),len); \ + } while (0) -#define QETH_MAX_QUEUES 4 +#define QETH_DBF_TEXT_(name,level,text...) \ + do { \ + sprintf(qeth_dbf_text_buf, text); \ + debug_text_event(qeth_dbf_##name,level,qeth_dbf_text_buf);\ + } while (0) -#define UNIQUE_ID_IF_CREATE_ADDR_FAILED 0xfffe -#define UNIQUE_ID_NOT_BY_CARD 0x10000 +#define QETH_DBF_SPRINTF(name,level,text...) \ + do { \ + debug_sprintf_event(qeth_dbf_trace, level, ##text ); \ + debug_sprintf_event(qeth_dbf_trace, level, text ); \ + } while (0) -/* - * CU type & model, Dev type & model, card_type, odd_even_restriction, - * func level, no of queues, multicast is different (multicast-queue_no + 0x100) +/** + * some more debug stuff */ -#define QETH_MODELLIST_ARRAY \ - {{0x1731,0x01,0x1732,0x01,QETH_CARD_TYPE_OSAE,1, \ - QETH_IDX_FUNC_LEVEL_OSAE_ENA_IPAT, \ - QETH_IDX_FUNC_LEVEL_OSAE_DIS_IPAT, \ - QETH_MAX_QUEUES,0}, \ - {0x1731,0x05,0x1732,0x05,QETH_CARD_TYPE_IQD,0, \ - QETH_IDX_FUNC_LEVEL_IQD_ENA_IPAT, \ - QETH_IDX_FUNC_LEVEL_IQD_DIS_IPAT, \ - QETH_MAX_QUEUES,0x103}, \ - {0,0,0,0,0,0,0,0,0}} - -#define QETH_MPC_DIFINFO_LEN_INDICATES_LINK_TYPE 0x18 - /* only the first two bytes are looked at in qeth_get_cardname_short */ -#define QETH_MPC_LINK_TYPE_FAST_ETHERNET 0x01 -#define QETH_MPC_LINK_TYPE_HSTR 0x02 -#define QETH_MPC_LINK_TYPE_GIGABIT_ETHERNET 0x03 -#define QETH_MPC_LINK_TYPE_10GIGABIT_ETHERNET 0x10 -#define QETH_MPC_LINK_TYPE_LANE_ETH100 0x81 -#define QETH_MPC_LINK_TYPE_LANE_TR 0x82 -#define QETH_MPC_LINK_TYPE_LANE_ETH1000 0x83 -#define QETH_MPC_LINK_TYPE_LANE 0x88 -#define QETH_MPC_LINK_TYPE_ATM_NATIVE 0x90 - -#define DEFAULT_ADD_HHLEN 0 -#define MAX_ADD_HHLEN 1024 - -#define QETH_HEADER_SIZE 32 -#define QETH_IP_HEADER_SIZE 40 -#define QETH_HEADER_LEN_POS 8 -/* flags for the header: */ -#define QETH_HEADER_PASSTHRU 0x10 -#define QETH_HEADER_IPV6 0x80 - -#define QETH_ETH_MAC_V4 0x0100 /* like v4 */ -#define QETH_ETH_MAC_V6 0x3333 /* like v6 */ -/* tr mc mac is longer, but that will be enough to detect mc frames */ -#define QETH_TR_MAC_NC 0xc000 /* non-canonical */ -#define QETH_TR_MAC_C 0x0300 /* canonical */ - -#define QETH_CAST_FLAGS 0x07 -#define QETH_CAST_UNICAST 6 -#define QETH_CAST_MULTICAST 4 -#define QETH_CAST_BROADCAST 5 -#define QETH_CAST_ANYCAST 7 -#define QETH_CAST_NOCAST 0 - -/* VLAN defines */ -#define QETH_EXT_HEADER_VLAN_FRAME 0x01 -#define QETH_EXT_HEADER_TOKEN_ID 0x02 -#define QETH_EXT_HEADER_INCLUDE_VLAN_TAG 0x04 - -#define QETH_EXT_HEADER_SRC_MAC_ADDRESS 0x08 -#define QETH_EXT_HEADER_CSUM_HDR_REQ 0x10 -#define QETH_EXT_HEADER_CSUM_TRANSP_REQ 0x20 -#define QETH_EXT_HEADER_CSUM_TRANSP_FRAME_TYPE 0x40 - -#define QETH_UDP_CSUM_OFFSET 6 -#define QETH_TCP_CSUM_OFFSET 16 +#define PRINTK_HEADER "qeth: " -#define QETH_VERIFY_IS_REAL_DEV 1 -#define QETH_VERIFY_IS_VLAN_DEV 2 +#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)); -inline static unsigned int -qeth_get_ipa_timeout(int cardtype) +static inline void +qeth_hex_dump(unsigned char *buf, size_t len) { - switch (cardtype) { - case QETH_CARD_TYPE_IQD: - return 2000; - default: - return 20000; - } -} + size_t i; -inline static unsigned short -qeth_get_additional_dev_flags(int cardtype) -{ - switch (cardtype) { - case QETH_CARD_TYPE_IQD: - return IFF_NOARP; -#ifdef QETH_IPV6 - default: - return 0; -#else /* QETH_IPV6 */ - default: - return IFF_NOARP; -#endif /* QETH_IPV6 */ + for (i = 0; i < len; i++) { + if (i && !(i % 16)) + printk("\n"); + printk("%02x ", *(buf + i)); } + printk("\n"); } -inline static int -qeth_get_hlen(__u8 link_type) -{ -#ifdef QETH_IPV6 - switch (link_type) { - case QETH_MPC_LINK_TYPE_HSTR: - case QETH_MPC_LINK_TYPE_LANE_TR: - return QETH_HEADER_SIZE + TR_HLEN; - default: -#ifdef QETH_VLAN - return QETH_HEADER_SIZE + VLAN_ETH_HLEN; -#else - return QETH_HEADER_SIZE + ETH_HLEN; -#endif - } -#else /* QETH_IPV6 */ -#ifdef QETH_VLAN - return QETH_HEADER_SIZE + VLAN_HLEN; -#else - return QETH_HEADER_SIZE; -#endif +#define SENSE_COMMAND_REJECT_BYTE 0 +#define SENSE_COMMAND_REJECT_FLAG 0x80 +#define SENSE_RESETTING_EVENT_BYTE 1 +#define SENSE_RESETTING_EVENT_FLAG 0x80 -#endif /* QETH_IPV6 */ -} +/* + * Common IO related definitions + */ +extern struct device *qeth_root_dev; +extern struct ccw_driver qeth_ccw_driver; +extern struct ccwgroup_driver qeth_ccwgroup_driver; + +#define CARD_RDEV(card) card->read.ccwdev +#define CARD_WDEV(card) card->write.ccwdev +#define CARD_DDEV(card) card->data.ccwdev +#define CARD_BUS_ID(card) card->gdev->dev.bus_id +#define CARD_RDEV_ID(card) card->read.ccwdev->dev.bus_id +#define CARD_WDEV_ID(card) card->write.ccwdev->dev.bus_id +#define CARD_DDEV_ID(card) card->data.ccwdev->dev.bus_id +#define CHANNEL_ID(channel) channel->ccwdev->dev.bus_id -static int (*qeth_my_eth_header) (struct sk_buff *, struct net_device *, - unsigned short, void *, void *, unsigned); -#ifdef CONFIG_TR -static int (*qeth_my_tr_header) (struct sk_buff *, struct net_device *, - unsigned short, void *, void *, unsigned); -#endif /* CONFIG_TR */ -static int (*qeth_my_eth_rebuild_header) (struct sk_buff *); -#ifdef CONFIG_TR -static int (*qeth_my_tr_rebuild_header) (struct sk_buff *); -#endif /* CONFIG_TR */ -static int (*qeth_my_eth_header_cache) (struct neighbour *, struct hh_cache *); -static void (*qeth_my_eth_header_cache_update) (struct hh_cache *, - struct net_device *, - unsigned char *); - -#ifdef QETH_IPV6 -typedef int (*__qeth_temp1) (struct sk_buff *, struct net_device *, - unsigned short, void *, void *, unsigned); -inline static __qeth_temp1 -qeth_get_hard_header(__u8 link_type) -{ - switch (link_type) { -#ifdef CONFIG_TR - case QETH_MPC_LINK_TYPE_HSTR: - case QETH_MPC_LINK_TYPE_LANE_TR: - return qeth_my_tr_header; -#endif /* CONFIG_TR */ - default: - return qeth_my_eth_header; - } -} +#define CARD_FROM_CDEV(cdev) (struct qeth_card *) \ + ((struct ccwgroup_device *)cdev->dev.driver_data)\ + ->dev.driver_data; -typedef int (*__qeth_temp2) (struct sk_buff *); -inline static __qeth_temp2 -qeth_get_rebuild_header(__u8 link_type) -{ - switch (link_type) { -#ifdef CONFIG_TR - case QETH_MPC_LINK_TYPE_HSTR: - case QETH_MPC_LINK_TYPE_LANE_TR: - return qeth_my_tr_rebuild_header; -#endif /* CONFIG_TR */ - default: - return qeth_my_eth_rebuild_header; - } -} +/** + * card stuff + */ +#ifdef CONFIG_QETH_PERF_STATS +struct qeth_perf_stats { + unsigned int bufs_rec; + unsigned int bufs_sent; -typedef int (*__qeth_temp3) (struct neighbour *, struct hh_cache *); -inline static __qeth_temp3 -qeth_get_hard_header_cache(__u8 link_type) -{ - switch (link_type) { - case QETH_MPC_LINK_TYPE_HSTR: - case QETH_MPC_LINK_TYPE_LANE_TR: - return NULL; - default: - return qeth_my_eth_header_cache; - } -} + unsigned int skbs_sent_pack; + unsigned int bufs_sent_pack; -typedef void (*__qeth_temp4) (struct hh_cache *, struct net_device *, - unsigned char *); -inline static __qeth_temp4 -qeth_get_header_cache_update(__u8 link_type) -{ - switch (link_type) { - case QETH_MPC_LINK_TYPE_HSTR: - case QETH_MPC_LINK_TYPE_LANE_TR: - return NULL; - default: - return qeth_my_eth_header_cache_update; - } -} + unsigned int sc_dp_p; + unsigned int sc_p_dp; -static unsigned short -qeth_eth_type_trans(struct sk_buff *skb, struct net_device *dev) -{ - struct ethhdr *eth; + __u64 inbound_start_time; + unsigned int inbound_cnt; + unsigned int inbound_time; + __u64 outbound_start_time; + unsigned int outbound_cnt; + unsigned int outbound_time; +}; +#endif /* CONFIG_QETH_PERF_STATS */ - skb->mac.raw = skb->data; - skb_pull(skb, ETH_ALEN * 2 + sizeof (short)); - eth = skb->mac.ethernet; - - if (*eth->h_dest & 1) { - if (memcmp(eth->h_dest, dev->broadcast, ETH_ALEN) == 0) - skb->pkt_type = PACKET_BROADCAST; - else - skb->pkt_type = PACKET_MULTICAST; - } else { - skb->pkt_type = PACKET_OTHERHOST; - } - if (ntohs(eth->h_proto) >= 1536) - return eth->h_proto; - if (*(unsigned short *) (skb->data) == 0xFFFF) - return htons(ETH_P_802_3); - return htons(ETH_P_802_2); -} +/* Routing stuff */ +struct qeth_routing_info { + enum qeth_routing_types type; +}; -typedef unsigned short (*__qeth_temp5) (struct sk_buff *, struct net_device *); -inline static __qeth_temp5 -qeth_get_type_trans(__u8 link_type) -{ - switch (link_type) { -#ifdef CONFIG_TR - case QETH_MPC_LINK_TYPE_HSTR: - case QETH_MPC_LINK_TYPE_LANE_TR: - return tr_type_trans; -#endif - default: - return qeth_eth_type_trans; - } -} -#endif /* QETH_IPV6 */ +/* IPA stuff */ +struct qeth_ipa_info { + __u32 supported_funcs; + __u32 enabled_funcs; +}; -inline static const char * -qeth_get_link_type_name(int cardtype, __u8 linktype) +static inline int +qeth_is_ipa_supported(struct qeth_ipa_info *ipa, enum qeth_ipa_funcs func) { - switch (cardtype) { - case QETH_CARD_TYPE_UNKNOWN: - return "unknown"; - case QETH_CARD_TYPE_OSAE: - switch (linktype) { - case QETH_MPC_LINK_TYPE_FAST_ETHERNET: - return "Fast Eth"; - case QETH_MPC_LINK_TYPE_HSTR: - return "HSTR"; - case QETH_MPC_LINK_TYPE_GIGABIT_ETHERNET: - return "Gigabit Eth"; - case QETH_MPC_LINK_TYPE_LANE_ETH100: - return "LANE Eth100"; - case QETH_MPC_LINK_TYPE_LANE_TR: - return "LANE TR"; - case QETH_MPC_LINK_TYPE_LANE_ETH1000: - return "LANE Eth1000"; - default: - return "unknown"; - } - case QETH_CARD_TYPE_IQD: - return "magic"; - default: - return "unknown"; - } + return (ipa->supported_funcs & func); } -inline static const char * -qeth_get_dev_basename(int cardtype, __u8 link_type) +static inline int +qeth_is_ipa_enabled(struct qeth_ipa_info *ipa, enum qeth_ipa_funcs func) { - switch (cardtype) { - case QETH_CARD_TYPE_UNKNOWN: - return "eth"; - case QETH_CARD_TYPE_OSAE: - switch (link_type) { - case QETH_MPC_LINK_TYPE_LANE_TR: - /* fallthrough */ - case QETH_MPC_LINK_TYPE_HSTR: - return "tr"; - default: - return "eth"; - } - case QETH_CARD_TYPE_IQD: - return "hsi"; - default: - return "eth"; - } + return (ipa->supported_funcs & ipa->enabled_funcs & func); } -/* inbound: */ -#define DEFAULT_BUFFER_SIZE 65536 -#define DEFAULT_BUFFER_COUNT 128 -#define BUFCNT_MIN 8 -#define BUFCNT_MAX 128 -#define BUFFER_SIZE (card->inbound_buffer_size) -#define BUFFER_MAX_ELEMENTS (BUFFER_SIZE>>12) - /* 8k for each pair header-buffer: */ +#define qeth_adp_supported(c,f) \ + qeth_is_ipa_supported(&c->options.adp, f) +#define qeth_adp_enabled(c,f) \ + qeth_is_ipa_enabled(&c->options.adp, f) +#define qeth_is_supported(c,f) \ + qeth_is_ipa_supported(&c->options.ipa4, f) +#define qeth_is_enabled(c,f) \ + qeth_is_ipa_enabled(&c->options.ipa4, f) +#ifdef CONFIG_QETH_IPV6 +#define qeth_is_supported6(c,f) \ + qeth_is_ipa_supported(&c->options.ipa6, f) +#define qeth_is_enabled6(c,f) \ + qeth_is_ipa_enabled(&c->options.ipa6, f) +#else /* CONFIG_QETH_IPV6 */ +#define qeth_is_supported6(c,f) 0 +#define qeth_is_enabled6(c,f) 0 +#endif /* CONFIG_QETH_IPV6 */ +#define qeth_is_ipafunc_supported(c,prot,f) \ + (prot==QETH_PROT_IPV6)? qeth_is_supported6(c,f):qeth_is_supported(c,f) +#define qeth_is_ipafunc_enabled(c,prot,f) \ + (prot==QETH_PROT_IPV6)? qeth_is_enabled6(c,f):qeth_is_enabled(c,f) -inline static int -qeth_sbal_packing_on_card(int cardtype) -{ - switch (cardtype) { - case QETH_CARD_TYPE_IQD: - return 0; - default: - return 1; - } -} -/* - * do it this way round -> __MODULE_STRING needs with - * QETH_PRIO_NICE_LEVELS a single number - */ -#define QETH_MAX_PRIO_QUEUES QETH_PRIO_NICE_LEVELS+1 +#define QETH_IDX_FUNC_LEVEL_OSAE_ENA_IPAT 0x0101 +#define QETH_IDX_FUNC_LEVEL_OSAE_DIS_IPAT 0x0101 +#define QETH_IDX_FUNC_LEVEL_IQD_ENA_IPAT 0x4108 +#define QETH_IDX_FUNC_LEVEL_IQD_DIS_IPAT 0x5108 -static inline int -qeth_sbalf15_in_retrieable_range(int sbalf15) -{ - return ((sbalf15 >= 15) && (sbalf15 <= 31)); -} +#define QETH_MODELLIST_ARRAY \ + {{0x1731,0x01,0x1732,0x01,QETH_CARD_TYPE_OSAE,1, \ + QETH_IDX_FUNC_LEVEL_OSAE_ENA_IPAT, \ + QETH_IDX_FUNC_LEVEL_OSAE_DIS_IPAT, \ + QETH_MAX_QUEUES,0}, \ + {0x1731,0x05,0x1732,0x05,QETH_CARD_TYPE_IQD,0, \ + QETH_IDX_FUNC_LEVEL_IQD_ENA_IPAT, \ + QETH_IDX_FUNC_LEVEL_IQD_DIS_IPAT, \ + QETH_MAX_QUEUES,0x103}, \ + {0,0,0,0,0,0,0,0,0}} + +#define QETH_REAL_CARD 1 +#define QETH_VLAN_CARD 2 +#define QETH_BUFSIZE 4096 -#define INBOUND_BUFFER_POS(card,bufno,sbale) \ - ( (bufno&SPAREBUF_MASK)? \ - ( \ - (sparebufs[bufno&(~SPAREBUF_MASK)].buf+ \ - PAGE_SIZE*sbale) \ - ):( \ - (card->inbound_buffer_pool_entry[card-> \ - inbound_buffer_entry_no[bufno]][sbale]) \ - ) ) - -#define SPAREBUF_UNAVAIL 0 -#define SPAREBUF_FREE 1 -#define SPAREBUF_USED 2 - -struct sparebufs { - char *buf; - atomic_t status; -}; - -#define SEND_STATE_INACTIVE 0 -#define SEND_STATE_DONT_PACK 1 -#define SEND_STATE_PACK 2 - -#define QETH_LOCK_UNLOCKED 0 -#define QETH_LOCK_NORMAL 1 -#define QETH_LOCK_FLUSH 2 - -#define QETH_TX_TIMEOUT 100*HZ /* 100 seconds */ - -#define QETH_REMOVE_WAIT_TIME 200 -#define QETH_WAIT_FOR_THREAD_TIME 20 -#define QETH_IDLE_WAIT_TIME 10 -#define QETH_WAIT_BEFORE_2ND_DOIO 1000 - -#define QETH_FAKE_LL_LEN ETH_HLEN /* 14 */ -#define QETH_FAKE_LL_PROT_LEN 2 -#define QETH_FAKE_LL_ADDR_LEN ETH_ALEN /* 6 */ -#define QETH_FAKE_LL_DEST_MAC_POS 0 -#define QETH_FAKE_LL_SRC_MAC_POS 6 -#define QETH_FAKE_LL_SRC_MAC_POS_IN_QDIO_HDR 6 -#define QETH_FAKE_LL_PROT_POS 12 -#define QETH_FAKE_LL_V4_ADDR_POS 16 +/** + * some more defs + */ +#define IF_NAME_LEN 16 +#define QETH_TX_TIMEOUT 100 * HZ +#define QETH_HEADER_SIZE 32 +#define MAX_PORTNO 15 +#define QETH_FAKE_LL_LEN ETH_HLEN #define QETH_FAKE_LL_V6_ADDR_POS 24 -#define DEV_NAME_LEN 16 -#define IOCTL_MAX_TRANSFER_SIZE 65535 +/*IPv6 address autoconfiguration stuff*/ +#define UNIQUE_ID_IF_CREATE_ADDR_FAILED 0xfffe +#define UNIQUE_ID_NOT_BY_CARD 0x10000 + +/*****************************************************************************/ +/* QDIO queue and buffer handling */ +/*****************************************************************************/ +#define QETH_MAX_QUEUES 4 +#define QETH_IN_BUF_SIZE_DEFAULT 65536 +#define QETH_IN_BUF_COUNT_DEFAULT 16 +#define QETH_IN_BUF_COUNT_MIN 8 +#define QETH_IN_BUF_COUNT_MAX 128 +#define QETH_MAX_BUFFER_ELEMENTS(card) ((card)->qdio.in_buf_size >> 12) +#define QETH_IN_BUF_REQUEUE_THRESHOLD(card) \ + ((card)->qdio.in_buf_pool.buf_count / 4) + +/* buffers we have to be behind before we get a PCI */ +#define QETH_PCI_THRESHOLD_A(card) ((card)->qdio.in_buf_pool.buf_count+1) +/*enqueued free buffers left before we get a PCI*/ +#define QETH_PCI_THRESHOLD_B(card) 0 +/*not used unless the microcode gets patched*/ +#define QETH_PCI_TIMER_VALUE(card) 3 + +#define QETH_MIN_INPUT_THRESHOLD 1 +#define QETH_MAX_INPUT_THRESHOLD 500 +#define QETH_MIN_OUTPUT_THRESHOLD 1 +#define QETH_MAX_OUTPUT_THRESHOLD 300 +/* priority queing */ +#define QETH_PRIOQ_DEFAULT QETH_NO_PRIO_QUEUEING +#define QETH_DEFAULT_QUEUE 2 +#define QETH_NO_PRIO_QUEUEING 0 +#define QETH_PRIO_Q_ING_PREC 1 +#define QETH_PRIO_Q_ING_TOS 2 #define IP_TOS_LOWDELAY 0x10 #define IP_TOS_HIGHTHROUGHPUT 0x08 #define IP_TOS_HIGHRELIABILITY 0x04 #define IP_TOS_NOTIMPORTANT 0x02 -#define QETH_RCD_LENGTH 128 - -#define __max(a,b) ( ((a)>(b))?(a):(b) ) -#define __min(a,b) ( ((a)<(b))?(a):(b) ) -#define QETH_BUFSIZE __max(__max(IPA_PDU_HEADER_SIZE+sizeof(struct arp_cmd), \ - IPA_PDU_HEADER_SIZE+sizeof(struct ipa_cmd)), \ - QETH_RCD_LENGTH) - -#define QETH_NOP_TIMEOUT 1500 -#define QETH_QUIESCE_NETDEV_TIME 300 -#define QETH_QUIESCE_WAIT_BEFORE_CLEAR 4000 -#define QETH_QUIESCE_WAIT_AFTER_CLEAR 4000 - -#define NOP_STATE 0x1001 -#define IDX_ACTIVATE_READ_STATE 0x1003 -#define IDX_ACTIVATE_WRITE_STATE 0x1004 -#define MPC_SETUP_STATE 0x1005 -#define CLEAR_STATE 0x1006 -#define IPA_CMD_STATE 0x1007 -#define IPA_IOCTL_STATE 0x1009 -#define IPA_SETIP_FLAG 0x100000 - -#define QETH_REMOVE_CARD_PROPER 1 -#define QETH_REMOVE_CARD_QUICK 2 - -#define NO_PRIO_QUEUEING 0 -#define PRIO_QUEUEING_PREC 1 -#define PRIO_QUEUEING_TOS 2 -#define NO_ROUTER 0 -#define PRIMARY_ROUTER 1 -#define SECONDARY_ROUTER 2 -#define MULTICAST_ROUTER 3 -#define PRIMARY_CONNECTOR 4 -#define SECONDARY_CONNECTOR 5 -#define ROUTER_MASK 0xf /* used to remove SET_ROUTING_FLAG - from routing_type */ -#define RESET_ROUTING_FLAG 0x10 /* used to indicate, that setting - the routing type is desired */ -#define BROADCAST_ALLRINGS 0 -#define BROADCAST_LOCAL 1 -#define MACADDR_NONCANONICAL 0 -#define MACADDR_CANONICAL 1 -#define ENABLE_TAKEOVER 0 -#define DISABLE_TAKEOVER 1 -#define FAKE_BROADCAST 0 -#define DONT_FAKE_BROADCAST 1 - -#define FAKE_LL 0 -#define DONT_FAKE_LL 1 - -#define QETH_BREAKOUT_LEAVE 1 -#define QETH_BREAKOUT_AGAIN 2 - -#define QETH_WAIT_FOR_LOCK 0 -#define QETH_DONT_WAIT_FOR_LOCK 1 -#define QETH_LOCK_ALREADY_HELD 2 - -#define PROBLEM_CARD_HAS_STARTLANED 1 -#define PROBLEM_RECEIVED_IDX_TERMINATE 2 -#define PROBLEM_ACTIVATE_CHECK_CONDITION 3 -#define PROBLEM_RESETTING_EVENT_INDICATOR 4 -#define PROBLEM_COMMAND_REJECT 5 -#define PROBLEM_ZERO_SENSE_DATA 6 -#define PROBLEM_GENERAL_CHECK 7 -#define PROBLEM_BAD_SIGA_RESULT 8 -#define PROBLEM_USER_TRIGGERED_RECOVERY 9 -#define PROBLEM_AFFE 10 -#define PROBLEM_MACHINE_CHECK 11 -#define PROBLEM_TX_TIMEOUT 12 - -#define CARD_RDEV(card) card->gdev->cdev[0] -#define CARD_WDEV(card) card->gdev->cdev[1] -#define CARD_DDEV(card) card->gdev->cdev[2] -#define CARD_BUS_ID(card) card->gdev->dev.bus_id -#define CARD_RDEV_ID(card) card->gdev->cdev[0]->dev.bus_id -#define CARD_WDEV_ID(card) card->gdev->cdev[1]->dev.bus_id -#define CARD_DDEV_ID(card) card->gdev->cdev[2]->dev.bus_id -#define CARD_FROM_CDEV(cdev) (struct qeth_card *) \ - ((struct ccwgroup_device *) cdev->dev.driver_data)->dev.driver_data - -#define SENSE_COMMAND_REJECT_BYTE 0 -#define SENSE_COMMAND_REJECT_FLAG 0x80 -#define SENSE_RESETTING_EVENT_BYTE 1 -#define SENSE_RESETTING_EVENT_FLAG 0x80 - -#define BUFFER_USED 1 -#define BUFFER_UNUSED -1 +/* Packing */ +#define QETH_LOW_WATERMARK_PACK 2 +#define QETH_HIGH_WATERMARK_PACK 5 +#define QETH_WATERMARK_PACK_FUZZ 1 -typedef int (*reg_notifier_t) (struct notifier_block *); +#define QETH_IP_HEADER_SIZE 40 +/* VLAN defines */ +#define QETH_EXT_HDR_VLAN_FRAME 0x01 +#define QETH_EXT_HDR_TOKEN_ID 0x02 +#define QETH_EXT_HDR_INCLUDE_VLAN_TAG 0x04 -struct ipato_entry { - int version; - __u8 addr[16]; - int mask_bits; - char dev_name[DEV_NAME_LEN]; - struct ipato_entry *next; -}; +struct qeth_hdr { + __u8 id; + __u8 flags; + __u16 inbound_checksum; + __u32 token; + __u16 length; + __u8 vlan_prio; + __u8 ext_flags; + __u16 vlan_id; + __u16 frame_offset; + __u8 dest_addr[16]; +} __attribute__ ((packed)); -struct qeth_vipa_entry { - int version; - __u8 ip[16]; - int flag; - volatile int state; - struct qeth_vipa_entry *next; -}; +/* flags for qeth_hdr.flags */ +#define QETH_HDR_PASSTHRU 0x10 +#define QETH_HDR_IPV6 0x80 +#define QETH_HDR_CAST_MASK 0x07 +enum qeth_cast_flags { + QETH_CAST_UNICAST = 0x06, + QETH_CAST_MULTICAST = 0x04, + QETH_CAST_BROADCAST = 0x05, + QETH_CAST_ANYCAST = 0x07, + QETH_CAST_NOCAST = 0x00, +}; + +/* flags for qeth_hdr.ext_flags */ +#define QETH_HDR_EXT_VLAN_FRAME 0x01 +#define QETH_HDR_EXT_CSUM_HDR_REQ 0x10 +#define QETH_HDR_EXT_CSUM_TRANSP_REQ 0x20 +#define QETH_HDR_EXT_SRC_MAC_ADDR 0x08 -struct ip_state { - struct in_ifaddr *ip_ifa; /* pointer to IPv4 adresses */ - struct inet6_ifaddr *ip6_ifa; -}; +static inline int +qeth_is_last_sbale(struct qdio_buffer_element *sbale) +{ + return (sbale->flags & SBAL_FLAGS_LAST_ENTRY); +} -struct qeth_ipm_mac { - __u8 mac[ETH_ALEN]; - __u8 ip[16]; - struct qeth_ipm_mac *next; +enum qeth_qdio_buffer_states { + /* + * inbound: read out by driver; owned by hardware in order to be filled + * outbound: owned by driver in order to be filled + */ + QETH_QDIO_BUF_EMPTY, + /* + * inbound: filled by hardware; owned by driver in order to be read out + * outbound: filled by driver; owned by hardware in order to be sent + */ + QETH_QDIO_BUF_PRIMED, + /* + * inbound only: an error condition has been detected for a buffer + * the buffer will be discarded (not read out) + */ + QETH_QDIO_BUF_ERROR, +}; + +enum qeth_qdio_info_states { + QETH_QDIO_UNINITIALIZED, + QETH_QDIO_ALLOCATED, + QETH_QDIO_ESTABLISHED, +}; + +struct qeth_buffer_pool_entry { + struct list_head list; + struct list_head init_list; + void *elements[QDIO_MAX_ELEMENTS_PER_BUFFER]; +}; + +struct qeth_qdio_buffer_pool { + struct list_head entry_list; + int buf_count; +}; + +struct qeth_qdio_buffer { + struct qdio_buffer *buffer; + volatile enum qeth_qdio_buffer_states state; + /* the buffer pool entry currently associated to this buffer */ + struct qeth_buffer_pool_entry *pool_entry; +}; + +struct qeth_qdio_q { + struct qdio_buffer qdio_bufs[QDIO_MAX_BUFFERS_PER_Q]; + struct qeth_qdio_buffer bufs[QDIO_MAX_BUFFERS_PER_Q]; + /* + * buf_to_process means "buffer primed by hardware, + * has to be read in by driver"; current state PRIMED + */ + volatile int next_buf_to_process; + /* + * buf_to_init means "buffer must be initialized by driver and must + * be made available for hardware" -> state is set to EMPTY + */ + volatile int next_buf_to_init; +} __attribute__ ((aligned(256))); + +struct qeth_qdio_out_buffer { + struct qdio_buffer *buffer; + volatile enum qeth_qdio_buffer_states state; + volatile int next_element_to_fill; + struct sk_buff_head skb_list; }; -struct ip_mc_state { - struct qeth_ipm_mac *ipm_ifa; - struct qeth_ipm_mac *ipm6_ifa; -}; +struct qeth_card; -struct addr_request { - struct addr_request *next; - int request_type; - __u8 mac[ETH_ALEN]; - __u8 ip[16]; -}; +struct qeth_qdio_out_q { + struct qdio_buffer qdio_bufs[QDIO_MAX_BUFFERS_PER_Q]; + struct qeth_qdio_out_buffer bufs[QDIO_MAX_BUFFERS_PER_Q]; + int queue_no; + struct qeth_card *card; + struct tasklet_struct tasklet; + spinlock_t lock; + volatile int do_pack; + /* + * index of buffer to be filled by driver; state EMPTY or PACKING + */ + volatile int next_buf_to_fill; + volatile int next_buf_to_flush; + /* + * number of buffers that are currently filled (PRIMED) + * -> these buffers are hardware-owned + */ + atomic_t used_buffers; + /* indicates whether PCI flag must be set (or if one is outstanding) */ + atomic_t set_pci_flags_count; +} __attribute__ ((aligned(256))); + +struct qeth_qdio_info { + volatile enum qeth_qdio_info_states state; + /* input */ + struct qeth_qdio_q *in_q; + struct qeth_qdio_buffer_pool in_buf_pool; + struct qeth_qdio_buffer_pool init_pool; + int in_buf_size; + struct tasklet_struct in_tasklet; + + /* output */ + int no_out_queues; + struct qeth_qdio_out_q **out_qs; -struct qeth_card_options { - char devname[DEV_NAME_LEN]; - volatile int routing_type4; -#ifdef QETH_IPV6 - volatile int routing_type6; -#endif /* QETH_IPV6 */ - int checksum_type; + /* priority queueing */ int do_prio_queueing; - int default_queue; - int inbound_buffer_count; - int polltime; - char portname[9]; - int portno; - int broadcast_mode; - int macaddr_mode; - int ena_ipat; - int fake_broadcast; - int add_hhlen; - int fake_ll; + int default_out_queue; }; -struct qeth_hdr { - __u8 id; - __u8 flags; - __u16 inbound_checksum; - __u32 token; - __u16 length; - __u8 vlan_prio; - __u8 ext_flags; - __u16 vlan_id; - __u16 frame_offset; - __u8 dest_addr[16]; +enum qeth_send_errors { + QETH_SEND_ERROR_NONE, + QETH_SEND_ERROR_LINK_FAILURE, + QETH_SEND_ERROR_RETRY, + QETH_SEND_ERROR_KICK_IT, }; -struct qeth_ringbuffer_element { - struct sk_buff_head skb_list; - int next_element_to_fill; -} __attribute__ ((packed)); +#define QETH_ETH_MAC_V4 0x0100 /* like v4 */ +#define QETH_ETH_MAC_V6 0x3333 /* like v6 */ +/* tr mc mac is longer, but that will be enough to detect mc frames */ +#define QETH_TR_MAC_NC 0xc000 /* non-canonical */ +#define QETH_TR_MAC_C 0x0300 /* canonical */ -struct qeth_ringbuffer { - struct qdio_buffer buffer[QDIO_MAX_BUFFERS_PER_Q]; - struct qeth_ringbuffer_element ringbuf_element[QDIO_MAX_BUFFERS_PER_Q]; -}__attribute__ ((packed, aligned(PAGE_SIZE))); - -struct qeth_dma_stuff { - unsigned char *sendbuf; - unsigned char *recbuf; - struct ccw1 read_ccw; - struct ccw1 write_ccw; -}__attribute__ ((packed, aligned(PAGE_SIZE))); +#define DEFAULT_ADD_HHLEN 0 +#define MAX_ADD_HHLEN 1024 -struct qeth_perf_stats { - unsigned int skbs_rec; - unsigned int bufs_rec; +/** + * buffer stuff for read channel + */ +#define QETH_CMD_BUFFER_NO 8 - unsigned int skbs_sent; - unsigned int bufs_sent; +/** + * channel state machine + */ +enum qeth_channel_states { + CH_STATE_UP, + CH_STATE_DOWN, + CH_STATE_ACTIVATING, + CH_STATE_HALTED, + CH_STATE_STOPPED, +}; +/** + * card state machine + */ +enum qeth_card_states { + CARD_STATE_DOWN, + CARD_STATE_HARDSETUP, + CARD_STATE_SOFTSETUP, + CARD_STATE_UP_LAN_OFFLINE, + CARD_STATE_UP_LAN_ONLINE, + CARD_STATE_RECOVER, +}; - unsigned int skbs_sent_dont_pack; - unsigned int bufs_sent_dont_pack; - unsigned int skbs_sent_pack; - unsigned int bufs_sent_pack; - unsigned int skbs_sent_pack_better; - unsigned int bufs_sent_pack_better; +/** + * Protocol versions + */ +enum qeth_prot_versions { + QETH_PROT_SNA = 0x0001, + QETH_PROT_IPV4 = 0x0004, + QETH_PROT_IPV6 = 0x0006, +}; - unsigned int sc_dp_p; - unsigned int sc_p_dp; +enum qeth_ip_types { + QETH_IP_TYPE_NORMAL, + QETH_IP_TYPE_VIPA, + QETH_IP_TYPE_RXIP, +}; - __u64 inbound_start_time; - unsigned int inbound_cnt; - unsigned int inbound_time; - __u64 outbound_start_time; - unsigned int outbound_cnt; - unsigned int outbound_time; +enum qeth_cmd_buffer_state { + BUF_STATE_FREE, + BUF_STATE_LOCKED, + BUF_STATE_PROCESSED, +}; +/** + * IP address and multicast list + */ +struct qeth_ipaddr { + struct list_head entry; + enum qeth_ip_types type; + enum qeth_ipa_setdelip_flags set_flags; + enum qeth_ipa_setdelip_flags del_flags; + int is_multicast; + volatile int users; + enum qeth_prot_versions proto; + unsigned char mac[OSA_ADDR_LEN]; + union { + struct { + unsigned int addr; + unsigned int mask; + } a4; + struct { + struct in6_addr addr; + unsigned int pfxlen; + } a6; + } u; +}; + +struct qeth_ipato_entry { + struct list_head entry; + enum qeth_prot_versions proto; + char addr[16]; + int mask_bits; }; -/* ugly. I know. */ -struct qeth_card { /* pointed to by dev->priv */ +struct qeth_ipato { + int enabled; + int invert4; + int invert6; + struct list_head entries; +}; - /* pointer to options (defaults + parameters) */ - struct qeth_card_options options; +struct qeth_channel; - atomic_t is_startlaned; /* card did not get a stoplan */ - /* also 0 when card is gone after a - * machine check */ - - __u8 link_type; - - int is_guest_lan; - - /* inbound buffer management */ - atomic_t inbound_buffer_refcnt[QDIO_MAX_BUFFERS_PER_Q]; - struct qdio_buffer inbound_qdio_buffers[QDIO_MAX_BUFFERS_PER_Q]; - /* inbound data area */ - void *inbound_buffer_pool_entry[QDIO_MAX_BUFFERS_PER_Q] - [QDIO_MAX_ELEMENTS_PER_BUFFER]; - volatile int inbound_buffer_pool_entry_used[QDIO_MAX_BUFFERS_PER_Q]; - int inbound_buffer_entry_no[QDIO_MAX_BUFFERS_PER_Q]; - - /* for requeueing of buffers */ - spinlock_t requeue_input_lock; - atomic_t requeue_position; - atomic_t requeue_counter; - - /* outbound QDIO stuff */ - volatile int send_state[QETH_MAX_QUEUES]; - volatile int outbound_first_free_buffer[QETH_MAX_QUEUES]; - atomic_t outbound_used_buffers[QETH_MAX_QUEUES]; - int outbound_buffer_send_state[QETH_MAX_QUEUES] - [QDIO_MAX_BUFFERS_PER_Q]; - int send_retries[QETH_MAX_QUEUES][QDIO_MAX_BUFFERS_PER_Q]; - volatile int outbound_bytes_in_buffer[QETH_MAX_QUEUES]; - struct qeth_ringbuffer *outbound_ringbuffer[QETH_MAX_QUEUES]; - atomic_t outbound_ringbuffer_lock[QETH_MAX_QUEUES]; - atomic_t last_pci_pos[QETH_MAX_QUEUES]; - -#ifdef QETH_IPV6 - int (*hard_header) (struct sk_buff *, struct net_device *, - unsigned short, void *, void *, unsigned); - int (*rebuild_header) (struct sk_buff *); - int (*hard_header_cache) (struct neighbour *, struct hh_cache *); - void (*header_cache_update) (struct hh_cache *, struct net_device *, - unsigned char *); - unsigned short (*type_trans) (struct sk_buff *, struct net_device *); -#endif /* QETH_IPV6 */ +struct qeth_cmd_buffer { + enum qeth_cmd_buffer_state state; + struct qeth_channel *channel; + unsigned char *data; + int rc; + void (*callback) (struct qeth_channel *, struct qeth_cmd_buffer *); +}; -#ifdef QETH_VLAN - struct vlan_group *vlangrp; - spinlock_t vlan_lock; -#endif - char dev_name[DEV_NAME_LEN]; /* pointed to by dev->name */ - struct net_device *dev; - struct net_device_stats *stats; +/** + * definition of a qeth channel, used for read and write + */ +struct qeth_channel { + enum qeth_channel_states state; + struct ccw1 ccw; + spinlock_t iob_lock; + wait_queue_head_t wait_q; + struct tasklet_struct irq_tasklet; + struct ccw_device *ccwdev; +/*command buffer for control data*/ + struct qeth_cmd_buffer iob[QETH_CMD_BUFFER_NO]; + atomic_t irq_pending; + volatile int io_buf_no; + volatile int buf_no; +}; - int no_queues; +/** + * OSA card related definitions + */ +struct qeth_token { + __u32 issuer_rm_w; + __u32 issuer_rm_r; + __u32 cm_filter_w; + __u32 cm_filter_r; + __u32 cm_connection_w; + __u32 cm_connection_r; + __u32 ulp_filter_w; + __u32 ulp_filter_r; + __u32 ulp_connection_w; + __u32 ulp_connection_r; +}; + +struct qeth_seqno { + __u32 trans_hdr; + __u32 pdu_hdr; + __u32 pdu_hdr_ack; + __u32 ipa; +}; -#ifdef QETH_PERFORMANCE_STATS - struct qeth_perf_stats perf_stats; -#endif /* QETH_PERFORMANCE_STATS */ +struct qeth_reply { + struct list_head list; + wait_queue_head_t wait_q; + int (*callback)(struct qeth_card *,struct qeth_reply *,unsigned long); + int seqno; + int received; + int rc; + void *param; + struct qeth_card *card; + atomic_t refcnt; +}; - /* our state */ - atomic_t is_registered; /* card registered as netdev? */ - atomic_t is_hardsetup; /* card has gone through hardsetup */ - atomic_t is_softsetup; /* card is setup by softsetup */ - atomic_t is_open; /* card is in use */ - - /* prevents deadlocks :-O */ - struct semaphore softsetup_sema; - struct semaphore hardsetup_sema; - spinlock_t ioctl_lock; - atomic_t softsetup_thread_is_running; - struct semaphore softsetup_thread_sem; - struct work_struct tqueue_sst; - - atomic_t escape_softsetup; /* active, when recovery has to - wait for softsetup */ - struct semaphore reinit_thread_sem; - atomic_t in_recovery; - atomic_t reinit_counter; - - /* problem management */ - atomic_t break_out; - atomic_t problem; - struct work_struct tqueue; - - struct { - __u32 trans_hdr; - __u32 pdu_hdr; - __u32 pdu_hdr_ack; - __u32 ipa; - } seqno; - - struct { - __u32 issuer_rm_w; - __u32 issuer_rm_r; - __u32 cm_filter_w; - __u32 cm_filter_r; - __u32 cm_connection_w; - __u32 cm_connection_r; - __u32 ulp_filter_w; - __u32 ulp_filter_r; - __u32 ulp_connection_w; - __u32 ulp_connection_r; - } token; +struct qeth_card_info { - /* this is card-related */ - int type; + char if_name[IF_NAME_LEN]; + unsigned short unit_addr2; + unsigned short cula; + unsigned short chpid; __u16 func_level; + char mcl_level[QETH_MCL_LENGTH + 1]; + int guestlan; + int portname_required; + int portno; + char portname[9]; + enum qeth_card_types type; + enum qeth_link_types link_type; + int is_multicast_different; int initial_mtu; int max_mtu; - int inbound_buffer_size; - - int is_multicast_different; /* if multicast traffic is to be sent - on a different queue, this is the - queue+no_queues */ - __u32 ipa_supported; - __u32 ipa_enabled; - __u32 ipa6_supported; - __u32 ipa6_enabled; - __u32 adp_supported; - - __u32 csum_enable_mask; - - atomic_t startlan_attempts; - atomic_t enable_routing_attempts4; - atomic_t rt4fld; -#ifdef QETH_IPV6 - atomic_t enable_routing_attempts6; - atomic_t rt6fld; -#endif /* QETH_IPV6 */ + int broadcast_capable; int unique_id; + __u32 csum_mask; +}; - /* device and I/O data */ - struct ccwgroup_device *gdev; - unsigned short unit_addr2; - unsigned short cula; - unsigned short chpid; - - unsigned char ipa_buf[QETH_BUFSIZE]; - unsigned char send_buf[QETH_BUFSIZE]; - -/* IOCTL Stuff */ - unsigned char *ioctl_data_buffer; - unsigned char *ioctl_buffer_pointer; - int ioctl_returncode; - int ioctl_buffersize; - int number_of_entries; - - atomic_t ioctl_data_has_arrived; - wait_queue_head_t ioctl_wait_q; +struct qeth_card_options { + struct qeth_routing_info route4; + struct qeth_ipa_info ipa4; + struct qeth_ipa_info adp; /*Adapter parameters*/ +#ifdef CONFIG_QETH_IPV6 + struct qeth_routing_info route6; + struct qeth_ipa_info ipa6; +#endif /* QETH_IPV6 */ + enum qeth_checksum_types checksum_type; + int broadcast_mode; + int macaddr_mode; + int enable_takeover; + int fake_broadcast; + int add_hhlen; + int fake_ll; +}; -/* stuff under 2 gb */ - struct qeth_dma_stuff *dma_stuff; +/* + * thread bits for qeth_card thread masks + */ +enum qeth_threads { + QETH_SET_IP_THREAD = 1, + QETH_SET_MC_THREAD = 2, + QETH_RECOVER_THREAD = 4, +}; - unsigned int ipa_timeout; +struct qeth_card { + struct list_head list; + enum qeth_card_states state; + int lan_online; + spinlock_t lock; +/*hardware and sysfs stuff*/ + struct ccwgroup_device *gdev; + struct qeth_channel read; + struct qeth_channel write; + struct qeth_channel data; - atomic_t write_busy; + struct net_device *dev; + struct net_device_stats stats; - /* vipa stuff */ - rwlock_t vipa_list_lock; - struct qeth_vipa_entry *vipa_list; + struct qeth_card_info info; + struct qeth_token token; + struct qeth_seqno seqno; + struct qeth_card_options options; - /* state information when doing I/O */ - atomic_t shutdown_phase; - atomic_t data_has_arrived; wait_queue_head_t wait_q; +#ifdef CONFIG_QETH_VLAN + spinlock_t vlanlock; + struct vlan_group *vlangrp; +#endif + struct work_struct kernel_thread_starter; + spinlock_t thread_mask_lock; + volatile unsigned long thread_start_mask; + volatile unsigned long thread_allowed_mask; + volatile unsigned long thread_running_mask; + spinlock_t ip_lock; + struct list_head ip_list; + struct list_head ip_tbd_list; + struct qeth_ipato ipato; + struct list_head cmd_waiter_list; + /* QDIO buffer handling */ + struct qeth_qdio_info qdio; +#ifdef CONFIG_QETH_PERF_STATS + struct qeth_perf_stats perf_stats; +#endif /* CONFIG_QETH_PERF_STATS */ + int use_hard_stop; +}; - atomic_t clear_succeeded0; - atomic_t clear_succeeded1; - atomic_t clear_succeeded2; - - /* bookkeeping of IP and multicast addresses */ - struct ip_state ip_current_state; - struct ip_state ip_new_state; - - struct ip_mc_state ip_mc_current_state; - struct ip_mc_state ip_mc_new_state; - - int broadcast_capable; - int portname_required; - - int realloc_message; - - char level[QETH_MCL_LENGTH + 1]; - - volatile int saved_dev_flags; - - /* for our linked list */ - struct qeth_card *next; +struct qeth_card_list_struct { + struct list_head list; + rwlock_t rwlock; }; -inline static int -qeth_get_arphrd_type(int cardtype, int linktype) -{ - switch (cardtype) { - case QETH_CARD_TYPE_OSAE: - switch (linktype) { - case QETH_MPC_LINK_TYPE_LANE_TR: - /* fallthrough */ - case QETH_MPC_LINK_TYPE_HSTR: - return ARPHRD_IEEE802_TR; - default: - return ARPHRD_ETHER; - } - case QETH_CARD_TYPE_IQD: - return ARPHRD_ETHER; - default: - return ARPHRD_ETHER; - } -} +extern struct qeth_card_list_struct qeth_card_list; + +/*some helper functions*/ inline static __u8 -qeth_get_adapter_type_for_ipa(int link_type) +qeth_get_ipa_adp_type(enum qeth_link_types link_type) { switch (link_type) { - case QETH_MPC_LINK_TYPE_HSTR: + case QETH_LINK_TYPE_HSTR: return 2; default: return 1; } } -inline static const char * -qeth_get_cardname(int cardtype, int is_guest_lan) +inline static int +qeth_get_hlen(__u8 link_type) { - - if (is_guest_lan) { - switch (cardtype) { - case QETH_CARD_TYPE_UNKNOWN: - return "n unknown"; - case QETH_CARD_TYPE_OSAE: - return " Guest LAN QDIO"; - case QETH_CARD_TYPE_IQD: - return " Guest LAN Hiper"; - default: return - " strange"; - } - } else { - switch (cardtype) { - case QETH_CARD_TYPE_UNKNOWN: - return "n unknown"; - case QETH_CARD_TYPE_OSAE: - return "n OSD Express"; - case QETH_CARD_TYPE_IQD: - return " HiperSockets"; - default: - return " strange"; - } +#ifdef CONFIG_QETH_IPV6 + switch (link_type) { + case QETH_LINK_TYPE_HSTR: + case QETH_LINK_TYPE_LANE_TR: + return sizeof(struct qeth_hdr) + TR_HLEN; + default: +#ifdef CONFIG_QETH_VLAN + return sizeof(struct qeth_hdr) + VLAN_ETH_HLEN; +#else + return sizeof(struct qeth_hdr) + ETH_HLEN; +#endif } +#else /* CONFIG_QETH_IPV6 */ +#ifdef CONFIG_QETH_VLAN + return sizeof(struct qeth_hdr) + VLAN_HLEN; +#else + return sizeof(struct qeth_hdr); +#endif +#endif /* CONFIG_QETH_IPV6 */ } -/* max length to be returned: 14 */ -inline static const char * -qeth_get_cardname_short(int cardtype, __u8 link_type, int is_guest_lan) +inline static unsigned short +qeth_get_netdev_flags(int cardtype) { switch (cardtype) { - case QETH_CARD_TYPE_UNKNOWN: - return "unknown"; - case QETH_CARD_TYPE_OSAE: - if (is_guest_lan) - return "GuestLAN QDIO"; - switch (link_type) { - case QETH_MPC_LINK_TYPE_FAST_ETHERNET: - return "OSD_100"; - case QETH_MPC_LINK_TYPE_HSTR: - return "HSTR"; - case QETH_MPC_LINK_TYPE_GIGABIT_ETHERNET: - return "OSD_1000"; - case QETH_MPC_LINK_TYPE_LANE_ETH100: - return "OSD_FE_LANE"; - case QETH_MPC_LINK_TYPE_LANE_TR: - return "OSD_TR_LANE"; - case QETH_MPC_LINK_TYPE_LANE_ETH1000: - return "OSD_GbE_LANE"; - case QETH_MPC_LINK_TYPE_LANE: - return "OSD_ATM_LANE"; - default: - return "OSD_Express"; - } case QETH_CARD_TYPE_IQD: - return is_guest_lan ? "GuestLAN Hiper" : "HiperSockets"; + return IFF_NOARP; +#ifdef CONFIG_QETH_IPV6 default: - return " strange"; - } -} - -inline static int -qeth_mtu_is_valid(struct qeth_card * card, int mtu) -{ - switch (card->type) { - case QETH_CARD_TYPE_UNKNOWN: - return 1; - case QETH_CARD_TYPE_OSAE: - return ((mtu >= 576) && (mtu <= 61440)); - case QETH_CARD_TYPE_IQD: - return ((mtu >= 576) && (mtu <= card->max_mtu + 4096 - 32)); + return 0; +#else default: - return 1; + return IFF_NOARP; +#endif } } inline static int qeth_get_initial_mtu_for_card(struct qeth_card * card) { - switch (card->type) { + switch (card->info.type) { case QETH_CARD_TYPE_UNKNOWN: return 1500; case QETH_CARD_TYPE_IQD: - return card->max_mtu; + return card->info.max_mtu; case QETH_CARD_TYPE_OSAE: - switch (card->link_type) { - case QETH_MPC_LINK_TYPE_HSTR: - case QETH_MPC_LINK_TYPE_LANE_TR: + switch (card->info.link_type) { + case QETH_LINK_TYPE_HSTR: + case QETH_LINK_TYPE_LANE_TR: return 2000; default: return 1492; @@ -1195,39 +837,50 @@ qeth_get_mtu_outof_framesize(int framesi } inline static int -qeth_get_buffersize_for_card(int cardtype) +qeth_mtu_is_valid(struct qeth_card * card, int mtu) { - switch (cardtype) { - case QETH_CARD_TYPE_UNKNOWN: - return 65536; + switch (card->info.type) { case QETH_CARD_TYPE_OSAE: - return 65536; + return ((mtu >= 576) && (mtu <= 61440)); case QETH_CARD_TYPE_IQD: - return 16384; + return ((mtu >= 576) && + (mtu <= card->info.max_mtu + 4096 - 32)); + case QETH_CARD_TYPE_UNKNOWN: default: - return 65536; + return 1; } } inline static int -qeth_get_min_number_of_buffers(int cardtype) +qeth_get_arphdr_type(int cardtype, int linktype) { switch (cardtype) { - case QETH_CARD_TYPE_UNKNOWN: - return 32; case QETH_CARD_TYPE_OSAE: - return 32; + switch (linktype) { + case QETH_LINK_TYPE_LANE_TR: + case QETH_LINK_TYPE_HSTR: + return ARPHRD_IEEE802_TR; + default: + return ARPHRD_ETHER; + } case QETH_CARD_TYPE_IQD: - return 64; default: - return 64; + return ARPHRD_ETHER; } } +#ifdef CONFIG_QETH_PERF_STATS inline static int -qeth_get_q_format(int cardtype) +qeth_get_micros(void) { - switch (cardtype) { + return (int) (get_clock() >> 12); +} +#endif + +static inline int +qeth_get_qdio_q_format(struct qeth_card *card) +{ + switch (card->info.type) { case QETH_CARD_TYPE_IQD: return 2; default: @@ -1235,100 +888,120 @@ qeth_get_q_format(int cardtype) } } -inline static int -qeth_get_device_tx_q_len(int cardtype) +static inline void +qeth_ipaddr4_to_string(const __u8 *addr, char *buf) { - return 100; + sprintf(buf, "%i.%i.%i.%i", addr[0], addr[1], addr[2], addr[3]); } -inline static int -qeth_get_max_number_of_buffers(int cardtype) +static inline int +qeth_string_to_ipaddr4(const char *buf, __u8 *addr) { - return 127; + const char *start, *end; + char abuf[4]; + char *tmp; + int len; + int i; + + start = buf; + for (i = 0; i < 3; i++) { + if (!(end = strchr(start, '.'))) + return -EINVAL; + len = end - start; + memset(abuf, 0, 4); + strncpy(abuf, start, len); + addr[i] = simple_strtoul(abuf, &tmp, 10); + start = end + 1; + } + memset(abuf, 0, 4); + strcpy(abuf, start); + addr[3] = simple_strtoul(abuf, &tmp, 10); + return 0; +} + +static inline void +qeth_ipaddr6_to_string(const __u8 *addr, char *buf) +{ + sprintf(buf, "%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]); } -/******************** OUTPUT FACILITIES **************************/ - -#ifdef PRINT_INFO -#undef PRINTK_HEADER -#undef PRINT_STUPID -#undef PRINT_ALL -#undef PRINT_INFO -#undef PRINT_WARN -#undef PRINT_ERR -#undef PRINT_CRIT -#undef PRINT_ALERT -#undef PRINT_EMERG -#endif /* PRINT_INFO */ - -#define PRINTK_HEADER QETH_NAME ": " - -#if QETH_VERBOSE_LEVEL>8 -#define PRINT_STUPID(x...) printk( KERN_DEBUG PRINTK_HEADER x) -#else -#define PRINT_STUPID(x...) -#endif +static inline int +qeth_string_to_ipaddr6(const char *buf, __u8 *addr) +{ + const char *start, *end; + u16 *tmp_addr; + char abuf[5]; + char *tmp; + int len; + int i; + + tmp_addr = (u16 *)addr; + start = buf; + for (i = 0; i < 7; i++) { + if (!(end = strchr(start, ':'))) + return -EINVAL; + len = end - start; + memset(abuf, 0, 5); + strncpy(abuf, start, len); + tmp_addr[i] = simple_strtoul(abuf, &tmp, 16); + start = end + 1; + } + memset(abuf, 0, 5); + strcpy(abuf, start); + tmp_addr[7] = simple_strtoul(abuf, &tmp, 16); + return 0; +} + +static inline void +qeth_ipaddr_to_string(enum qeth_prot_versions proto, const __u8 *addr, + char *buf) +{ + if (proto == QETH_PROT_IPV4) + return qeth_ipaddr4_to_string(addr, buf); + else if (proto == QETH_PROT_IPV6) + return qeth_ipaddr6_to_string(addr, buf); +} -#if QETH_VERBOSE_LEVEL>7 -#define PRINT_ALL(x...) printk( KERN_DEBUG PRINTK_HEADER x) -#else -#define PRINT_ALL(x...) -#endif +static inline int +qeth_string_to_ipaddr(const char *buf, enum qeth_prot_versions proto, + __u8 *addr) +{ + if (proto == QETH_PROT_IPV4) + return qeth_string_to_ipaddr4(buf, addr); + else if (proto == QETH_PROT_IPV6) + return qeth_string_to_ipaddr6(buf, addr); + else + return -EINVAL; +} -#if QETH_VERBOSE_LEVEL>6 -#define PRINT_INFO(x...) printk( KERN_INFO PRINTK_HEADER x) -#else -#define PRINT_INFO(x...) -#endif +extern int +qeth_setrouting_v4(struct qeth_card *); +extern int +qeth_setrouting_v6(struct qeth_card *); -#if QETH_VERBOSE_LEVEL>5 -#define PRINT_WARN(x...) printk( KERN_WARNING PRINTK_HEADER x) -#else -#define PRINT_WARN(x...) -#endif +int +qeth_add_ipato_entry(struct qeth_card *, struct qeth_ipato_entry *); -#if QETH_VERBOSE_LEVEL>4 -#define PRINT_ERR(x...) printk( KERN_ERR PRINTK_HEADER x) -#else -#define PRINT_ERR(x...) -#endif +void +qeth_del_ipato_entry(struct qeth_card *, enum qeth_prot_versions, u8 *, int); -#if QETH_VERBOSE_LEVEL>3 -#define PRINT_CRIT(x...) printk( KERN_CRIT PRINTK_HEADER x) -#else -#define PRINT_CRIT(x...) -#endif +int +qeth_add_vipa(struct qeth_card *, enum qeth_prot_versions, const u8 *); -#if QETH_VERBOSE_LEVEL>2 -#define PRINT_ALERT(x...) printk( KERN_ALERT PRINTK_HEADER x) -#else -#define PRINT_ALERT(x...) -#endif +void +qeth_del_vipa(struct qeth_card *, enum qeth_prot_versions, const u8 *); -#if QETH_VERBOSE_LEVEL>1 -#define PRINT_EMERG(x...) printk( KERN_EMERG PRINTK_HEADER x) -#else -#define PRINT_EMERG(x...) -#endif +int +qeth_add_rxip(struct qeth_card *, enum qeth_prot_versions, const u8 *); -#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)); +void +qeth_del_rxip(struct qeth_card *, enum qeth_prot_versions, const u8 *); +void +qeth_schedule_recovery(struct qeth_card *); #endif /* __QETH_H__ */ diff -puN /dev/null drivers/s390/net/qeth_main.c --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ 25-akpm/drivers/s390/net/qeth_main.c 2004-04-08 13:55:46.761915280 -0700 @@ -0,0 +1,6820 @@ +/* + * + * linux/drivers/s390/net/qeth_main.c ($Revision: 1.77 $) + * + * Linux on zSeries OSA Express and HiperSockets support + * + * Copyright 2000,2003 IBM Corporation + * + * Author(s): Original Code written by + * Utz Bacher (utz.bacher@de.ibm.com) + * Rewritten by + * Frank Pavlic (pavlic@de.ibm.com) and + * Thomas Spatzier + * + * $Revision: 1.77 $ $Date: 2004/04/06 14:38:19 $ + * + * 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. + */ + + +/*** + * eye catcher; just for debugging purposes + */ +void volatile +qeth_eyecatcher(void) +{ + return; +} + +#include +#include +#include + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "qeth.h" +#include "qeth_mpc.h" +#include "qeth_fs.h" + +#define VERSION_QETH_C "$Revision: 1.77 $" +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 ")"; +/** + * Debug Facility Stuff + */ +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 char qeth_dbf_text_buf[255]; + +/** + * some more definitions and declarations + */ +static unsigned int known_devices[][10] = QETH_MODELLIST_ARRAY; + +/* list of our cards */ +struct qeth_card_list_struct qeth_card_list; + +static void qeth_send_control_data_cb(struct qeth_channel *, + struct qeth_cmd_buffer *); + +static atomic_t qeth_hsi_count; + +/** + * here we go with function implementation + */ +static void +qeth_init_qdio_info(struct qeth_card *card); + +static int +qeth_init_qdio_queues(struct qeth_card *card); + +static int +qeth_alloc_qdio_buffers(struct qeth_card *card); + +static void +qeth_free_qdio_buffers(struct qeth_card *); + +static void +qeth_clear_qdio_buffers(struct qeth_card *); + +static void +qeth_clear_ip_list(struct qeth_card *, int, int); + +static void +qeth_clear_ipacmd_list(struct qeth_card *); + +static int +qeth_qdio_clear_card(struct qeth_card *, int); + +static void +qeth_clear_working_pool_list(struct qeth_card *); + +static void +qeth_clear_cmd_buffers(struct qeth_channel *); + +static int +qeth_stop(struct net_device *); + +static void +qeth_clear_ipato_list(struct qeth_card *); + +static int +qeth_is_addr_covered_by_ipato(struct qeth_card *, struct qeth_ipaddr *); + +static void +qeth_irq_tasklet(unsigned long); + +static int +qeth_set_online(struct ccwgroup_device *); +/** + * free channel command buffers + */ +static void +qeth_clean_channel(struct qeth_channel *channel) +{ + int cnt; + + QETH_DBF_TEXT(setup, 2, "freech"); + for (cnt = 0; cnt < QETH_CMD_BUFFER_NO; cnt++) + kfree(channel->iob[cnt].data); +} + +/** + * free card + */ +static void +qeth_free_card(struct qeth_card *card) +{ + + QETH_DBF_TEXT(setup, 2, "freecrd"); + QETH_DBF_HEX(setup, 2, &card, sizeof(void *)); + qeth_clean_channel(&card->read); + qeth_clean_channel(&card->write); + if (card->dev) + free_netdev(card->dev); + qeth_clear_ip_list(card, 0, 0); + qeth_clear_ipato_list(card); + qeth_free_qdio_buffers(card); + kfree(card); +} + +/** + * alloc memory for command buffer per channel + */ +static int +qeth_setup_channel(struct qeth_channel *channel) +{ + int cnt; + + QETH_DBF_TEXT(setup, 2, "setupch"); + for (cnt=0; cnt < QETH_CMD_BUFFER_NO; cnt++) { + channel->iob[cnt].data = (char *) + kmalloc(QETH_BUFSIZE, GFP_DMA|GFP_KERNEL); + if (channel->iob[cnt].data == NULL) + break; + channel->iob[cnt].state = BUF_STATE_FREE; + channel->iob[cnt].channel = channel; + channel->iob[cnt].callback = qeth_send_control_data_cb; + channel->iob[cnt].rc = 0; + } + if (cnt < QETH_CMD_BUFFER_NO) { + while (cnt-- > 0) + kfree(channel->iob[cnt].data); + return -ENOMEM; + } + channel->buf_no = 0; + channel->io_buf_no = 0; + atomic_set(&channel->irq_pending, 0); + spin_lock_init(&channel->iob_lock); + + init_waitqueue_head(&channel->wait_q); + channel->irq_tasklet.data = (unsigned long) channel; + channel->irq_tasklet.func = qeth_irq_tasklet; + return 0; +} + +/** + * alloc memory for card structure + */ +static struct qeth_card * +qeth_alloc_card(void) +{ + struct qeth_card *card; + + QETH_DBF_TEXT(setup, 2, "alloccrd"); + card = (struct qeth_card *) kmalloc(sizeof(struct qeth_card), + GFP_DMA|GFP_KERNEL); + if (!card) + return NULL; + QETH_DBF_HEX(setup, 2, &card, sizeof(void *)); + memset(card, 0, sizeof(struct qeth_card)); + if (qeth_setup_channel(&card->read)) { + kfree(card); + return NULL; + } + if (qeth_setup_channel(&card->write)) { + qeth_clean_channel(&card->read); + kfree(card); + return NULL; + } + return card; +} + +static long +__qeth_check_irb_error(struct ccw_device *cdev, struct irb *irb) +{ + if (!IS_ERR(irb)) + return 0; + + switch (PTR_ERR(irb)) { + case -EIO: + PRINT_WARN("i/o-error on device %s\n", cdev->dev.bus_id); + QETH_DBF_TEXT(trace, 2, "ckirberr"); + QETH_DBF_TEXT_(trace, 2, " rc%d", -EIO); + break; + case -ETIMEDOUT: + PRINT_WARN("timeout on device %s\n", cdev->dev.bus_id); + QETH_DBF_TEXT(trace, 2, "ckirberr"); + QETH_DBF_TEXT_(trace, 2, " rc%d", -ETIMEDOUT); + break; + default: + PRINT_WARN("unknown error %ld on device %s\n", PTR_ERR(irb), + cdev->dev.bus_id); + QETH_DBF_TEXT(trace, 2, "ckirberr"); + QETH_DBF_TEXT(trace, 2, " rc???"); + } + return PTR_ERR(irb); +} + +static int +qeth_get_problem(struct ccw_device *cdev, struct irb *irb) +{ + int dstat,cstat; + char *sense; + + sense = (char *) irb->ecw; + cstat = irb->scsw.cstat; + dstat = irb->scsw.dstat; + + 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)) { + QETH_DBF_TEXT(trace,2, "CGENCHK"); + PRINT_WARN("check on device %s, dstat=x%x, cstat=x%x ", + cdev->dev.bus_id, dstat, cstat); + HEXDUMP16(WARN, "irb: ", irb); + HEXDUMP16(WARN, "irb: ", ((char *) irb) + 32); + return 1; + } + + if (dstat & DEV_STAT_UNIT_CHECK) { + if (sense[SENSE_RESETTING_EVENT_BYTE] & + SENSE_RESETTING_EVENT_FLAG) { + QETH_DBF_TEXT(trace,2,"REVIND"); + return 1; + } + if (sense[SENSE_COMMAND_REJECT_BYTE] & + SENSE_COMMAND_REJECT_FLAG) { + QETH_DBF_TEXT(trace,2,"CMDREJi"); + return 0; + } + if ((sense[2] == 0xaf) && (sense[3] == 0xfe)) { + QETH_DBF_TEXT(trace,2,"AFFE"); + return 1; + } + if ((!sense[0]) && (!sense[1]) && (!sense[2]) && (!sense[3])) { + QETH_DBF_TEXT(trace,2,"ZEROSEN"); + return 0; + } + QETH_DBF_TEXT(trace,2,"DGENCHK"); + return 1; + } + return 0; +} +static int qeth_issue_next_read(struct qeth_card *); + +/** + * interrupt handler + */ +static void +qeth_irq(struct ccw_device *cdev, unsigned long intparm, struct irb *irb) +{ + int rc; + int cstat,dstat; + struct qeth_cmd_buffer *buffer; + struct qeth_channel *channel; + struct qeth_card *card; + + QETH_DBF_TEXT(trace,5,"irq"); + + if (__qeth_check_irb_error(cdev, irb)) + return; + cstat = irb->scsw.cstat; + dstat = irb->scsw.dstat; + + card = CARD_FROM_CDEV(cdev); + if (!card) + return; + + if (card->read.ccwdev == cdev){ + channel = &card->read; + QETH_DBF_TEXT(trace,5,"read"); + } else if (card->write.ccwdev == cdev) { + channel = &card->write; + QETH_DBF_TEXT(trace,5,"write"); + } else { + channel = &card->data; + QETH_DBF_TEXT(trace,5,"data"); + } + atomic_set(&channel->irq_pending, 0); + + if (irb->scsw.fctl & (SCSW_FCTL_CLEAR_FUNC)) + channel->state = CH_STATE_STOPPED; + + if (irb->scsw.fctl & (SCSW_FCTL_HALT_FUNC)) + channel->state = CH_STATE_HALTED; + + /*let's wake up immediately on data channel*/ + if ((channel == &card->data) && (intparm != 0)) + goto out; + + if (intparm == QETH_CLEAR_CHANNEL_PARM) { + QETH_DBF_TEXT(trace, 6, "clrchpar"); + /* we don't have to handle this further */ + intparm = 0; + } + if (intparm == QETH_HALT_CHANNEL_PARM) { + QETH_DBF_TEXT(trace, 6, "hltchpar"); + /* we don't have to handle this further */ + intparm = 0; + } + if ((dstat & DEV_STAT_UNIT_EXCEP) || + (dstat & DEV_STAT_UNIT_CHECK) || + (cstat)) { + if (irb->esw.esw0.erw.cons) { + /* TODO: we should make this s390dbf */ + PRINT_WARN("sense data available on channel %s.\n", + CHANNEL_ID(channel)); + PRINT_WARN(" cstat 0x%X\n dstat 0x%X\n", cstat, dstat); + HEXDUMP16(WARN,"irb: ",irb); + HEXDUMP16(WARN,"sense data: ",irb->ecw); + } + rc = qeth_get_problem(cdev,irb); + if (rc) { + qeth_schedule_recovery(card); + goto out; + } + } + + if (intparm) { + buffer = (struct qeth_cmd_buffer *) __va((addr_t)intparm); + buffer->state = BUF_STATE_PROCESSED; + } + if (channel == &card->data) + return; + + if (channel == &card->read && + channel->state == CH_STATE_UP) + qeth_issue_next_read(card); + + tasklet_schedule(&channel->irq_tasklet); + return; +out: + wake_up(&card->wait_q); +} + +/** + * tasklet function scheduled from irq handler + */ +static void +qeth_irq_tasklet(unsigned long data) +{ + struct qeth_card *card; + struct qeth_channel *channel; + struct qeth_cmd_buffer *iob; + __u8 index; + + QETH_DBF_TEXT(trace,5,"irqtlet"); + channel = (struct qeth_channel *) data; + iob = channel->iob; + index = channel->buf_no; + card = CARD_FROM_CDEV(channel->ccwdev); + while (iob[index].state == BUF_STATE_PROCESSED) { + if (iob[index].callback !=NULL) { + iob[index].callback(channel,iob + index); + } + index = (index + 1) % QETH_CMD_BUFFER_NO; + } + channel->buf_no = index; + wake_up(&card->wait_q); +} + +static int qeth_stop_card(struct qeth_card *); + +static int +qeth_set_offline(struct ccwgroup_device *cgdev) +{ + struct qeth_card *card = (struct qeth_card *) cgdev->dev.driver_data; + enum qeth_card_states recover_flag; + + QETH_DBF_TEXT(setup, 3, "setoffl"); + QETH_DBF_HEX(setup, 3, &card, sizeof(void *)); + + recover_flag = card->state; + if (qeth_stop_card(card) == -ERESTARTSYS){ + PRINT_WARN("Stopping card %s interrupted by user!\n", + CARD_BUS_ID(card)); + return -ERESTARTSYS; + } + ccw_device_set_offline(CARD_DDEV(card)); + ccw_device_set_offline(CARD_WDEV(card)); + ccw_device_set_offline(CARD_RDEV(card)); + if ((recover_flag == CARD_STATE_UP_LAN_ONLINE) || + (recover_flag == CARD_STATE_UP_LAN_OFFLINE)) + card->state = CARD_STATE_RECOVER; + return 0; +} + +static void +qeth_remove_device(struct ccwgroup_device *cgdev) +{ + struct qeth_card *card = (struct qeth_card *) cgdev->dev.driver_data; + unsigned long flags; + + QETH_DBF_TEXT(setup, 3, "rmdev"); + QETH_DBF_HEX(setup, 3, &card, sizeof(void *)); + + if (!card) + return; + + if (cgdev->state == CCWGROUP_ONLINE){ + card->use_hard_stop = 1; + qeth_set_offline(cgdev); + } + if (card->info.type == QETH_CARD_TYPE_IQD) + atomic_dec(&qeth_hsi_count); + /* remove form our internal list */ + write_lock_irqsave(&qeth_card_list.rwlock, flags); + list_del(&card->list); + write_unlock_irqrestore(&qeth_card_list.rwlock, flags); + unregister_netdev(card->dev); + qeth_free_card(card); + cgdev->dev.driver_data = NULL; + put_device(&cgdev->dev); +} + +static int +qeth_register_addr_entry(struct qeth_card *, struct qeth_ipaddr *); +static int +qeth_deregister_addr_entry(struct qeth_card *, struct qeth_ipaddr *); + +/** + * Add/remove address to/from card's ip list, i.e. try to add or remove + * reference to/from an IP address that is already registered on the card. + * Returns: + * 0 address was on card and its reference count has been adjusted, + * but is still > 0, so nothing has to be done + * also returns 0 if card was not on card and the todo was to delete + * the address -> there is also nothing to be done + * 1 address was not on card and the todo is to add it to the card's ip + * list + * -1 address was on card and its reference count has been decremented + * to <= 0 by the todo -> address must be removed from card + */ +static int +__qeth_ref_ip_on_card(struct qeth_card *card, struct qeth_ipaddr *todo, + struct qeth_ipaddr **__addr) +{ + struct qeth_ipaddr *addr; + int found = 0; + + list_for_each_entry(addr, &card->ip_list, entry) { + if ((addr->proto == QETH_PROT_IPV4) && + (todo->proto == QETH_PROT_IPV4) && + (addr->type == todo->type) && + (addr->u.a4.addr == todo->u.a4.addr) && + (addr->u.a4.mask == todo->u.a4.mask) ){ + found = 1; + break; + } + if ((addr->proto == QETH_PROT_IPV6) && + (todo->proto == QETH_PROT_IPV6) && + (addr->type == todo->type) && + (addr->u.a6.pfxlen == todo->u.a6.pfxlen) && + (memcmp(&addr->u.a6.addr, &todo->u.a6.addr, + sizeof(struct in6_addr)) == 0)) { + found = 1; + break; + } + } + if (found){ + addr->users += todo->users; + if (addr->users <= 0){ + *__addr = addr; + return -1; + } else { + /* for VIPA and RXIP limit refcount to 1 */ + if (addr->type != QETH_IP_TYPE_NORMAL) + addr->users = 1; + return 0; + } + } + if (todo->users > 0){ + /* for VIPA and RXIP limit refcount to 1 */ + if (todo->type != QETH_IP_TYPE_NORMAL) + addr->users = 1; + return 1; + } else + return 0; +} + +static inline int +__qeth_address_exists_in_list(struct list_head *list, struct qeth_ipaddr *addr, + int same_type) +{ + struct qeth_ipaddr *tmp; + + list_for_each_entry(tmp, list, entry) { + if ((tmp->proto == QETH_PROT_IPV4) && + (addr->proto == QETH_PROT_IPV4) && + ((same_type && (tmp->type == addr->type)) || + (!same_type && (tmp->type != addr->type)) ) && + (tmp->u.a4.addr == addr->u.a4.addr) ){ + return 1; + } + if ((tmp->proto == QETH_PROT_IPV6) && + (addr->proto == QETH_PROT_IPV6) && + ((same_type && (tmp->type == addr->type)) || + (!same_type && (tmp->type != addr->type)) ) && + (memcmp(&tmp->u.a6.addr, &addr->u.a6.addr, + sizeof(struct in6_addr)) == 0) ) { + return 1; + } + } + return 0; +} + +/* + * Add IP to be added to todo list. If there is already an "add todo" + * in this list we just incremenent the reference count. + * Returns 0 if we just incremented reference count. + */ +static int +__qeth_insert_ip_todo(struct qeth_card *card, struct qeth_ipaddr *addr, int add) +{ + struct qeth_ipaddr *tmp, *t; + int found = 0; + + list_for_each_entry_safe(tmp, t, &card->ip_tbd_list, entry) { + if ((tmp->proto == QETH_PROT_IPV4) && + (addr->proto == QETH_PROT_IPV4) && + (tmp->type == addr->type) && + (tmp->is_multicast == addr->is_multicast) && + (tmp->u.a4.addr == addr->u.a4.addr) && + (tmp->u.a4.mask == addr->u.a4.mask) ){ + found = 1; + break; + } + if ((tmp->proto == QETH_PROT_IPV6) && + (addr->proto == QETH_PROT_IPV6) && + (tmp->type == addr->type) && + (tmp->is_multicast == addr->is_multicast) && + (tmp->u.a6.pfxlen == addr->u.a6.pfxlen) && + (memcmp(&tmp->u.a6.addr, &addr->u.a6.addr, + sizeof(struct in6_addr)) == 0) ){ + found = 1; + break; + } + } + if (found){ + if (addr->users != 0) + tmp->users += addr->users; + else + tmp->users += add? 1:-1; + if (tmp->users == 0){ + list_del(&tmp->entry); + kfree(tmp); + } + return 0; + } else { + if (addr->users == 0) + addr->users += add? 1:-1; + if (add && (addr->type == QETH_IP_TYPE_NORMAL) && + qeth_is_addr_covered_by_ipato(card, addr)){ + QETH_DBF_TEXT(trace, 2, "tkovaddr"); + addr->set_flags |= QETH_IPA_SETIP_TAKEOVER_FLAG; + } + list_add_tail(&addr->entry, &card->ip_tbd_list); + return 1; + } +} + +/** + * Remove IP address from list + */ +static int +qeth_delete_ip(struct qeth_card *card, struct qeth_ipaddr *addr) +{ + unsigned long flags; + int rc = 0; + + QETH_DBF_TEXT(trace,4,"delip"); + if (addr->proto == QETH_PROT_IPV4) + QETH_DBF_HEX(trace,4,&addr->u.a4.addr,4); + else { + QETH_DBF_HEX(trace,4,&addr->u.a6.addr,4); + QETH_DBF_HEX(trace,4,((char *)&addr->u.a6.addr)+4,4); + } + spin_lock_irqsave(&card->ip_lock, flags); + rc = __qeth_insert_ip_todo(card, addr, 0); + spin_unlock_irqrestore(&card->ip_lock, flags); + return rc; +} + +static int +qeth_add_ip(struct qeth_card *card, struct qeth_ipaddr *addr) +{ + unsigned long flags; + int rc = 0; + + QETH_DBF_TEXT(trace,4,"addip"); + if (addr->proto == QETH_PROT_IPV4) + QETH_DBF_HEX(trace,4,&addr->u.a4.addr,4); + else { + QETH_DBF_HEX(trace,4,&addr->u.a6.addr,4); + QETH_DBF_HEX(trace,4,((char *)&addr->u.a6.addr)+4,4); + } + spin_lock_irqsave(&card->ip_lock, flags); + rc = __qeth_insert_ip_todo(card, addr, 1); + spin_unlock_irqrestore(&card->ip_lock, flags); + return rc; +} + +static void +qeth_reinsert_todos(struct qeth_card *card, struct list_head *todos) +{ + struct qeth_ipaddr *todo, *tmp; + + list_for_each_entry_safe(todo, tmp, todos, entry){ + list_del_init(&todo->entry); + if (todo->users < 0) { + if (!qeth_delete_ip(card, todo)) + kfree(todo); + } else { + if (!qeth_add_ip(card, todo)) + kfree(todo); + } + } +} + +static void +qeth_set_ip_addr_list(struct qeth_card *card) +{ + struct list_head failed_todos; + struct qeth_ipaddr *todo, *addr, *tmp; + unsigned long flags; + int rc; + + QETH_DBF_TEXT(trace, 2, "sdiplist"); + QETH_DBF_HEX(trace, 2, &card, sizeof(void *)); + + INIT_LIST_HEAD(&failed_todos); + +process_todos: + spin_lock_irqsave(&card->ip_lock, flags); + list_for_each_entry_safe(todo, tmp, &card->ip_tbd_list, entry) { + list_del_init(&todo->entry); + rc = __qeth_ref_ip_on_card(card, todo, &addr); + if (rc == 0) { + /* nothing to be done; only adjusted refcount */ + kfree(todo); + } else if (rc == 1) { + /* new entry to be added to on-card list */ + spin_unlock_irqrestore(&card->ip_lock, flags); + rc = qeth_register_addr_entry(card, todo); + if (!rc){ + spin_lock_irqsave(&card->ip_lock, flags); + list_add_tail(&todo->entry, &card->ip_list); + spin_unlock_irqrestore(&card->ip_lock, flags); + } else + list_add_tail(&todo->entry, &failed_todos); + goto process_todos; + } else if (rc == -1) { + /* on-card entry to be removed */ + list_del_init(&addr->entry); + spin_unlock_irqrestore(&card->ip_lock, flags); + rc = qeth_deregister_addr_entry(card, addr); + if (!rc) { + kfree(addr); + kfree(todo); + } else { + spin_lock_irqsave(&card->ip_lock, flags); + list_add_tail(&addr->entry, &card->ip_list); + list_add_tail(&todo->entry, &failed_todos); + spin_unlock_irqrestore(&card->ip_lock, flags); + } + goto process_todos; + } + } + spin_unlock_irqrestore(&card->ip_lock, flags); + qeth_reinsert_todos(card, &failed_todos); +} + +static void qeth_delete_mc_addresses(struct qeth_card *); +static void qeth_add_multicast_ipv4(struct qeth_card *); +#ifdef CONFIG_QETH_IPV6 +static void qeth_add_multicast_ipv6(struct qeth_card *); +#endif + +static void +qeth_set_thread_start_bit(struct qeth_card *card, unsigned long thread) +{ + unsigned long flags; + + spin_lock_irqsave(&card->thread_mask_lock, flags); + card->thread_start_mask |= thread; + spin_unlock_irqrestore(&card->thread_mask_lock, flags); +} + +static void +qeth_clear_thread_start_bit(struct qeth_card *card, unsigned long thread) +{ + unsigned long flags; + + spin_lock_irqsave(&card->thread_mask_lock, flags); + card->thread_start_mask &= ~thread; + spin_unlock_irqrestore(&card->thread_mask_lock, flags); + wake_up(&card->wait_q); +} + +static void +qeth_clear_thread_running_bit(struct qeth_card *card, unsigned long thread) +{ + unsigned long flags; + + spin_lock_irqsave(&card->thread_mask_lock, flags); + card->thread_running_mask &= ~thread; + spin_unlock_irqrestore(&card->thread_mask_lock, flags); + wake_up(&card->wait_q); +} + +static inline int +__qeth_do_run_thread(struct qeth_card *card, unsigned long thread) +{ + unsigned long flags; + int rc = 0; + + spin_lock_irqsave(&card->thread_mask_lock, flags); + if (card->thread_start_mask & thread){ + if ((card->thread_allowed_mask & thread) && + !(card->thread_running_mask & thread)){ + rc = 1; + card->thread_start_mask &= ~thread; + card->thread_running_mask |= thread; + } else + rc = -EPERM; + } + spin_unlock_irqrestore(&card->thread_mask_lock, flags); + return rc; +} + +static int +qeth_do_run_thread(struct qeth_card *card, unsigned long thread) +{ + int rc = 0; + + wait_event(card->wait_q, + (rc = __qeth_do_run_thread(card, thread)) >= 0); + return rc; +} + +static int +qeth_register_mc_addresses(void *ptr) +{ + struct qeth_card *card; + + card = (struct qeth_card *) ptr; + daemonize("getmcaddr"); + QETH_DBF_TEXT(trace,4,"regmcth1"); + if (!qeth_do_run_thread(card, QETH_SET_MC_THREAD)) + return 0; + QETH_DBF_TEXT(trace,4,"regmcth2"); + qeth_delete_mc_addresses(card); + qeth_add_multicast_ipv4(card); +#ifdef CONFIG_QETH_IPV6 + qeth_add_multicast_ipv6(card); +#endif + qeth_set_ip_addr_list(card); + qeth_clear_thread_running_bit(card, QETH_SET_MC_THREAD); + return 0; +} + +static int +qeth_register_ip_address(void *ptr) +{ + struct qeth_card *card; + + card = (struct qeth_card *) ptr; + daemonize("regip"); + QETH_DBF_TEXT(trace,4,"regipth1"); + if (!qeth_do_run_thread(card, QETH_SET_IP_THREAD)) + return 0; + QETH_DBF_TEXT(trace,4,"regipth2"); + qeth_set_ip_addr_list(card); + qeth_clear_thread_running_bit(card, QETH_SET_IP_THREAD); + return 0; +} + +static int +qeth_recover(void *ptr) +{ + struct qeth_card *card; + int rc = 0; + + card = (struct qeth_card *) ptr; + daemonize("recover"); + QETH_DBF_TEXT(trace,2,"recover1"); + QETH_DBF_HEX(trace, 2, &card, sizeof(void *)); + if (!qeth_do_run_thread(card, QETH_RECOVER_THREAD)) + return 0; + QETH_DBF_TEXT(trace,2,"recover2"); + PRINT_WARN("Recovery of device %s started ...\n", + CARD_BUS_ID(card)); + card->use_hard_stop = 1; + qeth_set_offline(card->gdev); + rc = qeth_set_online(card->gdev); + if (!rc) + PRINT_INFO("Device %s successfully recovered!\n", + CARD_BUS_ID(card)); + else + PRINT_INFO("Device %s could not be recovered!\n", + CARD_BUS_ID(card)); + /* don't run another scheduled recovery */ + qeth_clear_thread_start_bit(card, QETH_RECOVER_THREAD); + qeth_clear_thread_running_bit(card, QETH_RECOVER_THREAD); + return 0; +} + +void +qeth_schedule_recovery(struct qeth_card *card) +{ + QETH_DBF_TEXT(trace,2,"startrec"); + + qeth_set_thread_start_bit(card, QETH_RECOVER_THREAD); + schedule_work(&card->kernel_thread_starter); +} + +static int +qeth_do_start_thread(struct qeth_card *card, unsigned long thread) +{ + unsigned long flags; + int rc = 0; + + spin_lock_irqsave(&card->thread_mask_lock, flags); + QETH_DBF_TEXT_(trace, 4, " %02x%02x%02x", + (u8) card->thread_start_mask, + (u8) card->thread_allowed_mask, + (u8) card->thread_running_mask); + rc = (card->thread_start_mask & thread); + spin_unlock_irqrestore(&card->thread_mask_lock, flags); + return rc; +} + +static void +qeth_start_kernel_thread(struct qeth_card *card) +{ + QETH_DBF_TEXT(trace , 2, "strthrd"); + + if (card->read.state != CH_STATE_UP && + card->write.state != CH_STATE_UP) + return; + + if (qeth_do_start_thread(card, QETH_SET_IP_THREAD)) + kernel_thread(qeth_register_ip_address, (void *) card, SIGCHLD); + if (qeth_do_start_thread(card, QETH_SET_MC_THREAD)) + kernel_thread(qeth_register_mc_addresses, (void *)card,SIGCHLD); + if (qeth_do_start_thread(card, QETH_RECOVER_THREAD)) + kernel_thread(qeth_recover, (void *) card, SIGCHLD); +} + + +static void +qeth_set_intial_options(struct qeth_card *card) +{ + card->options.route4.type = NO_ROUTER; +#ifdef CONFIG_QETH_IPV6 + card->options.route6.type = NO_ROUTER; +#endif /* QETH_IPV6 */ + card->options.checksum_type = QETH_CHECKSUM_DEFAULT; + card->options.broadcast_mode = QETH_TR_BROADCAST_ALLRINGS; + card->options.macaddr_mode = QETH_TR_MACADDR_NONCANONICAL; + card->options.enable_takeover = 1; + card->options.fake_broadcast = 0; + card->options.add_hhlen = DEFAULT_ADD_HHLEN; + card->options.fake_ll = 0; +} + +/** + * initialize channels ,card and all state machines + */ +static int +qeth_setup_card(struct qeth_card *card) +{ + + QETH_DBF_TEXT(setup, 2, "setupcrd"); + QETH_DBF_HEX(setup, 2, &card, sizeof(void *)); + + card->read.state = CH_STATE_DOWN; + card->write.state = CH_STATE_DOWN; + card->data.state = CH_STATE_DOWN; + card->state = CARD_STATE_DOWN; + card->lan_online = 0; + card->use_hard_stop = 0; + card->dev = NULL; +#ifdef CONFIG_QETH_VLAN + spin_lock_init(&card->vlanlock); + card->vlangrp = NULL; +#endif + spin_lock_init(&card->ip_lock); + spin_lock_init(&card->thread_mask_lock); + card->thread_start_mask = 0; + card->thread_allowed_mask = 0; + card->thread_running_mask = 0; + INIT_WORK(&card->kernel_thread_starter, + (void *)qeth_start_kernel_thread,card); + INIT_LIST_HEAD(&card->ip_list); + INIT_LIST_HEAD(&card->ip_tbd_list); + INIT_LIST_HEAD(&card->cmd_waiter_list); + init_waitqueue_head(&card->wait_q); + /* intial options */ + qeth_set_intial_options(card); + /* IP address takeover */ + INIT_LIST_HEAD(&card->ipato.entries); + card->ipato.enabled = 0; + card->ipato.invert4 = 0; + card->ipato.invert6 = 0; + /* init QDIO stuff */ + qeth_init_qdio_info(card); + return 0; +} + +static int +qeth_determine_card_type(struct qeth_card *card) +{ + int i = 0; + + QETH_DBF_TEXT(setup, 2, "detcdtyp"); + + while (known_devices[i][4]) { + if ((CARD_RDEV(card)->id.dev_type == known_devices[i][2]) && + (CARD_RDEV(card)->id.dev_model == known_devices[i][3])) { + card->info.type = known_devices[i][4]; + if (card->options.enable_takeover) + card->info.func_level = known_devices[i][6]; + else + card->info.func_level = known_devices[i][7]; + card->qdio.no_out_queues = known_devices[i][8]; + card->info.is_multicast_different = known_devices[i][9]; + return 0; + } + i++; + } + card->info.type = QETH_CARD_TYPE_UNKNOWN; + PRINT_ERR("unknown card type on device %s\n", CARD_BUS_ID(card)); + return -ENOENT; +} + +static int +qeth_probe_device(struct ccwgroup_device *gdev) +{ + struct qeth_card *card; + struct device *dev; + unsigned long flags; + int rc; + + QETH_DBF_TEXT(setup, 2, "probedev"); + + dev = &gdev->dev; + if (!get_device(dev)) + return -ENODEV; + + card = qeth_alloc_card(); + if (!card) { + put_device(dev); + QETH_DBF_TEXT_(setup, 2, "1err%d", -ENOMEM); + return -ENOMEM; + } + if ((rc = qeth_setup_card(card))){ + QETH_DBF_TEXT_(setup, 2, "2err%d", rc); + put_device(dev); + qeth_free_card(card); + return rc; + } + gdev->dev.driver_data = card; + card->gdev = gdev; + gdev->cdev[0]->handler = qeth_irq; + gdev->cdev[1]->handler = qeth_irq; + gdev->cdev[2]->handler = qeth_irq; + + rc = qeth_create_device_attributes(dev); + if (rc) { + put_device(dev); + qeth_free_card(card); + return rc; + } + card->read.ccwdev = gdev->cdev[0]; + card->write.ccwdev = gdev->cdev[1]; + card->data.ccwdev = gdev->cdev[2]; + if ((rc = qeth_determine_card_type(card))){ + PRINT_WARN("%s: not a valid card type\n", __func__); + QETH_DBF_TEXT_(setup, 2, "3err%d", rc); + put_device(dev); + qeth_free_card(card); + return rc; + } + /* insert into our internal list */ + write_lock_irqsave(&qeth_card_list.rwlock, flags); + list_add_tail(&card->list, &qeth_card_list.list); + write_unlock_irqrestore(&qeth_card_list.rwlock, flags); + return rc; +} + + +static int +qeth_get_unitaddr(struct qeth_card *card) +{ + int length; + char *prcd; + int rc; + + QETH_DBF_TEXT(setup, 2, "getunit"); + rc = read_conf_data(CARD_DDEV(card), (void **) &prcd, &length); + if (rc) { + PRINT_ERR("read_conf_data for device %s returned %i\n", + CARD_DDEV_ID(card), rc); + return rc; + } + card->info.chpid = prcd[30]; + card->info.unit_addr2 = prcd[31]; + card->info.cula = prcd[63]; + card->info.guestlan = ((prcd[0x10] == _ascebc['V']) && + (prcd[0x11] == _ascebc['M'])); + return 0; +} + +static void +qeth_init_tokens(struct qeth_card *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 inline __u16 +raw_devno_from_bus_id(char *id) +{ + id += (strlen(id) - 4); + return (__u16) simple_strtoul(id, &id, 16); +} +/** + * setup channel + */ +static void +qeth_setup_ccw(struct qeth_channel *channel,unsigned char *iob, __u32 len) +{ + struct qeth_card *card; + + QETH_DBF_TEXT(trace, 4, "setupccw"); + card = CARD_FROM_CDEV(channel->ccwdev); + if (channel == &card->read) + memcpy(&channel->ccw, READ_CCW, sizeof(struct ccw1)); + else + memcpy(&channel->ccw, WRITE_CCW, sizeof(struct ccw1)); + channel->ccw.count = len; + channel->ccw.cda = (__u32) __pa(iob); +} + +/** + * get free buffer for ccws (IDX activation, lancmds,ipassists...) + */ +static struct qeth_cmd_buffer * +__qeth_get_buffer(struct qeth_channel *channel) +{ + __u8 index; + + QETH_DBF_TEXT(trace, 6, "getbuff"); + index = channel->io_buf_no; + do { + if (channel->iob[index].state == BUF_STATE_FREE) { + channel->iob[index].state = BUF_STATE_LOCKED; + channel->io_buf_no = (channel->io_buf_no + 1) % + QETH_CMD_BUFFER_NO; + memset(channel->iob[index].data, 0, QETH_BUFSIZE); + return channel->iob + index; + } + index = (index + 1) % QETH_CMD_BUFFER_NO; + } while(index != channel->io_buf_no); + + return NULL; +} + +/** + * release command buffer + */ +static void +qeth_release_buffer(struct qeth_channel *channel, struct qeth_cmd_buffer *iob) +{ + unsigned long flags; + + QETH_DBF_TEXT(trace, 6, "relbuff"); + spin_lock_irqsave(&channel->iob_lock, flags); + memset(iob->data, 0, QETH_BUFSIZE); + iob->state = BUF_STATE_FREE; + iob->callback = qeth_send_control_data_cb; + iob->rc = 0; + spin_unlock_irqrestore(&channel->iob_lock, flags); +} + +static struct qeth_cmd_buffer * +qeth_get_buffer(struct qeth_channel *channel) +{ + struct qeth_cmd_buffer *buffer = NULL; + unsigned long flags; + + spin_lock_irqsave(&channel->iob_lock, flags); + buffer = __qeth_get_buffer(channel); + spin_unlock_irqrestore(&channel->iob_lock, flags); + return buffer; +} + +static struct qeth_cmd_buffer * +qeth_wait_for_buffer(struct qeth_channel *channel) +{ + struct qeth_cmd_buffer *buffer; + wait_event(channel->wait_q, + ((buffer = qeth_get_buffer(channel)) != NULL)); + return buffer; +} + +static void +qeth_clear_cmd_buffers(struct qeth_channel *channel) +{ + int cnt = 0; + + for (cnt=0; cnt < QETH_CMD_BUFFER_NO; cnt++) + qeth_release_buffer(channel,&channel->iob[cnt]); + channel->buf_no = 0; + channel->io_buf_no = 0; +} + +/** + * start IDX for read and write channel + */ +static int +qeth_idx_activate_get_answer(struct qeth_channel *channel, + void (*idx_reply_cb)(struct qeth_channel *, + struct qeth_cmd_buffer *)) +{ + struct qeth_cmd_buffer *iob; + unsigned long flags; + int rc; + struct qeth_card *card; + + QETH_DBF_TEXT(setup, 2, "idxanswr"); + card = CARD_FROM_CDEV(channel->ccwdev); + iob = qeth_get_buffer(channel); + iob->callback = idx_reply_cb; + memcpy(&channel->ccw, READ_CCW, sizeof(struct ccw1)); + channel->ccw.count = QETH_BUFSIZE; + channel->ccw.cda = (__u32) __pa(iob->data); + + wait_event(card->wait_q, + atomic_compare_and_swap(0,1,&channel->irq_pending) == 0); + QETH_DBF_TEXT(setup, 6, "noirqpnd"); + spin_lock_irqsave(get_ccwdev_lock(channel->ccwdev), flags); + rc = ccw_device_start(channel->ccwdev, + &channel->ccw,(addr_t) iob, 0, 0); + spin_unlock_irqrestore(get_ccwdev_lock(channel->ccwdev), flags); + + if (rc) { + PRINT_ERR("qeth: Error2 in activating channel rc=%d\n",rc); + QETH_DBF_TEXT_(setup, 2, "2err%d", rc); + atomic_set(&channel->irq_pending, 0); + wake_up(&card->wait_q); + return rc; + } + rc = wait_event_interruptible_timeout(card->wait_q, + channel->state == CH_STATE_UP, QETH_TIMEOUT); + if (rc == -ERESTARTSYS) + return rc; + if (channel->state != CH_STATE_UP){ + rc = -ETIME; + QETH_DBF_TEXT_(setup, 2, "3err%d", rc); + } else + rc = 0; + return rc; +} + +static int +qeth_idx_activate_channel(struct qeth_channel *channel, + void (*idx_reply_cb)(struct qeth_channel *, + struct qeth_cmd_buffer *)) +{ + struct qeth_card *card; + struct qeth_cmd_buffer *iob; + unsigned long flags; + __u16 temp; + int rc; + + card = CARD_FROM_CDEV(channel->ccwdev); + + QETH_DBF_TEXT(setup, 2, "idxactch"); + + iob = qeth_get_buffer(channel); + iob->callback = idx_reply_cb; + memcpy(&channel->ccw, WRITE_CCW, sizeof(struct ccw1)); + channel->ccw.count = IDX_ACTIVATE_SIZE; + channel->ccw.cda = (__u32) __pa(iob->data); + if (channel == &card->write) { + memcpy(iob->data, IDX_ACTIVATE_WRITE, IDX_ACTIVATE_SIZE); + memcpy(QETH_TRANSPORT_HEADER_SEQ_NO(iob->data), + &card->seqno.trans_hdr, QETH_SEQ_NO_LENGTH); + card->seqno.trans_hdr++; + } else { + memcpy(iob->data, IDX_ACTIVATE_READ, IDX_ACTIVATE_SIZE); + memcpy(QETH_TRANSPORT_HEADER_SEQ_NO(iob->data), + &card->seqno.trans_hdr, QETH_SEQ_NO_LENGTH); + } + memcpy(QETH_IDX_ACT_ISSUER_RM_TOKEN(iob->data), + &card->token.issuer_rm_w,QETH_MPC_TOKEN_LENGTH); + memcpy(QETH_IDX_ACT_FUNC_LEVEL(iob->data), + &card->info.func_level,sizeof(__u16)); + temp = raw_devno_from_bus_id(CARD_DDEV_ID(card)); + memcpy(QETH_IDX_ACT_QDIO_DEV_CUA(iob->data), &temp, 2); + temp = (card->info.cula << 8) + card->info.unit_addr2; + memcpy(QETH_IDX_ACT_QDIO_DEV_REALADDR(iob->data), &temp, 2); + + wait_event(card->wait_q, + atomic_compare_and_swap(0,1,&channel->irq_pending) == 0); + QETH_DBF_TEXT(setup, 6, "noirqpnd"); + spin_lock_irqsave(get_ccwdev_lock(channel->ccwdev), flags); + rc = ccw_device_start(channel->ccwdev, + &channel->ccw,(addr_t) iob, 0, 0); + spin_unlock_irqrestore(get_ccwdev_lock(channel->ccwdev), flags); + + if (rc) { + PRINT_ERR("qeth: Error1 in activating channel. rc=%d\n",rc); + QETH_DBF_TEXT_(setup, 2, "1err%d", rc); + atomic_set(&channel->irq_pending, 0); + wake_up(&card->wait_q); + return rc; + } + rc = wait_event_interruptible_timeout(card->wait_q, + channel->state == CH_STATE_ACTIVATING, QETH_TIMEOUT); + if (rc == -ERESTARTSYS) + return rc; + if (channel->state != CH_STATE_ACTIVATING) { + PRINT_WARN("qeth: IDX activate timed out!\n"); + QETH_DBF_TEXT_(setup, 2, "2err%d", -ETIME); + return -ETIME; + } + return qeth_idx_activate_get_answer(channel,idx_reply_cb); +} + +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; +} + +static void +qeth_idx_write_cb(struct qeth_channel *channel, struct qeth_cmd_buffer *iob) +{ + struct qeth_card *card; + __u16 temp; + + QETH_DBF_TEXT(setup ,2, "idxwrcb"); + + if (channel->state == CH_STATE_DOWN) { + channel->state = CH_STATE_ACTIVATING; + goto out; + } + card = CARD_FROM_CDEV(channel->ccwdev); + + if (!(QETH_IS_IDX_ACT_POS_REPLY(iob->data))) { + PRINT_ERR("IDX_ACTIVATE on write channel device %s: negative " + "reply\n", CARD_WDEV_ID(card)); + goto out; + } + memcpy(&temp, QETH_IDX_ACT_FUNC_LEVEL(iob->data), 2); + if ((temp & ~0x0100) != qeth_peer_func_level(card->info.func_level)) { + PRINT_WARN("IDX_ACTIVATE on write channel device %s: " + "function level mismatch " + "(sent: 0x%x, received: 0x%x)\n", + CARD_WDEV_ID(card), card->info.func_level, temp); + goto out; + } + channel->state = CH_STATE_UP; +out: + qeth_release_buffer(channel, iob); +} + +static int +qeth_check_idx_response(unsigned char *buffer) +{ + if (!buffer) + return 0; + + QETH_DBF_HEX(control, 2, buffer, QETH_DBF_CONTROL_LEN); + if ((buffer[2] & 0xc0) == 0xc0) { + PRINT_WARN("received an IDX TERMINATE " + "with cause code 0x%02x%s\n", + buffer[4], + ((buffer[4] == 0x22) ? + " -- try another portname" : "")); + QETH_DBF_TEXT(trace, 2, "ckidxres"); + QETH_DBF_TEXT(trace, 2, " idxterm"); + QETH_DBF_TEXT_(trace, 2, " rc%d", -EIO); + return -EIO; + } + return 0; +} + +static void +qeth_idx_read_cb(struct qeth_channel *channel, struct qeth_cmd_buffer *iob) +{ + struct qeth_card *card; + __u16 temp; + + QETH_DBF_TEXT(setup , 2, "idxrdcb"); + if (channel->state == CH_STATE_DOWN) { + channel->state = CH_STATE_ACTIVATING; + goto out; + } + + card = CARD_FROM_CDEV(channel->ccwdev); + if (qeth_check_idx_response(iob->data)) { + goto out; + } + if (!(QETH_IS_IDX_ACT_POS_REPLY(iob->data))) { + PRINT_ERR("IDX_ACTIVATE on read channel device %s: negative " + "reply\n", CARD_RDEV_ID(card)); + goto out; + } + +/** + * temporary fix for microcode bug + * to revert it,replace OR by AND + */ + if ( (!QETH_IDX_NO_PORTNAME_REQUIRED(iob->data)) || + (card->info.type == QETH_CARD_TYPE_OSAE) ) + card->info.portname_required = 1; + + memcpy(&temp, QETH_IDX_ACT_FUNC_LEVEL(iob->data), 2); + if (temp != qeth_peer_func_level(card->info.func_level)) { + PRINT_WARN("IDX_ACTIVATE on read channel device %s: function " + "level mismatch (sent: 0x%x, received: 0x%x)\n", + CARD_RDEV_ID(card), card->info.func_level, temp); + goto out; + } + memcpy(&card->token.issuer_rm_r, + QETH_IDX_ACT_ISSUER_RM_TOKEN(iob->data), + QETH_MPC_TOKEN_LENGTH); + memcpy(&card->info.mcl_level[0], + QETH_IDX_REPLY_LEVEL(iob->data), QETH_MCL_LENGTH); + channel->state = CH_STATE_UP; +out: + qeth_release_buffer(channel,iob); +} + +static int +qeth_issue_next_read(struct qeth_card *card) +{ + int rc; + struct qeth_cmd_buffer *iob; + + QETH_DBF_TEXT(trace,5,"issnxrd"); + if (card->read.state != CH_STATE_UP) + return -EIO; + iob = qeth_get_buffer(&card->read); + if (!iob) { + PRINT_WARN("issue_next_read failed: no iob available!\n"); + return -ENOMEM; + } + qeth_setup_ccw(&card->read, iob->data, QETH_BUFSIZE); + wait_event(card->wait_q, + atomic_compare_and_swap(0,1,&card->read.irq_pending) == 0); + QETH_DBF_TEXT(trace, 6, "noirqpnd"); + rc = ccw_device_start(card->read.ccwdev, &card->read.ccw, + (addr_t) iob, 0, 0); + if (rc) { + PRINT_ERR("Error in starting next read ccw! rc=%i\n", rc); + atomic_set(&card->read.irq_pending, 0); + qeth_schedule_recovery(card); + wake_up(&card->wait_q); + } + return rc; +} + +static struct qeth_reply * +qeth_alloc_reply(struct qeth_card *card) +{ + struct qeth_reply *reply; + + reply = kmalloc(sizeof(struct qeth_reply), GFP_KERNEL|GFP_ATOMIC); + if (reply){ + memset(reply, 0, sizeof(struct qeth_reply)); + atomic_set(&reply->refcnt, 1); + reply->card = card; + }; + return reply; +} + +static void +qeth_get_reply(struct qeth_reply *reply) +{ + WARN_ON(atomic_read(&reply->refcnt) <= 0); + atomic_inc(&reply->refcnt); +} + +static void +qeth_put_reply(struct qeth_reply *reply) +{ + WARN_ON(atomic_read(&reply->refcnt) <= 0); + if (atomic_dec_and_test(&reply->refcnt)) + kfree(reply); +} + +static void +qeth_cmd_timeout(unsigned long data) +{ + struct qeth_reply *reply, *list_reply, *r; + unsigned long flags; + + reply = (struct qeth_reply *) data; + spin_lock_irqsave(&reply->card->lock, flags); + list_for_each_entry_safe(list_reply, r, + &reply->card->cmd_waiter_list, list) { + if (reply == list_reply){ + qeth_get_reply(reply); + list_del_init(&reply->list); + spin_unlock_irqrestore(&reply->card->lock, flags); + reply->rc = -ETIME; + reply->received = 1; + wake_up(&reply->wait_q); + qeth_put_reply(reply); + return; + } + } + spin_unlock_irqrestore(&reply->card->lock, flags); +} + +static struct qeth_ipa_cmd * +qeth_check_ipa_data(struct qeth_card *card, struct qeth_cmd_buffer *iob) +{ + struct qeth_ipa_cmd *cmd = NULL; + enum qeth_card_states old_state; + + QETH_DBF_TEXT(trace,5,"chkipad"); + if (IS_IPA(iob->data)){ + cmd = (struct qeth_ipa_cmd *) PDU_ENCAPSULATION(iob->data); + if (IS_IPA_REPLY(cmd)) + return cmd; + else { + switch (cmd->hdr.command) { + case IPA_CMD_STOPLAN: + PRINT_WARN("Link failure on %s (CHPID 0x%X) - " + "there is a network problem or " + "someone pulled the cable or " + "disabled the port. Setting state " + "of interface to DOWN.\n", + card->info.if_name, + card->info.chpid); + card->lan_online = 0; + old_state = card->state; + rtnl_lock(); + dev_close(card->dev); + rtnl_unlock(); + if ((old_state == CARD_STATE_UP_LAN_ONLINE) || + (old_state == CARD_STATE_UP_LAN_OFFLINE)) + card->state = CARD_STATE_UP_LAN_OFFLINE; + return NULL; + case IPA_CMD_STARTLAN: + PRINT_INFO("Link reestablished on %s " + "(CHPID 0x%X)\n", + card->info.if_name, + card->info.chpid); + card->lan_online = 1; + if (card->state == CARD_STATE_UP_LAN_OFFLINE){ + rtnl_lock(); + dev_open(card->dev); + rtnl_unlock(); + } + return NULL; + case IPA_CMD_REGISTER_LOCAL_ADDR: + QETH_DBF_TEXT(trace,3, "irla"); + break; + case IPA_CMD_UNREGISTER_LOCAL_ADDR: + PRINT_WARN("probably problem on %s: " + "received IPA command 0x%X\n", + card->info.if_name, + cmd->hdr.command); + break; + default: + PRINT_WARN("Received data is IPA " + "but not a reply!\n"); + break; + } + } + } + return cmd; +} + +/** + * wake all waiting ipa commands + */ +static void +qeth_clear_ipacmd_list(struct qeth_card *card) +{ + struct qeth_reply *reply, *r; + unsigned long flags; + + QETH_DBF_TEXT(trace, 4, "clipalst"); + + spin_lock_irqsave(&card->lock, flags); + list_for_each_entry_safe(reply, r, &card->cmd_waiter_list, list) { + qeth_get_reply(reply); + reply->rc = -EIO; + reply->received = 1; + list_del_init(&reply->list); + wake_up(&reply->wait_q); + qeth_put_reply(reply); + } + spin_unlock_irqrestore(&card->lock, flags); +} + +static void +qeth_send_control_data_cb(struct qeth_channel *channel, + struct qeth_cmd_buffer *iob) +{ + struct qeth_card *card; + struct qeth_reply *reply, *r; + struct qeth_ipa_cmd *cmd; + unsigned long flags; + int keep_reply; + + QETH_DBF_TEXT(trace,4,"sndctlcb"); + + card = CARD_FROM_CDEV(channel->ccwdev); + if (qeth_check_idx_response(iob->data)) { + qeth_clear_ipacmd_list(card); + qeth_schedule_recovery(card); + goto out; + } + + cmd = qeth_check_ipa_data(card, iob); + if ((cmd == NULL) && (card->state != CARD_STATE_DOWN)) + goto out; + + spin_lock_irqsave(&card->lock, flags); + list_for_each_entry_safe(reply, r, &card->cmd_waiter_list, list) { + if ((reply->seqno == QETH_IDX_COMMAND_SEQNO) || + ((cmd) && (reply->seqno == cmd->hdr.seqno))) { + qeth_get_reply(reply); + list_del_init(&reply->list); + spin_unlock_irqrestore(&card->lock, flags); + keep_reply = 0; + if (reply->callback != NULL) { + if (cmd) + keep_reply = reply->callback(card, + reply, + (unsigned long)cmd); + else + keep_reply = reply->callback(card, + reply, + (unsigned long)iob); + } + if (cmd) + reply->rc = cmd->hdr.return_code; + else if (iob->rc) + reply->rc = iob->rc; + if (keep_reply) { + spin_lock_irqsave(&card->lock, flags); + list_add_tail(&reply->list, + &card->cmd_waiter_list); + spin_unlock_irqrestore(&card->lock, flags); + } else { + reply->received = 1; + wake_up(&reply->wait_q); + } + qeth_put_reply(reply); + goto out; + } + } + spin_unlock_irqrestore(&card->lock, flags); +out: + memcpy(&card->seqno.pdu_hdr_ack, + QETH_PDU_HEADER_SEQ_NO(iob->data), + QETH_SEQ_NO_LENGTH); + qeth_release_buffer(channel,iob); +} + +static int +qeth_send_control_data(struct qeth_card *card, int len, + struct qeth_cmd_buffer *iob, + int (*reply_cb) + (struct qeth_card *, struct qeth_reply*, unsigned long), + void *reply_param) + +{ + int rc; + unsigned long flags; + struct qeth_reply *reply; + struct timer_list timer; + + QETH_DBF_TEXT(trace, 2, "sendctl"); + + qeth_setup_ccw(&card->write,iob->data,len); + + memcpy(QETH_TRANSPORT_HEADER_SEQ_NO(iob->data), + &card->seqno.trans_hdr, QETH_SEQ_NO_LENGTH); + card->seqno.trans_hdr++; + + memcpy(QETH_PDU_HEADER_SEQ_NO(iob->data), + &card->seqno.pdu_hdr, QETH_SEQ_NO_LENGTH); + card->seqno.pdu_hdr++; + memcpy(QETH_PDU_HEADER_ACK_SEQ_NO(iob->data), + &card->seqno.pdu_hdr_ack, QETH_SEQ_NO_LENGTH); + iob->callback = qeth_release_buffer; + + reply = qeth_alloc_reply(card); + if (!reply) { + PRINT_WARN("Could no alloc qeth_reply!\n"); + return -ENOMEM; + } + reply->callback = reply_cb; + reply->param = reply_param; + if (card->state == CARD_STATE_DOWN) + reply->seqno = QETH_IDX_COMMAND_SEQNO; + else + reply->seqno = card->seqno.ipa++; + init_timer(&timer); + timer.function = qeth_cmd_timeout; + timer.data = (unsigned long) reply; + timer.expires = jiffies + QETH_TIMEOUT; + init_waitqueue_head(&reply->wait_q); + spin_lock_irqsave(&card->lock, flags); + list_add_tail(&reply->list, &card->cmd_waiter_list); + spin_unlock_irqrestore(&card->lock, flags); + QETH_DBF_HEX(control, 2, iob->data, QETH_DBF_CONTROL_LEN); + wait_event(card->wait_q, + atomic_compare_and_swap(0,1,&card->write.irq_pending) == 0); + QETH_DBF_TEXT(trace, 6, "noirqpnd"); + spin_lock_irqsave(get_ccwdev_lock(card->write.ccwdev), flags); + rc = ccw_device_start(card->write.ccwdev, &card->write.ccw, + (addr_t) iob, 0, 0); + spin_unlock_irqrestore(get_ccwdev_lock(card->write.ccwdev), flags); + if (rc){ + PRINT_WARN("qeth_send_control_data: " + "ccw_device_start rc = %i\n", rc); + QETH_DBF_TEXT_(trace, 2, " err%d", rc); + spin_lock_irqsave(&card->lock, flags); + list_del_init(&reply->list); + qeth_put_reply(reply); + spin_unlock_irqrestore(&card->lock, flags); + qeth_release_buffer(iob->channel, iob); + atomic_set(&card->write.irq_pending, 0); + wake_up(&card->wait_q); + return rc; + } + add_timer(&timer); + wait_event(reply->wait_q, reply->received); + del_timer(&timer); + rc = reply->rc; + qeth_put_reply(reply); + return rc; +} + +static int +qeth_send_ipa_cmd(struct qeth_card *card, struct qeth_cmd_buffer *iob, + int (*reply_cb) + (struct qeth_card *,struct qeth_reply*, unsigned long), + void *reply_param) +{ + struct qeth_ipa_cmd *cmd; + int rc; + + QETH_DBF_TEXT(trace,4,"sendipa"); + + cmd = (struct qeth_ipa_cmd *)(iob->data+IPA_PDU_HEADER_SIZE); + memcpy(iob->data, IPA_PDU_HEADER, IPA_PDU_HEADER_SIZE); + memcpy(QETH_IPA_CMD_DEST_ADDR(iob->data), + &card->token.ulp_connection_r, QETH_MPC_TOKEN_LENGTH); + + rc = qeth_send_control_data(card, IPA_CMD_LENGTH, iob, + reply_cb, reply_param); + return rc; +} + + +static int +qeth_cm_enable_cb(struct qeth_card *card, struct qeth_reply *reply, + unsigned long data) +{ + struct qeth_cmd_buffer *iob; + + QETH_DBF_TEXT(setup, 2, "cmenblcb"); + + iob = (struct qeth_cmd_buffer *) data; + memcpy(&card->token.cm_filter_r, + QETH_CM_ENABLE_RESP_FILTER_TOKEN(iob->data), + QETH_MPC_TOKEN_LENGTH); + QETH_DBF_TEXT_(setup, 2, " rc%d", iob->rc); + return 0; +} + +static int +qeth_cm_enable(struct qeth_card *card) +{ + int rc; + struct qeth_cmd_buffer *iob; + + QETH_DBF_TEXT(setup,2,"cmenable"); + + iob = qeth_wait_for_buffer(&card->write); + memcpy(iob->data, CM_ENABLE, CM_ENABLE_SIZE); + memcpy(QETH_CM_ENABLE_ISSUER_RM_TOKEN(iob->data), + &card->token.issuer_rm_r, QETH_MPC_TOKEN_LENGTH); + memcpy(QETH_CM_ENABLE_FILTER_TOKEN(iob->data), + &card->token.cm_filter_w, QETH_MPC_TOKEN_LENGTH); + + rc = qeth_send_control_data(card, CM_ENABLE_SIZE, iob, + qeth_cm_enable_cb, NULL); + return rc; +} + +static int +qeth_cm_setup_cb(struct qeth_card *card, struct qeth_reply *reply, + unsigned long data) +{ + + struct qeth_cmd_buffer *iob; + + QETH_DBF_TEXT(setup, 2, "cmsetpcb"); + + iob = (struct qeth_cmd_buffer *) data; + memcpy(&card->token.cm_connection_r, + QETH_CM_SETUP_RESP_DEST_ADDR(iob->data), + QETH_MPC_TOKEN_LENGTH); + QETH_DBF_TEXT_(setup, 2, " rc%d", iob->rc); + return 0; +} + +static int +qeth_cm_setup(struct qeth_card *card) +{ + int rc; + struct qeth_cmd_buffer *iob; + + QETH_DBF_TEXT(setup,2,"cmsetup"); + + iob = qeth_wait_for_buffer(&card->write); + memcpy(iob->data, CM_SETUP, CM_SETUP_SIZE); + memcpy(QETH_CM_SETUP_DEST_ADDR(iob->data), + &card->token.issuer_rm_r, QETH_MPC_TOKEN_LENGTH); + memcpy(QETH_CM_SETUP_CONNECTION_TOKEN(iob->data), + &card->token.cm_connection_w, QETH_MPC_TOKEN_LENGTH); + memcpy(QETH_CM_SETUP_FILTER_TOKEN(iob->data), + &card->token.cm_filter_r, QETH_MPC_TOKEN_LENGTH); + rc = qeth_send_control_data(card, CM_SETUP_SIZE, iob, + qeth_cm_setup_cb, NULL); + return rc; + +} + +static int +qeth_ulp_enable_cb(struct qeth_card *card, struct qeth_reply *reply, + unsigned long data) +{ + + __u16 mtu, framesize; + __u16 len; + __u8 link_type; + struct qeth_cmd_buffer *iob; + + QETH_DBF_TEXT(setup, 2, "ulpenacb"); + + iob = (struct qeth_cmd_buffer *) data; + memcpy(&card->token.ulp_filter_r, + QETH_ULP_ENABLE_RESP_FILTER_TOKEN(iob->data), + QETH_MPC_TOKEN_LENGTH); + if (qeth_get_mtu_out_of_mpc(card->info.type)) { + memcpy(&framesize, QETH_ULP_ENABLE_RESP_MAX_MTU(iob->data), 2); + mtu = qeth_get_mtu_outof_framesize(framesize); + if (!mtu) { + iob->rc = -EINVAL; + QETH_DBF_TEXT_(setup, 2, " rc%d", iob->rc); + return 0; + } + card->info.max_mtu = mtu; + card->info.initial_mtu = mtu; + card->qdio.in_buf_size = mtu + 2 * PAGE_SIZE; + } else { + card->info.initial_mtu = qeth_get_initial_mtu_for_card(card); + card->info.max_mtu = qeth_get_max_mtu_for_card(card->info.type); + card->qdio.in_buf_size = QETH_IN_BUF_SIZE_DEFAULT; + } + + memcpy(&len, QETH_ULP_ENABLE_RESP_DIFINFO_LEN(iob->data), 2); + if (len >= QETH_MPC_DIFINFO_LEN_INDICATES_LINK_TYPE) { + memcpy(&link_type, + QETH_ULP_ENABLE_RESP_LINK_TYPE(iob->data), 1); + card->info.link_type = link_type; + } else + card->info.link_type = 0; + QETH_DBF_TEXT_(setup, 2, " rc%d", iob->rc); + return 0; +} + +static int +qeth_ulp_enable(struct qeth_card *card) +{ + int rc; + struct qeth_cmd_buffer *iob; + + /*FIXME: trace view callbacks*/ + QETH_DBF_TEXT(setup,2,"ulpenabl"); + + iob = qeth_wait_for_buffer(&card->write); + memcpy(iob->data, ULP_ENABLE, ULP_ENABLE_SIZE); + + *(QETH_ULP_ENABLE_LINKNUM(iob->data)) = + (__u8) card->info.portno; + + memcpy(QETH_ULP_ENABLE_DEST_ADDR(iob->data), + &card->token.cm_connection_r, QETH_MPC_TOKEN_LENGTH); + memcpy(QETH_ULP_ENABLE_FILTER_TOKEN(iob->data), + &card->token.ulp_filter_w, QETH_MPC_TOKEN_LENGTH); + memcpy(QETH_ULP_ENABLE_PORTNAME_AND_LL(iob->data), + card->info.portname, 9); + rc = qeth_send_control_data(card, ULP_ENABLE_SIZE, iob, + qeth_ulp_enable_cb, NULL); + return rc; + +} + +static inline __u16 +__raw_devno_from_bus_id(char *id) +{ + id += (strlen(id) - 4); + return (__u16) simple_strtoul(id, &id, 16); +} + +static int +qeth_ulp_setup_cb(struct qeth_card *card, struct qeth_reply *reply, + unsigned long data) +{ + struct qeth_cmd_buffer *iob; + + QETH_DBF_TEXT(setup, 2, "ulpstpcb"); + + iob = (struct qeth_cmd_buffer *) data; + memcpy(&card->token.ulp_connection_r, + QETH_ULP_SETUP_RESP_CONNECTION_TOKEN(iob->data), + QETH_MPC_TOKEN_LENGTH); + QETH_DBF_TEXT_(setup, 2, " rc%d", iob->rc); + return 0; +} + +static int +qeth_ulp_setup(struct qeth_card *card) +{ + int rc; + __u16 temp; + struct qeth_cmd_buffer *iob; + + QETH_DBF_TEXT(setup,2,"ulpsetup"); + + iob = qeth_wait_for_buffer(&card->write); + memcpy(iob->data, ULP_SETUP, ULP_SETUP_SIZE); + + memcpy(QETH_ULP_SETUP_DEST_ADDR(iob->data), + &card->token.cm_connection_r, QETH_MPC_TOKEN_LENGTH); + memcpy(QETH_ULP_SETUP_CONNECTION_TOKEN(iob->data), + &card->token.ulp_connection_w, QETH_MPC_TOKEN_LENGTH); + memcpy(QETH_ULP_SETUP_FILTER_TOKEN(iob->data), + &card->token.ulp_filter_r, QETH_MPC_TOKEN_LENGTH); + + temp = __raw_devno_from_bus_id(CARD_DDEV_ID(card)); + memcpy(QETH_ULP_SETUP_CUA(iob->data), &temp, 2); + temp = (card->info.cula << 8) + card->info.unit_addr2; + memcpy(QETH_ULP_SETUP_REAL_DEVADDR(iob->data), &temp, 2); + rc = qeth_send_control_data(card, ULP_SETUP_SIZE, iob, + qeth_ulp_setup_cb, NULL); + return rc; +} + +static inline int +qeth_check_for_inbound_error(struct qeth_qdio_buffer *buf, + unsigned int qdio_error, + unsigned int siga_error) +{ + int rc = 0; + + if (qdio_error || siga_error) { + QETH_DBF_TEXT(trace, 2, "qdinerr"); + QETH_DBF_TEXT(qerr, 2, "qdinerr"); + QETH_DBF_TEXT_(qerr, 2, " F15=%02X", + buf->buffer->element[15].flags & 0xff); + QETH_DBF_TEXT_(qerr, 2, " F14=%02X", + buf->buffer->element[14].flags & 0xff); + QETH_DBF_TEXT_(qerr, 2, " qerr=%X", qdio_error); + QETH_DBF_TEXT_(qerr, 2, " serr=%X", siga_error); + rc = 1; + } + return rc; +} + +static void +qeth_qdio_input_handler(struct ccw_device * ccwdev, unsigned int status, + unsigned int qdio_err, unsigned int siga_err, + unsigned int queue, int first_element, int count, + unsigned long card_ptr) +{ + struct net_device *net_dev; + struct qeth_card *card; + struct qeth_qdio_buffer *buffer; + int i; + + QETH_DBF_TEXT(trace, 6, "qdinput"); + card = (struct qeth_card *) card_ptr; + net_dev = card->dev; +#ifdef CONFIG_QETH_PERF_STATS + card->perf_stats.inbound_start_time = qeth_get_micros(); +#endif + if (status & QDIO_STATUS_LOOK_FOR_ERROR) { + if (status & QDIO_STATUS_ACTIVATE_CHECK_CONDITION){ + QETH_DBF_TEXT(trace, 1,"qdinchk"); + QETH_DBF_TEXT_(trace,1,"%s",CARD_BUS_ID(card)); + QETH_DBF_TEXT_(trace,1,"%04X%04X",first_element,count); + QETH_DBF_TEXT_(trace,1,"%04X%04X", queue, status); + qeth_schedule_recovery(card); + return; + } + } + for (i = first_element; i < (first_element + count); ++i) { + buffer = &card->qdio.in_q->bufs[i % QDIO_MAX_BUFFERS_PER_Q]; + if ((status == QDIO_STATUS_LOOK_FOR_ERROR) && + qeth_check_for_inbound_error(buffer, qdio_err, siga_err)) + buffer->state = QETH_QDIO_BUF_ERROR; + else + buffer->state = QETH_QDIO_BUF_PRIMED; + } + + tasklet_schedule(&card->qdio.in_tasklet); +} + +static inline struct sk_buff * +qeth_get_skb(unsigned int length) +{ + struct sk_buff* skb; +#ifdef CONFIG_QETH_VLAN + if ((skb = dev_alloc_skb(length + VLAN_HLEN))) + skb_reserve(skb, VLAN_HLEN); +#else + skb = dev_alloc_skb(length); +#endif + return skb; +} + +static inline struct sk_buff * +qeth_get_next_skb(struct qeth_card *card, struct qdio_buffer *buffer, + struct qdio_buffer_element **__element, int *__offset, + struct qeth_hdr **hdr) +{ + struct qdio_buffer_element *element = *__element; + int offset = *__offset; + struct sk_buff *skb = NULL; + int skb_len; + void *data_ptr; + int data_len; + + QETH_DBF_TEXT(trace,6,"nextskb"); + /* qeth_hdr must not cross element boundaries */ + if (element->length < offset + sizeof(struct qeth_hdr)){ + if (qeth_is_last_sbale(element)) + return NULL; + element++; + offset = 0; + if (element->length < sizeof(struct qeth_hdr)) + return NULL; + } + *hdr = element->addr + offset; + + offset += sizeof(struct qeth_hdr); + skb_len = (*hdr)->length; + if (!skb_len) + return NULL; + if (card->options.fake_ll){ + if (!(skb = qeth_get_skb(skb_len + QETH_FAKE_LL_LEN))) + goto no_mem; + skb_pull(skb, QETH_FAKE_LL_LEN); + } else if (!(skb = qeth_get_skb(skb_len))) + goto no_mem; + data_ptr = element->addr + offset; + while (skb_len) { + data_len = min(skb_len, (int)(element->length - offset)); + if (data_len) + memcpy(skb_put(skb, data_len), data_ptr, data_len); + skb_len -= data_len; + if (skb_len){ + if (qeth_is_last_sbale(element)){ + QETH_DBF_TEXT(trace,4,"unexeob"); + QETH_DBF_TEXT_(trace,4,"%s",CARD_BUS_ID(card)); + QETH_DBF_TEXT(qerr,2,"unexeob"); + QETH_DBF_TEXT_(qerr,2,"%s",CARD_BUS_ID(card)); + QETH_DBF_HEX(misc,4,buffer,sizeof(*buffer)); + dev_kfree_skb_irq(skb); + card->stats.rx_errors++; + return NULL; + } + element++; + offset = 0; + data_ptr = element->addr; + } else { + offset += data_len; + } + } + *__element = element; + *__offset = offset; + return skb; +no_mem: + if (net_ratelimit()){ + PRINT_WARN("No memory for packet received on %s.\n", + card->info.if_name); + QETH_DBF_TEXT(trace,2,"noskbmem"); + QETH_DBF_TEXT_(trace,2,"%s",CARD_BUS_ID(card)); + } + card->stats.rx_dropped++; + return NULL; +} + +static inline unsigned short +qeth_type_trans(struct sk_buff *skb, struct net_device *dev) +{ + struct ethhdr *eth; + struct qeth_card *card; + + QETH_DBF_TEXT(trace,5,"typtrans"); + + card = (struct qeth_card *)dev->priv; +#ifdef CONFIG_TR + if ((card->info.link_type == QETH_LINK_TYPE_HSTR) || + (card->info.link_type == QETH_LINK_TYPE_LANE_TR)) + return tr_type_trans(skb,dev); +#endif /* CONFIG_TR */ + + skb->mac.raw = skb->data; + skb_pull(skb, ETH_ALEN * 2 + sizeof (short)); + eth = skb->mac.ethernet; + + if (*eth->h_dest & 1) { + if (memcmp(eth->h_dest, dev->broadcast, ETH_ALEN) == 0) + skb->pkt_type = PACKET_BROADCAST; + else + skb->pkt_type = PACKET_MULTICAST; + } else { + skb->pkt_type = PACKET_OTHERHOST; + } + if (ntohs(eth->h_proto) >= 1536) + return eth->h_proto; + if (*(unsigned short *) (skb->data) == 0xFFFF) + return htons(ETH_P_802_3); + return htons(ETH_P_802_2); +} + + +static inline void +qeth_rebuild_skb_fake_ll(struct qeth_card *card, struct sk_buff *skb, + struct qeth_hdr *hdr) +{ + struct ethhdr *fake_hdr; + struct iphdr *ip_hdr; + + QETH_DBF_TEXT(trace,5,"skbfake"); + skb->mac.raw = skb->data - QETH_FAKE_LL_LEN; + /* this is a fake ethernet header */ + fake_hdr = (struct ethhdr *) skb->mac.raw; + + /* the destination MAC address */ + switch (skb->pkt_type){ + case PACKET_MULTICAST: + switch (skb->protocol){ +#ifdef CONFIG_QETH_IPV6 + case __constant_htons(ETH_P_IPV6): + ndisc_mc_map((struct in6_addr *) + skb->data + QETH_FAKE_LL_V6_ADDR_POS, + fake_hdr->h_dest, card->dev, 0); + break; +#endif /* CONFIG_QETH_IPV6 */ + case __constant_htons(ETH_P_IP): + ip_hdr = (struct iphdr *)skb->data; + if (card->dev->type == ARPHRD_IEEE802_TR) + ip_tr_mc_map(ip_hdr->daddr, fake_hdr->h_dest); + else + ip_eth_mc_map(ip_hdr->daddr, fake_hdr->h_dest); + break; + default: + memcpy(fake_hdr->h_dest, card->dev->dev_addr, ETH_ALEN); + } + break; + case PACKET_BROADCAST: + memset(fake_hdr->h_dest, 0xff, ETH_ALEN); + break; + default: + memcpy(fake_hdr->h_dest, card->dev->dev_addr, ETH_ALEN); + } + /* the source MAC address */ + if (hdr->ext_flags & QETH_HDR_EXT_SRC_MAC_ADDR) + memcpy(fake_hdr->h_source, &hdr->dest_addr[2], ETH_ALEN); + else + memset(fake_hdr->h_source, 0, ETH_ALEN); + /* the protocol */ + fake_hdr->h_proto = skb->protocol; +} + +static inline void +qeth_rebuild_skb_vlan(struct qeth_card *card, struct sk_buff *skb, + struct qeth_hdr *hdr) +{ +#ifdef CONFIG_QETH_VLAN + u16 *vlan_tag; + + if (hdr->ext_flags & QETH_HDR_EXT_VLAN_FRAME) { + vlan_tag = (u16 *) skb_push(skb, VLAN_HLEN); + *vlan_tag = hdr->vlan_id; + *(vlan_tag + 1) = skb->protocol; + skb->protocol = __constant_htons(ETH_P_8021Q); + } +#endif /* CONFIG_QETH_VLAN */ +} + + +static inline void +qeth_rebuild_skb(struct qeth_card *card, struct sk_buff *skb, + struct qeth_hdr *hdr) +{ +#ifdef CONFIG_QETH_IPV6 + if (hdr->flags & QETH_HDR_PASSTHRU){ + skb->protocol = qeth_type_trans(skb, card->dev); + return; + } +#endif /* CONFIG_QETH_IPV6 */ + skb->protocol = htons((hdr->flags & QETH_HDR_IPV6)? ETH_P_IPV6 : + ETH_P_IP); + switch (hdr->flags & QETH_HDR_CAST_MASK){ + case QETH_CAST_UNICAST: + skb->pkt_type = PACKET_HOST; + break; + case QETH_CAST_MULTICAST: + skb->pkt_type = PACKET_MULTICAST; + card->stats.multicast++; + break; + case QETH_CAST_BROADCAST: + skb->pkt_type = PACKET_BROADCAST; + card->stats.multicast++; + break; + case QETH_CAST_ANYCAST: + case QETH_CAST_NOCAST: + default: + skb->pkt_type = PACKET_HOST; + } + if (card->options.fake_ll) + qeth_rebuild_skb_fake_ll(card, skb, hdr); + else + skb->mac.raw = skb->data; + skb->ip_summed = card->options.checksum_type; + if (card->options.checksum_type == HW_CHECKSUMMING){ + if ( (hdr->ext_flags & + (QETH_HDR_EXT_CSUM_HDR_REQ | + QETH_HDR_EXT_CSUM_TRANSP_REQ)) == + (QETH_HDR_EXT_CSUM_HDR_REQ | + QETH_HDR_EXT_CSUM_TRANSP_REQ) ) + skb->ip_summed = CHECKSUM_UNNECESSARY; + else + skb->ip_summed = SW_CHECKSUMMING; + } + qeth_rebuild_skb_vlan(card, skb, hdr); +} + + +static inline struct qeth_buffer_pool_entry * +qeth_get_buffer_pool_entry(struct qeth_card *card) +{ + struct qeth_buffer_pool_entry *entry, *tmp; + + QETH_DBF_TEXT(trace, 6, "gtbfplen"); + entry = NULL; + list_for_each_entry_safe(entry, tmp, + &card->qdio.in_buf_pool.entry_list, list){ + list_del_init(&entry->list); + break; + } + return entry; +} + +static inline void +qeth_init_input_buffer(struct qeth_card *card, struct qeth_qdio_buffer *buf) +{ + struct qeth_buffer_pool_entry *pool_entry; + int i; + + pool_entry = qeth_get_buffer_pool_entry(card); + /* + * since the buffer is accessed only from the input_tasklet + * there shouldn't be a need to synchronize; also, since we use + * the QETH_IN_BUF_REQUEUE_THRESHOLD we should never run out off + * buffers + */ + BUG_ON(!pool_entry); + + buf->pool_entry = pool_entry; + for(i = 0; i < QETH_MAX_BUFFER_ELEMENTS(card); ++i){ + buf->buffer->element[i].length = PAGE_SIZE; + buf->buffer->element[i].addr = pool_entry->elements[i]; + if (i == QETH_MAX_BUFFER_ELEMENTS(card) - 1) + buf->buffer->element[i].flags = SBAL_FLAGS_LAST_ENTRY; + else + buf->buffer->element[i].flags = 0; + } + buf->state = QETH_QDIO_BUF_EMPTY; +} + +static void +qeth_clear_output_buffer(struct qeth_card *card, + struct qeth_qdio_out_buffer *buf) +{ + int i; + struct sk_buff *skb; + + for(i = 0; i < QETH_MAX_BUFFER_ELEMENTS(card); ++i){ + buf->buffer->element[i].length = 0; + buf->buffer->element[i].addr = NULL; + buf->buffer->element[i].flags = 0; + while ((skb = skb_dequeue(&buf->skb_list))){ + atomic_dec(&skb->users); + dev_kfree_skb_irq(skb); + } + } + buf->next_element_to_fill = 0; + buf->state = QETH_QDIO_BUF_EMPTY; +} + +static inline void +qeth_queue_input_buffer(struct qeth_card *card, int index) +{ + struct qeth_qdio_q *queue = card->qdio.in_q; + int count; + int i; + int rc; + + QETH_DBF_TEXT(trace,6,"queinbuf"); + count = (index < queue->next_buf_to_init)? + card->qdio.in_buf_pool.buf_count - + (queue->next_buf_to_init - index) : + card->qdio.in_buf_pool.buf_count - + (queue->next_buf_to_init + QDIO_MAX_BUFFERS_PER_Q - index); + /* only requeue at a certain threshold to avoid SIGAs */ + if (count >= QETH_IN_BUF_REQUEUE_THRESHOLD(card)){ + for (i = queue->next_buf_to_init; + i < queue->next_buf_to_init + count; ++i) + qeth_init_input_buffer(card, + &queue->bufs[i % QDIO_MAX_BUFFERS_PER_Q]); + /* + * according to old code it should be avoided to requeue all + * 128 buffers in order to benefit from PCI avoidance. + * this function keeps at least one buffer (the buffer at + * 'index') un-requeued -> this buffer is the first buffer that + * will be requeued the next time + */ + rc = do_QDIO(CARD_DDEV(card), + QDIO_FLAG_SYNC_INPUT, + 0, queue->next_buf_to_init, count, NULL); + if (rc){ + PRINT_WARN("qeth_queue_input_buffer's do_QDIO " + "return %i (device %s).\n", + rc, CARD_DDEV_ID(card)); + QETH_DBF_TEXT(trace,2,"qinberr"); + QETH_DBF_TEXT_(trace,2,"%s",CARD_BUS_ID(card)); + } + queue->next_buf_to_init = (queue->next_buf_to_init + count) % + QDIO_MAX_BUFFERS_PER_Q; + } +} + +static inline void +qeth_put_buffer_pool_entry(struct qeth_card *card, + struct qeth_buffer_pool_entry *entry) +{ + QETH_DBF_TEXT(trace, 6, "ptbfplen"); + list_add_tail(&entry->list, &card->qdio.in_buf_pool.entry_list); +} + +static void +qeth_qdio_input_tasklet(unsigned long data) +{ + struct qeth_card *card = (struct qeth_card *) data; + int current_buf = card->qdio.in_q->next_buf_to_process; + struct qeth_qdio_buffer *buf; + struct qdio_buffer_element *element; + int offset; + struct sk_buff *skb; + struct qeth_hdr *hdr; + int rxrc; + + QETH_DBF_TEXT(trace,6,"qdintlet"); + buf = &card->qdio.in_q->bufs[current_buf]; + while((buf->state == QETH_QDIO_BUF_PRIMED) || + (buf->state == QETH_QDIO_BUF_ERROR)){ + if (buf->state == QETH_QDIO_BUF_ERROR) + goto clear_buffer; + if (netif_queue_stopped(card->dev)) + goto clear_buffer; + /* get first element of current buffer */ + element = (struct qdio_buffer_element *) + &buf->buffer->element[0]; + offset = 0; +#ifdef CONFIG_QETH_PERF_STATS + card->perf_stats.bufs_rec++; +#endif + while((skb = qeth_get_next_skb(card, buf->buffer, &element, + &offset, &hdr))){ + + qeth_rebuild_skb(card, skb, hdr); +#ifdef CONFIG_QETH_PERF_STATS + card->perf_stats.inbound_time += qeth_get_micros() - + card->perf_stats.inbound_start_time; + card->perf_stats.inbound_cnt++; +#endif + skb->dev = card->dev; + if (netif_queue_stopped(card->dev)) { + dev_kfree_skb_irq(skb); + card->stats.rx_dropped++; + } else { + rxrc = netif_rx(skb); + card->dev->last_rx = jiffies; + card->stats.rx_packets++; + card->stats.rx_bytes += skb->len; + } + } +clear_buffer: + qeth_put_buffer_pool_entry(card, buf->pool_entry); + /* give buffer back to hardware */ + qeth_queue_input_buffer(card, current_buf); + current_buf = (current_buf + 1) % QDIO_MAX_BUFFERS_PER_Q; + buf = &card->qdio.in_q->bufs[current_buf]; + } + /* set index for next time the tasklet is scheduled */ + card->qdio.in_q->next_buf_to_process = current_buf; +} + +static inline int +qeth_handle_send_error(struct qeth_card *card, + struct qeth_qdio_out_buffer *buffer, + int qdio_err, int siga_err) +{ + int sbalf15 = buffer->buffer->element[15].flags & 0xff; + int cc = siga_err & 3; + + QETH_DBF_TEXT(trace, 6, "hdsnderr"); + switch (cc) { + case 0: + if (qdio_err){ + QETH_DBF_TEXT(trace, 1,"lnkfail"); + QETH_DBF_TEXT_(trace,1,"%s",CARD_BUS_ID(card)); + QETH_DBF_TEXT_(trace,1,"%04x %02x", + (u16)qdio_err, (u8)sbalf15); + return QETH_SEND_ERROR_LINK_FAILURE; + } + return QETH_SEND_ERROR_NONE; + case 2: + if (siga_err & QDIO_SIGA_ERROR_B_BIT_SET) { + QETH_DBF_TEXT(trace, 1, "SIGAcc2B"); + QETH_DBF_TEXT_(trace,1,"%s",CARD_BUS_ID(card)); + return QETH_SEND_ERROR_KICK_IT; + } + if ((sbalf15 >= 15) && (sbalf15 <= 31)) + return QETH_SEND_ERROR_RETRY; + return QETH_SEND_ERROR_LINK_FAILURE; + /* look at qdio_error and sbalf 15 */ + case 1: + QETH_DBF_TEXT(trace, 1, "SIGAcc1"); + QETH_DBF_TEXT_(trace,1,"%s",CARD_BUS_ID(card)); + return QETH_SEND_ERROR_LINK_FAILURE; + case 3: + QETH_DBF_TEXT(trace, 1, "SIGAcc3"); + QETH_DBF_TEXT_(trace,1,"%s",CARD_BUS_ID(card)); + return QETH_SEND_ERROR_KICK_IT; + } + return QETH_SEND_ERROR_LINK_FAILURE; +} + +static inline void +qeth_flush_buffers(struct qeth_qdio_out_q *queue, int under_int, + int index, int count) +{ + struct qeth_qdio_out_buffer *buf; + int rc; + int i; + + QETH_DBF_TEXT(trace, 6, "flushbuf"); + + for (i = index; i < index + count; ++i) { + buf = &queue->bufs[i % QDIO_MAX_BUFFERS_PER_Q]; + buf->buffer->element[buf->next_element_to_fill - 1].flags |= + SBAL_FLAGS_LAST_ENTRY; + + if (!queue->do_pack){ + if ((atomic_read(&queue->used_buffers) >= + (QETH_HIGH_WATERMARK_PACK - + QETH_WATERMARK_PACK_FUZZ)) && + !atomic_read(&queue->set_pci_flags_count)){ + /* it's likely that we'll go to packing + * mode soon */ + atomic_inc(&queue->set_pci_flags_count); + buf->buffer->element[0].flags |= 0x40; + } + } else { + if (!atomic_read(&queue->set_pci_flags_count)){ + /* + * there's no outstanding PCI any more, so we + * have to request a PCI to be sure the the PCI + * will wake at some time in the future then we + * can flush packed buffers that might still be + * hanging around, which can happen if no + * further send was requested by the stack + */ + atomic_inc(&queue->set_pci_flags_count); + buf->buffer->element[0].flags |= 0x40; + } +#ifdef CONFIG_QETH_PERF_STATS + queue->card->perf_stats.bufs_sent_pack++; +#endif + } + } + + queue->card->dev->trans_start = jiffies; + if (under_int) + rc = do_QDIO(CARD_DDEV(queue->card), + QDIO_FLAG_SYNC_OUTPUT | QDIO_FLAG_UNDER_INTERRUPT, + queue->queue_no, index, count, NULL); + else + rc = do_QDIO(CARD_DDEV(queue->card), QDIO_FLAG_SYNC_OUTPUT, + queue->queue_no, index, count, NULL); + if (rc){ + QETH_DBF_SPRINTF(trace, 0, "qeth_flush_buffers: do_QDIO " + "returned error (%i) on device %s.", + rc, CARD_DDEV_ID(queue->card)); + QETH_DBF_TEXT(trace, 2, "flushbuf"); + QETH_DBF_TEXT_(trace, 2, " err%d", rc); + queue->card->stats.tx_errors += count; + return; + } +#ifdef CONFIG_QETH_PERF_STATS + queue->card->perf_stats.bufs_sent += count; + queue->card->perf_stats.outbound_cnt++; +#endif +} + +/* + * switches between PACKING and non-PACKING state if needed. + * has to be called holding queue->lock + */ +static inline void +qeth_switch_packing_state(struct qeth_qdio_out_q *queue) +{ + struct qeth_qdio_out_buffer *buffer; + + QETH_DBF_TEXT(trace, 6, "swipack"); + if (!queue->do_pack) { + if (atomic_read(&queue->used_buffers) + >= QETH_HIGH_WATERMARK_PACK){ + /* switch non-PACKING -> PACKING */ + QETH_DBF_TEXT(trace, 6, "np->pack"); +#ifdef CONFIG_QETH_PERF_STATS + queue->card->perf_stats.sc_dp_p++; +#endif + queue->do_pack = 1; + } + } else { + if (atomic_read(&queue->used_buffers) + <= QETH_LOW_WATERMARK_PACK) { + /* switch PACKING -> non-PACKING */ + QETH_DBF_TEXT(trace, 6, "pack->np"); +#ifdef CONFIG_QETH_PERF_STATS + queue->card->perf_stats.sc_p_dp++; +#endif + queue->do_pack = 0; + /* flush packing buffers */ + buffer = &queue->bufs[queue->next_buf_to_fill]; + BUG_ON(buffer->state == QETH_QDIO_BUF_PRIMED); + if (buffer->next_element_to_fill > 0) { + buffer->state = QETH_QDIO_BUF_PRIMED; + atomic_inc(&queue->used_buffers); + queue->next_buf_to_fill = + (queue->next_buf_to_fill + 1) % + QDIO_MAX_BUFFERS_PER_Q; + } + } + } +} + +static void +qeth_qdio_output_handler(struct ccw_device * ccwdev, unsigned int status, + unsigned int qdio_error, unsigned int siga_error, + unsigned int __queue, int first_element, int count, + unsigned long card_ptr) +{ + struct qeth_card *card = (struct qeth_card *) card_ptr; + struct qeth_qdio_out_q *queue = card->qdio.out_qs[__queue]; + struct qeth_qdio_out_buffer *buffer; + int i; + + QETH_DBF_TEXT(trace, 6, "qdouhdl"); + if (status & QDIO_STATUS_LOOK_FOR_ERROR) { + if (status & QDIO_STATUS_ACTIVATE_CHECK_CONDITION){ + QETH_DBF_SPRINTF(trace, 2, "On device %s: " + "received active check " + "condition (0x%08x).", + CARD_BUS_ID(card), status); + QETH_DBF_TEXT(trace, 2, "chkcond"); + QETH_DBF_TEXT_(trace, 2, "%08x", status); + netif_stop_queue(card->dev); + qeth_schedule_recovery(card); + return; + } + } + + for(i = first_element; i < (first_element + count); ++i){ + buffer = &queue->bufs[i % QDIO_MAX_BUFFERS_PER_Q]; + /*we only handle the KICK_IT error by doing a recovery */ + if (qeth_handle_send_error(card, buffer, qdio_error, siga_error) + == QETH_SEND_ERROR_KICK_IT){ + netif_stop_queue(card->dev); + qeth_schedule_recovery(card); + return; + } + /* is PCI flag set on buffer? */ + if (buffer->buffer->element[0].flags & 0x40) + atomic_dec(&queue->set_pci_flags_count); + + qeth_clear_output_buffer(card, buffer); + } + atomic_sub(count, &queue->used_buffers); + + //if (!atomic_read(&queue->set_pci_flags_count)) + tasklet_schedule(&queue->tasklet); + + netif_wake_queue(card->dev); +} + +static void +qeth_qdio_output_tasklet(unsigned long data) +{ + struct qeth_qdio_out_q *queue = (struct qeth_qdio_out_q *) data; + struct qeth_qdio_out_buffer *buffer; + int index; + int count; + + QETH_DBF_TEXT(trace, 6, "outtlet"); + + /* flush all PRIMED buffers */ + index = queue->next_buf_to_flush; + count = 0; + while (queue->bufs[index].state == QETH_QDIO_BUF_PRIMED) { + count++; + index = (index + 1) % QDIO_MAX_BUFFERS_PER_Q; + } + qeth_flush_buffers(queue, 0, queue->next_buf_to_flush, count); + queue->next_buf_to_flush = index; + + /* flush a buffer with data, if no more PCIs are + * outstanding */ + if (!atomic_read(&queue->set_pci_flags_count)){ + spin_lock(&queue->lock); + buffer = &queue->bufs[index]; + if (buffer->state == QETH_QDIO_BUF_PRIMED){ + qeth_flush_buffers(queue, 0, index, 1); + index = (index + 1) % QDIO_MAX_BUFFERS_PER_Q; + queue->next_buf_to_flush = index; + } else if (buffer->next_element_to_fill > 0){ + /* it's a packing buffer */ + BUG_ON(index != queue->next_buf_to_fill); + buffer->state = QETH_QDIO_BUF_PRIMED; + atomic_inc(&queue->used_buffers); + qeth_flush_buffers(queue, 0, index, 1); + index = (index + 1) % QDIO_MAX_BUFFERS_PER_Q; + queue->next_buf_to_flush = index; + queue->next_buf_to_fill = index; + } + spin_unlock(&queue->lock); + } +} + +static char* +qeth_create_qib_param_field(struct qeth_card *card) +{ + char *param_field; + + param_field = kmalloc(QDIO_MAX_BUFFERS_PER_Q * sizeof(char), + GFP_KERNEL); + if (!param_field) + return NULL; + + memset(param_field, 0, QDIO_MAX_BUFFERS_PER_Q * sizeof(char)); + + param_field[0] = _ascebc['P']; + param_field[1] = _ascebc['C']; + param_field[2] = _ascebc['I']; + param_field[3] = _ascebc['T']; + *((unsigned int *) (¶m_field[4])) = QETH_PCI_THRESHOLD_A(card); + *((unsigned int *) (¶m_field[8])) = QETH_PCI_THRESHOLD_B(card); + *((unsigned int *) (¶m_field[12])) = QETH_PCI_TIMER_VALUE(card); + + return param_field; +} + +static void +qeth_initialize_working_pool_list(struct qeth_card *card) +{ + struct qeth_buffer_pool_entry *entry; + + QETH_DBF_TEXT(trace,5,"inwrklst"); + + list_for_each_entry(entry, + &card->qdio.init_pool.entry_list, init_list) { + qeth_put_buffer_pool_entry(card,entry); + } +} + +static void +qeth_clear_working_pool_list(struct qeth_card *card) +{ + struct qeth_buffer_pool_entry *pool_entry, *tmp; + + QETH_DBF_TEXT(trace,5,"clwrklst"); + list_for_each_entry_safe(pool_entry, tmp, + &card->qdio.in_buf_pool.entry_list, list){ + list_del(&pool_entry->list); + } +} + +static void +qeth_free_buffer_pool(struct qeth_card *card) +{ + struct qeth_buffer_pool_entry *pool_entry, *tmp; + int i=0; + QETH_DBF_TEXT(trace,5,"freepool"); + list_for_each_entry_safe(pool_entry, tmp, + &card->qdio.init_pool.entry_list, init_list){ + for (i = 0; i < QETH_MAX_BUFFER_ELEMENTS(card); ++i) + free_page((unsigned long)pool_entry->elements[i]); + list_del(&pool_entry->init_list); + kfree(pool_entry); + } +} + +static int +qeth_alloc_buffer_pool(struct qeth_card *card) +{ + struct qeth_buffer_pool_entry *pool_entry; + void *ptr; + int i, j; + + for (i = 0; i < card->qdio.init_pool.buf_count; ++i){ + pool_entry = kmalloc(sizeof(*pool_entry), GFP_KERNEL); + if (!pool_entry){ + qeth_free_buffer_pool(card); + return -ENOMEM; + } + for(j = 0; j < QETH_MAX_BUFFER_ELEMENTS(card); ++j){ + ptr = (void *) __get_free_page(GFP_KERNEL); + if (!ptr) { + while (j > 0) + free_page((unsigned long) + pool_entry->elements[--j]); + kfree(pool_entry); + qeth_free_buffer_pool(card); + return -ENOMEM; + } + pool_entry->elements[j] = ptr; + } + list_add(&pool_entry->init_list, + &card->qdio.init_pool.entry_list); + list_add(&pool_entry->list, + &card->qdio.in_buf_pool.entry_list); + } + return 0; +} + +static int +qeth_alloc_qdio_buffers(struct qeth_card *card) +{ + int i, j; + + QETH_DBF_TEXT(setup, 2, "allcqdbf"); + + if (card->qdio.state == QETH_QDIO_ALLOCATED) { + qeth_initialize_working_pool_list(card); + return 0; + } + card->qdio.in_q = kmalloc(sizeof(struct qeth_qdio_q), GFP_KERNEL); + if (!card->qdio.in_q) + return - ENOMEM; + QETH_DBF_TEXT(setup, 2, "inq"); + QETH_DBF_HEX(setup, 2, &card->qdio.in_q, sizeof(void *)); + memset(card->qdio.in_q, 0, sizeof(struct qeth_qdio_q)); + /* give inbound qeth_qdio_buffers their qdio_buffers */ + for (i = 0; i < QDIO_MAX_BUFFERS_PER_Q; ++i) + card->qdio.in_q->bufs[i].buffer = + &card->qdio.in_q->qdio_bufs[i]; + /* inbound buffer pool */ + if (qeth_alloc_buffer_pool(card)){ + kfree(card->qdio.in_q); + return -ENOMEM; + } + /* outbound */ + card->qdio.out_qs = + kmalloc(card->qdio.no_out_queues * + sizeof(struct qeth_qdio_out_q *), GFP_KERNEL); + if (!card->qdio.out_qs){ + qeth_free_buffer_pool(card); + return -ENOMEM; + } + for (i = 0; i < card->qdio.no_out_queues; ++i){ + card->qdio.out_qs[i] = kmalloc(sizeof(struct qeth_qdio_out_q), + GFP_KERNEL); + if (!card->qdio.out_qs[i]){ + while (i > 0) + kfree(card->qdio.out_qs[--i]); + kfree(card->qdio.out_qs); + return -ENOMEM; + } + QETH_DBF_TEXT_(setup, 2, "outq %i", i); + QETH_DBF_HEX(setup, 2, &card->qdio.out_qs[i], sizeof(void *)); + memset(card->qdio.out_qs[i], 0, sizeof(struct qeth_qdio_out_q)); + card->qdio.out_qs[i]->queue_no = i; + /* give inbound qeth_qdio_buffers their qdio_buffers */ + for (j = 0; j < QDIO_MAX_BUFFERS_PER_Q; ++j){ + card->qdio.out_qs[i]->bufs[j].buffer = + &card->qdio.out_qs[i]->qdio_bufs[j]; + skb_queue_head_init(&card->qdio.out_qs[i]->bufs[j]. + skb_list); + } + } + card->qdio.state = QETH_QDIO_ALLOCATED; + return 0; +} + +static void +qeth_free_qdio_buffers(struct qeth_card *card) +{ + int i, j; + + QETH_DBF_TEXT(trace, 2, "freeqdbf"); + if (card->qdio.state == QETH_QDIO_UNINITIALIZED) + return; + kfree(card->qdio.in_q); + /* inbound buffer pool */ + qeth_free_buffer_pool(card); + /* free outbound qdio_qs */ + for (i = 0; i < card->qdio.no_out_queues; ++i){ + for (j = 0; j < QDIO_MAX_BUFFERS_PER_Q; ++j) + qeth_clear_output_buffer(card, &card->qdio. + out_qs[i]->bufs[j]); + kfree(card->qdio.out_qs[i]); + } + kfree(card->qdio.out_qs); + card->qdio.state = QETH_QDIO_UNINITIALIZED; +} + +static void +qeth_clear_qdio_buffers(struct qeth_card *card) +{ + int i, j; + + QETH_DBF_TEXT(trace, 2, "clearqdbf"); + /* clear outbound buffers to free skbs */ + for (i = 0; i < card->qdio.no_out_queues; ++i) + if (card->qdio.out_qs[i]){ + for (j = 0; j < QDIO_MAX_BUFFERS_PER_Q; ++j) + qeth_clear_output_buffer(card, &card->qdio. + out_qs[i]->bufs[j]); + } +} + +static void +qeth_init_qdio_info(struct qeth_card *card) +{ + QETH_DBF_TEXT(setup, 4, "intqdinf"); + card->qdio.state = QETH_QDIO_UNINITIALIZED; + /* inbound */ + card->qdio.in_buf_size = QETH_IN_BUF_SIZE_DEFAULT; + card->qdio.init_pool.buf_count = QETH_IN_BUF_COUNT_DEFAULT; + card->qdio.in_buf_pool.buf_count = card->qdio.init_pool.buf_count; + INIT_LIST_HEAD(&card->qdio.in_buf_pool.entry_list); + INIT_LIST_HEAD(&card->qdio.init_pool.entry_list); + card->qdio.in_tasklet.data = (unsigned long) card; + card->qdio.in_tasklet.func = qeth_qdio_input_tasklet; + /* outbound */ + card->qdio.do_prio_queueing = QETH_PRIOQ_DEFAULT; + card->qdio.default_out_queue = QETH_DEFAULT_QUEUE; +} + +static int +qeth_init_qdio_queues(struct qeth_card *card) +{ + int i, j; + int rc; + + QETH_DBF_TEXT(setup, 2, "initqdqs"); + + /* inbound queue */ + memset(card->qdio.in_q->qdio_bufs, 0, + QDIO_MAX_BUFFERS_PER_Q * sizeof(struct qdio_buffer)); + card->qdio.in_q->next_buf_to_process = 0; + card->qdio.in_q->next_buf_to_init = 0; + /*give only as many buffers to hardware as we have buffer pool entries*/ + for (i = 0; i < card->qdio.in_buf_pool.buf_count; ++i) + qeth_init_input_buffer(card, &card->qdio.in_q->bufs[i]); + card->qdio.in_q->next_buf_to_init = card->qdio.in_buf_pool.buf_count; + rc = do_QDIO(CARD_DDEV(card), QDIO_FLAG_SYNC_INPUT, 0, 0, + card->qdio.in_buf_pool.buf_count, NULL); + if (rc) { + QETH_DBF_TEXT_(setup, 2, "1err%d", rc); + return rc; + } + rc = qdio_synchronize(CARD_DDEV(card), QDIO_FLAG_SYNC_INPUT, 0); + if (rc) { + QETH_DBF_TEXT_(setup, 2, "2err%d", rc); + return rc; + } + /* outbound queue */ + for (i = 0; i < card->qdio.no_out_queues; ++i){ + memset(card->qdio.out_qs[i]->qdio_bufs, 0, + QDIO_MAX_BUFFERS_PER_Q * sizeof(struct qdio_buffer)); + for (j = 0; j < QDIO_MAX_BUFFERS_PER_Q; ++j){ + qeth_clear_output_buffer(card, &card->qdio. + out_qs[i]->bufs[j]); + } + card->qdio.out_qs[i]->card = card; + card->qdio.out_qs[i]->next_buf_to_fill = 0; + card->qdio.out_qs[i]->next_buf_to_flush = 0; + card->qdio.out_qs[i]->do_pack = 0; + atomic_set(&card->qdio.out_qs[i]->used_buffers,0); + atomic_set(&card->qdio.out_qs[i]->set_pci_flags_count, 0); + card->qdio.out_qs[i]->tasklet.data = + (unsigned long) card->qdio.out_qs[i]; + card->qdio.out_qs[i]->tasklet.func = qeth_qdio_output_tasklet; + spin_lock_init(&card->qdio.out_qs[i]->lock); + } + return 0; +} + +static int +qeth_qdio_establish(struct qeth_card *card) +{ + struct qdio_initialize init_data; + char *qib_param_field; + struct qdio_buffer **in_sbal_ptrs; + struct qdio_buffer **out_sbal_ptrs; + int i, j, k; + int rc; + + QETH_DBF_TEXT(setup, 2, "qdioest"); + qib_param_field = qeth_create_qib_param_field(card); + if (!qib_param_field) + return -ENOMEM; + + in_sbal_ptrs = kmalloc(QDIO_MAX_BUFFERS_PER_Q * sizeof(void *), + GFP_KERNEL); + if (!in_sbal_ptrs) { + kfree(qib_param_field); + return -ENOMEM; + } + for(i = 0; i < QDIO_MAX_BUFFERS_PER_Q; ++i) + in_sbal_ptrs[i] = (struct qdio_buffer *) + virt_to_phys(card->qdio.in_q->bufs[i].buffer); + + out_sbal_ptrs = + kmalloc(card->qdio.no_out_queues * QDIO_MAX_BUFFERS_PER_Q * + sizeof(void *), GFP_KERNEL); + if (!out_sbal_ptrs) { + kfree(in_sbal_ptrs); + kfree(qib_param_field); + return -ENOMEM; + } + for(i = 0, k = 0; i < card->qdio.no_out_queues; ++i) + for(j = 0; j < QDIO_MAX_BUFFERS_PER_Q; ++j, ++k){ + out_sbal_ptrs[k] = (struct qdio_buffer *) + virt_to_phys(card->qdio.out_qs[i]-> + bufs[j].buffer); + } + + memset(&init_data, 0, sizeof(struct qdio_initialize)); + init_data.cdev = CARD_DDEV(card); + init_data.q_format = qeth_get_qdio_q_format(card); + init_data.qib_param_field_format = 0; + init_data.qib_param_field = qib_param_field; + init_data.min_input_threshold = QETH_MIN_INPUT_THRESHOLD; + init_data.max_input_threshold = QETH_MAX_INPUT_THRESHOLD; + 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->qdio.no_out_queues; + init_data.input_handler = (qdio_handler_t *) + qeth_qdio_input_handler; + init_data.output_handler = (qdio_handler_t *) + 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; + init_data.input_sbal_addr_array = (void **) in_sbal_ptrs; + init_data.output_sbal_addr_array = (void **) out_sbal_ptrs; + + if (!(rc = qdio_initialize(&init_data))) + card->qdio.state = QETH_QDIO_ESTABLISHED; + + kfree(out_sbal_ptrs); + kfree(in_sbal_ptrs); + kfree(qib_param_field); + return rc; +} + +static int +qeth_qdio_activate(struct qeth_card *card) +{ + QETH_DBF_TEXT(setup,3,"qdioact"); + return qdio_activate(CARD_DDEV(card), 0); +} + +static int +qeth_clear_channel(struct qeth_channel *channel) +{ + unsigned long flags; + struct qeth_card *card; + int rc; + + QETH_DBF_TEXT(trace,3,"clearch"); + card = CARD_FROM_CDEV(channel->ccwdev); + spin_lock_irqsave(get_ccwdev_lock(channel->ccwdev), flags); + rc = ccw_device_clear(channel->ccwdev, QETH_CLEAR_CHANNEL_PARM); + spin_unlock_irqrestore(get_ccwdev_lock(channel->ccwdev), flags); + + if (rc) + return rc; + rc = wait_event_interruptible_timeout(card->wait_q, + channel->state==CH_STATE_STOPPED, QETH_TIMEOUT); + if (rc == -ERESTARTSYS) + return rc; + if (channel->state != CH_STATE_STOPPED) + return -ETIME; + channel->state = CH_STATE_DOWN; + return 0; +} + +static int +qeth_halt_channel(struct qeth_channel *channel) +{ + unsigned long flags; + struct qeth_card *card; + int rc; + + QETH_DBF_TEXT(trace,3,"haltch"); + card = CARD_FROM_CDEV(channel->ccwdev); + spin_lock_irqsave(get_ccwdev_lock(channel->ccwdev), flags); + rc = ccw_device_halt(channel->ccwdev, QETH_HALT_CHANNEL_PARM); + spin_unlock_irqrestore(get_ccwdev_lock(channel->ccwdev), flags); + + if (rc) + return rc; + rc = wait_event_interruptible_timeout(card->wait_q, + channel->state==CH_STATE_HALTED, QETH_TIMEOUT); + if (rc == -ERESTARTSYS) + return rc; + if (channel->state != CH_STATE_HALTED) + return -ETIME; + return 0; +} + +static int +qeth_halt_channels(struct qeth_card *card) +{ + int rc = 0; + + QETH_DBF_TEXT(trace,3,"haltchs"); + if ((rc = qeth_halt_channel(&card->read))) + return rc; + if ((rc = qeth_halt_channel(&card->write))) + return rc; + return qeth_halt_channel(&card->data); +} +static int +qeth_clear_channels(struct qeth_card *card) +{ + int rc = 0; + + QETH_DBF_TEXT(trace,3,"clearchs"); + if ((rc = qeth_clear_channel(&card->read))) + return rc; + if ((rc = qeth_clear_channel(&card->write))) + return rc; + return qeth_clear_channel(&card->data); +} + +static int +qeth_clear_halt_card(struct qeth_card *card, int halt) +{ + int rc = 0; + + QETH_DBF_TEXT(trace,3,"clhacrd"); + QETH_DBF_HEX(trace, 3, &card, sizeof(void *)); + + if (halt) + rc = qeth_halt_channels(card); + if (rc) + return rc; + return qeth_clear_channels(card); +} + +static int +qeth_qdio_clear_card(struct qeth_card *card, int use_halt) +{ + int rc = 0; + + QETH_DBF_TEXT(trace,3,"qdioclr"); + if (card->qdio.state == QETH_QDIO_ESTABLISHED){ + qdio_cleanup(CARD_DDEV(card), + (card->info.type == QETH_CARD_TYPE_IQD) ? + QDIO_FLAG_CLEANUP_USING_HALT : + QDIO_FLAG_CLEANUP_USING_CLEAR); + card->qdio.state = QETH_QDIO_ALLOCATED; + } + rc = qeth_clear_halt_card(card, use_halt); + card->state = CARD_STATE_DOWN; + return rc; +} + +static int +qeth_dm_act(struct qeth_card *card) +{ + int rc; + struct qeth_cmd_buffer *iob; + + QETH_DBF_TEXT(setup,2,"dmact"); + + iob = qeth_wait_for_buffer(&card->write); + memcpy(iob->data, DM_ACT, DM_ACT_SIZE); + + memcpy(QETH_DM_ACT_DEST_ADDR(iob->data), + &card->token.cm_connection_r, QETH_MPC_TOKEN_LENGTH); + memcpy(QETH_DM_ACT_CONNECTION_TOKEN(iob->data), + &card->token.ulp_connection_r, QETH_MPC_TOKEN_LENGTH); + rc = qeth_send_control_data(card, DM_ACT_SIZE, iob, NULL, NULL); + return rc; +} + +static int +qeth_mpc_initialize(struct qeth_card *card) +{ + int rc; + + QETH_DBF_TEXT(setup,2,"mpcinit"); + + if ((rc = qeth_issue_next_read(card))){ + QETH_DBF_TEXT_(setup, 2, "1err%d", rc); + return rc; + } + if ((rc = qeth_cm_enable(card))){ + QETH_DBF_TEXT_(setup, 2, "2err%d", rc); + return rc; + } + if ((rc = qeth_cm_setup(card))){ + QETH_DBF_TEXT_(setup, 2, "3err%d", rc); + return rc; + } + if ((rc = qeth_ulp_enable(card))){ + QETH_DBF_TEXT_(setup, 2, "4err%d", rc); + return rc; + } + if ((rc = qeth_ulp_setup(card))){ + QETH_DBF_TEXT_(setup, 2, "5err%d", rc); + return rc; + } + if ((rc = qeth_alloc_qdio_buffers(card))){ + QETH_DBF_TEXT_(setup, 2, "5err%d", rc); + return rc; + } + if ((rc = qeth_qdio_establish(card))){ + QETH_DBF_TEXT_(setup, 2, "6err%d", rc); + qeth_free_qdio_buffers(card); + goto out_qdio; + } + if ((rc = qeth_qdio_activate(card))){ + QETH_DBF_TEXT_(setup, 2, "7err%d", rc); + goto out_qdio; + } + if ((rc = qeth_dm_act(card))){ + QETH_DBF_TEXT_(setup, 2, "8err%d", rc); + goto out_qdio; + } + + return 0; +out_qdio: + qeth_qdio_clear_card(card, card->info.type==QETH_CARD_TYPE_OSAE); + return rc; +} + +static void +qeth_set_device_name(struct qeth_card *card) +{ + char buf[IF_NAME_LEN]; + + memset(buf, 0, IF_NAME_LEN); + if (card->info.type == QETH_CARD_TYPE_IQD) { + sprintf(buf,"hsi%d", atomic_read(&qeth_hsi_count)); + atomic_inc(&qeth_hsi_count); + memcpy(card->dev->name,buf,IF_NAME_LEN); + } + +} + +static struct net_device * +qeth_get_netdevice(enum qeth_card_types type, enum qeth_link_types linktype) +{ + struct net_device *dev = NULL; + + switch (type) { + case QETH_CARD_TYPE_OSAE: + switch (linktype) { + case QETH_LINK_TYPE_LANE_TR: + case QETH_LINK_TYPE_HSTR: +#ifdef CONFIG_TR + dev = alloc_trdev(0); +#endif /* CONFIG_TR */ + break; + default: + dev = alloc_etherdev(0); + } + break; + case QETH_CARD_TYPE_IQD: + default: + dev = alloc_etherdev(0); + } + return dev; +} + +static inline int +qeth_send_packet(struct qeth_card *, struct sk_buff *); + +static int +qeth_hard_start_xmit(struct sk_buff *skb, struct net_device *dev) +{ + int rc; + struct qeth_card *card; + + QETH_DBF_TEXT(trace, 6, "hrdstxmi"); + card = (struct qeth_card *)dev->priv; + if (skb==NULL) { + card->stats.tx_dropped++; + card->stats.tx_errors++; + return -EIO; + } + if (card->state != CARD_STATE_UP_LAN_ONLINE) { + card->stats.tx_dropped++; + card->stats.tx_errors++; + card->stats.tx_carrier_errors++; + return -EIO; + } + if (netif_queue_stopped(dev) ) { + card->stats.tx_dropped++; + return -EBUSY; + } +#ifdef CONFIG_QETH_PERF_STATS + card->perf_stats.outbound_start_time = qeth_get_micros(); +#endif + /* + * dev_queue_xmit should ensure that we are called packet + * after packet + */ + netif_stop_queue(dev); + if (!(rc = qeth_send_packet(card, skb))) + netif_wake_queue(dev); + + return rc; +} + +static int +qeth_verify_vlan_dev(struct net_device *dev, struct qeth_card *card) +{ + int rc = 0; +#ifdef CONFIG_QETH_VLAN + struct vlan_group *vg; + int i; + + if (!(vg = card->vlangrp)) + return rc; + + for (i = 0; i < VLAN_GROUP_ARRAY_LEN; i++){ + if (vg->vlan_devices[i] == dev){ + rc = QETH_VLAN_CARD; + break; + } + } +#endif + return rc; +} + +static int +qeth_verify_dev(struct net_device *dev) +{ + struct qeth_card *card; + unsigned long flags; + int rc = 0; + + read_lock_irqsave(&qeth_card_list.rwlock, flags); + list_for_each_entry(card, &qeth_card_list.list, list){ + if (card->dev == dev){ + rc = QETH_REAL_CARD; + break; + } + rc = qeth_verify_vlan_dev(dev, card); + if (rc) + break; + } + read_unlock_irqrestore(&qeth_card_list.rwlock, flags); + + return rc; +} + +static struct qeth_card * +qeth_get_card_from_dev(struct net_device *dev) +{ + struct qeth_card *card = NULL; + int rc; + + rc = qeth_verify_dev(dev); + if (rc == QETH_REAL_CARD) + card = (struct qeth_card *)dev->priv; + else if (rc == QETH_VLAN_CARD) + card = (struct qeth_card *) + VLAN_DEV_INFO(dev)->real_dev->priv; + + QETH_DBF_TEXT_(trace, 4, "%d", rc); + return card ; +} + +static void +qeth_tx_timeout(struct net_device *dev) +{ + struct qeth_card *card; + + card = (struct qeth_card *) dev->priv; + card->stats.tx_errors++; + qeth_schedule_recovery(card); +} + +static int +qeth_open(struct net_device *dev) +{ + struct qeth_card *card; + + QETH_DBF_TEXT(trace, 4, "qethopen"); + + card = (struct qeth_card *) dev->priv; + + if ((card->state != CARD_STATE_SOFTSETUP) && + (card->state != CARD_STATE_UP_LAN_OFFLINE)) + return -ENODEV; + if (!card->lan_online){ + card->state = CARD_STATE_UP_LAN_OFFLINE; + return -EIO; + } + + card->dev->flags |= IFF_UP; + netif_start_queue(dev); + card->data.state = CH_STATE_UP; + card->state = CARD_STATE_UP_LAN_ONLINE; + return 0; +} + +static int +qeth_stop(struct net_device *dev) +{ + struct qeth_card *card; + + QETH_DBF_TEXT(trace, 4, "qethstop"); + + card = (struct qeth_card *) dev->priv; + + netif_stop_queue(dev); + card->dev->flags &= ~IFF_UP; + if ((card->state == CARD_STATE_UP_LAN_ONLINE) || + (card->state == CARD_STATE_UP_LAN_OFFLINE)) + card->state = CARD_STATE_SOFTSETUP; + return 0; +} + +static inline int +qeth_get_cast_type(struct qeth_card *card, struct sk_buff *skb) +{ + int cast_type = RTN_UNSPEC; + + if (skb->dst && skb->dst->neighbour){ + cast_type = skb->dst->neighbour->type; + if ((cast_type == RTN_BROADCAST) || + (cast_type == RTN_MULTICAST) || + (cast_type == RTN_ANYCAST)) + return cast_type; + else + return RTN_UNSPEC; + } + /* try something else */ + if (skb->protocol == ETH_P_IPV6) + return (skb->nh.raw[24] == 0xff) ? RTN_MULTICAST : 0; + else if (skb->protocol == ETH_P_IP) + return ((skb->nh.raw[16] & 0xf0) == 0xe0) ? RTN_MULTICAST : 0; + /* ... */ + if (!memcmp(skb->nh.raw, skb->dev->broadcast, 6)) + return RTN_BROADCAST; + else { + u16 hdr_mac; + + hdr_mac = *((u16 *)skb->nh.raw); + /* tr multicast? */ + switch (card->info.link_type) { + case QETH_LINK_TYPE_HSTR: + case QETH_LINK_TYPE_LANE_TR: + if ((hdr_mac == QETH_TR_MAC_NC) || + (hdr_mac == QETH_TR_MAC_C)) + return RTN_MULTICAST; + /* eth or so multicast? */ + default: + if ((hdr_mac == QETH_ETH_MAC_V4) || + (hdr_mac == QETH_ETH_MAC_V6)) + return RTN_MULTICAST; + } + } + return cast_type; +} + +static inline int +qeth_get_priority_queue(struct qeth_card *card, struct sk_buff *skb, + int ipv, int cast_type) +{ + if (!ipv && (card->info.type == QETH_CARD_TYPE_OSAE)) + return card->qdio.default_out_queue; + switch (card->qdio.no_out_queues) { + case 4: + if (cast_type && card->info.is_multicast_different) + return card->info.is_multicast_different & + (card->qdio.no_out_queues - 1); + if (card->qdio.do_prio_queueing && (ipv == 4)) { + if (card->qdio.do_prio_queueing==QETH_PRIO_Q_ING_TOS){ + if (skb->nh.iph->tos & IP_TOS_NOTIMPORTANT) + return 3; + if (skb->nh.iph->tos & IP_TOS_HIGHRELIABILITY) + return 2; + if (skb->nh.iph->tos & IP_TOS_HIGHTHROUGHPUT) + return 1; + if (skb->nh.iph->tos & IP_TOS_LOWDELAY) + return 0; + } + if (card->qdio.do_prio_queueing==QETH_PRIO_Q_ING_PREC) + return 3 - (skb->nh.iph->tos >> 6); + } else if (card->qdio.do_prio_queueing && (ipv == 6)) { + /* TODO: IPv6!!! */ + } + return card->qdio.default_out_queue; + default: + return 0; + } +} + +static inline int +qeth_get_ip_version(struct sk_buff *skb) +{ + switch (skb->protocol) { + case ETH_P_IPV6: + return 6; + case ETH_P_IP: + return 4; + default: + return 0; + } +} + +static inline int +qeth_prepare_skb(struct qeth_card *card, struct sk_buff **skb, + struct qeth_hdr **hdr, int ipv) +{ + struct sk_buff *new_skb; +#ifdef CONFIG_QETH_VLAN + u16 *tag; +#endif + + QETH_DBF_TEXT(trace, 6, "prepskb"); + if (skb_headroom(*skb) < sizeof(struct qeth_hdr)){ + new_skb = skb_realloc_headroom(*skb, sizeof(struct qeth_hdr)); + if (!new_skb) { + PRINT_ERR("qeth_prepare_skb: could " + "not realloc headroom for qeth_hdr " + "on interface %s", card->info.if_name); + return -ENOMEM; + } + *skb = new_skb; + } +#ifdef CONFIG_QETH_VLAN + if (card->vlangrp && vlan_tx_tag_present(*skb) && (ipv == 6)){ + /* + * Move the mac addresses (6 bytes src, 6 bytes dest) + * to the beginning of the new header. We are using three + * memcpys instead of one memmove to save cycles. + */ + skb_push(*skb, VLAN_HLEN); + memcpy((*skb)->data, (*skb)->data + 4, 4); + memcpy((*skb)->data + 4, (*skb)->data + 8, 4); + memcpy((*skb)->data + 8, (*skb)->data + 12, 4); + tag = (u16 *) (*skb)->data + 12; + /* + * 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)); + } +#endif + *hdr = (struct qeth_hdr *) skb_push(*skb, sizeof(struct qeth_hdr)); + /* + * 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) + */ + if ((((unsigned long) *hdr) & (~(PAGE_SIZE - 1))) != + (((unsigned long) *hdr + sizeof(struct qeth_hdr) + + QETH_IP_HEADER_SIZE) & (~(PAGE_SIZE - 1)))) { + PRINT_ERR("qeth_prepare_skb: misaligned " + "packet on interface %s. Discarded.", + card->info.if_name); + return -EINVAL; + } + return 0; +} + +static inline u8 +qeth_get_qeth_hdr_flags4(int cast_type) +{ + if (cast_type == RTN_MULTICAST) + return QETH_CAST_MULTICAST; + if (cast_type == RTN_BROADCAST) + return QETH_CAST_BROADCAST; + return QETH_CAST_UNICAST; +} + +static inline u8 +qeth_get_qeth_hdr_flags6(int cast_type) +{ + u8 ct = QETH_HDR_PASSTHRU | QETH_HDR_IPV6; + if (cast_type == RTN_MULTICAST) + return ct | QETH_CAST_MULTICAST; + if (cast_type == RTN_ANYCAST) + return ct | QETH_CAST_ANYCAST; + if (cast_type == RTN_BROADCAST) + return ct | QETH_CAST_BROADCAST; + return ct | QETH_CAST_UNICAST; +} + +static inline void +qeth_fill_header(struct qeth_card *card, struct qeth_hdr *hdr, + struct sk_buff *skb, int ipv, int cast_type) +{ + hdr->id = 1; + hdr->ext_flags = 0; + + QETH_DBF_TEXT(trace, 6, "fillhdr"); +#ifdef CONFIG_QETH_VLAN + /* + * before we're going to overwrite this location with next hop ip. + * v6 uses passthrough, v4 sets the tag in the QDIO header. + */ + if (card->vlangrp && vlan_tx_tag_present(skb)) { + hdr->ext_flags = (ipv == 4)? QETH_EXT_HDR_VLAN_FRAME : + QETH_EXT_HDR_INCLUDE_VLAN_TAG; + hdr->vlan_id = vlan_tx_tag_get(skb); + } +#endif /* CONFIG_QETH_VLAN */ + hdr->length = skb->len - sizeof(struct qeth_hdr); + if (ipv == 4) { /* IPv4 */ + hdr->flags = qeth_get_qeth_hdr_flags4(cast_type); + memset(hdr->dest_addr, 0, 12); + 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 (ipv == 6) { /* IPv6 or passthru */ + hdr->flags = qeth_get_qeth_hdr_flags6(cast_type); + 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 + sizeof(struct qeth_hdr), + skb->dev->broadcast, 6)) { /* broadcast? */ + hdr->flags = QETH_CAST_BROADCAST | QETH_HDR_PASSTHRU; + } else { + hdr->flags = (cast_type == RTN_MULTICAST) ? + QETH_CAST_MULTICAST | QETH_HDR_PASSTHRU : + QETH_CAST_UNICAST | QETH_HDR_PASSTHRU; + } + } +} + +static inline int +qeth_fill_buffer(struct qeth_qdio_out_q *queue, struct qeth_qdio_out_buffer *buf, + char *data, struct sk_buff *skb) +{ + struct qdio_buffer *buffer; + int length = skb->len; + int length_here; + int element; + int first_lap = 1; + + QETH_DBF_TEXT(trace, 6, "qdfillbf"); + + buffer = buf->buffer; + atomic_inc(&skb->users); + skb_queue_tail(&buf->skb_list, skb); + element = buf->next_element_to_fill; + while (length > 0) { + /* length_here is the remaining amount of data in this page */ + length_here = PAGE_SIZE - ((unsigned long) data % PAGE_SIZE); + if (length < length_here) + length_here = length; + buffer->element[element].addr = data; + 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; + } + data += length_here; + element++; + first_lap = 0; + } + buf->next_element_to_fill = element; + if (!queue->do_pack) { + QETH_DBF_TEXT(trace, 6, "fillbfnp"); + /* set state to PRIMED -> will be flushed */ + buf->state = QETH_QDIO_BUF_PRIMED; + } else { + QETH_DBF_TEXT(trace, 6, "fillbfpa"); +#ifdef CONFIG_QETH_PERF_STATS + queue->card->perf_stats.skbs_sent_pack++; +#endif + if (buf->next_element_to_fill >= + QETH_MAX_BUFFER_ELEMENTS(queue->card)) { + /* + * packed buffer if full -> set state PRIMED + * -> will be flushed + */ + buf->state = QETH_QDIO_BUF_PRIMED; + } + } + return 0; +} + +static inline int +qeth_do_send_packet(struct qeth_card *card, struct sk_buff *skb, + struct qeth_qdio_out_q *queue, int ipv, + int cast_type) +{ + struct qeth_hdr *hdr; + struct qeth_qdio_out_buffer *buffer; + int elements_needed; + int rc; + + QETH_DBF_TEXT(trace, 6, "dosndpkt"); + + if ((rc = qeth_prepare_skb(card, &skb, &hdr, ipv))){ + QETH_DBF_TEXT_(trace, 4, "1err%d", rc); + return rc; + } + qeth_fill_header(card, hdr, skb, ipv, cast_type); + elements_needed = 1 + (((((unsigned long) hdr) % PAGE_SIZE) + skb->len) + >> PAGE_SHIFT); + if (elements_needed > QETH_MAX_BUFFER_ELEMENTS(card)){ + PRINT_ERR("qeth_do_send_packet: invalid size of " + "IP packet. Discarded."); + return -EINVAL; + } + + spin_lock(&queue->lock); + /* check if we need to switch packing state of this queue */ + if (card->info.type != QETH_CARD_TYPE_IQD) + qeth_switch_packing_state(queue); + buffer = &queue->bufs[queue->next_buf_to_fill]; + BUG_ON(buffer->state == QETH_QDIO_BUF_PRIMED); + if (queue->do_pack){ + /* does packet fit in current buffer? */ + if((QETH_MAX_BUFFER_ELEMENTS(card) - buffer->next_element_to_fill) + < elements_needed){ + /* ... no -> set state PRIMED */ + buffer->state = QETH_QDIO_BUF_PRIMED; + atomic_inc(&queue->used_buffers); + queue->next_buf_to_fill = + (queue->next_buf_to_fill + 1) % + QDIO_MAX_BUFFERS_PER_Q; + buffer = &queue->bufs[queue->next_buf_to_fill]; + } + } + + rc = qeth_fill_buffer(queue, buffer, (char *)hdr, skb); + if (rc) { + PRINT_WARN("qeth_do_send_packet: error during " + "qeth_fill_buffer."); + card->stats.tx_dropped++; + spin_unlock(&queue->lock); + return rc; + } + if (buffer->state == QETH_QDIO_BUF_PRIMED){ + /* next time fill the next buffer */ + atomic_inc(&queue->used_buffers); + queue->next_buf_to_fill = (queue->next_buf_to_fill + 1) % + QDIO_MAX_BUFFERS_PER_Q; + } + spin_unlock(&queue->lock); + + tasklet_schedule(&queue->tasklet); + + return rc; +} + +static inline int +qeth_send_packet(struct qeth_card *card, struct sk_buff *skb) +{ + int ipv; + int cast_type; + struct qeth_qdio_out_q *queue; + int rc; + + QETH_DBF_TEXT(trace, 6, "sendpkt"); + + ipv = qeth_get_ip_version(skb); + cast_type = qeth_get_cast_type(card, skb); + queue = card->qdio.out_qs + [qeth_get_priority_queue(card, skb, ipv, cast_type)]; + /* do we have empty buffers? */ + rc = (atomic_read(&queue->used_buffers) >= + QDIO_MAX_BUFFERS_PER_Q - 1) ? -EBUSY : 0; + if (rc) { + card->stats.tx_dropped++; + QETH_DBF_TEXT_(trace, 4, "1err%d", rc); + return rc; + } + + rc = qeth_do_send_packet(card, skb, queue, ipv, cast_type); + + if (!rc){ + card->stats.tx_packets++; + card->stats.tx_bytes += skb->len; +#ifdef CONFIG_QETH_PERF_STATS + card->perf_stats.outbound_time += qeth_get_micros() - + card->perf_stats.outbound_start_time; +#endif + } + return rc; +} + +static int +qeth_mdio_read(struct net_device *dev, int phy_id, int regnum) +{ + struct qeth_card *card = (struct qeth_card *) dev->priv; + int rc = 0; + + switch(regnum){ + case MII_BMCR: /* Basic mode control register */ + rc = BMCR_FULLDPLX; + if(card->info.link_type != QETH_LINK_TYPE_GBIT_ETH) + rc |= BMCR_SPEED100; + break; + case MII_BMSR: /* Basic mode status register */ + rc = BMSR_ERCAP | BMSR_ANEGCOMPLETE | BMSR_LSTATUS | + BMSR_10HALF | BMSR_10FULL | BMSR_100HALF | BMSR_100FULL | + BMSR_100BASE4; + break; + case MII_PHYSID1: /* PHYS ID 1 */ + rc = (dev->dev_addr[0] << 16) | (dev->dev_addr[1] << 8) | + dev->dev_addr[2]; + rc = (rc >> 5) & 0xFFFF; + break; + case MII_PHYSID2: /* PHYS ID 2 */ + rc = (dev->dev_addr[2] << 10) & 0xFFFF; + break; + case MII_ADVERTISE: /* Advertisement control reg */ + rc = ADVERTISE_ALL; + break; + case MII_LPA: /* Link partner ability reg */ + rc = LPA_10HALF | LPA_10FULL | LPA_100HALF | LPA_100FULL | + LPA_100BASE4 | LPA_LPACK; + break; + case MII_EXPANSION: /* Expansion register */ + break; + case MII_DCOUNTER: /* disconnect counter */ + break; + case MII_FCSCOUNTER: /* false carrier counter */ + break; + case MII_NWAYTEST: /* N-way auto-neg test register */ + break; + case MII_RERRCOUNTER: /* rx error counter */ + rc = card->stats.rx_errors; + break; + case MII_SREVISION: /* silicon revision */ + break; + case MII_RESV1: /* reserved 1 */ + break; + case MII_LBRERROR: /* loopback, rx, bypass error */ + break; + case MII_PHYADDR: /* physical address */ + break; + case MII_RESV2: /* reserved 2 */ + break; + case MII_TPISTATUS: /* TPI status for 10mbps */ + break; + case MII_NCONFIG: /* network interface config */ + break; + default: + rc = 0; + break; + } + return rc; +} + +static void +qeth_mdio_write(struct net_device *dev, int phy_id, int regnum, int value) +{ + switch(regnum){ + case MII_BMCR: /* Basic mode control register */ + case MII_BMSR: /* Basic mode status register */ + case MII_PHYSID1: /* PHYS ID 1 */ + case MII_PHYSID2: /* PHYS ID 2 */ + case MII_ADVERTISE: /* Advertisement control reg */ + case MII_LPA: /* Link partner ability reg */ + case MII_EXPANSION: /* Expansion register */ + case MII_DCOUNTER: /* disconnect counter */ + case MII_FCSCOUNTER: /* false carrier counter */ + case MII_NWAYTEST: /* N-way auto-neg test register */ + case MII_RERRCOUNTER: /* rx error counter */ + case MII_SREVISION: /* silicon revision */ + case MII_RESV1: /* reserved 1 */ + case MII_LBRERROR: /* loopback, rx, bypass error */ + case MII_PHYADDR: /* physical address */ + case MII_RESV2: /* reserved 2 */ + case MII_TPISTATUS: /* TPI status for 10mbps */ + case MII_NCONFIG: /* network interface config */ + default: + break; + } +} + +static inline const char * +qeth_arp_get_error_cause(int *rc) +{ + switch (*rc) { + case QETH_IPA_ARP_RC_FAILED: + *rc = -EIO; + return "operation failed"; + case QETH_IPA_ARP_RC_NOTSUPP: + *rc = -EOPNOTSUPP; + return "operation not supported"; + case QETH_IPA_ARP_RC_OUT_OF_RANGE: + *rc = -EINVAL; + return "argument out of range"; + case QETH_IPA_ARP_RC_Q_NOTSUPP: + *rc = -EOPNOTSUPP; + return "query operation not supported"; + case QETH_IPA_ARP_RC_Q_NO_DATA: + *rc = -ENOENT; + return "no query data available"; + default: + return "unknown error"; + } +} + +static int +qeth_send_simple_setassparms(struct qeth_card *, enum qeth_ipa_funcs, + __u16, long); + +static int +qeth_arp_set_no_entries(struct qeth_card *card, int no_entries) +{ + int tmp; + int rc; + + QETH_DBF_TEXT(trace,3,"arpstnoe"); + + /* TODO: really not supported by GuestLAN? */ + if (card->info.guestlan) + return -EOPNOTSUPP; + if (!qeth_is_supported(card,IPA_ARP_PROCESSING)) { + PRINT_WARN("ARP processing not supported " + "on %s!\n", card->info.if_name); + return -EOPNOTSUPP; + } + rc = qeth_send_simple_setassparms(card, IPA_ARP_PROCESSING, + IPA_CMD_ASS_ARP_SET_NO_ENTRIES, + no_entries); + if (rc) { + tmp = rc; + PRINT_WARN("Could not set number of ARP entries on %s: " + "%s (0x%x)\n", + card->info.if_name, qeth_arp_get_error_cause(&rc), + tmp); + } + return rc; +} + +static int +qeth_arp_query_cb(struct qeth_card *card, struct qeth_reply *reply, + unsigned long data) +{ + struct qeth_ipa_arp_cmd *cmd; + struct qeth_arp_query_data *qdata; + struct qeth_arp_query_info *qinfo; + int entry_size; + int i; + + QETH_DBF_TEXT(trace,4,"arpquecb"); + + qinfo = (struct qeth_arp_query_info *) reply->param; + cmd = (struct qeth_ipa_arp_cmd *) data; + if (cmd->ihdr.return_code) { + QETH_DBF_TEXT_(trace,4,"qaer1%i", cmd->ihdr.return_code); + return 0; + } + if (cmd->shdr.return_code) { + cmd->ihdr.return_code = cmd->shdr.return_code; + QETH_DBF_TEXT_(trace,4,"qaer2%i", cmd->ihdr.return_code); + return 0; + } + qdata = &cmd->data.query_arp; + switch(qdata->reply_bits){ + case 5: + entry_size = sizeof(struct qeth_arp_qi_entry5); + break; + case 7: + entry_size = sizeof(struct qeth_arp_qi_entry7); + break; + default: + /* tr is the same as eth -> entry7 */ + entry_size = sizeof(struct qeth_arp_qi_entry7); + break; + } + /* check if there is enough room in userspace */ + if ((qinfo->udata_len - qinfo->udata_offset) < + qdata->no_entries * entry_size){ + QETH_DBF_TEXT_(trace, 4, "qaer3%i", -ENOMEM); + cmd->ihdr.return_code = -ENOMEM; + goto out_error; + } + QETH_DBF_TEXT_(trace, 4, "anore%i", cmd->shdr.number_of_replies); + QETH_DBF_TEXT_(trace, 4, "aseqn%i", cmd->shdr.seq_no); + QETH_DBF_TEXT_(trace, 4, "anoen%i", qdata->no_entries); + for (i = 0; i < qdata->no_entries; ++i){ + memcpy(qinfo->udata + qinfo->udata_offset, + qdata->data + i*entry_size, entry_size); + qinfo->no_entries++; + qinfo->udata_offset += entry_size; + } + /* check if all replies received ... */ + if (cmd->shdr.seq_no < cmd->shdr.number_of_replies) + return 1; + memcpy(qinfo->udata, &qinfo->no_entries, 4); + memcpy(qinfo->udata + QETH_QARP_MASK_OFFSET,&qdata->reply_bits,2); + return 0; +out_error: + i = 0; + memcpy(qinfo->udata, &i, 4); + return 0; +} + +static struct qeth_cmd_buffer * +qeth_get_ipacmd_buffer(struct qeth_card *, enum qeth_ipa_cmds, + enum qeth_prot_versions); + +struct qeth_cmd_buffer * +qeth_get_ipa_arp_cmd_buffer(struct qeth_card *card, u16 cmd_code, + u32 data_len, enum qeth_prot_versions proto) +{ + struct qeth_cmd_buffer *iob; + struct qeth_ipa_arp_cmd *cmd; + u16 s1, s2; + + QETH_DBF_TEXT(trace,4,"getarpcm"); + iob = qeth_get_ipacmd_buffer(card, IPA_CMD_SETASSPARMS, proto); + + memcpy(iob->data, IPA_PDU_HEADER, IPA_PDU_HEADER_SIZE); + /* adjust sizes in IPA_PDU_HEADER */ + s1 = (u32) IPA_PDU_HEADER_SIZE + QETH_ARP_CMD_BASE_LEN + data_len; + s2 = (u32) QETH_ARP_CMD_BASE_LEN + data_len; + memcpy(QETH_IPA_PDU_LEN_TOTAL(iob->data), &s1, 2); + memcpy(QETH_IPA_PDU_LEN_PDU1(iob->data), &s2, 2); + memcpy(QETH_IPA_PDU_LEN_PDU2(iob->data), &s2, 2); + memcpy(QETH_IPA_PDU_LEN_PDU3(iob->data), &s2, 2); + + cmd = (struct qeth_ipa_arp_cmd *)(iob->data+IPA_PDU_HEADER_SIZE); + cmd->shdr.assist_no = IPA_ARP_PROCESSING; + cmd->shdr.length = 8 + data_len; + cmd->shdr.command_code = cmd_code; + cmd->shdr.return_code = 0; + cmd->shdr.seq_no = 0; + + return iob; +} + +static int +qeth_send_ipa_arp_cmd(struct qeth_card *card, struct qeth_cmd_buffer *iob, + char *data, int data_len, + int (*reply_cb) + (struct qeth_card *,struct qeth_reply*, unsigned long), + void *reply_param) +{ + int rc; + + QETH_DBF_TEXT(trace,4,"sendarp"); + + memcpy(QETH_IPA_ARP_DATA_POS(iob->data), data, data_len); + memcpy(QETH_IPA_CMD_DEST_ADDR(iob->data), + &card->token.ulp_connection_r, QETH_MPC_TOKEN_LENGTH); + + rc = qeth_send_control_data(card, IPA_PDU_HEADER_SIZE + + QETH_ARP_CMD_BASE_LEN + data_len, iob, + reply_cb, reply_param); + return rc; +} + +static int +qeth_arp_query(struct qeth_card *card, char *udata) +{ + struct qeth_cmd_buffer *iob; + struct qeth_arp_query_data *qdata; + struct qeth_arp_query_info qinfo = {0, }; + int tmp; + int rc; + + QETH_DBF_TEXT(trace,3,"arpquery"); + + /* TODO: really not supported by GuestLAN? */ + if (card->info.guestlan) + return -EOPNOTSUPP; + if (!qeth_is_supported(card,IPA_ARP_PROCESSING)) { + PRINT_WARN("ARP processing not supported " + "on %s!\n", card->info.if_name); + return -EOPNOTSUPP; + } + /* get size of userspace mem area */ + if (copy_from_user(&qinfo.udata_len, udata, 4)) + return -EFAULT; + if (!(qinfo.udata = kmalloc(qinfo.udata_len, GFP_KERNEL))) + return -ENOMEM; + memset(qinfo.udata, 0, qinfo.udata_len); + qinfo.udata_offset = QETH_QARP_ENTRIES_OFFSET; + /* alloc mem area for the actual query */ + if (!(qdata = kmalloc(sizeof(struct qeth_arp_query_data), + GFP_KERNEL))){ + kfree(qinfo.udata); + return -ENOMEM; + } + memset(qdata, 0, sizeof(struct qeth_arp_query_data)); + iob = qeth_get_ipa_arp_cmd_buffer(card, IPA_CMD_ASS_ARP_QUERY_INFO, + sizeof(struct qeth_arp_query_data), + QETH_PROT_IPV4); + rc = qeth_send_ipa_arp_cmd(card, iob, + (char *) qdata, + sizeof(struct qeth_arp_query_data), + qeth_arp_query_cb, + (void *)&qinfo); + if (rc) { + tmp = rc; + PRINT_WARN("Error while querying ARP cache on %s: %s (0x%x)\n", + card->info.if_name, qeth_arp_get_error_cause(&rc), + tmp); + copy_to_user(udata, qinfo.udata, 4); + } else { + copy_to_user(udata, qinfo.udata, qinfo.udata_len); + } + kfree(qinfo.udata); + return rc; +} + +static int +qeth_default_setassparms_cb(struct qeth_card *, struct qeth_reply *, + unsigned long); + +static struct qeth_cmd_buffer * +qeth_get_setassparms_cmd(struct qeth_card *, enum qeth_ipa_funcs, + __u16, __u16, enum qeth_prot_versions); + +static int +qeth_send_setassparms(struct qeth_card *, struct qeth_cmd_buffer *, + __u16, long, + int (*reply_cb) + (struct qeth_card *, struct qeth_reply *, unsigned long), + void *reply_param); + +static int +qeth_arp_add_entry(struct qeth_card *card, struct qeth_arp_cache_entry *entry) +{ + struct qeth_cmd_buffer *iob; + char buf[16]; + int tmp; + int rc; + + QETH_DBF_TEXT(trace,3,"arpadent"); + + /* TODO: really not supported by GuestLAN? */ + if (card->info.guestlan) + return -EOPNOTSUPP; + if (!qeth_is_supported(card,IPA_ARP_PROCESSING)) { + PRINT_WARN("ARP processing not supported " + "on %s!\n", card->info.if_name); + return -EOPNOTSUPP; + } + + iob = qeth_get_setassparms_cmd(card, IPA_ARP_PROCESSING, + IPA_CMD_ASS_ARP_ADD_ENTRY, + sizeof(struct qeth_arp_cache_entry), + QETH_PROT_IPV4); + rc = qeth_send_setassparms(card, iob, + sizeof(struct qeth_arp_cache_entry), + (unsigned long) entry, + qeth_default_setassparms_cb, NULL); + if (rc) { + tmp = rc; + qeth_ipaddr4_to_string((u8 *)entry->ipaddr, buf); + PRINT_WARN("Could not add ARP entry for address %s on %s: " + "%s (0x%x)\n", + buf, card->info.if_name, + qeth_arp_get_error_cause(&rc), tmp); + } + return rc; +} + +static int +qeth_arp_remove_entry(struct qeth_card *card, struct qeth_arp_cache_entry *entry) +{ + struct qeth_cmd_buffer *iob; + char buf[16] = {0, }; + int tmp; + int rc; + + QETH_DBF_TEXT(trace,3,"arprment"); + + /* TODO: really not supported by GuestLAN? */ + if (card->info.guestlan) + return -EOPNOTSUPP; + if (!qeth_is_supported(card,IPA_ARP_PROCESSING)) { + PRINT_WARN("ARP processing not supported " + "on %s!\n", card->info.if_name); + return -EOPNOTSUPP; + } + memcpy(buf, entry, 12); + iob = qeth_get_setassparms_cmd(card, IPA_ARP_PROCESSING, + IPA_CMD_ASS_ARP_REMOVE_ENTRY, + 12, + QETH_PROT_IPV4); + rc = qeth_send_setassparms(card, iob, + 12, (unsigned long)buf, + qeth_default_setassparms_cb, NULL); + if (rc) { + tmp = rc; + memset(buf, 0, 16); + qeth_ipaddr4_to_string((u8 *)entry->ipaddr, buf); + PRINT_WARN("Could not delete ARP entry for address %s on %s: " + "%s (0x%x)\n", + buf, card->info.if_name, + qeth_arp_get_error_cause(&rc), tmp); + } + return rc; +} + +static int +qeth_arp_flush_cache(struct qeth_card *card) +{ + int rc; + int tmp; + + QETH_DBF_TEXT(trace,3,"arpflush"); + + /* TODO: really not supported by GuestLAN? */ + if (card->info.guestlan) + return -EOPNOTSUPP; + if (!qeth_is_supported(card,IPA_ARP_PROCESSING)) { + PRINT_WARN("ARP processing not supported " + "on %s!\n", card->info.if_name); + return -EOPNOTSUPP; + } + rc = qeth_send_simple_setassparms(card, IPA_ARP_PROCESSING, + IPA_CMD_ASS_ARP_FLUSH_CACHE, 0); + if (rc){ + tmp = rc; + PRINT_WARN("Could not flush ARP cache on %s: %s (0x%x)\n", + card->info.if_name, qeth_arp_get_error_cause(&rc), + tmp); + } + return rc; +} + +static int +qeth_do_ioctl(struct net_device *dev, struct ifreq *rq, int cmd) +{ + struct qeth_card *card = (struct qeth_card *)dev->priv; + struct qeth_arp_cache_entry arp_entry; + struct mii_ioctl_data *mii_data; + int rc = 0; + + if (!card) + return -ENODEV; + + if ((card->state != CARD_STATE_UP_LAN_ONLINE) && + (card->state != CARD_STATE_UP_LAN_OFFLINE)) + return -ENODEV; + + switch (cmd){ + case SIOCDEVPRIVATE: + case SIOC_QETH_ARP_SET_NO_ENTRIES: + if (!capable(CAP_NET_ADMIN)){ + rc = -EPERM; + break; + } + rc = qeth_arp_set_no_entries(card, rq->ifr_ifru.ifru_ivalue); + break; + case SIOCDEVPRIVATE+1: + case SIOC_QETH_ARP_QUERY_INFO: + if (!capable(CAP_NET_ADMIN)){ + rc = -EPERM; + break; + } + rc = qeth_arp_query(card, rq->ifr_ifru.ifru_data); + break; + case SIOCDEVPRIVATE+2: + case SIOC_QETH_ARP_ADD_ENTRY: + if (!capable(CAP_NET_ADMIN)){ + rc = -EPERM; + break; + } + if (copy_from_user(&arp_entry, rq->ifr_ifru.ifru_data, + sizeof(struct qeth_arp_cache_entry))) + rc = -EFAULT; + else + rc = qeth_arp_add_entry(card, &arp_entry); + break; + case SIOCDEVPRIVATE+3: + case SIOC_QETH_ARP_REMOVE_ENTRY: + if (!capable(CAP_NET_ADMIN)){ + rc = -EPERM; + break; + } + if (copy_from_user(&arp_entry, rq->ifr_ifru.ifru_data, + sizeof(struct qeth_arp_cache_entry))) + rc = -EFAULT; + else + rc = qeth_arp_remove_entry(card, &arp_entry); + break; + case SIOCDEVPRIVATE+4: + case SIOC_QETH_ARP_FLUSH_CACHE: + if (!capable(CAP_NET_ADMIN)){ + rc = -EPERM; + break; + } + rc = qeth_arp_flush_cache(card); + break; + case SIOCDEVPRIVATE+5: + case SIOC_QETH_ADP_SET_SNMP_CONTROL: + break; + case SIOCDEVPRIVATE+6: + case SIOC_QETH_GET_CARD_TYPE: + break; + case SIOCGMIIPHY: + mii_data = (struct mii_ioctl_data *) &rq->ifr_ifru.ifru_data; + mii_data->phy_id = 0; + break; + case SIOCGMIIREG: + mii_data = (struct mii_ioctl_data *) &rq->ifr_ifru.ifru_data; + if (mii_data->phy_id != 0) + rc = -EINVAL; + else + mii_data->val_out = qeth_mdio_read(dev,mii_data->phy_id, + mii_data->reg_num); + break; + case SIOCSMIIREG: + rc = -EOPNOTSUPP; + break; + /* TODO: remove return if qeth_mdio_write does something */ + if (!capable(CAP_NET_ADMIN)){ + rc = -EPERM; + break; + } + mii_data = (struct mii_ioctl_data *) &rq->ifr_ifru.ifru_data; + if (mii_data->phy_id != 0) + rc = -EINVAL; + else + qeth_mdio_write(dev, mii_data->phy_id, mii_data->reg_num, + mii_data->val_in); + break; + default: + rc = -EOPNOTSUPP; + } + return rc; +} + +static struct net_device_stats * +qeth_get_stats(struct net_device *dev) +{ + struct qeth_card *card; + + card = (struct qeth_card *) (dev->priv); + + QETH_DBF_TEXT(trace,5,"getstat"); + + return &card->stats; +} + +static int +qeth_change_mtu(struct net_device *dev, int new_mtu) +{ + struct qeth_card *card; + char dbf_text[15]; + + card = (struct qeth_card *) (dev->priv); + + QETH_DBF_TEXT(trace,4,"chgmtu"); + sprintf(dbf_text, "%8x", new_mtu); + QETH_DBF_TEXT(trace,4,dbf_text); + + if (new_mtu < 64) + return -EINVAL; + if (new_mtu > 65535) + return -EINVAL; + if ((!qeth_is_supported(card,IPA_IP_FRAGMENTATION)) && + (!qeth_mtu_is_valid(card, new_mtu))) + return -EINVAL; + dev->mtu = new_mtu; + return 0; +} + +#ifdef CONFIG_QETH_VLAN +static void +qeth_vlan_rx_register(struct net_device *dev, struct vlan_group *grp) +{ + struct qeth_card *card; + + QETH_DBF_TEXT(trace,4,"vlanreg"); + + card = (struct qeth_card *) dev->priv; + spin_lock_irq(&card->vlanlock); + card->vlangrp = grp; + spin_unlock_irq(&card->vlanlock); +} + +static void +qeth_vlan_rx_kill_vid(struct net_device *dev, unsigned short vid) +{ + struct qeth_card *card; + + QETH_DBF_TEXT(trace,4,"vlkilvid"); + + card = (struct qeth_card *) dev->priv; + spin_lock_irq(&card->vlanlock); + if (card->vlangrp) + card->vlangrp->vlan_devices[vid] = NULL; + spin_unlock_irq(&card->vlanlock); + /* delete mc addresses for this vlan dev */ + qeth_set_thread_start_bit(card, QETH_SET_MC_THREAD); + schedule_work(&card->kernel_thread_starter); +} +#endif + +static int +qeth_neigh_setup(struct net_device *dev, struct neigh_parms *np) +{ + return 0; +} + +#ifdef CONFIG_QETH_IPV6 +int +qeth_ipv6_generate_eui64(u8 * eui, struct net_device *dev) +{ + switch (dev->type) { + case ARPHRD_ETHER: + case ARPHRD_FDDI: + case ARPHRD_IEEE802_TR: + if (dev->addr_len != ETH_ALEN) + return -1; + memcpy(eui, dev->dev_addr, 3); + memcpy(eui + 5, dev->dev_addr + 3, 3); + eui[3] = (dev->dev_id >> 8) & 0xff; + eui[4] = dev->dev_id & 0xff; + return 0; + } + return -1; + +} +#endif + +static void +qeth_get_mac_for_ipm(__u32 ipm, char *mac, struct net_device *dev) +{ + if (dev->type == ARPHRD_IEEE802_TR) + ip_tr_mc_map(ipm, mac); + else + ip_eth_mc_map(ipm, mac); +} + +static struct qeth_ipaddr * +qeth_get_addr_buffer(enum qeth_prot_versions prot) +{ + struct qeth_ipaddr *addr; + + addr = kmalloc(sizeof(struct qeth_ipaddr), GFP_ATOMIC); + if (addr == NULL) { + PRINT_WARN("Not enough memory to add address\n"); + return NULL; + } + memset(addr,0,sizeof(struct qeth_ipaddr)); + addr->type = QETH_IP_TYPE_NORMAL; + addr->proto = prot; + addr->is_multicast = 0; + addr->users = 0; + addr->set_flags = 0; + addr->del_flags = 0; + return addr; +} + +static void +qeth_delete_mc_addresses(struct qeth_card *card) +{ + struct qeth_ipaddr *ipm, *iptodo; + unsigned long flags; + + QETH_DBF_TEXT(trace,4,"delmc"); + spin_lock_irqsave(&card->ip_lock, flags); + list_for_each_entry(ipm, &card->ip_list, entry){ + if (!ipm->is_multicast) + continue; + iptodo = qeth_get_addr_buffer(ipm->proto); + memcpy(iptodo, ipm, sizeof(struct qeth_ipaddr)); + iptodo->users = iptodo->users * -1; + if (!__qeth_insert_ip_todo(card, iptodo, 0)) + kfree(iptodo); + } + spin_unlock_irqrestore(&card->ip_lock, flags); +} + +static inline void +qeth_add_mc(struct qeth_card *card, struct in_device *in4_dev) +{ + struct qeth_ipaddr *ipm; + struct ip_mc_list *im4; + char buf[MAX_ADDR_LEN]; + + QETH_DBF_TEXT(trace,4,"addmc"); + for (im4 = in4_dev->mc_list; im4; im4 = im4->next) { + qeth_get_mac_for_ipm(im4->multiaddr, buf, in4_dev->dev); + ipm = qeth_get_addr_buffer(QETH_PROT_IPV4); + if (!ipm) + continue; + ipm->u.a4.addr = im4->multiaddr; + memcpy(ipm->mac,buf,OSA_ADDR_LEN); + ipm->is_multicast = 1; + if (!qeth_add_ip(card,ipm)) + kfree(ipm); + } +} + +static inline void +qeth_add_vlan_mc(struct qeth_card *card) +{ +#ifdef CONFIG_QETH_VLAN + struct in_device *in_dev; + struct vlan_group *vg; + int i; + + QETH_DBF_TEXT(trace,4,"addmcvl"); + if (!qeth_is_supported(card,IPA_FULL_VLAN) || + (card->vlangrp == NULL)) + return ; + + vg = card->vlangrp; + for (i = 0; i < VLAN_GROUP_ARRAY_LEN; i++) { + if (vg->vlan_devices[i] == NULL || + !(vg->vlan_devices[i]->flags & IFF_UP)) + continue; + in_dev = in_dev_get(vg->vlan_devices[i]); + if (!in_dev) + continue; + read_lock(&in_dev->lock); + qeth_add_mc(card,in_dev); + read_unlock(&in_dev->lock); + in_dev_put(in_dev); + } +#endif +} + +static void +qeth_add_multicast_ipv4(struct qeth_card *card) +{ + struct in_device *in4_dev; + + QETH_DBF_TEXT(trace,4,"chkmcv4"); + in4_dev = in_dev_get(card->dev); + if (in4_dev == NULL) + return; + read_lock(&in4_dev->lock); + qeth_add_mc(card, in4_dev); + qeth_add_vlan_mc(card); + read_unlock(&in4_dev->lock); + in_dev_put(in4_dev); +} + +#ifdef CONFIG_QETH_IPV6 +static inline void +qeth_add_mc6(struct qeth_card *card, struct inet6_dev *in6_dev) +{ + struct qeth_ipaddr *ipm; + struct ifmcaddr6 *im6; + char buf[MAX_ADDR_LEN]; + + QETH_DBF_TEXT(trace,4,"addmc6"); + for (im6 = in6_dev->mc_list; im6 != NULL; im6 = im6->next) { + ndisc_mc_map(&im6->mca_addr, buf, in6_dev->dev, 0); + ipm = qeth_get_addr_buffer(QETH_PROT_IPV6); + if (!ipm) + continue; + ipm->is_multicast = 1; + memcpy(ipm->mac,buf,OSA_ADDR_LEN); + memcpy(&ipm->u.a6.addr,&im6->mca_addr.s6_addr, + sizeof(struct in6_addr)); + if (!qeth_add_ip(card,ipm)) + kfree(ipm); + } +} + +static inline void +qeth_add_vlan_mc6(struct qeth_card *card) +{ +#ifdef CONFIG_QETH_VLAN + struct inet6_dev *in_dev; + struct vlan_group *vg; + int i; + + QETH_DBF_TEXT(trace,4,"admc6vl"); + if (!qeth_is_supported(card,IPA_FULL_VLAN) || + (card->vlangrp == NULL)) + return ; + + vg = card->vlangrp; + for (i = 0; i < VLAN_GROUP_ARRAY_LEN; i++) { + if (vg->vlan_devices[i] == NULL || + !(vg->vlan_devices[i]->flags & IFF_UP)) + continue; + in_dev = in6_dev_get(vg->vlan_devices[i]); + if (!in_dev) + continue; + read_lock(&in_dev->lock); + qeth_add_mc6(card,in_dev); + read_unlock(&in_dev->lock); + in6_dev_put(in_dev); + } +#endif /* CONFIG_QETH_VLAN */ +} + +static void +qeth_add_multicast_ipv6(struct qeth_card *card) +{ + struct inet6_dev *in6_dev; + + QETH_DBF_TEXT(trace,4,"chkmcv6"); + if (!qeth_is_supported(card, IPA_IPV6)) + return ; + + in6_dev = in6_dev_get(card->dev); + if (in6_dev == NULL) + return; + read_lock(&in6_dev->lock); + qeth_add_mc6(card, in6_dev); + qeth_add_vlan_mc6(card); + read_unlock(&in6_dev->lock); + in6_dev_put(in6_dev); +} +#endif /* CONFIG_QETH_IPV6 */ + +/** + * set multicast address on card + */ +static void +qeth_set_multicast_list(struct net_device *dev) +{ + struct qeth_card *card; + + QETH_DBF_TEXT(trace,3,"setmulti"); + card = (struct qeth_card *) dev->priv; + + qeth_set_thread_start_bit(card, QETH_SET_MC_THREAD); + schedule_work(&card->kernel_thread_starter); +} + +static void +qeth_fill_ipacmd_header(struct qeth_card *card, struct qeth_ipa_cmd *cmd, + __u8 command, enum qeth_prot_versions prot) +{ + memset(cmd, 0, sizeof (struct qeth_ipa_cmd)); + cmd->hdr.command = command; + cmd->hdr.initiator = IPA_CMD_INITIATOR_HOST; + cmd->hdr.seqno = card->seqno.ipa; + cmd->hdr.adapter_type = qeth_get_ipa_adp_type(card->info.link_type); + cmd->hdr.rel_adapter_no = (__u8) card->info.portno; + cmd->hdr.prim_version_no = 1; + cmd->hdr.param_count = 1; + cmd->hdr.prot_version = prot; + cmd->hdr.ipa_supported = 0; + cmd->hdr.ipa_enabled = 0; +} + +static struct qeth_cmd_buffer * +qeth_get_ipacmd_buffer(struct qeth_card *card, enum qeth_ipa_cmds ipacmd, + enum qeth_prot_versions prot) +{ + struct qeth_cmd_buffer *iob; + struct qeth_ipa_cmd *cmd; + + iob = qeth_wait_for_buffer(&card->write); + cmd = (struct qeth_ipa_cmd *)(iob->data+IPA_PDU_HEADER_SIZE); + qeth_fill_ipacmd_header(card, cmd, ipacmd, prot); + + return iob; +} + +static int +qeth_send_setdelmc(struct qeth_card *card, struct qeth_ipaddr *addr, int ipacmd) +{ + int rc; + struct qeth_cmd_buffer *iob; + struct qeth_ipa_cmd *cmd; + + QETH_DBF_TEXT(trace,4,"setdelmc"); + + iob = qeth_get_ipacmd_buffer(card, ipacmd, addr->proto); + cmd = (struct qeth_ipa_cmd *)(iob->data+IPA_PDU_HEADER_SIZE); + memcpy(&cmd->data.setdelipm.mac,addr->mac, OSA_ADDR_LEN); + if (addr->proto == QETH_PROT_IPV6) + memcpy(cmd->data.setdelipm.ip6, &addr->u.a6.addr, + sizeof(struct in6_addr)); + else + memcpy(&cmd->data.setdelipm.ip4, &addr->u.a4.addr,4); + + rc = qeth_send_ipa_cmd(card, iob, NULL, NULL); + + return rc; +} +static inline void +qeth_fill_netmask(u8 *netmask, unsigned int 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] = (u8)(0xFF00>>j); + else + netmask[i] = 0; + } +} + +static int +qeth_send_setdelip(struct qeth_card *card, struct qeth_ipaddr *addr, + int ipacmd, unsigned int flags) +{ + int rc; + struct qeth_cmd_buffer *iob; + struct qeth_ipa_cmd *cmd; + __u8 netmask[16]; + + QETH_DBF_TEXT(trace,4,"setdelip"); + QETH_DBF_TEXT_(trace,4,"flags%02X", flags); + + iob = qeth_get_ipacmd_buffer(card, ipacmd, addr->proto); + cmd = (struct qeth_ipa_cmd *)(iob->data+IPA_PDU_HEADER_SIZE); + if (addr->proto == QETH_PROT_IPV6) { + memcpy(cmd->data.setdelip6.ip_addr, &addr->u.a6.addr, + sizeof(struct in6_addr)); + qeth_fill_netmask(netmask,addr->u.a6.pfxlen); + memcpy(cmd->data.setdelip6.mask, netmask, + sizeof(struct in6_addr)); + cmd->data.setdelip6.flags = flags; + } else { + memcpy(cmd->data.setdelip4.ip_addr, &addr->u.a4.addr, 4); + memcpy(cmd->data.setdelip4.mask, &addr->u.a4.mask, 4); + cmd->data.setdelip4.flags = flags; + } + + rc = qeth_send_ipa_cmd(card, iob, NULL, NULL); + + return rc; +} + +static int +qeth_register_addr_entry(struct qeth_card *card, struct qeth_ipaddr *addr) +{ + //char buf[50]; + int rc; + int cnt = 3; + + if (addr->proto == QETH_PROT_IPV4) { + QETH_DBF_TEXT(trace, 2,"setaddr4"); + QETH_DBF_HEX(trace, 4, &addr->u.a4.addr, sizeof(int)); + } else if (addr->proto == QETH_PROT_IPV6) { + QETH_DBF_TEXT(trace, 2, "setaddr6"); + QETH_DBF_HEX(trace,4,&addr->u.a6.addr,4); + QETH_DBF_HEX(trace,4,((char *)&addr->u.a6.addr)+4,4); + } else { + QETH_DBF_TEXT(trace, 2, "setaddr?"); + QETH_DBF_HEX(trace, 4, addr, sizeof(struct qeth_ipaddr)); + } + do { + if (addr->is_multicast) + rc = qeth_send_setdelmc(card, addr, IPA_CMD_SETIPM); + else + rc = qeth_send_setdelip(card, addr, IPA_CMD_SETIP, + addr->set_flags); + if (rc) + QETH_DBF_TEXT(trace, 2, "failed"); + } while ((--cnt > 0) && rc); + if (rc){ + QETH_DBF_TEXT(trace, 2, "FAILED"); + /* TODO: re-activate this warning as soon as we have a + * clean mirco code + qeth_ipaddr_to_string(addr->proto, (u8 *)&addr->u, buf); + PRINT_WARN("Could not register IP address %s (rc=%x)\n", + buf, rc); + */ + } + return rc; +} + +static int +qeth_deregister_addr_entry(struct qeth_card *card, struct qeth_ipaddr *addr) +{ + //char buf[50]; + int rc; + + if (addr->proto == QETH_PROT_IPV4) { + QETH_DBF_TEXT(trace, 2,"deladdr4"); + QETH_DBF_HEX(trace, 2, &addr->u.a4.addr, sizeof(int)); + } else if (addr->proto == QETH_PROT_IPV6) { + QETH_DBF_TEXT(trace, 2, "deladdr6"); + QETH_DBF_HEX(trace, 2, &addr->u.a6.addr, + sizeof(struct in6_addr)); + } else { + QETH_DBF_TEXT(trace, 2, "deladdr?"); + QETH_DBF_HEX(trace, 2, addr, sizeof(struct qeth_ipaddr)); + } + if (addr->is_multicast) + rc = qeth_send_setdelmc(card, addr, IPA_CMD_DELIPM); + else + rc = qeth_send_setdelip(card, addr, IPA_CMD_DELIP, + addr->del_flags); + if (rc) { + QETH_DBF_TEXT(trace, 2, "failed"); + /* TODO: re-activate this warning as soon as we have a + * clean mirco code + qeth_ipaddr_to_string(addr->proto, (u8 *)&addr->u, buf); + PRINT_WARN("Could not deregister IP address %s (rc=%x)\n", + buf, rc); + */ + } + return rc; +} + +static int +qeth_netdev_init(struct net_device *dev) +{ + struct qeth_card *card; + + card = (struct qeth_card *) dev->priv; + + QETH_DBF_TEXT(trace,3,"initdev"); + + dev->tx_timeout = &qeth_tx_timeout; + dev->watchdog_timeo = QETH_TX_TIMEOUT; + dev->open = qeth_open; + dev->stop = qeth_stop; + 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; + dev->neigh_setup = qeth_neigh_setup; + dev->set_multicast_list = qeth_set_multicast_list; +#ifdef CONFIG_QETH_VLAN + dev->vlan_rx_register = qeth_vlan_rx_register; + dev->vlan_rx_kill_vid = qeth_vlan_rx_kill_vid; +#endif + if (qeth_get_netdev_flags(card->info.type) & IFF_NOARP) { + dev->rebuild_header = NULL; + dev->hard_header = NULL; + dev->header_cache_update = NULL; + dev->hard_header_cache = NULL; + } +#ifdef CONFIG_QETH_IPV6 + /*IPv6 address autoconfiguration stuff*/ + card->dev->dev_id = card->info.unique_id & 0xffff; + if (!(card->info.unique_id & UNIQUE_ID_NOT_BY_CARD)) + card->dev->generate_eui64 = qeth_ipv6_generate_eui64; + + +#endif + dev->hard_header_parse = NULL; + dev->set_mac_address = NULL; + dev->flags |= qeth_get_netdev_flags(card->info.type); + if ((card->options.fake_broadcast) || + (card->info.broadcast_capable)) + dev->flags |= IFF_BROADCAST; + + dev->hard_header_len = + qeth_get_hlen(card->info.link_type) + card->options.add_hhlen; + dev->addr_len = OSA_ADDR_LEN; + dev->mtu = card->info.initial_mtu; + + SET_MODULE_OWNER(dev); + return 0; +} + +/** + * hardsetup card, initialize MPC and QDIO stuff + */ +static int +qeth_hardsetup_card(struct qeth_card *card) +{ + int retries = 3; + int rc; + + QETH_DBF_TEXT(setup, 2, "hrdsetup"); + +retry: + if (retries < 3){ + PRINT_WARN("Retrying to do IDX activates.\n"); + ccw_device_set_offline(CARD_DDEV(card)); + ccw_device_set_offline(CARD_WDEV(card)); + ccw_device_set_offline(CARD_RDEV(card)); + ccw_device_set_online(CARD_RDEV(card)); + ccw_device_set_online(CARD_WDEV(card)); + ccw_device_set_online(CARD_DDEV(card)); + } + rc = qeth_qdio_clear_card(card,card->info.type==QETH_CARD_TYPE_OSAE); + if (rc == -ERESTARTSYS) { + QETH_DBF_TEXT(setup, 2, "break1"); + return rc; + } else if (rc) { + QETH_DBF_TEXT_(setup, 2, "1err%d", rc); + if (--retries < 0) + goto out; + else + goto retry; + } + if ((rc = qeth_get_unitaddr(card))){ + QETH_DBF_TEXT_(setup, 2, "2err%d", rc); + return rc; + } + qeth_init_tokens(card); + rc = qeth_idx_activate_channel(&card->read, qeth_idx_read_cb); + if (rc == -ERESTARTSYS) { + QETH_DBF_TEXT(setup, 2, "break2"); + return rc; + } else if (rc) { + QETH_DBF_TEXT_(setup, 2, "3err%d", rc); + if (--retries < 0) + goto out; + else + goto retry; + } + rc = qeth_idx_activate_channel(&card->write, qeth_idx_write_cb); + if (rc == -ERESTARTSYS) { + QETH_DBF_TEXT(setup, 2, "break3"); + return rc; + } else if (rc) { + QETH_DBF_TEXT_(setup, 2, "4err%d", rc); + if (--retries < 0) + goto out; + else + goto retry; + } + if ((rc = qeth_mpc_initialize(card))){ + QETH_DBF_TEXT_(setup, 2, "5err%d", rc); + goto out; + } + /* at first set_online allocate netdev */ + if (!card->dev){ + card->dev = qeth_get_netdevice(card->info.type, + card->info.link_type); + if (!card->dev){ + qeth_qdio_clear_card(card, card->info.type == + QETH_CARD_TYPE_OSAE); + rc = -ENODEV; + QETH_DBF_TEXT_(setup, 2, "6err%d", rc); + goto out; + } + qeth_set_device_name(card); + card->dev->priv = card; + card->dev->type = qeth_get_arphdr_type(card->info.type, + card->info.link_type); + card->dev->init = qeth_netdev_init; + } + return 0; +out: + PRINT_ERR("Initialization in hardsetup failed! rc=%d\n", rc); + return rc; +} + +static struct qeth_cmd_buffer * +qeth_get_adapter_cmd(struct qeth_card *card, __u32 command) +{ + struct qeth_cmd_buffer *iob; + struct qeth_ipa_cmd *cmd; + + iob = qeth_get_ipacmd_buffer(card,IPA_CMD_SETADAPTERPARMS, + QETH_PROT_IPV4); + cmd = (struct qeth_ipa_cmd *)(iob->data+IPA_PDU_HEADER_SIZE); + cmd->data.setadapterparms.cmdlength = + sizeof(struct qeth_ipacmd_setadpparms); + cmd->data.setadapterparms.command_code = command; + cmd->data.setadapterparms.frames_used_total = 1; + cmd->data.setadapterparms.frame_seq_no = 1; + + return iob; +} + +static int +qeth_default_setassparms_cb(struct qeth_card *card, struct qeth_reply *reply, + unsigned long data) +{ + struct qeth_ipa_cmd *cmd; + + QETH_DBF_TEXT(trace,4,"defadpcb"); + + cmd = (struct qeth_ipa_cmd *) data; + if (cmd->hdr.return_code == 0){ + cmd->hdr.return_code = cmd->data.setassparms.hdr.return_code; + if (cmd->hdr.prot_version == QETH_PROT_IPV4) + card->options.ipa4.enabled_funcs = cmd->hdr.ipa_enabled; +#ifdef CONFIG_QETH_IPV6 + if (cmd->hdr.prot_version == QETH_PROT_IPV6) + card->options.ipa6.enabled_funcs = cmd->hdr.ipa_enabled; +#endif + } + if (cmd->data.setassparms.hdr.assist_no == IPA_INBOUND_CHECKSUM && + cmd->data.setassparms.hdr.command_code == IPA_CMD_ASS_START) { + card->info.csum_mask = cmd->data.setassparms.data.flags_32bit; + QETH_DBF_TEXT_(trace, 3, "csum:%d", card->info.csum_mask); + } + return 0; +} + +static int +qeth_default_setadapterparms_cb(struct qeth_card *card, + struct qeth_reply *reply, + unsigned long data) +{ + struct qeth_ipa_cmd *cmd; + + QETH_DBF_TEXT(trace,4,"defadpcb"); + + cmd = (struct qeth_ipa_cmd *) data; + if (cmd->hdr.return_code == 0) + cmd->hdr.return_code = cmd->data.setadapterparms.return_code; + return 0; +} + +static int +qeth_query_setadapterparms_cb(struct qeth_card *card, struct qeth_reply *reply, + unsigned long data) +{ + struct qeth_ipa_cmd *cmd; + + QETH_DBF_TEXT(trace,3,"quyadpcb"); + + cmd = (struct qeth_ipa_cmd *) data; + if (cmd->data.setadapterparms.data.query_cmds_supp.lan_type & 0x7f) + card->info.link_type = + cmd->data.setadapterparms.data.query_cmds_supp.lan_type; + card->options.adp.supported_funcs = + cmd->data.setadapterparms.data.query_cmds_supp.supported_cmds; + return qeth_default_setadapterparms_cb(card, reply, (unsigned long)cmd); +} + +static int +qeth_query_setadapterparms(struct qeth_card *card) +{ + int rc; + struct qeth_cmd_buffer *iob; + + QETH_DBF_TEXT(trace,3,"queryadp"); + iob = qeth_get_adapter_cmd(card,IPA_SETADP_QUERY_COMMANDS_SUPPORTED); + rc = qeth_send_ipa_cmd(card, iob, qeth_query_setadapterparms_cb, NULL); + return rc; +} + +static int +qeth_setadpparms_change_macaddr_cb(struct qeth_card *card, + struct qeth_reply *reply, + unsigned long data) +{ + struct qeth_ipa_cmd *cmd; + + QETH_DBF_TEXT(trace,4,"chgmaccb"); + + cmd = (struct qeth_ipa_cmd *) data; + memcpy(card->dev->dev_addr, + &cmd->data.setadapterparms.data.change_addr.addr,OSA_ADDR_LEN); + qeth_default_setadapterparms_cb(card, reply, (unsigned long) cmd); + return 0; +} + +static int +qeth_setadpparms_change_macaddr(struct qeth_card *card) +{ + int rc; + struct qeth_cmd_buffer *iob; + struct qeth_ipa_cmd *cmd; + + QETH_DBF_TEXT(trace,4,"chgmac"); + + iob = qeth_get_adapter_cmd(card,IPA_SETADP_ALTER_MAC_ADDRESS); + cmd = (struct qeth_ipa_cmd *)(iob->data+IPA_PDU_HEADER_SIZE); + cmd->data.setadapterparms.data.change_addr.cmd = CHANGE_ADDR_READ_MAC; + cmd->data.setadapterparms.data.change_addr.addr_size = OSA_ADDR_LEN; + memcpy(&cmd->data.setadapterparms.data.change_addr.addr, + card->dev->dev_addr, OSA_ADDR_LEN); + rc = qeth_send_ipa_cmd(card, iob, qeth_setadpparms_change_macaddr_cb, + NULL); + return rc; +} + +static int +qeth_send_setadp_mode(struct qeth_card *card, __u32 command, __u32 mode) +{ + int rc; + struct qeth_cmd_buffer *iob; + struct qeth_ipa_cmd *cmd; + + QETH_DBF_TEXT(trace,4,"adpmode"); + + iob = qeth_get_adapter_cmd(card, command); + cmd = (struct qeth_ipa_cmd *)(iob->data+IPA_PDU_HEADER_SIZE); + cmd->data.setadapterparms.data.mode = mode; + rc = qeth_send_ipa_cmd(card, iob, qeth_default_setadapterparms_cb, + NULL); + return rc; +} + +static inline int +qeth_setadapter_hstr(struct qeth_card *card) +{ + int rc; + + QETH_DBF_TEXT(trace,4,"adphstr"); + + if (qeth_adp_supported(card,IPA_SETADP_SET_BROADCAST_MODE)) { + rc = qeth_send_setadp_mode(card, IPA_SETADP_SET_BROADCAST_MODE, + card->options.broadcast_mode); + if (rc) + PRINT_WARN("couldn't set broadcast mode on " + "device %s: x%x\n", + CARD_BUS_ID(card), rc); + rc = qeth_send_setadp_mode(card, IPA_SETADP_ALTER_MAC_ADDRESS, + card->options.macaddr_mode); + if (rc) + PRINT_WARN("couldn't set macaddr mode on " + "device %s: x%x\n", CARD_BUS_ID(card), rc); + return rc; + } + if (card->options.broadcast_mode == QETH_TR_BROADCAST_LOCAL) + PRINT_WARN("set adapter parameters not available " + "to set broadcast mode, using ALLRINGS " + "on device %s:\n", CARD_BUS_ID(card)); + if (card->options.macaddr_mode == QETH_TR_MACADDR_CANONICAL) + PRINT_WARN("set adapter parameters not available " + "to set macaddr mode, using NONCANONICAL " + "on device %s:\n", CARD_BUS_ID(card)); + return 0; +} + +static int +qeth_setadapter_parms(struct qeth_card *card) +{ + int rc; + + QETH_DBF_TEXT(setup, 2, "setadprm"); + + if (!qeth_is_supported(card, IPA_SETADAPTERPARMS)){ + PRINT_WARN("set adapter parameters not supported " + "on device %s.\n", + CARD_BUS_ID(card)); + QETH_DBF_TEXT(setup, 2, " notsupp"); + return 0; + } + rc = qeth_query_setadapterparms(card); + if (rc) { + PRINT_WARN("couldn't set adapter parameters on device %s: " + "x%x\n", CARD_BUS_ID(card), rc); + return rc; + } + if (qeth_adp_supported(card,IPA_SETADP_ALTER_MAC_ADDRESS)) { + rc = qeth_setadpparms_change_macaddr(card); + if (rc) + PRINT_WARN("couldn't get MAC address on " + "device %s: x%x\n", + CARD_BUS_ID(card), rc); + } + + if ((card->info.link_type == QETH_LINK_TYPE_HSTR) || + (card->info.link_type == QETH_LINK_TYPE_LANE_TR)) + rc = qeth_setadapter_hstr(card); + + return rc; +} + + +static int +qeth_send_startstoplan(struct qeth_card *card, enum qeth_ipa_cmds ipacmd, + enum qeth_prot_versions prot) +{ + int rc; + struct qeth_cmd_buffer *iob; + + iob = qeth_get_ipacmd_buffer(card,ipacmd,prot); + rc = qeth_send_ipa_cmd(card, iob, NULL, NULL); + + return rc; +} + +static int +qeth_send_startlan(struct qeth_card *card, enum qeth_prot_versions prot) +{ + int rc; + + QETH_DBF_TEXT_(setup, 2, "strtlan%i", prot); + + rc = qeth_send_startstoplan(card, IPA_CMD_STARTLAN, prot); + return rc; +} + +static int +qeth_send_stoplan(struct qeth_card *card) +{ + int rc = 0; + + /* + * TODO: according to the IPA format document page 14, + * TCP/IP (we!) never issue a STOPLAN + * is this right ?!? + */ + QETH_DBF_TEXT(trace, 2, "stoplan"); + + rc = qeth_send_startstoplan(card, IPA_CMD_STOPLAN, QETH_PROT_IPV4); + return rc; +} + +static int +qeth_query_ipassists_cb(struct qeth_card *card, struct qeth_reply *reply, + unsigned long data) +{ + struct qeth_ipa_cmd *cmd; + + QETH_DBF_TEXT(setup, 2, "qipasscb"); + + cmd = (struct qeth_ipa_cmd *) data; + if (cmd->hdr.prot_version == QETH_PROT_IPV4) { + card->options.ipa4.supported_funcs = cmd->hdr.ipa_supported; + card->options.ipa4.enabled_funcs = cmd->hdr.ipa_enabled; + } else { +#ifdef CONFIG_QETH_IPV6 + card->options.ipa6.supported_funcs = cmd->hdr.ipa_supported; + card->options.ipa6.enabled_funcs = cmd->hdr.ipa_enabled; +#endif + } + return 0; +} + +static int +qeth_query_ipassists(struct qeth_card *card, enum qeth_prot_versions prot) +{ + int rc; + struct qeth_cmd_buffer *iob; + + QETH_DBF_TEXT_(setup, 2, "qipassi%i", prot); + + iob = qeth_get_ipacmd_buffer(card,IPA_CMD_QIPASSIST,prot); + rc = qeth_send_ipa_cmd(card, iob, qeth_query_ipassists_cb, NULL); + return rc; +} + +static struct qeth_cmd_buffer * +qeth_get_setassparms_cmd(struct qeth_card *card, enum qeth_ipa_funcs ipa_func, + __u16 cmd_code, __u16 len, + enum qeth_prot_versions prot) +{ + struct qeth_cmd_buffer *iob; + struct qeth_ipa_cmd *cmd; + + QETH_DBF_TEXT(trace,4,"getasscm"); + iob = qeth_get_ipacmd_buffer(card,IPA_CMD_SETASSPARMS,prot); + + cmd = (struct qeth_ipa_cmd *)(iob->data+IPA_PDU_HEADER_SIZE); + cmd->data.setassparms.hdr.assist_no = ipa_func; + cmd->data.setassparms.hdr.length = 8 + len; + cmd->data.setassparms.hdr.command_code = cmd_code; + cmd->data.setassparms.hdr.return_code = 0; + cmd->data.setassparms.hdr.seq_no = 0; + + return iob; +} + +static int +qeth_send_setassparms(struct qeth_card *card, struct qeth_cmd_buffer *iob, + __u16 len, long data, + int (*reply_cb) + (struct qeth_card *,struct qeth_reply *,unsigned long), + void *reply_param) +{ + int rc; + struct qeth_ipa_cmd *cmd; + + QETH_DBF_TEXT(trace,4,"sendassp"); + + cmd = (struct qeth_ipa_cmd *)(iob->data+IPA_PDU_HEADER_SIZE); + if (len <= sizeof(__u32)) + cmd->data.setassparms.data.flags_32bit = (__u32) data; + else if (len > sizeof(__u32)) + memcpy(&cmd->data.setassparms.data, (void *) data, len); + + rc = qeth_send_ipa_cmd(card, iob, reply_cb, reply_param); + return rc; +} + +#ifdef CONFIG_QETH_IPV6 +static int +qeth_send_simple_setassparms_ipv6(struct qeth_card *card, + enum qeth_ipa_funcs ipa_func, __u16 cmd_code) + +{ + int rc; + struct qeth_cmd_buffer *iob; + + QETH_DBF_TEXT(trace,4,"simassp6"); + iob = qeth_get_setassparms_cmd(card, ipa_func, cmd_code, + 0, QETH_PROT_IPV6); + rc = qeth_send_setassparms(card, iob, 0, 0, + qeth_default_setassparms_cb, NULL); + return rc; +} +#endif + +static int +qeth_send_simple_setassparms(struct qeth_card *card, + enum qeth_ipa_funcs ipa_func, + __u16 cmd_code, long data) +{ + int rc; + int length = 0; + struct qeth_cmd_buffer *iob; + + QETH_DBF_TEXT(trace,4,"simassp4"); + if (data) + length = sizeof(__u32); + iob = qeth_get_setassparms_cmd(card, ipa_func, cmd_code, + length, QETH_PROT_IPV4); + rc = qeth_send_setassparms(card, iob, length, data, + qeth_default_setassparms_cb, NULL); + return rc; +} + +static inline int +qeth_start_ipa_arp_processing(struct qeth_card *card) +{ + int rc; + + QETH_DBF_TEXT(trace,3,"ipaarp"); + + if (!qeth_is_supported(card,IPA_ARP_PROCESSING)) { + PRINT_WARN("ARP processing not supported " + "on %s!\n", card->info.if_name); + return 0; + } + rc = qeth_send_simple_setassparms(card,IPA_ARP_PROCESSING, + IPA_CMD_ASS_START, 0); + if (rc) { + PRINT_WARN("Could not start ARP processing " + "assist on %s: 0x%x\n", + card->info.if_name, rc); + } + return rc; +} + +static int +qeth_start_ipa_ip_fragmentation(struct qeth_card *card) +{ + int rc; + + QETH_DBF_TEXT(trace,3,"ipaipfrg"); + + if (!qeth_is_supported(card, IPA_IP_FRAGMENTATION)) { + PRINT_INFO("IP fragmentation not supported on %s\n", + card->info.if_name); + return -EOPNOTSUPP; + } + + rc = qeth_send_simple_setassparms(card, IPA_IP_FRAGMENTATION, + IPA_CMD_ASS_START, 0); + if (rc) { + PRINT_WARN("Could not start IP fragmentation " + "assist on %s: 0x%x\n", + card->info.if_name, rc); + } else + PRINT_INFO("IP fragmentation enabled \n"); + return rc; +} + +static int +qeth_start_ipa_source_mac(struct qeth_card *card) +{ + int rc; + + QETH_DBF_TEXT(trace,3,"stsrcmac"); + + if (!card->options.fake_ll) + return -EOPNOTSUPP; + + if (!qeth_is_supported(card, IPA_SOURCE_MAC)) { + PRINT_INFO("Inbound source address not " + "supported on %s\n", card->info.if_name); + return -EOPNOTSUPP; + } + + rc = qeth_send_simple_setassparms(card, IPA_SOURCE_MAC, + IPA_CMD_ASS_START, 0); + if (rc) + PRINT_WARN("Could not start inbound source " + "assist on %s: 0x%x\n", + card->info.if_name, rc); + return rc; +} + +static int +qeth_start_ipa_vlan(struct qeth_card *card) +{ + int rc = 0; + + QETH_DBF_TEXT(trace,3,"strtvlan"); + +#ifdef CONFIG_QETH_VLAN + if (!qeth_is_supported(card, IPA_FULL_VLAN)) { + PRINT_WARN("VLAN not supported on %s\n", card->info.if_name); + return -EOPNOTSUPP; + } + + rc = qeth_send_simple_setassparms(card, IPA_VLAN_PRIO, + IPA_CMD_ASS_START,0); + if (rc) { + PRINT_WARN("Could not start vlan " + "assist on %s: 0x%x\n", + card->info.if_name, rc); + } else { + PRINT_INFO("VLAN enabled \n"); + card->dev->features |= NETIF_F_HW_VLAN_TX | NETIF_F_HW_VLAN_RX; + } +#endif /* QETH_VLAN */ + return rc; +} + +static int +qeth_start_ipa_multicast(struct qeth_card *card) +{ + int rc; + + QETH_DBF_TEXT(trace,3,"stmcast"); + + if (!qeth_is_supported(card, IPA_MULTICASTING)) { + PRINT_WARN("Multicast not supported on %s\n", + card->info.if_name); + return -EOPNOTSUPP; + } + + rc = qeth_send_simple_setassparms(card, IPA_MULTICASTING, + IPA_CMD_ASS_START,0); + if (rc) { + PRINT_WARN("Could not start multicast " + "assist on %s: rc=%i\n", + card->info.if_name, rc); + } else { + PRINT_INFO("Multicast enabled\n"); + card->dev->flags |= IFF_MULTICAST; + } + return rc; +} + +#ifdef CONFIG_QETH_IPV6 +static int +qeth_softsetup_ipv6(struct qeth_card *card) +{ + int rc; + + QETH_DBF_TEXT(trace,3,"softipv6"); + + netif_stop_queue(card->dev); + rc = qeth_send_startlan(card, QETH_PROT_IPV6); + if (rc) { + PRINT_ERR("IPv6 startlan failed on %s\n", + card->info.if_name); + return rc; + } + netif_wake_queue(card->dev); + rc = qeth_query_ipassists(card,QETH_PROT_IPV6); + if (rc) { + PRINT_ERR("IPv6 query ipassist failed on %s\n", + card->info.if_name); + return rc; + } + rc = qeth_send_simple_setassparms(card, IPA_IPV6, + IPA_CMD_ASS_START, 3); + if (rc) { + PRINT_WARN("IPv6 start assist (version 4) failed " + "on %s: 0x%x\n", + card->info.if_name, rc); + return rc; + } + rc = qeth_send_simple_setassparms_ipv6(card, IPA_IPV6, + IPA_CMD_ASS_START); + if (rc) { + PRINT_WARN("IPV6 start assist (version 6) failed " + "on %s: 0x%x\n", + card->info.if_name, rc); + return rc; + } + rc = qeth_send_simple_setassparms_ipv6(card, IPA_PASSTHRU, + IPA_CMD_ASS_START); + if (rc) { + PRINT_WARN("Could not enable passthrough " + "on %s: 0x%x\n", + card->info.if_name, rc); + return rc; + } + PRINT_INFO("IPV6 enabled \n"); + return 0; +} + +#endif + +static int +qeth_start_ipa_ipv6(struct qeth_card *card) +{ + int rc = 0; +#ifdef CONFIG_QETH_IPV6 + QETH_DBF_TEXT(trace,3,"strtipv6"); + + if (!qeth_is_supported(card, IPA_IPV6)) { + PRINT_WARN("IPv6 not supported on %s\n", + card->info.if_name); + return 0; + } + rc = qeth_softsetup_ipv6(card); +#endif + return rc ; +} + +static int +qeth_start_ipa_broadcast(struct qeth_card *card) +{ + int rc; + + QETH_DBF_TEXT(trace,3,"stbrdcst"); + if (!qeth_is_supported(card, IPA_FILTERING)) { + PRINT_WARN("Broadcast not supported on %s\n", + card->info.if_name); + return -EOPNOTSUPP; + } + rc = qeth_send_simple_setassparms(card, IPA_FILTERING, + IPA_CMD_ASS_START, 0); + if (rc) { + PRINT_WARN("Could not enable broadcasting " + "on %s: 0x%x\n", + card->info.if_name, rc); + return rc; + } + + rc = qeth_send_simple_setassparms(card, IPA_FILTERING, + IPA_CMD_ASS_CONFIGURE, 1); + if (rc) { + PRINT_WARN("Could not set up broadcast filtering on %s: 0x%x\n", + card->info.if_name, rc); + return rc; + } + PRINT_INFO("Broadcast enabled \n"); + card->dev->flags |= IFF_BROADCAST; + card->info.broadcast_capable = 1; + return 0; +} + +static int +qeth_send_checksum_command(struct qeth_card *card) +{ + int rc; + + rc = qeth_send_simple_setassparms(card, IPA_INBOUND_CHECKSUM, + IPA_CMD_ASS_START, 0); + if (rc) { + PRINT_WARN("Starting Inbound HW Checksumming failed on %s: " + "0x%x,\ncontinuing using Inbound SW Checksumming\n", + card->info.if_name, rc); + return rc; + } + rc = qeth_send_simple_setassparms(card, IPA_INBOUND_CHECKSUM, + IPA_CMD_ASS_ENABLE, + card->info.csum_mask); + if (rc) { + PRINT_WARN("Enabling Inbound HW Checksumming failed on %s: " + "0x%x,\ncontinuing using Inbound SW Checksumming\n", + card->info.if_name, rc); + return rc; + } + return 0; +} + +static int +qeth_start_ipa_checksum(struct qeth_card *card) +{ + int rc = 0; + + QETH_DBF_TEXT(trace,3,"strtcsum"); + + if (card->options.checksum_type == NO_CHECKSUMMING) { + PRINT_WARN("Using no checksumming on %s.\n", + card->info.if_name); + return 0; + } + if (card->options.checksum_type == SW_CHECKSUMMING) { + PRINT_WARN("Using SW checksumming on %s.\n", + card->info.if_name); + return 0; + } + if (!qeth_is_supported(card, IPA_INBOUND_CHECKSUM)) { + PRINT_WARN("Inbound HW Checksumming not " + "supported on %s,\ncontinuing " + "using Inbound SW Checksumming\n", + card->info.if_name); + card->options.checksum_type = SW_CHECKSUMMING; + return 0; + } + rc = qeth_send_checksum_command(card); + if (!rc) { + PRINT_INFO("HW Checksumming (inbound) enabled \n"); + } + return rc; +} + +/* +static inline void +qeth_print_ipassist_status(struct qeth_card *card) +{ + char buf[255]; + int offset = 0; + + offset += sprintf(buf, "IPAssist options of %s: ", card->info.if_name); + if (qeth_is_enabled(card, IPA_ARP_PROCESSING)) + offset += sprintf(buf+offset, "ARP "); + if (qeth_is_enabled(card, IPA_IP_FRAGMENTATION)) + offset += sprintf(buf+offset, "IP_FRAG"); + if (qeth_is_enabled(card, IPA_SOURCE_MAC)) + offset += sprintf(buf+offset, "SRC_MAC"); + if (qeth_is_enabled(card, IPA_FULL_VLAN)) + offset += sprintf(buf+offset, "VLAN"); + if (qeth_is_enabled(card, IPA_VLAN_PRIO)) + offset += sprintf(buf+offset, "VLAN_PRIO"); +} +*/ + +static int +qeth_start_ipassists(struct qeth_card *card) +{ + QETH_DBF_TEXT(trace,3,"strtipas"); + qeth_start_ipa_arp_processing(card); /* go on*/ + qeth_start_ipa_ip_fragmentation(card); /* go on*/ + qeth_start_ipa_source_mac(card); /* go on*/ + qeth_start_ipa_vlan(card); /* go on*/ + qeth_start_ipa_multicast(card); /* go on*/ + qeth_start_ipa_ipv6(card); /* go on*/ + qeth_start_ipa_broadcast(card); /* go on*/ + qeth_start_ipa_checksum(card); /* go on*/ + return 0; +} + +static int +qeth_send_setrouting(struct qeth_card *card, enum qeth_routing_types type, + enum qeth_prot_versions prot) +{ + int rc; + struct qeth_ipa_cmd *cmd; + struct qeth_cmd_buffer *iob; + + QETH_DBF_TEXT(trace,4,"setroutg"); + iob = qeth_get_ipacmd_buffer(card, IPA_CMD_SETRTG, prot); + cmd = (struct qeth_ipa_cmd *)(iob->data+IPA_PDU_HEADER_SIZE); + cmd->data.setrtg.type = (type); + rc = qeth_send_ipa_cmd(card, iob, NULL, NULL); + + return rc; + +} + +int +qeth_setrouting_v4(struct qeth_card *card) +{ + int rc; + + QETH_DBF_TEXT(trace,3,"setrtg4"); + + if (card->options.route4.type == NO_ROUTER) + return 0; + + rc = qeth_send_setrouting(card, card->options.route4.type, + QETH_PROT_IPV4); + if (rc) { + card->options.route4.type = NO_ROUTER; + PRINT_WARN("Error (0x%04x) while setting routing type on %s. " + "Type set to 'no router'.\n", + rc, card->info.if_name); + } + return rc; +} + +int +qeth_setrouting_v6(struct qeth_card *card) +{ + int rc = 0; + + QETH_DBF_TEXT(trace,3,"setrtg6"); +#ifdef CONFIG_QETH_IPV6 + + if ((card->options.route6.type == NO_ROUTER) || + ((card->info.type == QETH_CARD_TYPE_OSAE) && + (card->options.route6.type == MULTICAST_ROUTER) && + !qeth_is_supported6(card,IPA_OSA_MC_ROUTER))) + return 0; + rc = qeth_send_setrouting(card, card->options.route6.type, + QETH_PROT_IPV6); + if (rc) { + card->options.route6.type = NO_ROUTER; + PRINT_WARN("Error (0x%04x) while setting routing type on %s. " + "Type set to 'no router'.\n", + rc, card->info.if_name); + } +#endif + return rc; +} + +/* + * softsetup card: init IPA stuff + */ +static int +qeth_softsetup_card(struct qeth_card *card) +{ + int rc; + + QETH_DBF_TEXT(setup, 2, "softsetp"); + + if ((rc = qeth_send_startlan(card, QETH_PROT_IPV4))){ + QETH_DBF_TEXT_(setup, 2, "1err%d", rc); + if (rc == 0xe080){ + PRINT_WARN("LAN on card %s if offline! " + "Continuing softsetup.\n", + CARD_BUS_ID(card)); + card->lan_online = 0; + } else + return rc; + } else + card->lan_online = 1; + if ((rc = qeth_setadapter_parms(card))) + QETH_DBF_TEXT_(setup, 2, "2err%d", rc); + if ((rc = qeth_start_ipassists(card))) + QETH_DBF_TEXT_(setup, 2, "3err%d", rc); + if ((rc = qeth_setrouting_v4(card))) + QETH_DBF_TEXT_(setup, 2, "4err%d", rc); + if ((rc = qeth_setrouting_v6(card))) + QETH_DBF_TEXT_(setup, 2, "5err%d", rc); + netif_stop_queue(card->dev); + return 0; +} + +#ifdef CONFIG_QETH_IPV6 +static int +qeth_get_unique_id_cb(struct qeth_card *card, struct qeth_reply *reply, + unsigned long data) +{ + struct qeth_ipa_cmd *cmd; + + cmd = (struct qeth_ipa_cmd *) data; + if (cmd->hdr.return_code == 0) + card->info.unique_id = *((__u16 *) + &cmd->data.create_destroy_addr.unique_id[6]); + else { + card->info.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 device " + "%s (result=x%x), using default id. ipv6 " + "autoconfig on other lpars may lead to duplicate " + "ip addresses. please use manually " + "configured ones.\n", + CARD_BUS_ID(card), cmd->hdr.return_code); + } + return 0; +} +#endif + +static int +qeth_put_unique_id(struct qeth_card *card) +{ + + int rc = 0; +#ifdef CONFIG_QETH_IPV6 + struct qeth_cmd_buffer *iob; + struct qeth_ipa_cmd *cmd; + + QETH_DBF_TEXT(trace,2,"puniqeid"); + + if ((card->info.unique_id & UNIQUE_ID_NOT_BY_CARD) == + UNIQUE_ID_NOT_BY_CARD) + return -1; + iob = qeth_get_ipacmd_buffer(card, IPA_CMD_DESTROY_ADDR, + QETH_PROT_IPV6); + cmd = (struct qeth_ipa_cmd *)(iob->data+IPA_PDU_HEADER_SIZE); + *((__u16 *) &cmd->data.create_destroy_addr.unique_id[6]) = + card->info.unique_id; + memcpy(&cmd->data.create_destroy_addr.unique_id[0], + card->dev->dev_addr, OSA_ADDR_LEN); + rc = qeth_send_ipa_cmd(card, iob, NULL, NULL); +#else + card->info.unique_id = UNIQUE_ID_IF_CREATE_ADDR_FAILED | + UNIQUE_ID_NOT_BY_CARD; +#endif + return rc; +} + +/** + * Clear IP List + */ +static void +qeth_clear_ip_list(struct qeth_card *card, int clean, int recover) +{ + struct qeth_ipaddr *addr, *tmp; + int first_run = 1; + unsigned long flags; + + QETH_DBF_TEXT(trace,4,"clearip"); + spin_lock_irqsave(&card->ip_lock, flags); + /* clear todo list */ + list_for_each_entry_safe(addr, tmp, &card->ip_tbd_list, entry){ + list_del(&addr->entry); + kfree(addr); + } +again: + if (first_run) + first_run = 0; + else + spin_lock_irqsave(&card->ip_lock, flags); + + list_for_each_entry_safe(addr, tmp, &card->ip_list, entry) { + list_del_init(&addr->entry); + if (clean){ + spin_unlock_irqrestore(&card->ip_lock, flags); + qeth_deregister_addr_entry(card, addr); + } + if (!recover || addr->is_multicast) + kfree(addr); + else { + if (clean) + spin_lock_irqsave(&card->ip_lock, flags); + list_add_tail(&addr->entry, &card->ip_tbd_list); + if (clean) { + spin_unlock_irqrestore(&card->ip_lock, flags); + goto again; + } + } + } + spin_unlock_irqrestore(&card->ip_lock, flags); +} + +static void +qeth_set_allowed_threads(struct qeth_card *card, unsigned long threads, + int clear_start_mask) +{ + unsigned long flags; + + spin_lock_irqsave(&card->thread_mask_lock, flags); + card->thread_allowed_mask = threads; + if (clear_start_mask) + card->thread_start_mask &= threads; + spin_unlock_irqrestore(&card->thread_mask_lock, flags); + wake_up(&card->wait_q); +} + +static inline int +qeth_threads_running(struct qeth_card *card, unsigned long threads) +{ + unsigned long flags; + int rc = 0; + + spin_lock_irqsave(&card->thread_mask_lock, flags); + rc = (card->thread_running_mask & threads); + spin_unlock_irqrestore(&card->thread_mask_lock, flags); + return rc; +} + +static int +qeth_wait_for_threads(struct qeth_card *card, unsigned long threads) +{ + return wait_event_interruptible(card->wait_q, + qeth_threads_running(card, threads) == 0); +} + +static int +qeth_stop_card(struct qeth_card *card) +{ + int recover_flag = 0; + int rc = 0; + + QETH_DBF_TEXT(setup ,2,"stopcard"); + QETH_DBF_HEX(setup, 2, &card, sizeof(void *)); + + qeth_set_allowed_threads(card, 0, 1); + if (qeth_wait_for_threads(card, ~QETH_RECOVER_THREAD)) + return -ERESTARTSYS; + if (card->read.state == CH_STATE_UP && + card->write.state == CH_STATE_UP && + ((card->state == CARD_STATE_UP_LAN_ONLINE) || + (card->state == CARD_STATE_UP_LAN_OFFLINE))) { + recover_flag = 1; + rtnl_lock(); + dev_close(card->dev); + rtnl_unlock(); + if (!card->use_hard_stop) + if ((rc = qeth_send_stoplan(card))) + QETH_DBF_TEXT_(setup, 2, "1err%d", rc); + card->state = CARD_STATE_SOFTSETUP; + } + if (card->state == CARD_STATE_SOFTSETUP) { + qeth_clear_ip_list(card, !card->use_hard_stop, recover_flag); + qeth_clear_ipacmd_list(card); + card->state = CARD_STATE_HARDSETUP; + } + if (card->state == CARD_STATE_HARDSETUP) { + if (!card->use_hard_stop) + if ((rc = qeth_put_unique_id(card))) + QETH_DBF_TEXT_(setup, 2, "2err%d", rc); + qeth_qdio_clear_card(card, 0); + qeth_clear_qdio_buffers(card); + qeth_clear_working_pool_list(card); + card->state = CARD_STATE_DOWN; + } + if (card->state == CARD_STATE_DOWN) { + qeth_clear_cmd_buffers(&card->read); + qeth_clear_cmd_buffers(&card->write); + } + card->use_hard_stop = 0; + return rc; +} + + +static int +qeth_get_unique_id(struct qeth_card *card) +{ + int rc = 0; +#ifdef CONFIG_QETH_IPV6 + struct qeth_cmd_buffer *iob; + struct qeth_ipa_cmd *cmd; + + QETH_DBF_TEXT(setup, 2, "guniqeid"); + + if (!qeth_is_supported(card,IPA_IPV6)) { + card->info.unique_id = UNIQUE_ID_IF_CREATE_ADDR_FAILED | + UNIQUE_ID_NOT_BY_CARD; + return 0; + } + + iob = qeth_get_ipacmd_buffer(card, IPA_CMD_CREATE_ADDR, + QETH_PROT_IPV6); + cmd = (struct qeth_ipa_cmd *)(iob->data+IPA_PDU_HEADER_SIZE); + *((__u16 *) &cmd->data.create_destroy_addr.unique_id[6]) = + card->info.unique_id; + + rc = qeth_send_ipa_cmd(card, iob, qeth_get_unique_id_cb, NULL); +#else + card->info.unique_id = UNIQUE_ID_IF_CREATE_ADDR_FAILED | + UNIQUE_ID_NOT_BY_CARD; +#endif + return rc; +} +static void +qeth_print_status_with_portname(struct qeth_card *card) +{ + char dbf_text[15]; + int i; + + sprintf(dbf_text, "%s", card->info.portname + 1); + for (i = 0; i < 8; i++) + dbf_text[i] = + (char) _ebcasc[(__u8) dbf_text[i]]; + dbf_text[8] = 0; + printk("qeth: Device %s/%s/%s is a%s card%s%s%s\n" + "with link type %s (portname: %s)\n", + CARD_RDEV_ID(card), + CARD_WDEV_ID(card), + CARD_DDEV_ID(card), + qeth_get_cardname(card), + (card->info.mcl_level[0]) ? " (level: " : "", + (card->info.mcl_level[0]) ? card->info.mcl_level : "", + (card->info.mcl_level[0]) ? ")" : "", + qeth_get_cardname_short(card), + dbf_text); + +} + +static void +qeth_print_status_no_portname(struct qeth_card *card) +{ + if (card->info.portname[0]) + printk("qeth: Device %s/%s/%s is a%s " + "card%s%s%s\nwith link type %s " + "(no portname needed by interface).\n", + CARD_RDEV_ID(card), + CARD_WDEV_ID(card), + CARD_DDEV_ID(card), + qeth_get_cardname(card), + (card->info.mcl_level[0]) ? " (level: " : "", + (card->info.mcl_level[0]) ? card->info.mcl_level : "", + (card->info.mcl_level[0]) ? ")" : "", + qeth_get_cardname_short(card)); + else + printk("qeth: Device %s/%s/%s is a%s " + "card%s%s%s\nwith link type %s.\n", + CARD_RDEV_ID(card), + CARD_WDEV_ID(card), + CARD_DDEV_ID(card), + qeth_get_cardname(card), + (card->info.mcl_level[0]) ? " (level: " : "", + (card->info.mcl_level[0]) ? card->info.mcl_level : "", + (card->info.mcl_level[0]) ? ")" : "", + qeth_get_cardname_short(card)); +} + +static void +qeth_print_status_message(struct qeth_card *card) +{ + switch (card->info.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->info.mcl_level[0]) { + sprintf(card->info.mcl_level,"%02x%02x", + card->info.mcl_level[2], + card->info.mcl_level[3]); + + card->info.mcl_level[QETH_MCL_LENGTH] = 0; + break; + } + /* fallthrough */ + case QETH_CARD_TYPE_IQD: + card->info.mcl_level[0] = (char) _ebcasc[(__u8) + card->info.mcl_level[0]]; + card->info.mcl_level[1] = (char) _ebcasc[(__u8) + card->info.mcl_level[1]]; + card->info.mcl_level[2] = (char) _ebcasc[(__u8) + card->info.mcl_level[2]]; + card->info.mcl_level[3] = (char) _ebcasc[(__u8) + card->info.mcl_level[3]]; + card->info.mcl_level[QETH_MCL_LENGTH] = 0; + break; + default: + memset(&card->info.mcl_level[0], 0, QETH_MCL_LENGTH + 1); + } + if (card->info.portname_required) + qeth_print_status_with_portname(card); + else + qeth_print_status_no_portname(card); +} + +static int +qeth_register_netdev(struct qeth_card *card) +{ + int rc; + + QETH_DBF_TEXT(setup, 3, "regnetd"); + if (card->dev->reg_state != NETREG_UNINITIALIZED) + return 0; + /* sysfs magic */ + SET_NETDEV_DEV(card->dev, &card->gdev->dev); + rc = register_netdev(card->dev); + if (!rc) + strcpy(card->info.if_name, card->dev->name); + + return rc; +} + +static void +qeth_start_again(struct qeth_card *card) +{ + QETH_DBF_TEXT(setup ,2, "startag"); + + rtnl_lock(); + dev_open(card->dev); + rtnl_unlock(); + qeth_set_thread_start_bit(card, QETH_SET_MC_THREAD); + schedule_work(&card->kernel_thread_starter); +} + +static int +qeth_set_online(struct ccwgroup_device *gdev) +{ + struct qeth_card *card = gdev->dev.driver_data; + int rc = 0; + enum qeth_card_states recover_flag; + + BUG_ON(!card); + QETH_DBF_TEXT(setup ,2, "setonlin"); + QETH_DBF_HEX(setup, 2, &card, sizeof(void *)); + + qeth_set_allowed_threads(card, QETH_RECOVER_THREAD, 1); + if (qeth_wait_for_threads(card, ~QETH_RECOVER_THREAD)){ + PRINT_WARN("set_online of card %s interrupted by user!\n", + CARD_BUS_ID(card)); + return -ERESTARTSYS; + } + + recover_flag = card->state; + if (ccw_device_set_online(CARD_RDEV(card)) || + ccw_device_set_online(CARD_WDEV(card)) || + ccw_device_set_online(CARD_DDEV(card))){ + QETH_DBF_TEXT_(setup, 2, "1err%d", rc); + return -EIO; + } + + if ((rc = qeth_hardsetup_card(card))){ + QETH_DBF_TEXT_(setup, 2, "2err%d", rc); + goto out_remove; + } + card->state = CARD_STATE_HARDSETUP; + + if ((rc = qeth_query_ipassists(card,QETH_PROT_IPV4))){ + QETH_DBF_TEXT_(setup, 2, "3err%d", rc); + /*TODO: rc !=0*/ + } else + rc = qeth_get_unique_id(card); + + if (rc) { + QETH_DBF_TEXT_(setup, 2, "4err%d", rc); + goto out_remove; + } + qeth_print_status_message(card); + if ((rc = qeth_register_netdev(card))){ + QETH_DBF_TEXT_(setup, 2, "5err%d", rc); + goto out_remove; + } + if ((rc = qeth_softsetup_card(card))){ + QETH_DBF_TEXT_(setup, 2, "6err%d", rc); + goto out_remove; + } + card->state = CARD_STATE_SOFTSETUP; + + if ((rc = qeth_init_qdio_queues(card))){ + QETH_DBF_TEXT_(setup, 2, "7err%d", rc); + goto out_remove; + } +/*maybe it was set offline without ifconfig down + * we can also use this state for recovery purposes*/ + qeth_set_allowed_threads(card, 0xffffffff, 0); + if (recover_flag == CARD_STATE_RECOVER) + qeth_start_again(card); + + return 0; +out_remove: + card->use_hard_stop = 1; + qeth_stop_card(card); + ccw_device_set_offline(CARD_DDEV(card)); + ccw_device_set_offline(CARD_WDEV(card)); + ccw_device_set_offline(CARD_RDEV(card)); + if (recover_flag == CARD_STATE_RECOVER) + card->state = CARD_STATE_RECOVER; + else + card->state = CARD_STATE_DOWN; + return -ENODEV; +} + +static struct ccw_device_id qeth_ids[] = { + {CCW_DEVICE(0x1731, 0x01), driver_info:QETH_CARD_TYPE_OSAE}, + {CCW_DEVICE(0x1731, 0x05), driver_info:QETH_CARD_TYPE_IQD}, + {}, +}; +MODULE_DEVICE_TABLE(ccw, qeth_ids); + +struct device *qeth_root_dev = NULL; + +struct ccwgroup_driver qeth_ccwgroup_driver = { + .owner = THIS_MODULE, + .name = "qeth", + .driver_id = 0xD8C5E3C8, + .probe = qeth_probe_device, + .remove = qeth_remove_device, + .set_online = qeth_set_online, + .set_offline = qeth_set_offline, +}; + +struct ccw_driver qeth_ccw_driver = { + .name = "qeth", + .ids = qeth_ids, + .probe = ccwgroup_probe_ccwdev, + .remove = ccwgroup_remove_ccwdev, +}; + + +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_register_dbf_views(void) +{ + qeth_dbf_setup = debug_register(QETH_DBF_SETUP_NAME, + QETH_DBF_SETUP_INDEX, + QETH_DBF_SETUP_NR_AREAS, + QETH_DBF_SETUP_LEN); + qeth_dbf_misc = debug_register(QETH_DBF_MISC_NAME, + QETH_DBF_MISC_INDEX, + QETH_DBF_MISC_NR_AREAS, + QETH_DBF_MISC_LEN); + qeth_dbf_data = debug_register(QETH_DBF_DATA_NAME, + QETH_DBF_DATA_INDEX, + QETH_DBF_DATA_NR_AREAS, + QETH_DBF_DATA_LEN); + qeth_dbf_control = debug_register(QETH_DBF_CONTROL_NAME, + QETH_DBF_CONTROL_INDEX, + QETH_DBF_CONTROL_NR_AREAS, + QETH_DBF_CONTROL_LEN); + qeth_dbf_sense = debug_register(QETH_DBF_SENSE_NAME, + QETH_DBF_SENSE_INDEX, + QETH_DBF_SENSE_NR_AREAS, + QETH_DBF_SENSE_LEN); + qeth_dbf_qerr = debug_register(QETH_DBF_QERR_NAME, + QETH_DBF_QERR_INDEX, + QETH_DBF_QERR_NR_AREAS, + QETH_DBF_QERR_LEN); + 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_setup == NULL) || (qeth_dbf_misc == NULL) || + (qeth_dbf_data == NULL) || (qeth_dbf_control == NULL) || + (qeth_dbf_sense == NULL) || (qeth_dbf_qerr == NULL) || + (qeth_dbf_trace == NULL)) { + qeth_unregister_dbf_views(); + return -ENOMEM; + } + debug_register_view(qeth_dbf_setup, &debug_hex_ascii_view); + debug_set_level(qeth_dbf_setup, QETH_DBF_SETUP_LEVEL); + + debug_register_view(qeth_dbf_misc, &debug_hex_ascii_view); + debug_set_level(qeth_dbf_misc, QETH_DBF_MISC_LEVEL); + + debug_register_view(qeth_dbf_data, &debug_hex_ascii_view); + debug_set_level(qeth_dbf_data, QETH_DBF_DATA_LEVEL); + + debug_register_view(qeth_dbf_control, &debug_hex_ascii_view); + debug_set_level(qeth_dbf_control, QETH_DBF_CONTROL_LEVEL); + + debug_register_view(qeth_dbf_sense, &debug_hex_ascii_view); + debug_set_level(qeth_dbf_sense, QETH_DBF_SENSE_LEVEL); + + debug_register_view(qeth_dbf_qerr, &debug_hex_ascii_view); + debug_set_level(qeth_dbf_qerr, QETH_DBF_QERR_LEVEL); + + debug_register_view(qeth_dbf_trace, &debug_hex_ascii_view); + debug_set_level(qeth_dbf_trace, QETH_DBF_TRACE_LEVEL); + + return 0; +} + +#ifdef CONFIG_QETH_IPV6 +extern struct neigh_table arp_tbl; +static struct neigh_ops *arp_direct_ops; +static 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 +}; + +static int +qeth_arp_constructor(struct neighbour *neigh) +{ + struct net_device *dev = neigh->dev; + struct in_device *in_dev = in_dev_get(dev); + + if (in_dev == NULL) + return -EINVAL; + 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); + neigh->nud_state = NUD_NOARP; + neigh->ops = arp_direct_ops; + neigh->output = neigh->ops->queue_xmit; + return 0; +} +#endif /*CONFIG_QETH_IPV6*/ + +/* + * IP address takeover related functions + */ +static void +qeth_clear_ipato_list(struct qeth_card *card) +{ + struct qeth_ipato_entry *ipatoe, *tmp; + unsigned long flags; + + spin_lock_irqsave(&card->ip_lock, flags); + list_for_each_entry_safe(ipatoe, tmp, &card->ipato.entries, entry) { + list_del(&ipatoe->entry); + kfree(ipatoe); + } + spin_unlock_irqrestore(&card->ip_lock, flags); +} + +int +qeth_add_ipato_entry(struct qeth_card *card, struct qeth_ipato_entry *new) +{ + struct qeth_ipato_entry *ipatoe; + unsigned long flags; + int rc = 0; + + QETH_DBF_TEXT(trace, 2, "addipato"); + spin_lock_irqsave(&card->ip_lock, flags); + list_for_each_entry(ipatoe, &card->ipato.entries, entry){ + if (ipatoe->proto != new->proto) + continue; + if (!memcmp(ipatoe->addr, new->addr, + (ipatoe->proto == QETH_PROT_IPV4)? 4:16) && + (ipatoe->mask_bits == new->mask_bits)){ + PRINT_WARN("ipato entry already exists!\n"); + rc = -EEXIST; + break; + } + } + if (!rc) { + list_add_tail(&new->entry, &card->ipato.entries); + } + spin_unlock_irqrestore(&card->ip_lock, flags); + return rc; +} + +void +qeth_del_ipato_entry(struct qeth_card *card, enum qeth_prot_versions proto, + u8 *addr, int mask_bits) +{ + struct qeth_ipato_entry *ipatoe, *tmp; + unsigned long flags; + + QETH_DBF_TEXT(trace, 2, "delipato"); + spin_lock_irqsave(&card->ip_lock, flags); + list_for_each_entry_safe(ipatoe, tmp, &card->ipato.entries, entry){ + if (ipatoe->proto != proto) + continue; + if (!memcmp(ipatoe->addr, addr, + (proto == QETH_PROT_IPV4)? 4:16) && + (ipatoe->mask_bits == mask_bits)){ + list_del(&ipatoe->entry); + kfree(ipatoe); + } + } + spin_unlock_irqrestore(&card->ip_lock, flags); +} + +static inline void +qeth_convert_addr_to_bits(u8 *addr, u8 *bits, int len) +{ + int i, j; + u8 octet; + + for (i = 0; i < len; ++i){ + octet = addr[i]; + for (j = 7; j >= 0; --j){ + bits[i*8 + j] = octet & 1; + octet >>= 1; + } + } +} + +static int +qeth_is_addr_covered_by_ipato(struct qeth_card *card, struct qeth_ipaddr *addr) +{ + struct qeth_ipato_entry *ipatoe; + u8 addr_bits[128] = {0, }; + u8 ipatoe_bits[128] = {0, }; + int rc = 0; + + if (!card->ipato.enabled) + return 0; + + qeth_convert_addr_to_bits((u8 *) &addr->u, addr_bits, + (addr->proto == QETH_PROT_IPV4)? 4:16); + list_for_each_entry(ipatoe, &card->ipato.entries, entry){ + if (addr->proto != ipatoe->proto) + continue; + qeth_convert_addr_to_bits(ipatoe->addr, ipatoe_bits, + (ipatoe->proto==QETH_PROT_IPV4) ? + 4:16); + if (addr->proto == QETH_PROT_IPV4) + rc = !memcmp(addr_bits, ipatoe_bits, + min(32, ipatoe->mask_bits)); + else + rc = !memcmp(addr_bits, ipatoe_bits, + min(128, ipatoe->mask_bits)); + if (rc) + break; + } + /* invert? */ + if ((addr->proto == QETH_PROT_IPV4) && card->ipato.invert4) + rc = !rc; + else if ((addr->proto == QETH_PROT_IPV6) && card->ipato.invert6) + rc = !rc; + + return rc; +} + +/* + * VIPA related functions + */ +int +qeth_add_vipa(struct qeth_card *card, enum qeth_prot_versions proto, + const u8 *addr) +{ + struct qeth_ipaddr *ipaddr; + unsigned long flags; + int rc = 0; + + ipaddr = qeth_get_addr_buffer(proto); + if (ipaddr){ + if (proto == QETH_PROT_IPV4){ + QETH_DBF_TEXT(trace, 2, "addvipa4"); + memcpy(&ipaddr->u.a4.addr, addr, 4); + ipaddr->u.a4.mask = 0; +#ifdef CONFIG_QETH_IPV6 + } else if (proto == QETH_PROT_IPV6){ + QETH_DBF_TEXT(trace, 2, "addvipa6"); + memcpy(&ipaddr->u.a6.addr, addr, 16); + ipaddr->u.a6.pfxlen = 0; +#endif + } + ipaddr->type = QETH_IP_TYPE_VIPA; + ipaddr->set_flags = QETH_IPA_SETIP_VIPA_FLAG; + ipaddr->del_flags = QETH_IPA_DELIP_VIPA_FLAG; + } else + return -ENOMEM; + spin_lock_irqsave(&card->ip_lock, flags); + if (__qeth_address_exists_in_list(&card->ip_list, ipaddr, 0) || + __qeth_address_exists_in_list(&card->ip_tbd_list, ipaddr, 0)) + rc = -EEXIST; + spin_unlock_irqrestore(&card->ip_lock, flags); + if (rc){ + PRINT_WARN("Cannot add VIPA. Address already exists!\n"); + return rc; + } + if (!qeth_add_ip(card, ipaddr)) + kfree(ipaddr); + qeth_set_thread_start_bit(card, QETH_SET_IP_THREAD); + schedule_work(&card->kernel_thread_starter); + return rc; +} + +void +qeth_del_vipa(struct qeth_card *card, enum qeth_prot_versions proto, + const u8 *addr) +{ + struct qeth_ipaddr *ipaddr; + + ipaddr = qeth_get_addr_buffer(proto); + if (ipaddr){ + if (proto == QETH_PROT_IPV4){ + QETH_DBF_TEXT(trace, 2, "delvipa4"); + memcpy(&ipaddr->u.a4.addr, addr, 4); + ipaddr->u.a4.mask = 0; +#ifdef CONFIG_QETH_IPV6 + } else if (proto == QETH_PROT_IPV6){ + QETH_DBF_TEXT(trace, 2, "delvipa6"); + memcpy(&ipaddr->u.a6.addr, addr, 16); + ipaddr->u.a6.pfxlen = 0; +#endif + } + ipaddr->type = QETH_IP_TYPE_VIPA; + } else + return; + if (!qeth_delete_ip(card, ipaddr)) + kfree(ipaddr); + qeth_set_thread_start_bit(card, QETH_SET_IP_THREAD); + schedule_work(&card->kernel_thread_starter); +} + +/* + * proxy ARP related functions + */ +int +qeth_add_rxip(struct qeth_card *card, enum qeth_prot_versions proto, + const u8 *addr) +{ + struct qeth_ipaddr *ipaddr; + unsigned long flags; + int rc = 0; + + ipaddr = qeth_get_addr_buffer(proto); + if (ipaddr){ + if (proto == QETH_PROT_IPV4){ + QETH_DBF_TEXT(trace, 2, "addrxip4"); + memcpy(&ipaddr->u.a4.addr, addr, 4); + ipaddr->u.a4.mask = 0; +#ifdef CONFIG_QETH_IPV6 + } else if (proto == QETH_PROT_IPV6){ + QETH_DBF_TEXT(trace, 2, "addrxip6"); + memcpy(&ipaddr->u.a6.addr, addr, 16); + ipaddr->u.a6.pfxlen = 0; +#endif + } + ipaddr->type = QETH_IP_TYPE_RXIP; + ipaddr->set_flags = QETH_IPA_SETIP_TAKEOVER_FLAG; + ipaddr->del_flags = 0; + } else + return -ENOMEM; + spin_lock_irqsave(&card->ip_lock, flags); + if (__qeth_address_exists_in_list(&card->ip_list, ipaddr, 0) || + __qeth_address_exists_in_list(&card->ip_tbd_list, ipaddr, 0)) + rc = -EEXIST; + spin_unlock_irqrestore(&card->ip_lock, flags); + if (rc){ + PRINT_WARN("Cannot add RXIP. Address already exists!\n"); + return rc; + } + if (!qeth_add_ip(card, ipaddr)) + kfree(ipaddr); + qeth_set_thread_start_bit(card, QETH_SET_IP_THREAD); + schedule_work(&card->kernel_thread_starter); + return 0; +} + +void +qeth_del_rxip(struct qeth_card *card, enum qeth_prot_versions proto, + const u8 *addr) +{ + struct qeth_ipaddr *ipaddr; + + ipaddr = qeth_get_addr_buffer(proto); + if (ipaddr){ + if (proto == QETH_PROT_IPV4){ + QETH_DBF_TEXT(trace, 2, "addrxip4"); + memcpy(&ipaddr->u.a4.addr, addr, 4); + ipaddr->u.a4.mask = 0; +#ifdef CONFIG_QETH_IPV6 + } else if (proto == QETH_PROT_IPV6){ + QETH_DBF_TEXT(trace, 2, "addrxip6"); + memcpy(&ipaddr->u.a6.addr, addr, 16); + ipaddr->u.a6.pfxlen = 0; +#endif + } + ipaddr->type = QETH_IP_TYPE_RXIP; + } else + return; + if (!qeth_delete_ip(card, ipaddr)) + kfree(ipaddr); + qeth_set_thread_start_bit(card, QETH_SET_IP_THREAD); + schedule_work(&card->kernel_thread_starter); +} + +/** + * IP event handler + */ +static int +qeth_ip_event(struct notifier_block *this, + unsigned long event,void *ptr) +{ + struct in_ifaddr *ifa = (struct in_ifaddr *)ptr; + struct net_device *dev =(struct net_device *) ifa->ifa_dev->dev; + struct qeth_ipaddr *addr; + struct qeth_card *card; + + QETH_DBF_TEXT(trace,3,"ipevent"); + card = qeth_get_card_from_dev(dev); + if (!card) + return NOTIFY_DONE; + + addr = qeth_get_addr_buffer(QETH_PROT_IPV4); + if (addr != NULL) { + addr->u.a4.addr = ifa->ifa_address; + addr->u.a4.mask = ifa->ifa_mask; + addr->type = QETH_IP_TYPE_NORMAL; + } + switch(event) { + case NETDEV_UP: + if (addr) { + if (!qeth_add_ip(card, addr)) + kfree(addr); + } + break; + case NETDEV_DOWN: + if (addr) { + if (!qeth_delete_ip(card, addr)) + kfree(addr); + } + break; + default: + break; + } + qeth_set_thread_start_bit(card, QETH_SET_IP_THREAD); + schedule_work(&card->kernel_thread_starter); + + return NOTIFY_DONE; +} + +static struct notifier_block qeth_ip_notifier = { + qeth_ip_event, + 0 +}; + +#ifdef CONFIG_QETH_IPV6 +/** + * IPv6 event handler + */ +static int +qeth_ip6_event(struct notifier_block *this, + unsigned long event,void *ptr) +{ + + struct inet6_ifaddr *ifa = (struct inet6_ifaddr *)ptr; + struct net_device *dev = (struct net_device *)ifa->idev->dev; + struct qeth_ipaddr *addr; + struct qeth_card *card; + + QETH_DBF_TEXT(trace,3,"ip6event"); + + card = qeth_get_card_from_dev(dev); + if (!card) + return NOTIFY_DONE; + if (!qeth_is_supported(card, IPA_IPV6)) + return NOTIFY_DONE; + + addr = qeth_get_addr_buffer(QETH_PROT_IPV6); + if (addr != NULL) { + memcpy(&addr->u.a6.addr, &ifa->addr, sizeof(struct in6_addr)); + addr->u.a6.pfxlen = ifa->prefix_len; + addr->type = QETH_IP_TYPE_NORMAL; + } + switch(event) { + case NETDEV_UP: + if (addr){ + if (!qeth_add_ip(card, addr)) + kfree(addr); + } + break; + case NETDEV_DOWN: + if (addr){ + if (!qeth_delete_ip(card, addr)) + kfree(addr); + } + break; + default: + break; + } + qeth_set_thread_start_bit(card, QETH_SET_IP_THREAD); + schedule_work(&card->kernel_thread_starter); + + return NOTIFY_DONE; +} + +static struct notifier_block qeth_ip6_notifier = { + qeth_ip6_event, + 0 +}; +#endif + +static int +qeth_reboot_event(struct notifier_block *this, unsigned long event, void *ptr) +{ + + struct device *entry; + struct qeth_card *card; + + down_read(&qeth_ccwgroup_driver.driver.bus->subsys.rwsem); + list_for_each_entry(entry, &qeth_ccwgroup_driver.driver.devices, + driver_list) { + card = (struct qeth_card *) entry->driver_data; + qeth_clear_ip_list(card, 0, 0); + qeth_qdio_clear_card(card, 0); + } + up_read(&qeth_ccwgroup_driver.driver.bus->subsys.rwsem); + return NOTIFY_DONE; +} + + +static struct notifier_block qeth_reboot_notifier = { + qeth_reboot_event, + 0 +}; + +static int +qeth_register_notifiers(void) +{ + int r; + + QETH_DBF_TEXT(trace,5,"regnotif"); + if ((r = register_reboot_notifier(&qeth_reboot_notifier))) + return r; + if ((r = register_inetaddr_notifier(&qeth_ip_notifier))) + goto out_reboot; +#ifdef CONFIG_QETH_IPV6 + if ((r = register_inet6addr_notifier(&qeth_ip6_notifier))) + goto out_ipv4; +#endif + return 0; + +#ifdef CONFIG_QETH_IPV6 +out_ipv4: + unregister_inetaddr_notifier(&qeth_ip_notifier); +#endif +out_reboot: + unregister_reboot_notifier(&qeth_reboot_notifier); + return r; +} + +/** + * unregister all event notifiers + */ +static void +qeth_unregister_notifiers(void) +{ + + QETH_DBF_TEXT(trace,5,"unregnot"); + BUG_ON(unregister_reboot_notifier(&qeth_reboot_notifier)); + BUG_ON(unregister_inetaddr_notifier(&qeth_ip_notifier)); +#ifdef CONFIG_QETH_IPV6 + BUG_ON(unregister_inet6addr_notifier(&qeth_ip6_notifier)); +#endif /* QETH_IPV6 */ + +} + +#ifdef CONFIG_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); + + 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); + kfree(arp_direct_ops); +} +#endif /* CONFIG_QETH_IPV6 */ + +static void +qeth_sysfs_unregister(void) +{ + qeth_remove_driver_attributes(); + ccw_driver_unregister(&qeth_ccw_driver); + ccwgroup_driver_unregister(&qeth_ccwgroup_driver); + s390_root_dev_unregister(qeth_root_dev); +} +/** + * register qeth at sysfs + */ +static int +qeth_sysfs_register(void) +{ + int rc=0; + + rc = ccwgroup_driver_register(&qeth_ccwgroup_driver); + if (rc) + return rc; + rc = ccw_driver_register(&qeth_ccw_driver); + if (rc) + return rc; + rc = qeth_create_driver_attributes(); + if (rc) + return rc; + qeth_root_dev = s390_root_dev_register("qeth"); + if (IS_ERR(qeth_root_dev)) { + rc = PTR_ERR(qeth_root_dev); + return rc; + } + return 0; +} + +/*** + * init function + */ +static int __init +qeth_init(void) +{ + int rc=0; + + qeth_eyecatcher(); + printk(KERN_INFO "qeth: loading %s\n",version); + + INIT_LIST_HEAD(&qeth_card_list.list); + rwlock_init(&qeth_card_list.rwlock); + + atomic_set(&qeth_hsi_count, 0); + if (qeth_register_dbf_views()) + goto out_err; + if (qeth_sysfs_register()) + goto out_sysfs; + +#ifdef CONFIG_QETH_IPV6 + if (qeth_ipv6_init()) { + PRINT_ERR("Out of memory during ipv6 init.\n"); + goto out_sysfs; + } +#endif /* QETH_IPV6 */ + if (qeth_register_notifiers()) + goto out_ipv6; + if (qeth_create_procfs_entries()) + goto out_notifiers; + + return rc; + +out_notifiers: + qeth_unregister_notifiers(); +out_ipv6: +#ifdef CONFIG_QETH_IPV6 + qeth_ipv6_uninit(); +#endif /* QETH_IPV6 */ +out_sysfs: + qeth_sysfs_unregister(); + qeth_unregister_dbf_views(); +out_err: + PRINT_ERR("Initialization failed"); + return rc; +} + +static void +__exit qeth_exit(void) +{ + struct qeth_card *card, *tmp; + unsigned long flags; + + QETH_DBF_TEXT(trace,1, "cleanup."); + + /* + * Weed would not need to clean up our devices here, because the + * common device layer calls qeth_remove_device for each device + * as soon as we unregister our driver (done in qeth_sysfs_unregister). + * But we do cleanup here so we can do a "soft" shutdown of our cards. + * qeth_remove_device called by the common device layer would otherwise + * do a "hard" shutdown (card->use_hard_stop is set to one in + * qeth_remove_device). + */ +again: + read_lock_irqsave(&qeth_card_list.rwlock, flags); + list_for_each_entry_safe(card, tmp, &qeth_card_list.list, list){ + read_unlock_irqrestore(&qeth_card_list.rwlock, flags); + qeth_set_offline(card->gdev); + qeth_remove_device(card->gdev); + goto again; + } + read_unlock_irqrestore(&qeth_card_list.rwlock, flags); +#ifdef CONFIG_QETH_IPV6 + qeth_ipv6_uninit(); +#endif + qeth_unregister_notifiers(); + qeth_remove_procfs_entries(); + qeth_sysfs_unregister(); + qeth_unregister_dbf_views(); + printk("qeth: removed\n"); +} + +EXPORT_SYMBOL(qeth_eyecatcher); +module_init(qeth_init); +module_exit(qeth_exit); +MODULE_AUTHOR("Frank Pavlic "); +MODULE_DESCRIPTION("Linux on zSeries OSA Express and HiperSockets support\n" \ + "Copyright 2000,2003 IBM Corporation\n"); + +MODULE_LICENSE("GPL"); diff -puN drivers/s390/net/qeth_mpc.c~s390-12-12-rewritten-qeth-driver drivers/s390/net/qeth_mpc.c --- 25/drivers/s390/net/qeth_mpc.c~s390-12-12-rewritten-qeth-driver 2004-04-08 13:55:46.665929872 -0700 +++ 25-akpm/drivers/s390/net/qeth_mpc.c 2004-04-08 13:55:46.762915128 -0700 @@ -4,7 +4,8 @@ * Linux on zSeries OSA Express and HiperSockets support * * Copyright 2000,2003 IBM Corporation - * Author(s): Utz Bacher + * Author(s): Frank Pavlic + * Thomas Spatzier * */ #include @@ -126,16 +127,22 @@ unsigned char DM_ACT[]={ unsigned char IPA_PDU_HEADER[]={ 0x00,0xe0,0x00,0x00, 0x77,0x77,0x77,0x77, 0x00,0x00,0x00,0x14, 0x00,0x00, - (IPA_PDU_HEADER_SIZE+sizeof(struct ipa_cmd))/256, - (IPA_PDU_HEADER_SIZE+sizeof(struct ipa_cmd))%256, + (IPA_PDU_HEADER_SIZE+sizeof(struct qeth_ipa_cmd))/256, + (IPA_PDU_HEADER_SIZE+sizeof(struct qeth_ipa_cmd))%256, 0x10,0x00,0x00,0x01, 0x00,0x00,0x00,0x00, 0xc1,0x03,0x00,0x01, 0x00,0x00,0x00,0x00, - 0x00,0x00,0x00,0x00, 0x00,0x24,0x00,sizeof(struct ipa_cmd), - 0x00,0x00,sizeof(struct ipa_cmd),0x05, 0x77,0x77,0x77,0x77, + 0x00,0x00,0x00,0x00, 0x00,0x24, + sizeof(struct qeth_ipa_cmd)/256, + sizeof(struct qeth_ipa_cmd)%256, + 0x00, + sizeof(struct qeth_ipa_cmd)/256, + sizeof(struct qeth_ipa_cmd),0x05, 0x77,0x77,0x77,0x77, 0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00, - 0x01,0x00,sizeof(struct ipa_cmd)/256,sizeof(struct ipa_cmd)%256, - 0x00,0x00,0x00,0x40, + 0x01,0x00, + sizeof(struct qeth_ipa_cmd)/256, + sizeof(struct qeth_ipa_cmd)%256, + 0x00,0x00,0x00,0x40, }; unsigned char WRITE_CCW[]={ @@ -158,4 +165,3 @@ unsigned char READ_CCW[]={ - diff -puN drivers/s390/net/qeth_mpc.h~s390-12-12-rewritten-qeth-driver drivers/s390/net/qeth_mpc.h --- 25/drivers/s390/net/qeth_mpc.h~s390-12-12-rewritten-qeth-driver 2004-04-08 13:55:46.667929568 -0700 +++ 25-akpm/drivers/s390/net/qeth_mpc.h 2004-04-08 13:55:46.767914368 -0700 @@ -5,215 +5,279 @@ * * Copyright 2000,2003 IBM Corporation * Author(s): Utz Bacher + * Thomas Spatzier + * Frank Pavlic * */ #ifndef __QETH_MPC_H__ #define __QETH_MPC_H__ -#define VERSION_QETH_MPC_H "$Revision: 1.18 $" +#include -#define QETH_IPA_TIMEOUT (card->ipa_timeout) -#define QETH_MPC_TIMEOUT 2000 -#define QETH_ADDR_TIMEOUT 1000 +#define VERSION_QETH_MPC_H "$Revision: 1.27 $" -#define QETH_SETIP_RETRIES 2 - -#define IDX_ACTIVATE_SIZE 0x22 -#define CM_ENABLE_SIZE 0x63 -#define CM_SETUP_SIZE 0x64 -#define ULP_ENABLE_SIZE 0x6b -#define ULP_SETUP_SIZE 0x6c -#define DM_ACT_SIZE 0x55 - -#define QETH_MPC_TOKEN_LENGTH 4 -#define QETH_SEQ_NO_LENGTH 4 -#define QETH_IPA_SEQ_NO_LENGTH 2 - -#define QETH_TRANSPORT_HEADER_SEQ_NO(buffer) (buffer+4) -#define QETH_PDU_HEADER_SEQ_NO(buffer) (buffer+0x1c) -#define QETH_PDU_HEADER_ACK_SEQ_NO(buffer) (buffer+0x20) - -extern unsigned char IDX_ACTIVATE_READ[]; +#define IPA_PDU_HEADER_SIZE 0x40 +#define QETH_IPA_PDU_LEN_TOTAL(buffer) (buffer+0x0e) +#define QETH_IPA_PDU_LEN_PDU1(buffer) (buffer+0x26) +#define QETH_IPA_PDU_LEN_PDU2(buffer) (buffer+0x2a) +#define QETH_IPA_PDU_LEN_PDU3(buffer) (buffer+0x3a) -extern unsigned char IDX_ACTIVATE_WRITE[]; +extern unsigned char IPA_PDU_HEADER[]; +#define QETH_IPA_CMD_DEST_ADDR(buffer) (buffer+0x2c) -#define QETH_IDX_ACT_ISSUER_RM_TOKEN(buffer) (buffer+0x0c) -#define QETH_IDX_NO_PORTNAME_REQUIRED(buffer) ((buffer)[0x0b]&0x80) -#define QETH_IDX_ACT_FUNC_LEVEL(buffer) (buffer+0x10) -#define QETH_IDX_ACT_DATASET_NAME(buffer) (buffer+0x16) -#define QETH_IDX_ACT_QDIO_DEV_CUA(buffer) (buffer+0x1e) -#define QETH_IDX_ACT_QDIO_DEV_REALADDR(buffer) (buffer+0x20) +#define IPA_CMD_LENGTH (IPA_PDU_HEADER_SIZE + sizeof(struct qeth_ipa_cmd)) -#define QETH_IS_IDX_ACT_POS_REPLY(buffer) (((buffer)[0x08]&3)==2) +#define QETH_SEQ_NO_LENGTH 4 +#define QETH_MPC_TOKEN_LENGTH 4 +#define QETH_MCL_LENGTH 4 +#define OSA_ADDR_LEN 6 + +#define QETH_TIMEOUT (10 * HZ) +#define QETH_IDX_COMMAND_SEQNO -1 +#define SR_INFO_LEN 16 + +#define QETH_CLEAR_CHANNEL_PARM -10 +#define QETH_HALT_CHANNEL_PARM -11 + +/*****************************************************************************/ +/* IP Assist related definitions */ +/*****************************************************************************/ +#define IPA_CMD_INITIATOR_HOST 0x00 +#define IPA_CMD_INITIATOR_HYDRA 0x01 +#define IPA_CMD_PRIM_VERSION_NO 0x01 + +enum qeth_card_types { + QETH_CARD_TYPE_UNKNOWN = 0, + QETH_CARD_TYPE_OSAE = 10, + QETH_CARD_TYPE_IQD = 1234, +}; -#define QETH_IDX_REPLY_LEVEL(buffer) (buffer+0x12) -#define QETH_MCL_LENGTH 4 +#define QETH_MPC_DIFINFO_LEN_INDICATES_LINK_TYPE 0x18 +/* only the first two bytes are looked at in qeth_get_cardname_short */ +enum qeth_link_types { + QETH_LINK_TYPE_FAST_ETH = 0x01, + QETH_LINK_TYPE_HSTR = 0x02, + QETH_LINK_TYPE_GBIT_ETH = 0x03, + QETH_LINK_TYPE_10GBIT_ETH = 0x10, + QETH_LINK_TYPE_LANE_ETH100 = 0x81, + QETH_LINK_TYPE_LANE_TR = 0x82, + QETH_LINK_TYPE_LANE_ETH1000 = 0x83, + QETH_LINK_TYPE_LANE = 0x88, + QETH_LINK_TYPE_ATM_NATIVE = 0x90, +}; -extern unsigned char CM_ENABLE[]; +enum qeth_tr_macaddr_modes { + QETH_TR_MACADDR_NONCANONICAL = 0, + QETH_TR_MACADDR_CANONICAL = 1, +}; -#define QETH_CM_ENABLE_ISSUER_RM_TOKEN(buffer) (buffer+0x2c) -#define QETH_CM_ENABLE_FILTER_TOKEN(buffer) (buffer+0x53) -#define QETH_CM_ENABLE_USER_DATA(buffer) (buffer+0x5b) +enum qeth_tr_broadcast_modes { + QETH_TR_BROADCAST_ALLRINGS = 0, + QETH_TR_BROADCAST_LOCAL = 1, +}; -#define QETH_CM_ENABLE_RESP_FILTER_TOKEN(buffer) (PDU_ENCAPSULATION(buffer)+ \ - 0x13) +/* these values match CHECKSUM_* in include/linux/skbuff.h */ +enum qeth_checksum_types { + SW_CHECKSUMMING = 0, /* TODO: set to bit flag used in IPA Command */ + HW_CHECKSUMMING = 1, + NO_CHECKSUMMING = 2, +}; +#define QETH_CHECKSUM_DEFAULT SW_CHECKSUMMING -extern unsigned char CM_SETUP[]; +/* + * Routing stuff + */ +#define RESET_ROUTING_FLAG 0x10 /* indicate that routing type shall be set */ +enum qeth_routing_types { + NO_ROUTER = 0, /* TODO: set to bit flag used in IPA Command */ + PRIMARY_ROUTER = 1, + SECONDARY_ROUTER = 2, + MULTICAST_ROUTER = 3, + PRIMARY_CONNECTOR = 4, + SECONDARY_CONNECTOR = 5, +}; -#define QETH_CM_SETUP_DEST_ADDR(buffer) (buffer+0x2c) -#define QETH_CM_SETUP_CONNECTION_TOKEN(buffer) (buffer+0x51) -#define QETH_CM_SETUP_FILTER_TOKEN(buffer) (buffer+0x5a) -#define QETH_CM_SETUP_RESP_DEST_ADDR(buffer) (PDU_ENCAPSULATION(buffer)+ \ - 0x1a) +/* IPA Commands */ +enum qeth_ipa_cmds { + IPA_CMD_STARTLAN = 0x01, + IPA_CMD_STOPLAN = 0x02, + IPA_CMD_SETIP = 0xb1, + IPA_CMD_DELIP = 0xb7, + IPA_CMD_QIPASSIST = 0xb2, + IPA_CMD_SETASSPARMS = 0xb3, + IPA_CMD_SETIPM = 0xb4, + IPA_CMD_DELIPM = 0xb5, + IPA_CMD_SETRTG = 0xb6, + IPA_CMD_SETADAPTERPARMS = 0xb8, + IPA_CMD_IPFRAME = 0xb9, + IPA_CMD_ADD_ADDR_ENTRY = 0xc1, + IPA_CMD_DELETE_ADDR_ENTRY = 0xc2, + IPA_CMD_CREATE_ADDR = 0xc3, + IPA_CMD_DESTROY_ADDR = 0xc4, + IPA_CMD_REGISTER_LOCAL_ADDR = 0xd1, + IPA_CMD_UNREGISTER_LOCAL_ADDR = 0xd2, +}; -extern unsigned char ULP_ENABLE[]; +enum qeth_ip_ass_cmds { + IPA_CMD_ASS_START = 0x0001, + IPA_CMD_ASS_STOP = 0x0002, + IPA_CMD_ASS_CONFIGURE = 0x0003, + IPA_CMD_ASS_ENABLE = 0x0004, +}; -#define QETH_ULP_ENABLE_LINKNUM(buffer) (buffer+0x61) -#define QETH_ULP_ENABLE_DEST_ADDR(buffer) (buffer+0x2c) -#define QETH_ULP_ENABLE_FILTER_TOKEN(buffer) (buffer+0x53) -#define QETH_ULP_ENABLE_PORTNAME_AND_LL(buffer) (buffer+0x62) +enum qeth_arp_process_subcmds { + IPA_CMD_ASS_ARP_SET_NO_ENTRIES = 0x0003, + IPA_CMD_ASS_ARP_QUERY_CACHE = 0x0004, + IPA_CMD_ASS_ARP_ADD_ENTRY = 0x0005, + IPA_CMD_ASS_ARP_REMOVE_ENTRY = 0x0006, + IPA_CMD_ASS_ARP_FLUSH_CACHE = 0x0007, + IPA_CMD_ASS_ARP_QUERY_INFO = 0x0104, + IPA_CMD_ASS_ARP_QUERY_STATS = 0x0204, +}; -#define QETH_ULP_ENABLE_RESP_FILTER_TOKEN(buffer) (PDU_ENCAPSULATION(buffer)+ \ - 0x13) -#define QETH_ULP_ENABLE_RESP_MAX_MTU(buffer) (PDU_ENCAPSULATION(buffer)+ 0x1f) -#define QETH_ULP_ENABLE_RESP_DIFINFO_LEN(buffer) (PDU_ENCAPSULATION(buffer)+ \ - 0x17) -#define QETH_ULP_ENABLE_RESP_LINK_TYPE(buffer) (PDU_ENCAPSULATION(buffer)+ \ - 0x2b) +/* Return Codes for IPA Commands */ +enum qeth_ipa_return_codes { + IPA_RC_SUCCESS = 0x0000, + IPA_RC_NOTSUPP = 0x0001, + IPA_RC_NO_ACCESS = 0x0002, + IPA_RC_FAILED = 0x0003, + IPA_RC_DATA_MISMATCH = 0xe001, + IPA_RC_INVALID_LAN_TYPE = 0xe003, + IPA_RC_INVALID_LAN_NO = 0xe004, + IPA_RC_IPADDR_ALREADY_REG = 0xe005, + IPA_RC_IPADDR_TABLE_FULL = 0xe006, + IPA_RC_IPADDR_ALREADY_USED = 0xe00a, + IPA_RC_ASSNO_NOT_SUPP = 0xe00d, + IPA_RC_ASSCMD_START_FAILED = 0xe00e, + IPA_RC_ASSCMD_PART_SUCCESS = 0xe00f, + IPA_RC_IPADDR_NOT_DEFINED = 0xe010, + IPA_RC_LAN_OFFLINE = 0xe080, +}; -extern unsigned char ULP_SETUP[]; +/* IPA function flags; each flag marks availability of respective function */ +enum qeth_ipa_funcs { + IPA_ARP_PROCESSING = 0x00000001L, + IPA_INBOUND_CHECKSUM = 0x00000002L, + IPA_OUTBOUND_CHECKSUM = 0x00000004L, + IPA_IP_FRAGMENTATION = 0x00000008L, + IPA_FILTERING = 0x00000010L, + IPA_IPV6 = 0x00000020L, + IPA_MULTICASTING = 0x00000040L, + IPA_IP_REASSEMBLY = 0x00000080L, + IPA_QUERY_ARP_COUNTERS = 0x00000100L, + IPA_QUERY_ARP_ADDR_INFO = 0x00000200L, + IPA_SETADAPTERPARMS = 0x00000400L, + IPA_VLAN_PRIO = 0x00000800L, + IPA_PASSTHRU = 0x00001000L, + IPA_FULL_VLAN = 0x00004000L, + IPA_SOURCE_MAC = 0x00010000L, + IPA_OSA_MC_ROUTER = 0x00020000L, +}; -#define QETH_ULP_SETUP_DEST_ADDR(buffer) (buffer+0x2c) -#define QETH_ULP_SETUP_CONNECTION_TOKEN(buffer) (buffer+0x51) -#define QETH_ULP_SETUP_FILTER_TOKEN(buffer) (buffer+0x5a) -#define QETH_ULP_SETUP_CUA(buffer) (buffer+0x68) -#define QETH_ULP_SETUP_REAL_DEVADDR(buffer) (buffer+0x6a) +/* SETIP/DELIP IPA Command: ***************************************************/ +enum qeth_ipa_setdelip_flags { + QETH_IPA_SETDELIP_DEFAULT = 0x00L, /* default */ + QETH_IPA_SETIP_VIPA_FLAG = 0x01L, /* no grat. ARP */ + QETH_IPA_SETIP_TAKEOVER_FLAG = 0x02L, /* nofail on grat. ARP */ + QETH_IPA_DELIP_ADDR_2_B_TAKEN_OVER = 0x20L, + QETH_IPA_DELIP_VIPA_FLAG = 0x40L, + QETH_IPA_DELIP_ADDR_NEEDS_SETIP = 0x80L, +}; -#define QETH_ULP_SETUP_RESP_CONNECTION_TOKEN(buffer) (PDU_ENCAPSULATION \ - (buffer)+0x1a) +/* SETADAPTER IPA Command: ****************************************************/ +enum qeth_ipa_setadp_cmd { + IPA_SETADP_QUERY_COMMANDS_SUPPORTED = 0x01, + IPA_SETADP_ALTER_MAC_ADDRESS = 0x02, + IPA_SETADP_ADD_DELETE_GROUP_ADDRESS = 0x04, + IPA_SETADP_ADD_DELETE_FUNCTIONAL_ADDR = 0x08, + IPA_SETADP_SET_ADDRESSING_MODE = 0x10, + IPA_SETADP_SET_CONFIG_PARMS = 0x20, + IPA_SETADP_SET_CONFIG_PARMS_EXTENDED = 0x40, + IPA_SETADP_SET_BROADCAST_MODE = 0x80, + IPA_SETADP_SEND_OSA_MESSAGE = 0x0100, + IPA_SETADP_SET_SNMP_CONTROL = 0x0200, + IPA_SETADP_READ_SNMP_PARMS = 0x0400, + IPA_SETADP_WRITE_SNMP_PARMS = 0x0800, + IPA_SETADP_QUERY_CARD_INFO = 0x1000, +}; +enum qeth_ipa_mac_ops { + CHANGE_ADDR_READ_MAC = 0, + CHANGE_ADDR_REPLACE_MAC = 1, + CHANGE_ADDR_ADD_MAC = 2, + CHANGE_ADDR_DEL_MAC = 4, + CHANGE_ADDR_RESET_MAC = 8, +}; +enum qeth_ipa_addr_ops { + CHANGE_ADDR_READ_ADDR = 0, + CHANGE_ADDR_ADD_ADDR = 1, + CHANGE_ADDR_DEL_ADDR = 2, + CHANGE_ADDR_FLUSH_ADDR_TABLE = 4, -extern unsigned char DM_ACT[]; -#define QETH_DM_ACT_DEST_ADDR(buffer) (buffer+0x2c) -#define QETH_DM_ACT_CONNECTION_TOKEN(buffer) (buffer+0x51) +}; +/* (SET)DELIP(M) IPA stuff ***************************************************/ +struct qeth_ipacmd_setdelip4 { + __u8 ip_addr[4]; + __u8 mask[4]; + __u32 flags; +} __attribute__ ((packed)); + +struct qeth_ipacmd_setdelip6 { + __u8 ip_addr[16]; + __u8 mask[16]; + __u32 flags; +} __attribute__ ((packed)); + +struct qeth_ipacmd_setdelipm { + __u8 mac[6]; + __u8 padding[2]; + __u8 ip6[12]; + __u8 ip4[4]; +} __attribute__ ((packed)); + +struct qeth_ipacmd_setassparms_hdr { + __u32 assist_no; + __u16 length; + __u16 command_code; + __u16 return_code; + __u8 number_of_replies; + __u8 seq_no; +} __attribute__((packed)); + +/* SETASSPARMS IPA Command: */ +struct qeth_ipacmd_setassparms { + struct qeth_ipacmd_setassparms_hdr hdr; + union { + __u32 flags_32bit; + struct qeth_arp_cache_entry add_arp_entry; + __u8 ip[16]; + } data; +} __attribute__ ((packed)); -#define IPA_CMD_STARTLAN 0x01 -#define IPA_CMD_STOPLAN 0x02 -#define IPA_CMD_SETIP 0xb1 -#define IPA_CMD_DELIP 0xb7 -#define IPA_CMD_QIPASSIST 0xb2 -#define IPA_CMD_SETASSPARMS 0xb3 -#define IPA_CMD_SETIPM 0xb4 -#define IPA_CMD_DELIPM 0xb5 -#define IPA_CMD_SETRTG 0xb6 -#define IPA_CMD_SETADAPTERPARMS 0xb8 -#define IPA_CMD_ADD_ADDR_ENTRY 0xc1 -#define IPA_CMD_DELETE_ADDR_ENTRY 0xc2 -#define IPA_CMD_CREATE_ADDR 0xc3 -#define IPA_CMD_DESTROY_ADDR 0xc4 -#define IPA_CMD_REGISTER_LOCAL_ADDR 0xd1 -#define IPA_CMD_UNREGISTER_LOCAL_ADDR 0xd2 - -#define INITIATOR_HOST 0 -#define INITIATOR_HYDRA 1 - -#define PRIM_VERSION_IPA 1 - -#define PROT_VERSION_SNA 1 /* hahaha */ -#define PROT_VERSION_IPv4 4 -#define PROT_VERSION_IPv6 6 - -#define OSA_ADDR_LEN 6 -#define IPA_SETADAPTERPARMS_IP_VERSION PROT_VERSION_IPv4 -#define SR_INFO_LEN 16 - -#define IPA_ARP_PROCESSING 0x00000001L -#define IPA_INBOUND_CHECKSUM 0x00000002L -#define IPA_OUTBOUND_CHECKSUM 0x00000004L -#define IPA_IP_FRAGMENTATION 0x00000008L -#define IPA_FILTERING 0x00000010L -#define IPA_IPv6 0x00000020L -#define IPA_MULTICASTING 0x00000040L -#define IPA_IP_REASSEMBLY 0x00000080L -#define IPA_QUERY_ARP_COUNTERS 0x00000100L -#define IPA_QUERY_ARP_ADDR_INFO 0x00000200L -#define IPA_SETADAPTERPARMS 0x00000400L -#define IPA_VLAN_PRIO 0x00000800L -#define IPA_PASSTHRU 0x00001000L -#define IPA_FULL_VLAN 0x00004000L -#define IPA_SOURCE_MAC_AVAIL 0x00010000L -#define IPA_OSA_MC_ROUTER_AVAIL 0x00020000L - -#define IPA_SETADP_QUERY_COMMANDS_SUPPORTED 0x01 -#define IPA_SETADP_ALTER_MAC_ADDRESS 0x02 -#define IPA_SETADP_ADD_DELETE_GROUP_ADDRESS 0x04 -#define IPA_SETADP_ADD_DELETE_FUNCTIONAL_ADDR 0x08 -#define IPA_SETADP_SET_ADDRESSING_MODE 0x10 -#define IPA_SETADP_SET_CONFIG_PARMS 0x20 -#define IPA_SETADP_SET_CONFIG_PARMS_EXTENDED 0x40 -#define IPA_SETADP_SET_BROADCAST_MODE 0x80 -#define IPA_SETADP_SEND_OSA_MESSAGE 0x0100 -#define IPA_SETADP_SET_SNMP_CONTROL 0x0200 -#define IPA_SETADP_READ_SNMP_PARMS 0x0400 -#define IPA_SETADP_WRITE_SNMP_PARMS 0x0800 -#define IPA_SETADP_QUERY_CARD_INFO 0x1000 - -#define CHANGE_ADDR_READ_MAC 0 -#define CHANGE_ADDR_REPLACE_MAC 1 -#define CHANGE_ADDR_ADD_MAC 2 -#define CHANGE_ADDR_DEL_MAC 4 -#define CHANGE_ADDR_RESET_MAC 8 -#define CHANGE_ADDR_READ_ADDR 0 -#define CHANGE_ADDR_ADD_ADDR 1 -#define CHANGE_ADDR_DEL_ADDR 2 -#define CHANGE_ADDR_FLUSH_ADDR_TABLE 4 - -/* we assumed, that the card is named card */ -#define qeth_is_supported(str) (card->ipa_supported&str) -#define qeth_is_supported6(str) (card->ipa6_supported&str) -#define qeth_is_adp_supported(str) (card->adp_supported&str) - -/* the same for all assist parms: */ -#define IPA_CMD_ASS_START 0x0001 -#define IPA_CMD_ASS_STOP 0x0002 - -#define IPA_CMD_ASS_CONFIGURE 0x0003 -#define IPA_CMD_ASS_ENABLE 0x0004 - -#define IPA_CMD_ASS_ARP_SET_NO_ENTRIES 0x0003 -#define IPA_CMD_ASS_ARP_QUERY_CACHE 0x0004 -#define IPA_CMD_ASS_ARP_ADD_ENTRY 0x0005 -#define IPA_CMD_ASS_ARP_REMOVE_ENTRY 0x0006 -#define IPA_CMD_ASS_ARP_FLUSH_CACHE 0x0007 -#define IPA_CMD_ASS_ARP_QUERY_INFO 0x0104 -#define IPA_CMD_ASS_ARP_QUERY_STATS 0x0204 - -#define IPA_CHECKSUM_DEFAULT_ENABLE_MASK 0x001a - -#define IPA_CMD_ASS_FILTER_SET_TYPES 0x0003 - -#define IPA_CMD_ASS_IPv6_SET_FUNCTIONS 0x0003 - -#define IPA_REPLY_SUCCESS 0 -#define IPA_REPLY_FAILED 1 -#define IPA_REPLY_OPNOTSUPP 2 -#define IPA_REPLY_OPNOTSUPP2 4 -#define IPA_REPLY_NOINFO 8 - -#define IPA_SETIP_FLAGS 0 -#define IPA_SETIP_VIPA_FLAGS 1 -#define IPA_SETIP_TAKEOVER_FLAGS 2 - -#define VIPA_2_B_ADDED 0 -#define VIPA_ESTABLISHED 1 -#define VIPA_2_B_REMOVED 2 -#define IPA_DELIP_FLAGS 0 +/* SETRTG IPA Command: ****************************************************/ +struct qeth_set_routing { + __u8 type; +}; -#define IPA_SETADP_CMDSIZE 40 +/* SETADAPTERPARMS IPA Command: *******************************************/ +struct qeth_query_cmds_supp { + __u32 no_lantypes_supp; + __u8 lan_type; + __u8 reserved1[3]; + __u32 supported_cmds; + __u8 reserved2[8]; +} __attribute__ ((packed)); + +struct qeth_change_addr { + __u32 cmd; + __u32 addr_size; + __u32 no_macs; + __u8 addr[OSA_ADDR_LEN]; +} __attribute__ ((packed)); -struct ipa_setadp_cmd { +struct qeth_ipacmd_setadpparms { __u32 supp_hw_cmds; __u32 reserved1; __u16 cmdlength; @@ -224,221 +288,197 @@ struct ipa_setadp_cmd { __u8 frame_seq_no; __u32 reserved3; union { - struct { - __u32 no_lantypes_supp; - __u8 lan_type; - __u8 reserved1[3]; - __u32 supported_cmds; - __u8 reserved2[8]; - } query_cmds_supp; - struct { - __u32 cmd; - __u32 addr_size; - __u32 no_macs; - __u8 addr[OSA_ADDR_LEN]; - } change_addr; + struct qeth_query_cmds_supp query_cmds_supp; + struct qeth_change_addr change_addr; __u32 mode; } data; -}; +} __attribute__ ((packed)); -struct ipa_cmd{ - __u8 command; - __u8 initiator; - __u16 seq_no; - __u16 return_code; - __u8 adapter_type; - __u8 rel_adapter_no; - __u8 prim_version_no; - __u8 param_count; - __u16 prot_version; - __u32 ipa_supported; - __u32 ipa_enabled; - union { - struct { - __u8 ip[4]; - __u8 netmask[4]; - __u32 flags; - } setdelip4; - struct { - __u8 ip[16]; - __u8 netmask[16]; - __u32 flags; - } setdelip6; - struct { - __u32 assist_no; - __u16 length; - __u16 command_code; - __u16 return_code; - __u8 number_of_replies; - __u8 seq_no; - union { - __u32 flags_32bit; - struct { - __u8 mac[6]; - __u8 reserved[2]; - __u8 ip[16]; - __u8 reserved2[32]; - } add_arp_entry; - __u8 ip[16]; - } data; - } setassparms; - struct { - __u8 mac[6]; - __u8 padding[2]; - __u8 ip6[12]; - __u8 ip4_6[4]; - } setdelipm; - struct { - __u8 type; - } setrtg; - struct ipa_setadp_cmd setadapterparms; - struct { - __u32 command; -#define ADDR_FRAME_TYPE_DIX 1 -#define ADDR_FRAME_TYPE_802_3 2 -#define ADDR_FRAME_TYPE_TR_WITHOUT_SR 0x10 -#define ADDR_FRAME_TYPE_TR_WITH_SR 0x20 - __u32 frame_type; - __u32 cmd_flags; - __u8 ip_addr[16]; - __u32 tag_field; - __u8 mac_addr[6]; - __u8 reserved[10]; - __u32 sr_len; - __u8 sr_info[SR_INFO_LEN]; - } add_addr_entry; - struct { - __u32 command; - __u32 cmd_flags; - __u8 ip_addr[16]; - __u32 tag_field; - } delete_addr_entry; - struct { - __u8 unique_id[8]; - } create_destroy_addr; - } data; -}__attribute__ ((packed)); +/* IPFRAME IPA Command: ***************************************************/ +/* TODO: define in analogy to commands define above */ -#define QETH_IOC_MAGIC 0x22 -/* these don't really have 'unsigned long' arguments but were defined that way */ -#define QETH_IOCPROC_OSAEINTERFACES _IOWR(QETH_IOC_MAGIC, 1, unsigned long) -#define QETH_IOCPROC_INTERFACECHANGES _IOWR(QETH_IOC_MAGIC, 2, unsigned long) - -#define SNMP_QUERY_CARD_INFO 0x00000002L -#define SNMP_REGISETER_MIB 0x00000004L -#define SNMP_GET_OID 0x00000010L -#define SNMP_SET_OID 0x00000011L -#define SNMP_GET_NEXT_OID 0x00000012L -#define SNMP_QUERY_ALERTS 0x00000020L -#define SNMP_SET_TRAP 0x00000021L - - -#define ARP_DATA_SIZE 3968 -#define ARP_FLUSH -3 -#define ARP_RETURNCODE_NOARPDATA -2 -#define ARP_RETURNCODE_ERROR -1 -#define ARP_RETURNCODE_SUCCESS 0 -#define ARP_RETURNCODE_LASTREPLY 1 - -#define SNMP_BASE_CMDLENGTH 44 -#define SNMP_SETADP_CMDLENGTH 16 -#define SNMP_REQUEST_DATA_OFFSET 16 +/* ADD_ADDR_ENTRY IPA Command: ********************************************/ +/* TODO: define in analogy to commands define above */ -struct snmp_ipa_setadp_cmd { - __u32 supp_hw_cmds; - __u32 reserved1; - __u16 cmdlength; - __u16 reserved2; - __u32 command_code; - __u16 return_code; - __u8 frames_used_total; - __u8 frame_seq_no; - __u32 reserved3; - __u8 snmp_token[16]; +/* DELETE_ADDR_ENTRY IPA Command: *****************************************/ +/* TODO: define in analogy to commands define above */ + +/* CREATE_ADDR IPA Command: ***********************************************/ +struct qeth_create_destroy_address { + __u8 unique_id[8]; +} __attribute__ ((packed)); + +/* REGISTER_LOCAL_ADDR IPA Command: ***************************************/ +/* TODO: define in analogy to commands define above */ + +/* UNREGISTER_LOCAL_ADDR IPA Command: *************************************/ +/* TODO: define in analogy to commands define above */ + +/* Header for each IPA command */ +struct qeth_ipacmd_hdr { + __u8 command; + __u8 initiator; + __u16 seqno; + __u16 return_code; + __u8 adapter_type; + __u8 rel_adapter_no; + __u8 prim_version_no; + __u8 param_count; + __u16 prot_version; + __u32 ipa_supported; + __u32 ipa_enabled; +} __attribute__ ((packed)); + +/* The IPA command itself */ +struct qeth_ipa_cmd { + struct qeth_ipacmd_hdr hdr; union { - struct { - __u32 snmp_request; - __u32 snmp_interface; - __u32 snmp_returncode; - __u32 snmp_firmwarelevel; - __u32 snmp_seqno; - __u8 snmp_data[ARP_DATA_SIZE]; - } snmp_subcommand; + struct qeth_ipacmd_setdelip4 setdelip4; + struct qeth_ipacmd_setdelip6 setdelip6; + struct qeth_ipacmd_setdelipm setdelipm; + struct qeth_ipacmd_setassparms setassparms; + struct qeth_create_destroy_address create_destroy_addr; + struct qeth_ipacmd_setadpparms setadapterparms; + struct qeth_set_routing setrtg; } data; -}__attribute__ ((packed)); +} __attribute__ ((packed)); +/* + * special command for ARP processing. + * this is not included in setassparms command before, because we get + * problem with the size of struct qeth_ipacmd_setassparms otherwise + */ +enum qeth_ipa_arp_return_codes { + QETH_IPA_ARP_RC_SUCCESS = 0x0000, + QETH_IPA_ARP_RC_FAILED = 0x0001, + QETH_IPA_ARP_RC_NOTSUPP = 0x0002, + QETH_IPA_ARP_RC_OUT_OF_RANGE = 0x0003, + QETH_IPA_ARP_RC_Q_NOTSUPP = 0x0004, + QETH_IPA_ARP_RC_Q_NO_DATA = 0x0008, +}; -struct arp_cmd { - __u8 command; - __u8 initiator; - __u16 seq_no; - __u16 return_code; - __u8 adapter_type; - __u8 rel_adapter_no; - __u8 prim_version_no; - __u8 param_count; - __u16 prot_version; - __u32 ipa_supported; - __u32 ipa_enabled; +#define QETH_QARP_DATA_SIZE 3968 +struct qeth_arp_query_data { + __u16 request_bits; + __u16 reply_bits; + __u32 no_entries; + char data[QETH_QARP_DATA_SIZE]; +} __attribute__((packed)); + +/* used as parameter for arp_query reply */ +struct qeth_arp_query_info { + __u32 udata_len; + __u32 udata_offset; + __u32 no_entries; + char *udata; +}; + +#define IPA_ARP_CMD_LEN (IPA_PDU_HEADER_SIZE+sizeof(struct qeth_ipa_arp_cmd)) +#define QETH_ARP_CMD_BASE_LEN (sizeof(struct qeth_ipacmd_hdr) + \ + sizeof(struct qeth_ipacmd_setassparms_hdr)) +#define QETH_IPA_ARP_DATA_POS(buffer) (buffer + IPA_PDU_HEADER_SIZE + \ + QETH_ARP_CMD_BASE_LEN) +struct qeth_ipa_arp_cmd { + struct qeth_ipacmd_hdr ihdr; + struct qeth_ipacmd_setassparms_hdr shdr; union { - struct { - __u32 assist_no; - __u16 length; - __u16 command_code; - __u16 return_code; - __u8 number_of_replies; - __u8 seq_no; - union { - struct { - __u16 tcpip_requestbitmask; - __u16 osa_setbitmask; - __u32 number_of_entries; - __u8 arp_data[ARP_DATA_SIZE]; - } queryarp_data; - } data; - } setassparms; - struct snmp_ipa_setadp_cmd setadapterparms; + struct qeth_arp_query_data query_arp; } data; -}__attribute__ ((packed)); +} __attribute__((packed)); +/* Helper functions */ +#define IS_IPA_REPLY(cmd) (cmd->hdr.initiator == IPA_CMD_INITIATOR_HOST) -#define IPA_PDU_HEADER_SIZE 0x40 -#define QETH_IPA_PDU_LEN_TOTAL(buffer) (buffer+0x0e) -#define QETH_IPA_PDU_LEN_PDU1(buffer) (buffer+0x26) -#define QETH_IPA_PDU_LEN_PDU2(buffer) (buffer+0x2a) -#define QETH_IPA_PDU_LEN_PDU3(buffer) (buffer+0x3a) +/*****************************************************************************/ +/* END OF IP Assist related definitions */ +/*****************************************************************************/ -extern unsigned char IPA_PDU_HEADER[]; - -#define QETH_IPA_CMD_DEST_ADDR(buffer) (buffer+0x2c) -#define PDU_ENCAPSULATION(buffer) \ - (buffer+ \ - *(buffer+ (*(buffer+0x0b))+ *(buffer+*(buffer+0x0b)+0x11) +0x07)) +extern unsigned char WRITE_CCW[]; +extern unsigned char READ_CCW[]; -#define IS_IPA(buffer) ((buffer) && ( *(buffer+ ((*(buffer+0x0b))+4) )==0xc1) ) +extern unsigned char CM_ENABLE[]; +#define CM_ENABLE_SIZE 0x63 +#define QETH_CM_ENABLE_ISSUER_RM_TOKEN(buffer) (buffer+0x2c) +#define QETH_CM_ENABLE_FILTER_TOKEN(buffer) (buffer+0x53) +#define QETH_CM_ENABLE_USER_DATA(buffer) (buffer+0x5b) -#define IS_IPA_REPLY(buffer) ( (buffer) && ( (*(PDU_ENCAPSULATION(buffer)+1))==INITIATOR_HOST ) ) +#define QETH_CM_ENABLE_RESP_FILTER_TOKEN(buffer) \ + (PDU_ENCAPSULATION(buffer)+ 0x13) -#define CCW_NOP_CMD 0x03 -#define CCW_NOP_COUNT 1 -extern unsigned char WRITE_CCW[]; +extern unsigned char CM_SETUP[]; +#define CM_SETUP_SIZE 0x64 +#define QETH_CM_SETUP_DEST_ADDR(buffer) (buffer+0x2c) +#define QETH_CM_SETUP_CONNECTION_TOKEN(buffer) (buffer+0x51) +#define QETH_CM_SETUP_FILTER_TOKEN(buffer) (buffer+0x5a) -extern unsigned char READ_CCW[]; +#define QETH_CM_SETUP_RESP_DEST_ADDR(buffer) \ + (PDU_ENCAPSULATION(buffer) + 0x1a) -#endif /* __QETH_MPC_H__ */ +extern unsigned char ULP_ENABLE[]; +#define ULP_ENABLE_SIZE 0x6b +#define QETH_ULP_ENABLE_LINKNUM(buffer) (buffer+0x61) +#define QETH_ULP_ENABLE_DEST_ADDR(buffer) (buffer+0x2c) +#define QETH_ULP_ENABLE_FILTER_TOKEN(buffer) (buffer+0x53) +#define QETH_ULP_ENABLE_PORTNAME_AND_LL(buffer) (buffer+0x62) +#define QETH_ULP_ENABLE_RESP_FILTER_TOKEN(buffer) \ + (PDU_ENCAPSULATION(buffer) + 0x13) +#define QETH_ULP_ENABLE_RESP_MAX_MTU(buffer) \ + (PDU_ENCAPSULATION(buffer)+ 0x1f) +#define QETH_ULP_ENABLE_RESP_DIFINFO_LEN(buffer) \ + (PDU_ENCAPSULATION(buffer) + 0x17) +#define QETH_ULP_ENABLE_RESP_LINK_TYPE(buffer) \ + (PDU_ENCAPSULATION(buffer)+ 0x2b) +extern unsigned char ULP_SETUP[]; +#define ULP_SETUP_SIZE 0x6c +#define QETH_ULP_SETUP_DEST_ADDR(buffer) (buffer+0x2c) +#define QETH_ULP_SETUP_CONNECTION_TOKEN(buffer) (buffer+0x51) +#define QETH_ULP_SETUP_FILTER_TOKEN(buffer) (buffer+0x5a) +#define QETH_ULP_SETUP_CUA(buffer) (buffer+0x68) +#define QETH_ULP_SETUP_REAL_DEVADDR(buffer) (buffer+0x6a) +#define QETH_ULP_SETUP_RESP_CONNECTION_TOKEN(buffer) \ + (PDU_ENCAPSULATION(buffer)+0x1a) +extern unsigned char DM_ACT[]; +#define DM_ACT_SIZE 0x55 +#define QETH_DM_ACT_DEST_ADDR(buffer) (buffer+0x2c) +#define QETH_DM_ACT_CONNECTION_TOKEN(buffer) (buffer+0x51) +#define QETH_TRANSPORT_HEADER_SEQ_NO(buffer) (buffer+4) +#define QETH_PDU_HEADER_SEQ_NO(buffer) (buffer+0x1c) +#define QETH_PDU_HEADER_ACK_SEQ_NO(buffer) (buffer+0x20) +extern unsigned char IDX_ACTIVATE_READ[]; +extern unsigned char IDX_ACTIVATE_WRITE[]; +#define IDX_ACTIVATE_SIZE 0x22 +#define QETH_IDX_ACT_ISSUER_RM_TOKEN(buffer) (buffer+0x0c) +#define QETH_IDX_NO_PORTNAME_REQUIRED(buffer) ((buffer)[0x0b]&0x80) +#define QETH_IDX_ACT_FUNC_LEVEL(buffer) (buffer+0x10) +#define QETH_IDX_ACT_DATASET_NAME(buffer) (buffer+0x16) +#define QETH_IDX_ACT_QDIO_DEV_CUA(buffer) (buffer+0x1e) +#define QETH_IDX_ACT_QDIO_DEV_REALADDR(buffer) (buffer+0x20) +#define QETH_IS_IDX_ACT_POS_REPLY(buffer) (((buffer)[0x08]&3)==2) +#define QETH_IDX_REPLY_LEVEL(buffer) (buffer+0x12) +#define PDU_ENCAPSULATION(buffer) \ + (buffer + *(buffer + (*(buffer+0x0b)) + \ + *(buffer + *(buffer+0x0b)+0x11) +0x07)) +#define IS_IPA(buffer) \ + ((buffer) && \ + ( *(buffer + ((*(buffer+0x0b))+4) )==0xc1) ) +#define ADDR_FRAME_TYPE_DIX 1 +#define ADDR_FRAME_TYPE_802_3 2 +#define ADDR_FRAME_TYPE_TR_WITHOUT_SR 0x10 +#define ADDR_FRAME_TYPE_TR_WITH_SR 0x20 +#endif diff -puN /dev/null drivers/s390/net/qeth_proc.c --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ 25-akpm/drivers/s390/net/qeth_proc.c 2004-04-08 13:55:46.769914064 -0700 @@ -0,0 +1,468 @@ +/* + * + * linux/drivers/s390/net/qeth_fs.c ($Revision: 1.5 $) + * + * Linux on zSeries OSA Express and HiperSockets support + * This file contains code related to procfs. + * + * Copyright 2000,2003 IBM Corporation + * + * Author(s): Thomas Spatzier + * + */ +#include +#include +#include +#include +#include +#include + +#include "qeth.h" +#include "qeth_mpc.h" +#include "qeth_fs.h" + +/***** /proc/qeth *****/ +#define QETH_PROCFILE_NAME "qeth" +static struct proc_dir_entry *qeth_procfile; + +static void * +qeth_procfile_seq_start(struct seq_file *s, loff_t *offset) +{ + struct list_head *next_card = NULL; + int i = 0; + + down_read(&qeth_ccwgroup_driver.driver.bus->subsys.rwsem); + + if (*offset == 0) + return SEQ_START_TOKEN; + + /* get card at pos *offset */ + list_for_each(next_card, &qeth_ccwgroup_driver.driver.devices) + if (++i == *offset) + return next_card; + + return NULL; +} + +static void +qeth_procfile_seq_stop(struct seq_file *s, void* it) +{ + up_read(&qeth_ccwgroup_driver.driver.bus->subsys.rwsem); +} + +static void * +qeth_procfile_seq_next(struct seq_file *s, void *it, loff_t *offset) +{ + struct list_head *next_card = NULL; + struct list_head *current_card; + + if (it == SEQ_START_TOKEN) { + next_card = qeth_ccwgroup_driver.driver.devices.next; + if (next_card->next == next_card) /* list empty */ + return NULL; + (*offset)++; + } else { + current_card = (struct list_head *)it; + if (current_card->next == &qeth_ccwgroup_driver.driver.devices) + return NULL; /* end of list reached */ + next_card = current_card->next; + (*offset)++; + } + + return next_card; +} + +static inline const char * +qeth_get_router_str(struct qeth_card *card, int ipv) +{ + int routing_type = 0; + + if (ipv == 4){ + routing_type = card->options.route4.type; + } else { +#ifdef CONFIG_QETH_IPV6 + routing_type = card->options.route6.type; +#else + return "n/a"; +#endif /* CONFIG_QETH_IPV6 */ + } + + if (routing_type == PRIMARY_ROUTER) + return "pri"; + else if (routing_type == SECONDARY_ROUTER) + return "sec"; + else if (routing_type == MULTICAST_ROUTER) + return "mc"; + else if (routing_type == PRIMARY_CONNECTOR) + return "p.c"; + else if (routing_type == SECONDARY_CONNECTOR) + return "s.c"; + else if (routing_type == NO_ROUTER) + return "no"; + else + return "unk"; +} + +static int +qeth_procfile_seq_show(struct seq_file *s, void *it) +{ + struct device *device; + struct qeth_card *card; + char tmp[12]; /* for qeth_get_prioq_str */ + + if (it == SEQ_START_TOKEN){ + seq_printf(s, "devices CHPID interface " + "cardtype port chksum prio-q'ing rtr4 " + "rtr6 fsz cnt\n"); + seq_printf(s, "-------------------------- ----- ---------- " + "-------------- ---- ------ ---------- ---- " + "---- ----- -----\n"); + } else { + device = list_entry(it, struct device, driver_list); + card = device->driver_data; + seq_printf(s, "%s/%s/%s x%02X %-10s %-14s %-4i ", + CARD_RDEV_ID(card), + CARD_WDEV_ID(card), + CARD_DDEV_ID(card), + card->info.chpid, + card->info.if_name, + qeth_get_cardname_short(card), + card->info.portno); + if (card->lan_online) + seq_printf(s, "%-6s %-10s %-4s %-4s %-5s %-5i\n", + qeth_get_checksum_str(card), + qeth_get_prioq_str(card, tmp), + qeth_get_router_str(card, 4), + qeth_get_router_str(card, 6), + qeth_get_bufsize_str(card), + card->qdio.in_buf_pool.buf_count); + else + seq_printf(s, " +++ LAN OFFLINE +++\n"); + } + return 0; +} + +static struct seq_operations qeth_procfile_seq_ops = { + .start = qeth_procfile_seq_start, + .stop = qeth_procfile_seq_stop, + .next = qeth_procfile_seq_next, + .show = qeth_procfile_seq_show, +}; + +static int +qeth_procfile_open(struct inode *inode, struct file *file) +{ + return seq_open(file, &qeth_procfile_seq_ops); +} + +static struct file_operations qeth_procfile_fops = { + .owner = THIS_MODULE, + .open = qeth_procfile_open, + .read = seq_read, + .llseek = seq_lseek, + .release = seq_release, +}; + +/***** /proc/qeth_perf *****/ +#define QETH_PERF_PROCFILE_NAME "qeth_perf" +static struct proc_dir_entry *qeth_perf_procfile; + +#ifdef CONFIG_QETH_PERF_STATS + +static void * +qeth_perf_procfile_seq_start(struct seq_file *s, loff_t *offset) +{ + struct list_head *next_card = NULL; + int i = 0; + + down_read(&qeth_ccwgroup_driver.driver.bus->subsys.rwsem); + /* get card at pos *offset */ + list_for_each(next_card, &qeth_ccwgroup_driver.driver.devices){ + if (i == *offset) + return next_card; + i++; + } + return NULL; +} + +static void +qeth_perf_procfile_seq_stop(struct seq_file *s, void* it) +{ + up_read(&qeth_ccwgroup_driver.driver.bus->subsys.rwsem); +} + +static void * +qeth_perf_procfile_seq_next(struct seq_file *s, void *it, loff_t *offset) +{ + struct list_head *current_card = (struct list_head *)it; + + if (current_card->next == &qeth_ccwgroup_driver.driver.devices) + return NULL; /* end of list reached */ + (*offset)++; + return current_card->next; +} + +static int +qeth_perf_procfile_seq_show(struct seq_file *s, void *it) +{ + struct device *device; + struct qeth_card *card; + + device = list_entry(it, struct device, driver_list); + card = device->driver_data; + seq_printf(s, "For card with devnos %s/%s/%s (%s):\n", + CARD_RDEV_ID(card), + CARD_WDEV_ID(card), + CARD_DDEV_ID(card), + card->info.if_name + ); + seq_printf(s, " Skb's/buffers received : %li/%i\n" + " Skb's/buffers sent : %li/%i\n\n", + card->stats.rx_packets, card->perf_stats.bufs_rec, + card->stats.tx_packets, card->perf_stats.bufs_sent + ); + seq_printf(s, " Skb's/buffers sent without packing : %li/%i\n" + " Skb's/buffers sent with packing : %i/%i\n\n", + card->stats.tx_packets - card->perf_stats.skbs_sent_pack, + card->perf_stats.bufs_sent - card->perf_stats.bufs_sent_pack, + card->perf_stats.skbs_sent_pack, + card->perf_stats.bufs_sent_pack + ); + seq_printf(s, " Packing state changes no pkg.->packing : %i/%i\n" + " Current buffer usage (outbound q's) : " + "%i/%i/%i/%i\n\n", + card->perf_stats.sc_dp_p, card->perf_stats.sc_p_dp, + atomic_read(&card->qdio.out_qs[0]->used_buffers), + (card->qdio.no_out_queues > 1)? + atomic_read(&card->qdio.out_qs[1]->used_buffers) + : 0, + (card->qdio.no_out_queues > 2)? + atomic_read(&card->qdio.out_qs[2]->used_buffers) + : 0, + (card->qdio.no_out_queues > 3)? + atomic_read(&card->qdio.out_qs[3]->used_buffers) + : 0 + ); + seq_printf(s, " Inbound time (in us) : %i\n" + " Inbound cnt : %i\n" + " Outbound time (in us, incl QDIO) : %i\n" + " Outbound cnt : %i\n" + " Watermarks L/H : %i/%i\n\n", + card->perf_stats.inbound_time, + card->perf_stats.inbound_cnt, + card->perf_stats.outbound_time, + card->perf_stats.outbound_cnt, + QETH_LOW_WATERMARK_PACK, QETH_HIGH_WATERMARK_PACK + ); + + return 0; +} + +static struct seq_operations qeth_perf_procfile_seq_ops = { + .start = qeth_perf_procfile_seq_start, + .stop = qeth_perf_procfile_seq_stop, + .next = qeth_perf_procfile_seq_next, + .show = qeth_perf_procfile_seq_show, +}; + +static int +qeth_perf_procfile_open(struct inode *inode, struct file *file) +{ + return seq_open(file, &qeth_perf_procfile_seq_ops); +} + +static struct file_operations qeth_perf_procfile_fops = { + .owner = THIS_MODULE, + .open = qeth_perf_procfile_open, + .read = seq_read, + .llseek = seq_lseek, + .release = seq_release, +}; + +#define qeth_perf_procfile_created qeth_perf_procfile +#else +#define qeth_perf_procfile_created 1 +#endif /* CONFIG_QETH_PERF_STATS */ + +/***** /proc/qeth_ipa_takeover *****/ +#define QETH_IPATO_PROCFILE_NAME "qeth_ipa_takeover" +static struct proc_dir_entry *qeth_ipato_procfile; + +static void * +qeth_ipato_procfile_seq_start(struct seq_file *s, loff_t *offset) +{ + struct list_head *next_card = NULL; + int i = 0; + + down_read(&qeth_ccwgroup_driver.driver.bus->subsys.rwsem); + /* TODO: finish this */ + /* + * maybe SEQ_SATRT_TOKEN can be returned for offset 0 + * output driver settings then; + * else output setting for respective card + */ + /* get card at pos *offset */ + list_for_each(next_card, &qeth_ccwgroup_driver.driver.devices){ + if (i == *offset) + return next_card; + i++; + } + return NULL; +} + +static void +qeth_ipato_procfile_seq_stop(struct seq_file *s, void* it) +{ + up_read(&qeth_ccwgroup_driver.driver.bus->subsys.rwsem); +} + +static void * +qeth_ipato_procfile_seq_next(struct seq_file *s, void *it, loff_t *offset) +{ + struct list_head *current_card = (struct list_head *)it; + + /* TODO: finish this */ + /* + * maybe SEQ_SATRT_TOKEN can be returned for offset 0 + * output driver settings then; + * else output setting for respective card + */ + if (current_card->next == &qeth_ccwgroup_driver.driver.devices) + return NULL; /* end of list reached */ + (*offset)++; + return current_card->next; +} + +static int +qeth_ipato_procfile_seq_show(struct seq_file *s, void *it) +{ + struct device *device; + struct qeth_card *card; + + /* TODO: finish this */ + /* + * maybe SEQ_SATRT_TOKEN can be returned for offset 0 + * output driver settings then; + * else output setting for respective card + */ + device = list_entry(it, struct device, driver_list); + card = device->driver_data; + + return 0; +} + +static struct seq_operations qeth_ipato_procfile_seq_ops = { + .start = qeth_ipato_procfile_seq_start, + .stop = qeth_ipato_procfile_seq_stop, + .next = qeth_ipato_procfile_seq_next, + .show = qeth_ipato_procfile_seq_show, +}; + +static int +qeth_ipato_procfile_open(struct inode *inode, struct file *file) +{ + return seq_open(file, &qeth_ipato_procfile_seq_ops); +} + +static struct file_operations qeth_ipato_procfile_fops = { + .owner = THIS_MODULE, + .open = qeth_ipato_procfile_open, + .read = seq_read, + .llseek = seq_lseek, + .release = seq_release, +}; + +int __init +qeth_create_procfs_entries(void) +{ + qeth_procfile = create_proc_entry(QETH_PROCFILE_NAME, + S_IFREG | 0444, NULL); + if (qeth_procfile) + qeth_procfile->proc_fops = &qeth_procfile_fops; + +#ifdef CONFIG_QETH_PERF_STATS + qeth_perf_procfile = create_proc_entry(QETH_PERF_PROCFILE_NAME, + S_IFREG | 0444, NULL); + if (qeth_perf_procfile) + qeth_perf_procfile->proc_fops = &qeth_perf_procfile_fops; +#endif /* CONFIG_QETH_PERF_STATS */ + + qeth_ipato_procfile = create_proc_entry(QETH_IPATO_PROCFILE_NAME, + S_IFREG | 0444, NULL); + if (qeth_ipato_procfile) + qeth_ipato_procfile->proc_fops = &qeth_ipato_procfile_fops; + + if (qeth_procfile && + qeth_ipato_procfile && + qeth_perf_procfile_created) + return 0; + else + return -ENOMEM; +} + +void __exit +qeth_remove_procfs_entries(void) +{ + if (qeth_procfile) + remove_proc_entry(QETH_PROCFILE_NAME, NULL); + if (qeth_perf_procfile) + remove_proc_entry(QETH_PERF_PROCFILE_NAME, NULL); + if (qeth_ipato_procfile) + remove_proc_entry(QETH_IPATO_PROCFILE_NAME, NULL); +} + + +/* ONLY FOR DEVELOPMENT! -> make it as module */ +/* +static void +qeth_create_sysfs_entries(void) +{ + struct device *dev; + + down_read(&qeth_ccwgroup_driver.driver.bus->subsys.rwsem); + + list_for_each_entry(dev, &qeth_ccwgroup_driver.driver.devices, + driver_list) + qeth_create_device_attributes(dev); + + up_read(&qeth_ccwgroup_driver.driver.bus->subsys.rwsem); +} + +static void +qeth_remove_sysfs_entries(void) +{ + struct device *dev; + + down_read(&qeth_ccwgroup_driver.driver.bus->subsys.rwsem); + + list_for_each_entry(dev, &qeth_ccwgroup_driver.driver.devices, + driver_list) + qeth_remove_device_attributes(dev); + + up_read(&qeth_ccwgroup_driver.driver.bus->subsys.rwsem); +} + +static int __init +qeth_fs_init(void) +{ + printk(KERN_INFO "qeth_fs_init\n"); + qeth_create_procfs_entries(); + qeth_create_sysfs_entries(); + + return 0; +} + +static void __exit +qeth_fs_exit(void) +{ + printk(KERN_INFO "qeth_fs_exit\n"); + qeth_remove_procfs_entries(); + qeth_remove_sysfs_entries(); +} + + +module_init(qeth_fs_init); +module_exit(qeth_fs_exit); + +MODULE_LICENSE("GPL"); +*/ diff -puN /dev/null drivers/s390/net/qeth_sys.c --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ 25-akpm/drivers/s390/net/qeth_sys.c 2004-04-08 13:55:46.775913152 -0700 @@ -0,0 +1,1479 @@ +/* + * + * linux/drivers/s390/net/qeth_sys.c ($Revision: 1.19 $) + * + * Linux on zSeries OSA Express and HiperSockets support + * This file contains code related to sysfs. + * + * Copyright 2000,2003 IBM Corporation + * + * Author(s): Thomas Spatzier + * Frank Pavlic + * + */ +#include +#include + +#include + +#include "qeth.h" +#include "qeth_mpc.h" +#include "qeth_fs.h" + +/*****************************************************************************/ +/* */ +/* /sys-fs stuff UNDER DEVELOPMENT !!! */ +/* */ +/*****************************************************************************/ +//low/high watermark + +static ssize_t +qeth_dev_state_show(struct device *dev, char *buf) +{ + struct qeth_card *card = dev->driver_data; + if (!card) + return -EINVAL; + + switch (card->state) { + case CARD_STATE_DOWN: + return sprintf(buf, "DOWN\n"); + case CARD_STATE_HARDSETUP: + return sprintf(buf, "HARDSETUP\n"); + case CARD_STATE_SOFTSETUP: + return sprintf(buf, "SOFTSETUP\n"); + case CARD_STATE_UP_LAN_OFFLINE: + return sprintf(buf, "UP (LAN OFFLINE)\n"); + case CARD_STATE_UP_LAN_ONLINE: + return sprintf(buf, "UP (LAN ONLINE)\n"); + case CARD_STATE_RECOVER: + return sprintf(buf, "RECOVER\n"); + default: + return sprintf(buf, "UNKNOWN\n"); + } +} + +static DEVICE_ATTR(state, 0444, qeth_dev_state_show, NULL); + +static ssize_t +qeth_dev_chpid_show(struct device *dev, char *buf) +{ + struct qeth_card *card = dev->driver_data; + if (!card) + return -EINVAL; + + return sprintf(buf, "%02X\n", card->info.chpid); +} + +static DEVICE_ATTR(chpid, 0444, qeth_dev_chpid_show, NULL); + +static ssize_t +qeth_dev_if_name_show(struct device *dev, char *buf) +{ + struct qeth_card *card = dev->driver_data; + if (!card) + return -EINVAL; + + return sprintf(buf, "%s\n", card->info.if_name); +} + +static DEVICE_ATTR(if_name, 0444, qeth_dev_if_name_show, NULL); + +static ssize_t +qeth_dev_card_type_show(struct device *dev, char *buf) +{ + struct qeth_card *card = dev->driver_data; + if (!card) + return -EINVAL; + + return sprintf(buf, "%s\n", qeth_get_cardname_short(card)); +} + +static DEVICE_ATTR(card_type, 0444, qeth_dev_card_type_show, NULL); + +static ssize_t +qeth_dev_portno_show(struct device *dev, char *buf) +{ + struct qeth_card *card = dev->driver_data; + if (!card) + return -EINVAL; + + return sprintf(buf, "%i\n", card->info.portno); +} + +static ssize_t +qeth_dev_portno_store(struct device *dev, const char *buf, size_t count) +{ + struct qeth_card *card = dev->driver_data; + char *tmp; + unsigned int portno; + + if (!card) + return -EINVAL; + + if ((card->state != CARD_STATE_DOWN) && + (card->state != CARD_STATE_RECOVER)) + return -EPERM; + + portno = simple_strtoul(buf, &tmp, 16); + if ((portno < 0) || (portno > MAX_PORTNO)){ + PRINT_WARN("portno 0x%X is out of range\n", portno); + return -EINVAL; + } + + card->info.portno = portno; + return count; +} + +static DEVICE_ATTR(portno, 0644, qeth_dev_portno_show, qeth_dev_portno_store); + +static ssize_t +qeth_dev_portname_show(struct device *dev, char *buf) +{ + struct qeth_card *card = dev->driver_data; + char portname[9] = {0, }; + + if (!card) + return -EINVAL; + + if (card->info.portname_required) { + memcpy(portname, card->info.portname + 1, 8); + EBCASC(portname, 8); + return sprintf(buf, "%s\n", portname); + } else + return sprintf(buf, "no portname required\n"); +} + +static ssize_t +qeth_dev_portname_store(struct device *dev, const char *buf, size_t count) +{ + struct qeth_card *card = dev->driver_data; + char *tmp; + int i; + + if (!card) + return -EINVAL; + + if ((card->state != CARD_STATE_DOWN) && + (card->state != CARD_STATE_RECOVER)) + return -EPERM; + + tmp = strsep((char **) &buf, "\n"); + if ((strlen(tmp) > 8) || (strlen(tmp) < 2)) + return -EINVAL; + + card->info.portname[0] = strlen(tmp); + /* for beauty reasons */ + for (i = 1; i < 9; i++) + card->info.portname[i] = ' '; + strcpy(card->info.portname + 1, tmp); + ASCEBC(card->info.portname + 1, 8); + + return count; +} + +static DEVICE_ATTR(portname, 0644, qeth_dev_portname_show, + qeth_dev_portname_store); + +static ssize_t +qeth_dev_checksum_show(struct device *dev, char *buf) +{ + struct qeth_card *card = dev->driver_data; + + if (!card) + return -EINVAL; + + return sprintf(buf, "%s checksumming\n", qeth_get_checksum_str(card)); +} + +static ssize_t +qeth_dev_checksum_store(struct device *dev, const char *buf, size_t count) +{ + struct qeth_card *card = dev->driver_data; + char *tmp; + + if (!card) + return -EINVAL; + + if ((card->state != CARD_STATE_DOWN) && + (card->state != CARD_STATE_RECOVER)) + return -EPERM; + + tmp = strsep((char **) &buf, "\n"); + if (!strcmp(tmp, "sw_checksumming")) + card->options.checksum_type = SW_CHECKSUMMING; + else if (!strcmp(tmp, "hw_checksumming")) + card->options.checksum_type = HW_CHECKSUMMING; + else if (!strcmp(tmp, "no_checksumming")) + card->options.checksum_type = NO_CHECKSUMMING; + else { + PRINT_WARN("Unknown checksumming type '%s'\n", tmp); + return -EINVAL; + } + return count; +} + +static DEVICE_ATTR(checksumming, 0644, qeth_dev_checksum_show, + qeth_dev_checksum_store); + +static ssize_t +qeth_dev_prioqing_show(struct device *dev, char *buf) +{ + struct qeth_card *card = dev->driver_data; + + if (!card) + return -EINVAL; + + switch (card->qdio.do_prio_queueing) { + case QETH_PRIO_Q_ING_PREC: + return sprintf(buf, "%s\n", "by precedence"); + case QETH_PRIO_Q_ING_TOS: + return sprintf(buf, "%s\n", "by type of service"); + default: + return sprintf(buf, "always queue %i\n", + card->qdio.default_out_queue); + } +} + +static ssize_t +qeth_dev_prioqing_store(struct device *dev, const char *buf, size_t count) +{ + struct qeth_card *card = dev->driver_data; + char *tmp; + + if (!card) + return -EINVAL; + + if ((card->state != CARD_STATE_DOWN) && + (card->state != CARD_STATE_RECOVER)) + return -EPERM; + + tmp = strsep((char **) &buf, "\n"); + if (!strcmp(tmp, "prio_queueing_prec")) + card->qdio.do_prio_queueing = QETH_PRIO_Q_ING_PREC; + else if (!strcmp(tmp, "prio_queueing_tos")) + card->qdio.do_prio_queueing = QETH_PRIO_Q_ING_TOS; + else if (!strcmp(tmp, "no_prio_queueing:0")) { + card->qdio.do_prio_queueing = QETH_NO_PRIO_QUEUEING; + card->qdio.default_out_queue = 0; + } else if (!strcmp(tmp, "no_prio_queueing:1")) { + card->qdio.do_prio_queueing = QETH_NO_PRIO_QUEUEING; + card->qdio.default_out_queue = 1; + } else if (!strcmp(tmp, "no_prio_queueing:2")) { + card->qdio.do_prio_queueing = QETH_NO_PRIO_QUEUEING; + card->qdio.default_out_queue = 2; + } else if (!strcmp(tmp, "no_prio_queueing:3")) { + card->qdio.do_prio_queueing = QETH_NO_PRIO_QUEUEING; + card->qdio.default_out_queue = 3; + } else if (!strcmp(tmp, "no_prio_queueing")) { + card->qdio.do_prio_queueing = QETH_NO_PRIO_QUEUEING; + card->qdio.default_out_queue = QETH_DEFAULT_QUEUE; + } else { + PRINT_WARN("Unknown queueing type '%s'\n", tmp); + return -EINVAL; + } + return count; +} + +static DEVICE_ATTR(priority_queueing, 0644, qeth_dev_prioqing_show, + qeth_dev_prioqing_store); + +static ssize_t +qeth_dev_bufcnt_show(struct device *dev, char *buf) +{ + struct qeth_card *card = dev->driver_data; + + if (!card) + return -EINVAL; + + return sprintf(buf, "%i\n", card->qdio.in_buf_pool.buf_count); +} + +static ssize_t +qeth_dev_bufcnt_store(struct device *dev, const char *buf, size_t count) +{ + struct qeth_card *card = dev->driver_data; + char *tmp; + unsigned int cnt; + + if (!card) + return -EINVAL; + + if ((card->state != CARD_STATE_DOWN) && + (card->state != CARD_STATE_RECOVER)) + return -EPERM; + + cnt = simple_strtoul(buf, &tmp, 16); + cnt = (cnt < QETH_IN_BUF_COUNT_MIN) ? QETH_IN_BUF_COUNT_MIN : + ((cnt > QETH_IN_BUF_COUNT_MAX) ? QETH_IN_BUF_COUNT_MAX : cnt); + card->qdio.in_buf_pool.buf_count = cnt; + /* TODO: steel/add buffers from/to a running card's buffer pool (?) */ + + return count; +} + +static DEVICE_ATTR(buffer_count, 0644, qeth_dev_bufcnt_show, + qeth_dev_bufcnt_store); + +static inline ssize_t +qeth_dev_route_show(struct qeth_routing_info *route, char *buf) +{ + switch (route->type) { + case PRIMARY_ROUTER: + return sprintf(buf, "%s\n", "primary router"); + case SECONDARY_ROUTER: + return sprintf(buf, "%s\n", "secondary router"); + case MULTICAST_ROUTER: + return sprintf(buf, "%s\n", "multicast router"); + case PRIMARY_CONNECTOR: + return sprintf(buf, "%s\n", "primary connector"); + case SECONDARY_CONNECTOR: + return sprintf(buf, "%s\n", "secondary connector"); + default: + return sprintf(buf, "%s\n", "no"); + } +} + +static ssize_t +qeth_dev_route4_show(struct device *dev, char *buf) +{ + struct qeth_card *card = dev->driver_data; + + if (!card) + return -EINVAL; + + return qeth_dev_route_show(&card->options.route4, buf); +} + +static inline ssize_t +qeth_dev_route_store(struct qeth_card *card, struct qeth_routing_info *route, + enum qeth_prot_versions prot, const char *buf, size_t count) +{ + enum qeth_routing_types old_route_type = route->type; + char *tmp; + int rc; + + tmp = strsep((char **) &buf, "\n"); + + if (!strcmp(tmp, "no_router")){ + route->type = NO_ROUTER; + goto check_reset; + } + + if (card->info.type == QETH_CARD_TYPE_IQD) { + if (!strcmp(tmp, "primary_connector")) { + route->type = PRIMARY_CONNECTOR; + } else if (!strcmp(tmp, "secondary_connector")) { + route->type = SECONDARY_CONNECTOR; + } else if (!strcmp(tmp, "multicast_router")) { + route->type = MULTICAST_ROUTER; + } else + goto out_inval; + } else { + if (!strcmp(tmp, "primary_router")) { + route->type = PRIMARY_ROUTER; + } else if (!strcmp(tmp, "secondary_router")) { + route->type = SECONDARY_ROUTER; + } else if (!strcmp(tmp, "multicast_router")) { + if (qeth_is_ipafunc_supported(card, prot, + IPA_OSA_MC_ROUTER)) + route->type = MULTICAST_ROUTER; + else + goto out_inval; + } else + goto out_inval; + } +check_reset: + if (old_route_type != route->type){ + if (prot == QETH_PROT_IPV4) + rc = qeth_setrouting_v4(card); + else if (prot == QETH_PROT_IPV6) + rc = qeth_setrouting_v6(card); + } + return count; +out_inval: + PRINT_WARN("Routing type '%s' not supported for interface %s.\n" + "Router status not changed.\n", + tmp, card->info.if_name); + return -EINVAL; +} + +static ssize_t +qeth_dev_route4_store(struct device *dev, const char *buf, size_t count) +{ + struct qeth_card *card = dev->driver_data; + + if (!card) + return -EINVAL; + + return qeth_dev_route_store(card, &card->options.route4, + QETH_PROT_IPV4, buf, count); +} + +static DEVICE_ATTR(route4, 0644, qeth_dev_route4_show, qeth_dev_route4_store); + +#ifdef CONFIG_QETH_IPV6 +static ssize_t +qeth_dev_route6_show(struct device *dev, char *buf) +{ + struct qeth_card *card = dev->driver_data; + + if (!card) + return -EINVAL; + + if (!qeth_is_supported(card, IPA_IPV6)) + return sprintf(buf, "%s\n", "n/a"); + + return qeth_dev_route_show(&card->options.route6, buf); +} + +static ssize_t +qeth_dev_route6_store(struct device *dev, const char *buf, size_t count) +{ + struct qeth_card *card = dev->driver_data; + + if (!card) + return -EINVAL; + + if (!qeth_is_supported(card, IPA_IPV6)){ + PRINT_WARN("IPv6 not supported for interface %s.\n" + "Routing status no changed.\n", + card->info.if_name); + return -ENOTSUPP; + } + + return qeth_dev_route_store(card, &card->options.route6, + QETH_PROT_IPV6, buf, count); +} + +static DEVICE_ATTR(route6, 0644, qeth_dev_route6_show, qeth_dev_route6_store); +#endif + +static ssize_t +qeth_dev_add_hhlen_show(struct device *dev, char *buf) +{ + struct qeth_card *card = dev->driver_data; + + if (!card) + return -EINVAL; + + return sprintf(buf, "%i\n", card->options.add_hhlen); +} + +static ssize_t +qeth_dev_add_hhlen_store(struct device *dev, const char *buf, size_t count) +{ + struct qeth_card *card = dev->driver_data; + char *tmp; + int i; + + if (!card) + return -EINVAL; + + if ((card->state != CARD_STATE_DOWN) && + (card->state != CARD_STATE_RECOVER)) + return -EPERM; + + i = simple_strtoul(buf, &tmp, 16); + if ((i < 0) || (i > MAX_ADD_HHLEN)) { + PRINT_WARN("add_hhlen out of range\n"); + return -EINVAL; + } + card->options.add_hhlen = i; + + return count; +} + +static DEVICE_ATTR(add_hhlen, 0644, qeth_dev_add_hhlen_show, + qeth_dev_add_hhlen_store); + +static ssize_t +qeth_dev_fake_ll_show(struct device *dev, char *buf) +{ + struct qeth_card *card = dev->driver_data; + + if (!card) + return -EINVAL; + + return sprintf(buf, "%i\n", card->options.fake_ll? 1:0); +} + +static ssize_t +qeth_dev_fake_ll_store(struct device *dev, const char *buf, size_t count) +{ + struct qeth_card *card = dev->driver_data; + char *tmp; + int i; + + if (!card) + return -EINVAL; + + if ((card->state != CARD_STATE_DOWN) && + (card->state != CARD_STATE_RECOVER)) + return -EPERM; + + i = simple_strtoul(buf, &tmp, 16); + if ((i == 0) || (i == 1)) + card->options.fake_ll = i; + else { + PRINT_WARN("fake_ll: write 0 or 1 to this file!\n"); + return -EINVAL; + } + return count; +} + +static DEVICE_ATTR(fake_ll, 0644, qeth_dev_fake_ll_show, + qeth_dev_fake_ll_store); + +static ssize_t +qeth_dev_fake_broadcast_show(struct device *dev, char *buf) +{ + struct qeth_card *card = dev->driver_data; + + if (!card) + return -EINVAL; + + return sprintf(buf, "%i\n", card->options.fake_broadcast? 1:0); +} + +static ssize_t +qeth_dev_fake_broadcast_store(struct device *dev, const char *buf, size_t count) +{ + struct qeth_card *card = dev->driver_data; + char *tmp; + int i; + + if (!card) + return -EINVAL; + + if ((card->state != CARD_STATE_DOWN) && + (card->state != CARD_STATE_RECOVER)) + return -EPERM; + + i = simple_strtoul(buf, &tmp, 16); + if ((i == 0) || (i == 1)) + card->options.fake_broadcast = i; + else { + PRINT_WARN("fake_broadcast: write 0 or 1 to this file!\n"); + return -EINVAL; + } + return count; +} + +static DEVICE_ATTR(fake_broadcast, 0644, qeth_dev_fake_broadcast_show, + qeth_dev_fake_broadcast_store); + +static ssize_t +qeth_dev_recover_store(struct device *dev, const char *buf, size_t count) +{ + struct qeth_card *card = dev->driver_data; + char *tmp; + int i; + + if (!card) + return -EINVAL; + + if ((card->state != CARD_STATE_UP_LAN_ONLINE) && + (card->state != CARD_STATE_UP_LAN_OFFLINE)) + return -EPERM; + + i = simple_strtoul(buf, &tmp, 16); + if (i == 1) + qeth_schedule_recovery(card); + + return count; +} + +static DEVICE_ATTR(recover, 0200, NULL, qeth_dev_recover_store); + +/* TODO */ +static ssize_t +qeth_dev_broadcast_mode_show(struct device *dev, char *buf) +{ + struct qeth_card *card = dev->driver_data; + + if (!card) + return -EINVAL; + + if (!((card->info.link_type == QETH_LINK_TYPE_HSTR) || + (card->info.link_type == QETH_LINK_TYPE_LANE_TR))) + return sprintf(buf, "n/a\n"); + + return sprintf(buf, "%s\n", (card->options.broadcast_mode == + QETH_TR_BROADCAST_ALLRINGS)? + "all rings":"local"); +} + +/* TODO */ +static ssize_t +qeth_dev_broadcast_mode_store(struct device *dev, const char *buf, size_t count) +{ + struct qeth_card *card = dev->driver_data; + char *tmp; + + if (!card) + return -EINVAL; + + if ((card->state != CARD_STATE_DOWN) && + (card->state != CARD_STATE_RECOVER)) + return -EPERM; + + if (!((card->info.link_type == QETH_LINK_TYPE_HSTR) || + (card->info.link_type == QETH_LINK_TYPE_LANE_TR))){ + PRINT_WARN("Device is not a tokenring device!\n"); + return -EINVAL; + } + + tmp = strsep((char **) &buf, "\n"); + + if (!strcmp(tmp, "local")){ + card->options.broadcast_mode = QETH_TR_BROADCAST_LOCAL; + return count; + } else if (!strcmp(tmp, "all_rings")) { + card->options.broadcast_mode = QETH_TR_BROADCAST_ALLRINGS; + return count; + } else { + PRINT_WARN("broadcast_mode: invalid mode %s!\n", + tmp); + return -EINVAL; + } + return count; +} + +static DEVICE_ATTR(broadcast_mode, 0644, qeth_dev_broadcast_mode_show, + qeth_dev_broadcast_mode_store); + +/* TODO */ +static ssize_t +qeth_dev_canonical_macaddr_show(struct device *dev, char *buf) +{ + struct qeth_card *card = dev->driver_data; + + if (!card) + return -EINVAL; + + if (!((card->info.link_type == QETH_LINK_TYPE_HSTR) || + (card->info.link_type == QETH_LINK_TYPE_LANE_TR))) + return sprintf(buf, "n/a\n"); + + return sprintf(buf, "%i\n", (card->options.macaddr_mode == + QETH_TR_MACADDR_CANONICAL)? 1:0); +} + +/* TODO */ +static ssize_t +qeth_dev_canonical_macaddr_store(struct device *dev, const char *buf, + size_t count) +{ + struct qeth_card *card = dev->driver_data; + char *tmp; + int i; + + if (!card) + return -EINVAL; + + if ((card->state != CARD_STATE_DOWN) && + (card->state != CARD_STATE_RECOVER)) + return -EPERM; + + if (!((card->info.link_type == QETH_LINK_TYPE_HSTR) || + (card->info.link_type == QETH_LINK_TYPE_LANE_TR))){ + PRINT_WARN("Device is not a tokenring device!\n"); + return -EINVAL; + } + + i = simple_strtoul(buf, &tmp, 16); + if ((i == 0) || (i == 1)) + card->options.macaddr_mode = i? + QETH_TR_MACADDR_CANONICAL : + QETH_TR_MACADDR_NONCANONICAL; + else { + PRINT_WARN("canonical_macaddr: write 0 or 1 to this file!\n"); + return -EINVAL; + } + return count; +} + +static DEVICE_ATTR(canonical_macaddr, 0644, qeth_dev_canonical_macaddr_show, + qeth_dev_canonical_macaddr_store); + +static struct device_attribute * qeth_device_attrs[] = { + &dev_attr_state, + &dev_attr_chpid, + &dev_attr_if_name, + &dev_attr_card_type, + &dev_attr_portno, + &dev_attr_portname, + &dev_attr_checksumming, + &dev_attr_priority_queueing, + &dev_attr_buffer_count, + &dev_attr_route4, +#ifdef CONFIG_QETH_IPV6 + &dev_attr_route6, +#endif + &dev_attr_add_hhlen, + &dev_attr_fake_ll, + &dev_attr_fake_broadcast, + &dev_attr_recover, + &dev_attr_broadcast_mode, + &dev_attr_canonical_macaddr, + NULL, +}; + +static struct attribute_group qeth_device_attr_group = { + .attrs = (struct attribute **)qeth_device_attrs, +}; + + +#define QETH_DEVICE_ATTR(_id,_name,_mode,_show,_store) \ +struct device_attribute dev_attr_##_id = { \ + .attr = {.name=__stringify(_name), .mode=_mode, .owner=THIS_MODULE },\ + .show = _show, \ + .store = _store, \ +}; + +static ssize_t +qeth_dev_ipato_enable_show(struct device *dev, char *buf) +{ + struct qeth_card *card = dev->driver_data; + + if (!card) + return -EINVAL; + + return sprintf(buf, "%i\n", card->ipato.enabled? 1:0); +} + +static ssize_t +qeth_dev_ipato_enable_store(struct device *dev, const char *buf, size_t count) +{ + struct qeth_card *card = dev->driver_data; + char *tmp; + + if (!card) + return -EINVAL; + + tmp = strsep((char **) &buf, "\n"); + if (!strcmp(tmp, "toggle")){ + card->ipato.enabled = (card->ipato.enabled)? 0 : 1; + } else if (!strcmp(tmp, "1")){ + card->ipato.enabled = 1; + } else if (!strcmp(tmp, "0")){ + card->ipato.enabled = 0; + } else { + PRINT_WARN("ipato_enable: write 0, 1 or 'toggle' to " + "this file\n"); + return -EINVAL; + } + return count; +} + +static QETH_DEVICE_ATTR(ipato_enable, enable, 0644, + qeth_dev_ipato_enable_show, + qeth_dev_ipato_enable_store); + +static ssize_t +qeth_dev_ipato_invert4_show(struct device *dev, char *buf) +{ + struct qeth_card *card = dev->driver_data; + + if (!card) + return -EINVAL; + + return sprintf(buf, "%i\n", card->ipato.invert4? 1:0); +} + +static ssize_t +qeth_dev_ipato_invert4_store(struct device *dev, const char *buf, size_t count) +{ + struct qeth_card *card = dev->driver_data; + char *tmp; + + if (!card) + return -EINVAL; + + tmp = strsep((char **) &buf, "\n"); + if (!strcmp(tmp, "toggle")){ + card->ipato.invert4 = (card->ipato.invert4)? 0 : 1; + } else if (!strcmp(tmp, "1")){ + card->ipato.invert4 = 1; + } else if (!strcmp(tmp, "0")){ + card->ipato.invert4 = 0; + } else { + PRINT_WARN("ipato_invert4: write 0, 1 or 'toggle' to " + "this file\n"); + return -EINVAL; + } + return count; +} + +static QETH_DEVICE_ATTR(ipato_invert4, invert4, 0644, + qeth_dev_ipato_invert4_show, + qeth_dev_ipato_invert4_store); + +static inline ssize_t +qeth_dev_ipato_add_show(char *buf, struct qeth_card *card, + enum qeth_prot_versions proto) +{ + struct qeth_ipato_entry *ipatoe; + unsigned long flags; + char addr_str[49]; + int i = 0; + + spin_lock_irqsave(&card->ip_lock, flags); + list_for_each_entry(ipatoe, &card->ipato.entries, entry){ + if (ipatoe->proto != proto) + continue; + qeth_ipaddr_to_string(proto, ipatoe->addr, addr_str); + i += sprintf(buf + i, "%s/%i\n", addr_str, ipatoe->mask_bits); + } + spin_unlock_irqrestore(&card->ip_lock, flags); + i += sprintf(buf + i, "\n"); + + return i; +} + +static ssize_t +qeth_dev_ipato_add4_show(struct device *dev, char *buf) +{ + struct qeth_card *card = dev->driver_data; + + if (!card) + return -EINVAL; + + return qeth_dev_ipato_add_show(buf, card, QETH_PROT_IPV4); +} + +static inline int +qeth_parse_ipatoe(const char* buf, enum qeth_prot_versions proto, + u8 *addr, int *mask_bits) +{ + const char *start, *end; + char *tmp; + char buffer[49] = {0, }; + + start = buf; + /* get address string */ + end = strchr(start, '/'); + if (!end){ + PRINT_WARN("Invalid format for ipato_addx/delx. " + "Use /\n"); + return -EINVAL; + } + strncpy(buffer, start, end - start); + if (qeth_string_to_ipaddr(buffer, proto, addr)){ + PRINT_WARN("Invalid IP address format!\n"); + return -EINVAL; + } + start = end + 1; + *mask_bits = simple_strtoul(start, &tmp, 10); + + return 0; +} + +static inline ssize_t +qeth_dev_ipato_add_store(const char *buf, size_t count, + struct qeth_card *card, enum qeth_prot_versions proto) +{ + struct qeth_ipato_entry *ipatoe; + u8 addr[16]; + int mask_bits; + int rc; + + if ((rc = qeth_parse_ipatoe(buf, proto, addr, &mask_bits))) + return rc; + + if (!(ipatoe = kmalloc(sizeof(struct qeth_ipato_entry), GFP_KERNEL))){ + PRINT_WARN("No memory to allocate ipato entry\n"); + return -ENOMEM; + } + memset(ipatoe, 0, sizeof(struct qeth_ipato_entry)); + ipatoe->proto = proto; + memcpy(ipatoe->addr, addr, (proto == QETH_PROT_IPV4)? 4:16); + ipatoe->mask_bits = mask_bits; + + if ((rc = qeth_add_ipato_entry(card, ipatoe))){ + kfree(ipatoe); + return rc; + } + + return count; +} + +static ssize_t +qeth_dev_ipato_add4_store(struct device *dev, const char *buf, size_t count) +{ + struct qeth_card *card = dev->driver_data; + + if (!card) + return -EINVAL; + + return qeth_dev_ipato_add_store(buf, count, card, QETH_PROT_IPV4); +} + +static QETH_DEVICE_ATTR(ipato_add4, add4, 0644, + qeth_dev_ipato_add4_show, + qeth_dev_ipato_add4_store); + +static inline ssize_t +qeth_dev_ipato_del_store(const char *buf, size_t count, + struct qeth_card *card, enum qeth_prot_versions proto) +{ + u8 addr[16]; + int mask_bits; + int rc; + + if ((rc = qeth_parse_ipatoe(buf, proto, addr, &mask_bits))) + return rc; + + qeth_del_ipato_entry(card, proto, addr, mask_bits); + + return count; +} + +static ssize_t +qeth_dev_ipato_del4_store(struct device *dev, const char *buf, size_t count) +{ + struct qeth_card *card = dev->driver_data; + + if (!card) + return -EINVAL; + + return qeth_dev_ipato_del_store(buf, count, card, QETH_PROT_IPV4); +} + +static QETH_DEVICE_ATTR(ipato_del4, del4, 0200, NULL, + qeth_dev_ipato_del4_store); + +#ifdef CONFIG_QETH_IPV6 +static ssize_t +qeth_dev_ipato_invert6_show(struct device *dev, char *buf) +{ + struct qeth_card *card = dev->driver_data; + + if (!card) + return -EINVAL; + + return sprintf(buf, "%i\n", card->ipato.invert6? 1:0); +} + +static ssize_t +qeth_dev_ipato_invert6_store(struct device *dev, const char *buf, size_t count) +{ + struct qeth_card *card = dev->driver_data; + char *tmp; + + if (!card) + return -EINVAL; + + tmp = strsep((char **) &buf, "\n"); + if (!strcmp(tmp, "toggle")){ + card->ipato.invert6 = (card->ipato.invert6)? 0 : 1; + } else if (!strcmp(tmp, "1")){ + card->ipato.invert6 = 1; + } else if (!strcmp(tmp, "0")){ + card->ipato.invert6 = 0; + } else { + PRINT_WARN("ipato_invert6: write 0, 1 or 'toggle' to " + "this file\n"); + return -EINVAL; + } + return count; +} + +static QETH_DEVICE_ATTR(ipato_invert6, invert6, 0644, + qeth_dev_ipato_invert6_show, + qeth_dev_ipato_invert6_store); + + +static ssize_t +qeth_dev_ipato_add6_show(struct device *dev, char *buf) +{ + struct qeth_card *card = dev->driver_data; + + if (!card) + return -EINVAL; + + return qeth_dev_ipato_add_show(buf, card, QETH_PROT_IPV6); +} + +static ssize_t +qeth_dev_ipato_add6_store(struct device *dev, const char *buf, size_t count) +{ + struct qeth_card *card = dev->driver_data; + + if (!card) + return -EINVAL; + + return qeth_dev_ipato_add_store(buf, count, card, QETH_PROT_IPV6); +} + +static QETH_DEVICE_ATTR(ipato_add6, add6, 0644, + qeth_dev_ipato_add6_show, + qeth_dev_ipato_add6_store); + +static ssize_t +qeth_dev_ipato_del6_store(struct device *dev, const char *buf, size_t count) +{ + struct qeth_card *card = dev->driver_data; + + if (!card) + return -EINVAL; + + return qeth_dev_ipato_del_store(buf, count, card, QETH_PROT_IPV6); +} + +static QETH_DEVICE_ATTR(ipato_del6, del6, 0200, NULL, + qeth_dev_ipato_del6_store); +#endif /* CONFIG_QETH_IPV6 */ + +static struct device_attribute * qeth_ipato_device_attrs[] = { + &dev_attr_ipato_enable, + &dev_attr_ipato_invert4, + &dev_attr_ipato_add4, + &dev_attr_ipato_del4, +#ifdef CONFIG_QETH_IPV6 + &dev_attr_ipato_invert6, + &dev_attr_ipato_add6, + &dev_attr_ipato_del6, +#endif + NULL, +}; + +static struct attribute_group qeth_device_ipato_group = { + .name = "ipa_takeover", + .attrs = (struct attribute **)qeth_ipato_device_attrs, +}; + +static inline ssize_t +qeth_dev_vipa_add_show(char *buf, struct qeth_card *card, + enum qeth_prot_versions proto) +{ + struct qeth_ipaddr *ipaddr; + char addr_str[49]; + unsigned long flags; + int i = 0; + + spin_lock_irqsave(&card->ip_lock, flags); + list_for_each_entry(ipaddr, &card->ip_list, entry){ + if (ipaddr->proto != proto) + continue; + if (ipaddr->type != QETH_IP_TYPE_VIPA) + continue; + qeth_ipaddr_to_string(proto, (const u8 *)&ipaddr->u, addr_str); + i += sprintf(buf + i, "%s\n", addr_str); + } + spin_unlock_irqrestore(&card->ip_lock, flags); + i += sprintf(buf + i, "\n"); + + return i; +} + +static ssize_t +qeth_dev_vipa_add4_show(struct device *dev, char *buf) +{ + struct qeth_card *card = dev->driver_data; + + if (!card) + return -EINVAL; + + return qeth_dev_vipa_add_show(buf, card, QETH_PROT_IPV4); +} + +static inline int +qeth_parse_vipae(const char* buf, enum qeth_prot_versions proto, + u8 *addr) +{ + if (qeth_string_to_ipaddr(buf, proto, addr)){ + PRINT_WARN("Invalid IP address format!\n"); + return -EINVAL; + } + return 0; +} + +static inline ssize_t +qeth_dev_vipa_add_store(const char *buf, size_t count, + struct qeth_card *card, enum qeth_prot_versions proto) +{ + u8 addr[16] = {0, }; + int rc; + + if ((rc = qeth_parse_vipae(buf, proto, addr))) + return rc; + + if ((rc = qeth_add_vipa(card, proto, addr))) + return rc; + + return count; +} + +static ssize_t +qeth_dev_vipa_add4_store(struct device *dev, const char *buf, size_t count) +{ + struct qeth_card *card = dev->driver_data; + + if (!card) + return -EINVAL; + + return qeth_dev_vipa_add_store(buf, count, card, QETH_PROT_IPV4); +} + +static QETH_DEVICE_ATTR(vipa_add4, add4, 0644, + qeth_dev_vipa_add4_show, + qeth_dev_vipa_add4_store); + +static inline ssize_t +qeth_dev_vipa_del_store(const char *buf, size_t count, + struct qeth_card *card, enum qeth_prot_versions proto) +{ + u8 addr[16]; + int rc; + + if ((rc = qeth_parse_vipae(buf, proto, addr))) + return rc; + + qeth_del_vipa(card, proto, addr); + + return count; +} + +static ssize_t +qeth_dev_vipa_del4_store(struct device *dev, const char *buf, size_t count) +{ + struct qeth_card *card = dev->driver_data; + + if (!card) + return -EINVAL; + + return qeth_dev_vipa_del_store(buf, count, card, QETH_PROT_IPV4); +} + +static QETH_DEVICE_ATTR(vipa_del4, del4, 0200, NULL, + qeth_dev_vipa_del4_store); + +#ifdef CONFIG_QETH_IPV6 +static ssize_t +qeth_dev_vipa_add6_show(struct device *dev, char *buf) +{ + struct qeth_card *card = dev->driver_data; + + if (!card) + return -EINVAL; + + return qeth_dev_vipa_add_show(buf, card, QETH_PROT_IPV6); +} + +static ssize_t +qeth_dev_vipa_add6_store(struct device *dev, const char *buf, size_t count) +{ + struct qeth_card *card = dev->driver_data; + + if (!card) + return -EINVAL; + + return qeth_dev_vipa_add_store(buf, count, card, QETH_PROT_IPV6); +} + +static QETH_DEVICE_ATTR(vipa_add6, add6, 0644, + qeth_dev_vipa_add6_show, + qeth_dev_vipa_add6_store); + +static ssize_t +qeth_dev_vipa_del6_store(struct device *dev, const char *buf, size_t count) +{ + struct qeth_card *card = dev->driver_data; + + if (!card) + return -EINVAL; + + return qeth_dev_vipa_del_store(buf, count, card, QETH_PROT_IPV6); +} + +static QETH_DEVICE_ATTR(vipa_del6, del6, 0200, NULL, + qeth_dev_vipa_del6_store); +#endif /* CONFIG_QETH_IPV6 */ + +static struct device_attribute * qeth_vipa_device_attrs[] = { + &dev_attr_vipa_add4, + &dev_attr_vipa_del4, +#ifdef CONFIG_QETH_IPV6 + &dev_attr_vipa_add6, + &dev_attr_vipa_del6, +#endif + NULL, +}; + +static struct attribute_group qeth_device_vipa_group = { + .name = "vipa", + .attrs = (struct attribute **)qeth_vipa_device_attrs, +}; + +static inline ssize_t +qeth_dev_rxip_add_show(char *buf, struct qeth_card *card, + enum qeth_prot_versions proto) +{ + struct qeth_ipaddr *ipaddr; + char addr_str[49]; + unsigned long flags; + int i = 0; + + spin_lock_irqsave(&card->ip_lock, flags); + list_for_each_entry(ipaddr, &card->ip_list, entry){ + if (ipaddr->proto != proto) + continue; + if (ipaddr->type != QETH_IP_TYPE_RXIP) + continue; + qeth_ipaddr_to_string(proto, (const u8 *)&ipaddr->u, addr_str); + i += sprintf(buf + i, "%s\n", addr_str); + } + spin_unlock_irqrestore(&card->ip_lock, flags); + i += sprintf(buf + i, "\n"); + + return i; +} + +static ssize_t +qeth_dev_rxip_add4_show(struct device *dev, char *buf) +{ + struct qeth_card *card = dev->driver_data; + + if (!card) + return -EINVAL; + + return qeth_dev_rxip_add_show(buf, card, QETH_PROT_IPV4); +} + +static inline int +qeth_parse_rxipe(const char* buf, enum qeth_prot_versions proto, + u8 *addr) +{ + if (qeth_string_to_ipaddr(buf, proto, addr)){ + PRINT_WARN("Invalid IP address format!\n"); + return -EINVAL; + } + return 0; +} + +static inline ssize_t +qeth_dev_rxip_add_store(const char *buf, size_t count, + struct qeth_card *card, enum qeth_prot_versions proto) +{ + u8 addr[16] = {0, }; + int rc; + + if ((rc = qeth_parse_rxipe(buf, proto, addr))) + return rc; + + if ((rc = qeth_add_rxip(card, proto, addr))) + return rc; + + return count; +} + +static ssize_t +qeth_dev_rxip_add4_store(struct device *dev, const char *buf, size_t count) +{ + struct qeth_card *card = dev->driver_data; + + if (!card) + return -EINVAL; + + return qeth_dev_rxip_add_store(buf, count, card, QETH_PROT_IPV4); +} + +static QETH_DEVICE_ATTR(rxip_add4, add4, 0644, + qeth_dev_rxip_add4_show, + qeth_dev_rxip_add4_store); + +static inline ssize_t +qeth_dev_rxip_del_store(const char *buf, size_t count, + struct qeth_card *card, enum qeth_prot_versions proto) +{ + u8 addr[16]; + int rc; + + if ((rc = qeth_parse_rxipe(buf, proto, addr))) + return rc; + + qeth_del_rxip(card, proto, addr); + + return count; +} + +static ssize_t +qeth_dev_rxip_del4_store(struct device *dev, const char *buf, size_t count) +{ + struct qeth_card *card = dev->driver_data; + + if (!card) + return -EINVAL; + + return qeth_dev_rxip_del_store(buf, count, card, QETH_PROT_IPV4); +} + +static QETH_DEVICE_ATTR(rxip_del4, del4, 0200, NULL, + qeth_dev_rxip_del4_store); + +#ifdef CONFIG_QETH_IPV6 +static ssize_t +qeth_dev_rxip_add6_show(struct device *dev, char *buf) +{ + struct qeth_card *card = dev->driver_data; + + if (!card) + return -EINVAL; + + return qeth_dev_rxip_add_show(buf, card, QETH_PROT_IPV6); +} + +static ssize_t +qeth_dev_rxip_add6_store(struct device *dev, const char *buf, size_t count) +{ + struct qeth_card *card = dev->driver_data; + + if (!card) + return -EINVAL; + + return qeth_dev_rxip_add_store(buf, count, card, QETH_PROT_IPV6); +} + +static QETH_DEVICE_ATTR(rxip_add6, add6, 0644, + qeth_dev_rxip_add6_show, + qeth_dev_rxip_add6_store); + +static ssize_t +qeth_dev_rxip_del6_store(struct device *dev, const char *buf, size_t count) +{ + struct qeth_card *card = dev->driver_data; + + if (!card) + return -EINVAL; + + return qeth_dev_rxip_del_store(buf, count, card, QETH_PROT_IPV6); +} + +static QETH_DEVICE_ATTR(rxip_del6, del6, 0200, NULL, + qeth_dev_rxip_del6_store); +#endif /* CONFIG_QETH_IPV6 */ + +static struct device_attribute * qeth_rxip_device_attrs[] = { + &dev_attr_rxip_add4, + &dev_attr_rxip_del4, +#ifdef CONFIG_QETH_IPV6 + &dev_attr_rxip_add6, + &dev_attr_rxip_del6, +#endif + NULL, +}; + +static struct attribute_group qeth_device_rxip_group = { + .name = "rxip", + .attrs = (struct attribute **)qeth_rxip_device_attrs, +}; + +int +qeth_create_device_attributes(struct device *dev) +{ + int ret; + + if ((ret = sysfs_create_group(&dev->kobj, &qeth_device_attr_group))) + return ret; + if ((ret = sysfs_create_group(&dev->kobj, &qeth_device_ipato_group))){ + sysfs_remove_group(&dev->kobj, &qeth_device_attr_group); + return ret; + } + if ((ret = sysfs_create_group(&dev->kobj, &qeth_device_vipa_group))){ + sysfs_remove_group(&dev->kobj, &qeth_device_attr_group); + sysfs_remove_group(&dev->kobj, &qeth_device_ipato_group); + return ret; + } + if ((ret = sysfs_create_group(&dev->kobj, &qeth_device_rxip_group))){ + sysfs_remove_group(&dev->kobj, &qeth_device_attr_group); + sysfs_remove_group(&dev->kobj, &qeth_device_ipato_group); + sysfs_remove_group(&dev->kobj, &qeth_device_vipa_group); + } + + return ret; +} + +void +qeth_remove_device_attributes(struct device *dev) +{ + sysfs_remove_group(&dev->kobj, &qeth_device_attr_group); + sysfs_remove_group(&dev->kobj, &qeth_device_ipato_group); + sysfs_remove_group(&dev->kobj, &qeth_device_vipa_group); + sysfs_remove_group(&dev->kobj, &qeth_device_rxip_group); +} + +/**********************/ +/* DRIVER ATTRIBUTES */ +/**********************/ +static ssize_t +qeth_driver_group_store(struct device_driver *ddrv, const char *buf, + size_t count) +{ + const char *start, *end; + char bus_ids[3][BUS_ID_SIZE], *argv[3]; + int i; + int err; + + start = buf; + for (i = 0; i < 3; i++) { + static const char delim[] = { ',', ',', '\n' }; + int len; + + if (!(end = strchr(start, delim[i]))) + return -EINVAL; + len = min_t(ptrdiff_t, BUS_ID_SIZE, end - start); + strncpy(bus_ids[i], start, len); + bus_ids[i][len] = '\0'; + start = end + 1; + argv[i] = bus_ids[i]; + } + err = ccwgroup_create(qeth_root_dev, qeth_ccwgroup_driver.driver_id, + &qeth_ccw_driver, 3, argv); + if (err) + return err; + else + return count; +} + + +static DRIVER_ATTR(group, 0200, 0, qeth_driver_group_store); + +static ssize_t +qeth_driver_snmp_register_show(struct device_driver *ddrv, char *buf) +{ + /* TODO */ + return 0; +} + +static ssize_t +qeth_driver_snmp_register_store(struct device_driver *ddrv, const char *buf, + size_t count) +{ + /* TODO */ + return count; +} + +static DRIVER_ATTR(snmp_register, 0644, qeth_driver_snmp_register_show, + qeth_driver_snmp_register_store); + +int +qeth_create_driver_attributes(void) +{ + int rc; + + if ((rc = driver_create_file(&qeth_ccwgroup_driver.driver, + &driver_attr_group))) + return rc; + return driver_create_file(&qeth_ccwgroup_driver.driver, + &driver_attr_snmp_register); +} + +void +qeth_remove_driver_attributes(void) +{ + driver_remove_file(&qeth_ccwgroup_driver.driver, + &driver_attr_group); + driver_remove_file(&qeth_ccwgroup_driver.driver, + &driver_attr_snmp_register); +} diff -puN /dev/null include/asm-s390/qeth.h --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ 25-akpm/include/asm-s390/qeth.h 2004-04-08 13:55:46.775913152 -0700 @@ -0,0 +1,60 @@ +/* + * include/asm-s390/qeth.h + * + * ioctl definitions for qeth driver + * + * Copyright (C) 2004 IBM Corporation + * + * Author(s): Thomas Spatzier + * + */ +#ifndef __ASM_S390_IOCTL_H__ +#define __ASM_S390_IOCTL_H__ +#include + +#define QETH_IOCTL_LETTER 'Q' + +#define SIOC_QETH_ARP_SET_NO_ENTRIES _IOWR(QETH_IOCTL_LETTER, 1, int) +#define SIOC_QETH_ARP_QUERY_INFO _IOWR(QETH_IOCTL_LETTER, 2, int) +#define SIOC_QETH_ARP_ADD_ENTRY _IOWR(QETH_IOCTL_LETTER, 3, int) +#define SIOC_QETH_ARP_REMOVE_ENTRY _IOWR(QETH_IOCTL_LETTER, 4, int) +#define SIOC_QETH_ARP_FLUSH_CACHE _IOWR(QETH_IOCTL_LETTER, 5, int) +#define SIOC_QETH_ADP_SET_SNMP_CONTROL _IOWR(QETH_IOCTL_LETTER, 6, int) +#define SIOC_QETH_GET_CARD_TYPE _IOWR(QETH_IOCTL_LETTER, 7, int) + +struct qeth_arp_cache_entry { + __u8 macaddr[6]; + __u8 reserved1[2]; + __u8 ipaddr[16]; /* for both IPv4 and IPv6 */ + __u8 reserved2[32]; +} __attribute__ ((packed)); + +struct qeth_arp_qi_entry7 { + __u8 media_specific[32]; + __u8 macaddr_type; + __u8 ipaddr_type; + __u8 macaddr[6]; + __u8 ipaddr[4]; +} __attribute__((packed)); + +struct qeth_arp_qi_entry5 { + __u8 media_specific[32]; + __u8 macaddr_type; + __u8 ipaddr_type; + __u8 ipaddr[4]; +} __attribute__((packed)); + +/* data sent to user space as result of query arp ioctl */ +#define QETH_QARP_USER_DATA_SIZE 20000 +#define QETH_QARP_MASK_OFFSET 4 +#define QETH_QARP_ENTRIES_OFFSET 6 +struct qeth_arp_query_user_data { + union { + __u32 data_len; /* set by user space program */ + __u32 no_entries; /* set by kernel */ + } u; + __u16 mask_bits; + char *entries; +} __attribute__((packed)); + +#endif /* __ASM_S390_IOCTL_H__ */ _