aboutsummaryrefslogtreecommitdiff
path: root/drivers/s390/net
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/s390/net')
-rw-r--r--drivers/s390/net/Kconfig114
-rw-r--r--drivers/s390/net/Makefile17
-rw-r--r--drivers/s390/net/claw.c2851
-rw-r--r--drivers/s390/net/claw.h26
-rw-r--r--drivers/s390/net/ctcdbug.c80
-rw-r--r--drivers/s390/net/ctcdbug.h125
-rw-r--r--drivers/s390/net/ctcm_dbug.c77
-rw-r--r--drivers/s390/net/ctcm_dbug.h141
-rw-r--r--drivers/s390/net/ctcm_fsms.c2304
-rw-r--r--drivers/s390/net/ctcm_fsms.h356
-rw-r--r--drivers/s390/net/ctcm_main.c1871
-rw-r--r--drivers/s390/net/ctcm_main.h315
-rw-r--r--drivers/s390/net/ctcm_mpc.c2174
-rw-r--r--drivers/s390/net/ctcm_mpc.h238
-rw-r--r--drivers/s390/net/ctcm_sysfs.c208
-rw-r--r--drivers/s390/net/ctcmain.c3062
-rw-r--r--drivers/s390/net/ctcmain.h270
-rw-r--r--drivers/s390/net/cu3088.c167
-rw-r--r--drivers/s390/net/cu3088.h44
-rw-r--r--drivers/s390/net/fsm.c2
-rw-r--r--drivers/s390/net/fsm.h4
-rw-r--r--drivers/s390/net/lcs.c536
-rw-r--r--drivers/s390/net/lcs.h30
-rw-r--r--drivers/s390/net/netiucv.c694
-rw-r--r--drivers/s390/net/qeth.h1253
-rw-r--r--drivers/s390/net/qeth_core.h987
-rw-r--r--drivers/s390/net/qeth_core_main.c5894
-rw-r--r--drivers/s390/net/qeth_core_mpc.c270
-rw-r--r--drivers/s390/net/qeth_core_mpc.h (renamed from drivers/s390/net/qeth_mpc.h)423
-rw-r--r--drivers/s390/net/qeth_core_sys.c758
-rw-r--r--drivers/s390/net/qeth_eddp.c634
-rw-r--r--drivers/s390/net/qeth_eddp.h84
-rw-r--r--drivers/s390/net/qeth_fs.h168
-rw-r--r--drivers/s390/net/qeth_l2.h15
-rw-r--r--drivers/s390/net/qeth_l2_main.c1984
-rw-r--r--drivers/s390/net/qeth_l2_sys.c223
-rw-r--r--drivers/s390/net/qeth_l3.h69
-rw-r--r--drivers/s390/net/qeth_l3_main.c3771
-rw-r--r--drivers/s390/net/qeth_l3_sys.c1058
-rw-r--r--drivers/s390/net/qeth_main.c8956
-rw-r--r--drivers/s390/net/qeth_mpc.c269
-rw-r--r--drivers/s390/net/qeth_proc.c316
-rw-r--r--drivers/s390/net/qeth_sys.c1858
-rw-r--r--drivers/s390/net/qeth_tso.h148
-rw-r--r--drivers/s390/net/smsgiucv.c101
-rw-r--r--drivers/s390/net/smsgiucv.h10
-rw-r--r--drivers/s390/net/smsgiucv_app.c218
47 files changed, 25098 insertions, 20075 deletions
diff --git a/drivers/s390/net/Kconfig b/drivers/s390/net/Kconfig
index eada69dec4f..8b3f5599180 100644
--- a/drivers/s390/net/Kconfig
+++ b/drivers/s390/net/Kconfig
@@ -2,88 +2,110 @@ menu "S/390 network device drivers"
depends on NETDEVICES && S390
config LCS
- tristate "Lan Channel Station Interface"
- depends on CCW && NETDEVICES && (NET_ETHERNET || TR || FDDI)
+ def_tristate m
+ prompt "Lan Channel Station Interface"
+ depends on CCW && NETDEVICES && (ETHERNET || FDDI)
help
- Select this option if you want to use LCS networking on IBM S/390
- or zSeries. This device driver supports Token Ring (IEEE 802.5),
- FDDI (IEEE 802.7) and Ethernet.
- This option is also available as a module which will be
- called lcs.ko. If you do not know what it is, it's safe to say "Y".
+ Select this option if you want to use LCS networking on IBM System z.
+ This device driver supports FDDI (IEEE 802.7) and Ethernet.
+ To compile as a module, choose M. The module name is lcs.
+ If you do not know what it is, it's safe to choose Y.
-config CTC
- tristate "CTC device support"
+config CTCM
+ def_tristate m
+ prompt "CTC and MPC SNA device support"
depends on CCW && NETDEVICES
help
- Select this option if you want to use channel-to-channel networking
- on IBM S/390 or zSeries. This device driver supports real CTC
- coupling using ESCON. It also supports virtual CTCs when running
- under VM. It will use the channel device configuration if this is
- available. This option is also available as a module which will be
- called ctc.ko. If you do not know what it is, it's safe to say "Y".
+ Select this option if you want to use channel-to-channel
+ point-to-point networking on IBM System z.
+ This device driver supports real CTC coupling using ESCON.
+ It also supports virtual CTCs when running under VM.
+ This driver also supports channel-to-channel MPC SNA devices.
+ MPC is an SNA protocol device used by Communication Server for Linux.
+ To compile as a module, choose M. The module name is ctcm.
+ To compile into the kernel, choose Y.
+ If you do not need any channel-to-channel connection, choose N.
config NETIUCV
- tristate "IUCV network device support (VM only)"
+ def_tristate m
+ prompt "IUCV network device support (VM only)"
depends on IUCV && NETDEVICES
help
Select this option if you want to use inter-user communication
vehicle networking under VM or VIF. It enables a fast communication
link between VM guests. Using ifconfig a point-to-point connection
- can be established to the Linux for zSeries and S7390 system
- running on the other VM guest. This option is also available
- as a module which will be called netiucv.ko. If unsure, say "Y".
+ can be established to the Linux on IBM System z
+ running on the other VM guest. To compile as a module, choose M.
+ The module name is netiucv. If unsure, choose Y.
config SMSGIUCV
- tristate "IUCV special message support (VM only)"
+ def_tristate m
+ prompt "IUCV special message support (VM only)"
depends on IUCV
help
Select this option if you want to be able to receive SMSG messages
from other VM guest systems.
+config SMSGIUCV_EVENT
+ def_tristate m
+ prompt "Deliver IUCV special messages as uevents (VM only)"
+ depends on SMSGIUCV
+ help
+ Select this option to deliver CP special messages (SMSGs) as
+ uevents. The driver handles only those special messages that
+ start with "APP".
+
+ To compile as a module, choose M. The module name is "smsgiucv_app".
+
config CLAW
- tristate "CLAW device support"
+ def_tristate m
+ prompt "CLAW device support"
depends on CCW && NETDEVICES
help
This driver supports channel attached CLAW devices.
CLAW is Common Link Access for Workstation. Common devices
that use CLAW are RS/6000s, Cisco Routers (CIP) and 3172 devices.
- To compile as a module choose M here: The module will be called
- claw.ko to compile into the kernel choose Y
+ To compile as a module, choose M. The module name is claw.
+ To compile into the kernel, choose Y.
config QETH
- tristate "Gigabit Ethernet device support"
+ def_tristate y
+ prompt "Gigabit Ethernet device support"
depends on CCW && NETDEVICES && IP_MULTICAST && QDIO
help
- This driver supports the IBM S/390 and zSeries OSA Express adapters
- in QDIO mode (all media types), HiperSockets interfaces and VM GuestLAN
- interfaces in QDIO and HIPER mode.
+ This driver supports the IBM System z OSA Express adapters
+ in QDIO mode (all media types), HiperSockets interfaces and z/VM
+ virtual NICs for Guest LAN and VSWITCH.
- For details please refer to the documentation provided by IBM at
- <http://www10.software.ibm.com/developerworks/opensource/linux390>
+ For details please refer to the documentation provided by IBM at
+ <http://www.ibm.com/developerworks/linux/linux390>
- To compile this driver as a module, choose M here: the
- module will be called qeth.ko.
+ To compile this driver as a module, choose M.
+ The module name is qeth.
+config QETH_L2
+ def_tristate y
+ prompt "qeth layer 2 device support"
+ depends on QETH
+ help
+ Select this option to be able to run qeth devices in layer 2 mode.
+ To compile as a module, choose M. The module name is qeth_l2.
+ If unsure, choose y.
-comment "Gigabit Ethernet default settings"
+config QETH_L3
+ def_tristate y
+ prompt "qeth layer 3 device support"
depends on QETH
+ help
+ Select this option to be able to run qeth devices in layer 3 mode.
+ To compile as a module choose M. The module name is qeth_l3.
+ If unsure, choose Y.
config QETH_IPV6
- bool "IPv6 support for gigabit ethernet"
- depends on (QETH = IPV6) || (QETH && IPV6 = 'y')
- help
- If CONFIG_QETH is switched on, this option will include IPv6
- support in the qeth device driver.
-
-config QETH_VLAN
- bool "VLAN support for gigabit ethernet"
- depends on (QETH = VLAN_8021Q) || (QETH && VLAN_8021Q = 'y')
- help
- If CONFIG_QETH is switched on, this option will include IEEE
- 802.1q VLAN support in the qeth device driver.
+ def_bool y if (QETH_L3 = IPV6) || (QETH_L3 && IPV6 = 'y')
config CCWGROUP
- tristate
- default (LCS || CTC || QETH)
+ tristate
+ default (LCS || CTCM || QETH || CLAW)
endmenu
diff --git a/drivers/s390/net/Makefile b/drivers/s390/net/Makefile
index bbe3ab2e93d..d28f05d0c75 100644
--- a/drivers/s390/net/Makefile
+++ b/drivers/s390/net/Makefile
@@ -2,13 +2,16 @@
# S/390 network devices
#
-ctc-objs := ctcmain.o ctcdbug.o
-
+ctcm-y += ctcm_main.o ctcm_fsms.o ctcm_mpc.o ctcm_sysfs.o ctcm_dbug.o
+obj-$(CONFIG_CTCM) += ctcm.o fsm.o
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
-obj-$(CONFIG_CLAW) += claw.o cu3088.o
-qeth-y := qeth_main.o qeth_mpc.o qeth_sys.o qeth_eddp.o
-qeth-$(CONFIG_PROC_FS) += qeth_proc.o
+obj-$(CONFIG_SMSGIUCV_EVENT) += smsgiucv_app.o
+obj-$(CONFIG_LCS) += lcs.o
+obj-$(CONFIG_CLAW) += claw.o
+qeth-y += qeth_core_sys.o qeth_core_main.o qeth_core_mpc.o
obj-$(CONFIG_QETH) += qeth.o
+qeth_l2-y += qeth_l2_main.o qeth_l2_sys.o
+obj-$(CONFIG_QETH_L2) += qeth_l2.o
+qeth_l3-y += qeth_l3_main.o qeth_l3_sys.o
+obj-$(CONFIG_QETH_L3) += qeth_l3.o
diff --git a/drivers/s390/net/claw.c b/drivers/s390/net/claw.c
index d8a5c229c5a..d837c3c5330 100644
--- a/drivers/s390/net/claw.c
+++ b/drivers/s390/net/claw.c
@@ -1,14 +1,13 @@
/*
- * drivers/s390/net/claw.c
* ESCON CLAW network driver
*
* Linux for zSeries version
- * Copyright (C) 2002,2005 IBM Corporation
+ * Copyright IBM Corp. 2002, 2009
* Author(s) Original code written by:
- * Kazuo Iimura (iimura@jp.ibm.com)
+ * Kazuo Iimura <iimura@jp.ibm.com>
* Rewritten by
- * Andy Richter (richtera@us.ibm.com)
- * Marc Price (mwprice@us.ibm.com)
+ * Andy Richter <richtera@us.ibm.com>
+ * Marc Price <mwprice@us.ibm.com>
*
* sysfs parms:
* group x.x.rrrr,x.x.wwww
@@ -58,13 +57,16 @@
* 1.10 Changes for Buffer allocation
* 1.15 Changed for 2.6 Kernel No longer compiles on 2.4 or lower
* 1.25 Added Packing support
+ * 1.5
*/
+
+#define KMSG_COMPONENT "claw"
+
#include <asm/ccwdev.h>
#include <asm/ccwgroup.h>
#include <asm/debug.h>
#include <asm/idals.h>
#include <asm/io.h>
-
#include <linux/bitops.h>
#include <linux/ctype.h>
#include <linux/delay.h>
@@ -87,40 +89,13 @@
#include <linux/timer.h>
#include <linux/types.h>
-#include "cu3088.h"
#include "claw.h"
-MODULE_AUTHOR("Andy Richter <richtera@us.ibm.com>");
-MODULE_DESCRIPTION("Linux for zSeries CLAW Driver\n" \
- "Copyright 2000,2005 IBM Corporation\n");
-MODULE_LICENSE("GPL");
-
-/* Debugging is based on DEBUGMSG, IOTRACE, or FUNCTRACE options:
- DEBUGMSG - Enables output of various debug messages in the code
- IOTRACE - Enables output of CCW and other IO related traces
- FUNCTRACE - Enables output of function entry/exit trace
- Define any combination of above options to enable tracing
-
- CLAW also uses the s390dbf file system see claw_trace and claw_setup
+/*
+ CLAW uses the s390dbf file system see claw_trace and claw_setup
*/
-/* following enables tracing */
-//#define DEBUGMSG
-//#define IOTRACE
-//#define FUNCTRACE
-
-#ifdef DEBUGMSG
-#define DEBUG
-#endif
-
-#ifdef IOTRACE
-#define DEBUG
-#endif
-
-#ifdef FUNCTRACE
-#define DEBUG
-#endif
-
+static char version[] __initdata = "CLAW driver";
static char debug_buffer[255];
/**
* Debug Facility Stuff
@@ -146,7 +121,6 @@ claw_register_debug_facility(void)
claw_dbf_setup = debug_register("claw_setup", 2, 1, 8);
claw_dbf_trace = debug_register("claw_trace", 2, 2, 8);
if (claw_dbf_setup == NULL || claw_dbf_trace == NULL) {
- printk(KERN_WARNING "Not enough memory for debug facility.\n");
claw_unregister_debug_facility();
return -ENOMEM;
}
@@ -160,36 +134,33 @@ claw_register_debug_facility(void)
static inline void
claw_set_busy(struct net_device *dev)
{
- ((struct claw_privbk *) dev->priv)->tbusy=1;
- eieio();
+ ((struct claw_privbk *)dev->ml_priv)->tbusy = 1;
}
static inline void
claw_clear_busy(struct net_device *dev)
{
- clear_bit(0, &(((struct claw_privbk *) dev->priv)->tbusy));
+ clear_bit(0, &(((struct claw_privbk *) dev->ml_priv)->tbusy));
netif_wake_queue(dev);
- eieio();
}
static inline int
claw_check_busy(struct net_device *dev)
{
- eieio();
- return ((struct claw_privbk *) dev->priv)->tbusy;
+ return ((struct claw_privbk *) dev->ml_priv)->tbusy;
}
static inline void
claw_setbit_busy(int nr,struct net_device *dev)
{
netif_stop_queue(dev);
- set_bit(nr, (void *)&(((struct claw_privbk *)dev->priv)->tbusy));
+ set_bit(nr, (void *)&(((struct claw_privbk *)dev->ml_priv)->tbusy));
}
static inline void
claw_clearbit_busy(int nr,struct net_device *dev)
{
- clear_bit(nr,(void *)&(((struct claw_privbk *)dev->priv)->tbusy));
+ clear_bit(nr, (void *)&(((struct claw_privbk *)dev->ml_priv)->tbusy));
netif_wake_queue(dev);
}
@@ -198,7 +169,7 @@ claw_test_and_setbit_busy(int nr,struct net_device *dev)
{
netif_stop_queue(dev);
return test_and_set_bit(nr,
- (void *)&(((struct claw_privbk *) dev->priv)->tbusy));
+ (void *)&(((struct claw_privbk *) dev->ml_priv)->tbusy));
}
@@ -232,27 +203,32 @@ static void probe_error( struct ccwgroup_device *cgdev);
static struct net_device_stats *claw_stats(struct net_device *dev);
static int pages_to_order_of_mag(int num_of_pages);
static struct sk_buff *claw_pack_skb(struct claw_privbk *privptr);
-#ifdef DEBUG
-static void dumpit (char *buf, int len);
-#endif
/* sysfs Functions */
-static ssize_t claw_hname_show(struct device *dev, struct device_attribute *attr, char *buf);
-static ssize_t claw_hname_write(struct device *dev, struct device_attribute *attr,
+static ssize_t claw_hname_show(struct device *dev,
+ struct device_attribute *attr, char *buf);
+static ssize_t claw_hname_write(struct device *dev,
+ struct device_attribute *attr,
const char *buf, size_t count);
-static ssize_t claw_adname_show(struct device *dev, struct device_attribute *attr, char *buf);
-static ssize_t claw_adname_write(struct device *dev, struct device_attribute *attr,
+static ssize_t claw_adname_show(struct device *dev,
+ struct device_attribute *attr, char *buf);
+static ssize_t claw_adname_write(struct device *dev,
+ struct device_attribute *attr,
const char *buf, size_t count);
-static ssize_t claw_apname_show(struct device *dev, struct device_attribute *attr, char *buf);
-static ssize_t claw_apname_write(struct device *dev, struct device_attribute *attr,
+static ssize_t claw_apname_show(struct device *dev,
+ struct device_attribute *attr, char *buf);
+static ssize_t claw_apname_write(struct device *dev,
+ struct device_attribute *attr,
const char *buf, size_t count);
-static ssize_t claw_wbuff_show(struct device *dev, struct device_attribute *attr, char *buf);
-static ssize_t claw_wbuff_write(struct device *dev, struct device_attribute *attr,
+static ssize_t claw_wbuff_show(struct device *dev,
+ struct device_attribute *attr, char *buf);
+static ssize_t claw_wbuff_write(struct device *dev,
+ struct device_attribute *attr,
const char *buf, size_t count);
-static ssize_t claw_rbuff_show(struct device *dev, struct device_attribute *attr, char *buf);
-static ssize_t claw_rbuff_write(struct device *dev, struct device_attribute *attr,
+static ssize_t claw_rbuff_show(struct device *dev,
+ struct device_attribute *attr, char *buf);
+static ssize_t claw_rbuff_write(struct device *dev,
+ struct device_attribute *attr,
const char *buf, size_t count);
-static int claw_add_files(struct device *dev);
-static void claw_remove_files(struct device *dev);
/* Functions for System Validate */
static int claw_process_control( struct net_device *dev, struct ccwbk * p_ccw);
@@ -263,100 +239,76 @@ static int claw_snd_disc(struct net_device *dev, struct clawctl * p_ctl);
static int claw_snd_sys_validate_rsp(struct net_device *dev,
struct clawctl * p_ctl, __u32 return_code);
static int claw_strt_conn_req(struct net_device *dev );
-static void claw_strt_read ( struct net_device *dev, int lock );
-static void claw_strt_out_IO( struct net_device *dev );
-static void claw_free_wrt_buf( struct net_device *dev );
+static void claw_strt_read(struct net_device *dev, int lock);
+static void claw_strt_out_IO(struct net_device *dev);
+static void claw_free_wrt_buf(struct net_device *dev);
/* Functions for unpack reads */
-static void unpack_read (struct net_device *dev );
+static void unpack_read(struct net_device *dev);
+
+static int claw_pm_prepare(struct ccwgroup_device *gdev)
+{
+ return -EPERM;
+}
+
+/* the root device for claw group devices */
+static struct device *claw_root_dev;
/* ccwgroup table */
static struct ccwgroup_driver claw_group_driver = {
- .owner = THIS_MODULE,
- .name = "claw",
- .max_slaves = 2,
- .driver_id = 0xC3D3C1E6,
- .probe = claw_probe,
- .remove = claw_remove_device,
- .set_online = claw_new_device,
- .set_offline = claw_shutdown_device,
+ .driver = {
+ .owner = THIS_MODULE,
+ .name = "claw",
+ },
+ .setup = claw_probe,
+ .remove = claw_remove_device,
+ .set_online = claw_new_device,
+ .set_offline = claw_shutdown_device,
+ .prepare = claw_pm_prepare,
};
-/*
-*
-* Key functions
-*/
+static struct ccw_device_id claw_ids[] = {
+ {CCW_DEVICE(0x3088, 0x61), .driver_info = claw_channel_type_claw},
+ {},
+};
+MODULE_DEVICE_TABLE(ccw, claw_ids);
+
+static struct ccw_driver claw_ccw_driver = {
+ .driver = {
+ .owner = THIS_MODULE,
+ .name = "claw",
+ },
+ .ids = claw_ids,
+ .probe = ccwgroup_probe_ccwdev,
+ .remove = ccwgroup_remove_ccwdev,
+ .int_class = IRQIO_CLW,
+};
-/*----------------------------------------------------------------*
- * claw_probe *
- * this function is called for each CLAW device. *
- *----------------------------------------------------------------*/
-static int
-claw_probe(struct ccwgroup_device *cgdev)
+static ssize_t claw_driver_group_store(struct device_driver *ddrv,
+ const char *buf, size_t count)
{
- int rc;
- struct claw_privbk *privptr=NULL;
+ int err;
+ err = ccwgroup_create_dev(claw_root_dev, &claw_group_driver, 2, buf);
+ return err ? err : count;
+}
+static DRIVER_ATTR(group, 0200, NULL, claw_driver_group_store);
-#ifdef FUNCTRACE
- printk(KERN_INFO "%s Enter\n",__FUNCTION__);
-#endif
- CLAW_DBF_TEXT(2,setup,"probe");
- if (!get_device(&cgdev->dev))
- return -ENODEV;
-#ifdef DEBUGMSG
- printk(KERN_INFO "claw: variable cgdev =\n");
- dumpit((char *)cgdev, sizeof(struct ccwgroup_device));
-#endif
- privptr = kzalloc(sizeof(struct claw_privbk), GFP_KERNEL);
- if (privptr == NULL) {
- probe_error(cgdev);
- put_device(&cgdev->dev);
- printk(KERN_WARNING "Out of memory %s %s Exit Line %d \n",
- cgdev->cdev[0]->dev.bus_id,__FUNCTION__,__LINE__);
- CLAW_DBF_TEXT_(2,setup,"probex%d",-ENOMEM);
- return -ENOMEM;
- }
- privptr->p_mtc_envelope= kzalloc( MAX_ENVELOPE_SIZE, GFP_KERNEL);
- privptr->p_env = kzalloc(sizeof(struct claw_env), GFP_KERNEL);
- if ((privptr->p_mtc_envelope==NULL) || (privptr->p_env==NULL)) {
- probe_error(cgdev);
- put_device(&cgdev->dev);
- printk(KERN_WARNING "Out of memory %s %s Exit Line %d \n",
- cgdev->cdev[0]->dev.bus_id,__FUNCTION__,__LINE__);
- CLAW_DBF_TEXT_(2,setup,"probex%d",-ENOMEM);
- return -ENOMEM;
- }
- memcpy(privptr->p_env->adapter_name,WS_NAME_NOT_DEF,8);
- memcpy(privptr->p_env->host_name,WS_NAME_NOT_DEF,8);
- memcpy(privptr->p_env->api_type,WS_NAME_NOT_DEF,8);
- privptr->p_env->packing = 0;
- privptr->p_env->write_buffers = 5;
- privptr->p_env->read_buffers = 5;
- privptr->p_env->read_size = CLAW_FRAME_SIZE;
- privptr->p_env->write_size = CLAW_FRAME_SIZE;
- rc = claw_add_files(&cgdev->dev);
- if (rc) {
- probe_error(cgdev);
- put_device(&cgdev->dev);
- printk(KERN_WARNING "add_files failed %s %s Exit Line %d \n",
- cgdev->cdev[0]->dev.bus_id,__FUNCTION__,__LINE__);
- CLAW_DBF_TEXT_(2,setup,"probex%d",rc);
- return rc;
- }
- printk(KERN_INFO "claw: sysfs files added for %s\n",cgdev->cdev[0]->dev.bus_id);
- privptr->p_env->p_priv = privptr;
- cgdev->cdev[0]->handler = claw_irq_handler;
- cgdev->cdev[1]->handler = claw_irq_handler;
- cgdev->dev.driver_data = privptr;
-#ifdef FUNCTRACE
- printk(KERN_INFO "claw:%s exit on line %d, "
- "rc = 0\n",__FUNCTION__,__LINE__);
-#endif
- CLAW_DBF_TEXT(2,setup,"prbext 0");
+static struct attribute *claw_drv_attrs[] = {
+ &driver_attr_group.attr,
+ NULL,
+};
+static struct attribute_group claw_drv_attr_group = {
+ .attrs = claw_drv_attrs,
+};
+static const struct attribute_group *claw_drv_attr_groups[] = {
+ &claw_drv_attr_group,
+ NULL,
+};
- return 0;
-} /* end of claw_probe */
+/*
+* Key functions
+*/
/*-------------------------------------------------------------------*
* claw_tx *
@@ -366,41 +318,20 @@ static int
claw_tx(struct sk_buff *skb, struct net_device *dev)
{
int rc;
- struct claw_privbk *privptr=dev->priv;
+ struct claw_privbk *privptr = dev->ml_priv;
unsigned long saveflags;
struct chbk *p_ch;
-#ifdef FUNCTRACE
- printk(KERN_INFO "%s:%s enter\n",dev->name,__FUNCTION__);
-#endif
- CLAW_DBF_TEXT(4,trace,"claw_tx");
- p_ch=&privptr->channel[WRITE];
- if (skb == NULL) {
- printk(KERN_WARNING "%s: null pointer passed as sk_buffer\n",
- dev->name);
- privptr->stats.tx_dropped++;
-#ifdef FUNCTRACE
- printk(KERN_INFO "%s: %s() exit on line %d, rc = EIO\n",
- dev->name,__FUNCTION__, __LINE__);
-#endif
- CLAW_DBF_TEXT_(2,trace,"clawtx%d",-EIO);
- return -EIO;
- }
-
-#ifdef IOTRACE
- printk(KERN_INFO "%s: variable sk_buff=\n",dev->name);
- dumpit((char *) skb, sizeof(struct sk_buff));
- printk(KERN_INFO "%s: variable dev=\n",dev->name);
- dumpit((char *) dev, sizeof(struct net_device));
-#endif
+ CLAW_DBF_TEXT(4, trace, "claw_tx");
+ p_ch = &privptr->channel[WRITE_CHANNEL];
spin_lock_irqsave(get_ccwdev_lock(p_ch->cdev), saveflags);
rc=claw_hw_tx( skb, dev, 1 );
spin_unlock_irqrestore(get_ccwdev_lock(p_ch->cdev), saveflags);
-#ifdef FUNCTRACE
- printk(KERN_INFO "%s:%s exit on line %d, rc = %d\n",
- dev->name, __FUNCTION__, __LINE__, rc);
-#endif
- CLAW_DBF_TEXT_(4,trace,"clawtx%d",rc);
+ CLAW_DBF_TEXT_(4, trace, "clawtx%d", rc);
+ if (rc)
+ rc = NETDEV_TX_BUSY;
+ else
+ rc = NETDEV_TX_OK;
return rc;
} /* end of claw_tx */
@@ -413,13 +344,13 @@ static struct sk_buff *
claw_pack_skb(struct claw_privbk *privptr)
{
struct sk_buff *new_skb,*held_skb;
- struct chbk *p_ch = &privptr->channel[WRITE];
+ struct chbk *p_ch = &privptr->channel[WRITE_CHANNEL];
struct claw_env *p_env = privptr->p_env;
int pkt_cnt,pk_ind,so_far;
new_skb = NULL; /* assume no dice */
pkt_cnt = 0;
- CLAW_DBF_TEXT(4,trace,"PackSKBe");
+ CLAW_DBF_TEXT(4, trace, "PackSKBe");
if (!skb_queue_empty(&p_ch->collect_queue)) {
/* some data */
held_skb = skb_dequeue(&p_ch->collect_queue);
@@ -457,13 +388,8 @@ claw_pack_skb(struct claw_privbk *privptr)
skb_queue_head(&p_ch->collect_queue,held_skb);
}
}
-#ifdef IOTRACE
- printk(KERN_INFO "%s: %s() Packed %d len %d\n",
- p_env->ndev->name,
- __FUNCTION__,pkt_cnt,new_skb->len);
-#endif
}
- CLAW_DBF_TEXT(4,trace,"PackSKBx");
+ CLAW_DBF_TEXT(4, trace, "PackSKBx");
return new_skb;
}
@@ -475,31 +401,14 @@ claw_pack_skb(struct claw_privbk *privptr)
static int
claw_change_mtu(struct net_device *dev, int new_mtu)
{
- struct claw_privbk *privptr=dev->priv;
+ struct claw_privbk *privptr = dev->ml_priv;
int buff_size;
-#ifdef FUNCTRACE
- printk(KERN_INFO "%s:%s Enter \n",dev->name,__FUNCTION__);
-#endif
-#ifdef DEBUGMSG
- printk(KERN_INFO "variable dev =\n");
- dumpit((char *) dev, sizeof(struct net_device));
- printk(KERN_INFO "variable new_mtu = %d\n", new_mtu);
-#endif
- CLAW_DBF_TEXT(4,trace,"setmtu");
+ CLAW_DBF_TEXT(4, trace, "setmtu");
buff_size = privptr->p_env->write_size;
if ((new_mtu < 60) || (new_mtu > buff_size)) {
-#ifdef FUNCTRACE
- printk(KERN_INFO "%s:%s Exit on line %d, rc=EINVAL\n",
- dev->name,
- __FUNCTION__, __LINE__);
-#endif
return -EINVAL;
}
dev->mtu = new_mtu;
-#ifdef FUNCTRACE
- printk(KERN_INFO "%s:%s Exit on line %d\n",dev->name,
- __FUNCTION__, __LINE__);
-#endif
return 0;
} /* end of claw_change_mtu */
@@ -521,24 +430,13 @@ claw_open(struct net_device *dev)
struct timer_list timer;
struct ccwbk *p_buf;
-#ifdef FUNCTRACE
- printk(KERN_INFO "%s:%s Enter \n",dev->name,__FUNCTION__);
-#endif
- CLAW_DBF_TEXT(4,trace,"open");
- if (!dev || (dev->name[0] == 0x00)) {
- CLAW_DBF_TEXT(2,trace,"BadDev");
- printk(KERN_WARNING "claw: Bad device at open failing \n");
- return -ENODEV;
- }
- privptr = (struct claw_privbk *)dev->priv;
+ CLAW_DBF_TEXT(4, trace, "open");
+ privptr = (struct claw_privbk *)dev->ml_priv;
/* allocate and initialize CCW blocks */
if (privptr->buffs_alloc == 0) {
rc=init_ccw_bk(dev);
if (rc) {
- printk(KERN_INFO "%s:%s Exit on line %d, rc=ENOMEM\n",
- dev->name,
- __FUNCTION__, __LINE__);
- CLAW_DBF_TEXT(2,trace,"openmem");
+ CLAW_DBF_TEXT(2, trace, "openmem");
return -ENOMEM;
}
}
@@ -554,15 +452,15 @@ claw_open(struct net_device *dev)
privptr->p_env->write_size=CLAW_FRAME_SIZE;
}
claw_set_busy(dev);
- tasklet_init(&privptr->channel[READ].tasklet, claw_irq_tasklet,
- (unsigned long) &privptr->channel[READ]);
+ tasklet_init(&privptr->channel[READ_CHANNEL].tasklet, claw_irq_tasklet,
+ (unsigned long) &privptr->channel[READ_CHANNEL]);
for ( i = 0; i < 2; i++) {
- CLAW_DBF_TEXT_(2,trace,"opn_ch%d",i);
+ CLAW_DBF_TEXT_(2, trace, "opn_ch%d", i);
init_waitqueue_head(&privptr->channel[i].wait);
/* skb_queue_head_init(&p_ch->io_queue); */
- if (i == WRITE)
+ if (i == WRITE_CHANNEL)
skb_queue_head_init(
- &privptr->channel[WRITE].collect_queue);
+ &privptr->channel[WRITE_CHANNEL].collect_queue);
privptr->channel[i].flag_a = 0;
privptr->channel[i].IO_active = 0;
privptr->channel[i].flag &= ~CLAW_TIMER;
@@ -590,20 +488,14 @@ claw_open(struct net_device *dev)
if((privptr->channel[i].flag & CLAW_TIMER) == 0x00)
del_timer(&timer);
}
- if ((((privptr->channel[READ].last_dstat |
- privptr->channel[WRITE].last_dstat) &
+ if ((((privptr->channel[READ_CHANNEL].last_dstat |
+ privptr->channel[WRITE_CHANNEL].last_dstat) &
~(DEV_STAT_CHN_END | DEV_STAT_DEV_END)) != 0x00) ||
- (((privptr->channel[READ].flag |
- privptr->channel[WRITE].flag) & CLAW_TIMER) != 0x00)) {
-#ifdef DEBUGMSG
- printk(KERN_INFO "%s: channel problems during open - read:"
- " %02x - write: %02x\n",
- dev->name,
- privptr->channel[READ].last_dstat,
- privptr->channel[WRITE].last_dstat);
-#endif
- printk(KERN_INFO "%s: remote side is not ready\n", dev->name);
- CLAW_DBF_TEXT(2,trace,"notrdy");
+ (((privptr->channel[READ_CHANNEL].flag |
+ privptr->channel[WRITE_CHANNEL].flag) & CLAW_TIMER) != 0x00)) {
+ dev_info(&privptr->channel[READ_CHANNEL].cdev->dev,
+ "%s: remote side is not ready\n", dev->name);
+ CLAW_DBF_TEXT(2, trace, "notrdy");
for ( i = 0; i < 2; i++) {
spin_lock_irqsave(
@@ -653,29 +545,20 @@ claw_open(struct net_device *dev)
}
}
privptr->buffs_alloc = 0;
- privptr->channel[READ].flag= 0x00;
- privptr->channel[WRITE].flag = 0x00;
+ privptr->channel[READ_CHANNEL].flag = 0x00;
+ privptr->channel[WRITE_CHANNEL].flag = 0x00;
privptr->p_buff_ccw=NULL;
privptr->p_buff_read=NULL;
privptr->p_buff_write=NULL;
claw_clear_busy(dev);
-#ifdef FUNCTRACE
- printk(KERN_INFO "%s:%s Exit on line %d, rc=EIO\n",
- dev->name,__FUNCTION__,__LINE__);
-#endif
- CLAW_DBF_TEXT(2,trace,"open EIO");
+ CLAW_DBF_TEXT(2, trace, "open EIO");
return -EIO;
}
/* Send SystemValidate command */
claw_clear_busy(dev);
-
-#ifdef FUNCTRACE
- printk(KERN_INFO "%s:%s Exit on line %d, rc=0\n",
- dev->name,__FUNCTION__,__LINE__);
-#endif
- CLAW_DBF_TEXT(4,trace,"openok");
+ CLAW_DBF_TEXT(4, trace, "openok");
return 0;
} /* end of claw_open */
@@ -694,294 +577,191 @@ claw_irq_handler(struct ccw_device *cdev,
struct claw_env *p_env;
struct chbk *p_ch_r=NULL;
-
-#ifdef FUNCTRACE
- printk(KERN_INFO "%s enter \n",__FUNCTION__);
-#endif
- CLAW_DBF_TEXT(4,trace,"clawirq");
+ CLAW_DBF_TEXT(4, trace, "clawirq");
/* Bypass all 'unsolicited interrupts' */
- if (!cdev->dev.driver_data) {
- printk(KERN_WARNING "claw: unsolicited interrupt for device:"
- "%s received c-%02x d-%02x\n",
- cdev->dev.bus_id,irb->scsw.cstat, irb->scsw.dstat);
-#ifdef FUNCTRACE
- printk(KERN_INFO "claw: %s() "
- "exit on line %d\n",__FUNCTION__,__LINE__);
-#endif
- CLAW_DBF_TEXT(2,trace,"badirq");
+ privptr = dev_get_drvdata(&cdev->dev);
+ if (!privptr) {
+ dev_warn(&cdev->dev, "An uninitialized CLAW device received an"
+ " IRQ, c-%02x d-%02x\n",
+ irb->scsw.cmd.cstat, irb->scsw.cmd.dstat);
+ CLAW_DBF_TEXT(2, trace, "badirq");
return;
}
- privptr = (struct claw_privbk *)cdev->dev.driver_data;
/* Try to extract channel from driver data. */
- if (privptr->channel[READ].cdev == cdev)
- p_ch = &privptr->channel[READ];
- else if (privptr->channel[WRITE].cdev == cdev)
- p_ch = &privptr->channel[WRITE];
+ if (privptr->channel[READ_CHANNEL].cdev == cdev)
+ p_ch = &privptr->channel[READ_CHANNEL];
+ else if (privptr->channel[WRITE_CHANNEL].cdev == cdev)
+ p_ch = &privptr->channel[WRITE_CHANNEL];
else {
- printk(KERN_WARNING "claw: Can't determine channel for "
- "interrupt, device %s\n", cdev->dev.bus_id);
- CLAW_DBF_TEXT(2,trace,"badchan");
+ dev_warn(&cdev->dev, "The device is not a CLAW device\n");
+ CLAW_DBF_TEXT(2, trace, "badchan");
return;
}
- CLAW_DBF_TEXT_(4,trace,"IRQCH=%d",p_ch->flag);
+ CLAW_DBF_TEXT_(4, trace, "IRQCH=%d", p_ch->flag);
dev = (struct net_device *) (p_ch->ndev);
p_env=privptr->p_env;
-#ifdef IOTRACE
- printk(KERN_INFO "%s: interrupt for device: %04x "
- "received c-%02x d-%02x state-%02x\n",
- dev->name, p_ch->devno, irb->scsw.cstat,
- irb->scsw.dstat, p_ch->claw_state);
-#endif
-
/* Copy interruption response block. */
memcpy(p_ch->irb, irb, sizeof(struct irb));
- /* Check for good subchannel return code, otherwise error message */
- if (irb->scsw.cstat && !(irb->scsw.cstat & SCHN_STAT_PCI)) {
- printk(KERN_INFO "%s: subchannel check for device: %04x -"
+ /* Check for good subchannel return code, otherwise info message */
+ if (irb->scsw.cmd.cstat && !(irb->scsw.cmd.cstat & SCHN_STAT_PCI)) {
+ dev_info(&cdev->dev,
+ "%s: subchannel check for device: %04x -"
" Sch Stat %02x Dev Stat %02x CPA - %04x\n",
dev->name, p_ch->devno,
- irb->scsw.cstat, irb->scsw.dstat,irb->scsw.cpa);
-#ifdef IOTRACE
- dumpit((char *)irb,sizeof(struct irb));
- dumpit((char *)(unsigned long)irb->scsw.cpa,
- sizeof(struct ccw1));
-#endif
-#ifdef FUNCTRACE
- printk(KERN_INFO "%s:%s Exit on line %d\n",
- dev->name,__FUNCTION__,__LINE__);
-#endif
- CLAW_DBF_TEXT(2,trace,"chanchk");
+ irb->scsw.cmd.cstat, irb->scsw.cmd.dstat,
+ irb->scsw.cmd.cpa);
+ CLAW_DBF_TEXT(2, trace, "chanchk");
/* return; */
}
/* Check the reason-code of a unit check */
- if (irb->scsw.dstat & DEV_STAT_UNIT_CHECK) {
+ if (irb->scsw.cmd.dstat & DEV_STAT_UNIT_CHECK)
ccw_check_unit_check(p_ch, irb->ecw[0]);
- }
/* State machine to bring the connection up, down and to restart */
- p_ch->last_dstat = irb->scsw.dstat;
+ p_ch->last_dstat = irb->scsw.cmd.dstat;
switch (p_ch->claw_state) {
- case CLAW_STOP:/* HALT_IO by claw_release (halt sequence) */
-#ifdef DEBUGMSG
- printk(KERN_INFO "%s: CLAW_STOP enter\n", dev->name);
-#endif
- if (!((p_ch->irb->scsw.stctl & SCSW_STCTL_SEC_STATUS) ||
- (p_ch->irb->scsw.stctl == SCSW_STCTL_STATUS_PEND) ||
- (p_ch->irb->scsw.stctl ==
- (SCSW_STCTL_ALERT_STATUS | SCSW_STCTL_STATUS_PEND)))) {
-#ifdef FUNCTRACE
- printk(KERN_INFO "%s:%s Exit on line %d\n",
- dev->name,__FUNCTION__,__LINE__);
-#endif
- return;
- }
- wake_up(&p_ch->wait); /* wake up claw_release */
-
-#ifdef DEBUGMSG
- printk(KERN_INFO "%s: CLAW_STOP exit\n", dev->name);
-#endif
-#ifdef FUNCTRACE
- printk(KERN_INFO "%s:%s Exit on line %d\n",
- dev->name,__FUNCTION__,__LINE__);
-#endif
- CLAW_DBF_TEXT(4,trace,"stop");
- return;
-
- case CLAW_START_HALT_IO: /* HALT_IO issued by claw_open */
-#ifdef DEBUGMSG
- printk(KERN_INFO "%s: process CLAW_STAT_HALT_IO\n",
- dev->name);
-#endif
- if (!((p_ch->irb->scsw.stctl & SCSW_STCTL_SEC_STATUS) ||
- (p_ch->irb->scsw.stctl == SCSW_STCTL_STATUS_PEND) ||
- (p_ch->irb->scsw.stctl ==
- (SCSW_STCTL_ALERT_STATUS | SCSW_STCTL_STATUS_PEND)))) {
-#ifdef FUNCTRACE
- printk(KERN_INFO "%s:%s Exit on line %d\n",
- dev->name,__FUNCTION__,__LINE__);
-#endif
- CLAW_DBF_TEXT(4,trace,"haltio");
- return;
- }
- if (p_ch->flag == CLAW_READ) {
- p_ch->claw_state = CLAW_START_READ;
- wake_up(&p_ch->wait); /* wake claw_open (READ)*/
- }
+ case CLAW_STOP:/* HALT_IO by claw_release (halt sequence) */
+ if (!((p_ch->irb->scsw.cmd.stctl & SCSW_STCTL_SEC_STATUS) ||
+ (p_ch->irb->scsw.cmd.stctl == SCSW_STCTL_STATUS_PEND) ||
+ (p_ch->irb->scsw.cmd.stctl ==
+ (SCSW_STCTL_ALERT_STATUS | SCSW_STCTL_STATUS_PEND))))
+ return;
+ wake_up(&p_ch->wait); /* wake up claw_release */
+ CLAW_DBF_TEXT(4, trace, "stop");
+ return;
+ case CLAW_START_HALT_IO: /* HALT_IO issued by claw_open */
+ if (!((p_ch->irb->scsw.cmd.stctl & SCSW_STCTL_SEC_STATUS) ||
+ (p_ch->irb->scsw.cmd.stctl == SCSW_STCTL_STATUS_PEND) ||
+ (p_ch->irb->scsw.cmd.stctl ==
+ (SCSW_STCTL_ALERT_STATUS | SCSW_STCTL_STATUS_PEND)))) {
+ CLAW_DBF_TEXT(4, trace, "haltio");
+ return;
+ }
+ if (p_ch->flag == CLAW_READ) {
+ p_ch->claw_state = CLAW_START_READ;
+ wake_up(&p_ch->wait); /* wake claw_open (READ)*/
+ } else if (p_ch->flag == CLAW_WRITE) {
+ p_ch->claw_state = CLAW_START_WRITE;
+ /* send SYSTEM_VALIDATE */
+ claw_strt_read(dev, LOCK_NO);
+ claw_send_control(dev,
+ SYSTEM_VALIDATE_REQUEST,
+ 0, 0, 0,
+ p_env->host_name,
+ p_env->adapter_name);
+ } else {
+ dev_warn(&cdev->dev, "The CLAW device received"
+ " an unexpected IRQ, "
+ "c-%02x d-%02x\n",
+ irb->scsw.cmd.cstat,
+ irb->scsw.cmd.dstat);
+ return;
+ }
+ CLAW_DBF_TEXT(4, trace, "haltio");
+ return;
+ case CLAW_START_READ:
+ CLAW_DBF_TEXT(4, trace, "ReadIRQ");
+ if (p_ch->irb->scsw.cmd.dstat & DEV_STAT_UNIT_CHECK) {
+ clear_bit(0, (void *)&p_ch->IO_active);
+ if ((p_ch->irb->ecw[0] & 0x41) == 0x41 ||
+ (p_ch->irb->ecw[0] & 0x40) == 0x40 ||
+ (p_ch->irb->ecw[0]) == 0) {
+ privptr->stats.rx_errors++;
+ dev_info(&cdev->dev,
+ "%s: Restart is required after remote "
+ "side recovers \n",
+ dev->name);
+ }
+ CLAW_DBF_TEXT(4, trace, "notrdy");
+ return;
+ }
+ if ((p_ch->irb->scsw.cmd.cstat & SCHN_STAT_PCI) &&
+ (p_ch->irb->scsw.cmd.dstat == 0)) {
+ if (test_and_set_bit(CLAW_BH_ACTIVE,
+ (void *)&p_ch->flag_a) == 0)
+ tasklet_schedule(&p_ch->tasklet);
else
- if (p_ch->flag == CLAW_WRITE) {
- p_ch->claw_state = CLAW_START_WRITE;
- /* send SYSTEM_VALIDATE */
- claw_strt_read(dev, LOCK_NO);
- claw_send_control(dev,
- SYSTEM_VALIDATE_REQUEST,
- 0, 0, 0,
- p_env->host_name,
- p_env->adapter_name );
- } else {
- printk(KERN_WARNING "claw: unsolicited "
- "interrupt for device:"
- "%s received c-%02x d-%02x\n",
- cdev->dev.bus_id,
- irb->scsw.cstat,
- irb->scsw.dstat);
- return;
- }
-#ifdef DEBUGMSG
- printk(KERN_INFO "%s: process CLAW_STAT_HALT_IO exit\n",
- dev->name);
-#endif
-#ifdef FUNCTRACE
- printk(KERN_INFO "%s:%s Exit on line %d\n",
- dev->name,__FUNCTION__,__LINE__);
-#endif
- CLAW_DBF_TEXT(4,trace,"haltio");
- return;
- case CLAW_START_READ:
- CLAW_DBF_TEXT(4,trace,"ReadIRQ");
- if (p_ch->irb->scsw.dstat & DEV_STAT_UNIT_CHECK) {
- clear_bit(0, (void *)&p_ch->IO_active);
- if ((p_ch->irb->ecw[0] & 0x41) == 0x41 ||
- (p_ch->irb->ecw[0] & 0x40) == 0x40 ||
- (p_ch->irb->ecw[0]) == 0)
- {
- privptr->stats.rx_errors++;
- printk(KERN_INFO "%s: Restart is "
- "required after remote "
- "side recovers \n",
- dev->name);
- }
-#ifdef FUNCTRACE
- printk(KERN_INFO "%s:%s Exit on line %d\n",
- dev->name,__FUNCTION__,__LINE__);
-#endif
- CLAW_DBF_TEXT(4,trace,"notrdy");
- return;
- }
- if ((p_ch->irb->scsw.cstat & SCHN_STAT_PCI) &&
- (p_ch->irb->scsw.dstat==0)) {
- if (test_and_set_bit(CLAW_BH_ACTIVE,
- (void *)&p_ch->flag_a) == 0) {
- tasklet_schedule(&p_ch->tasklet);
- }
- else {
- CLAW_DBF_TEXT(4,trace,"PCINoBH");
- }
-#ifdef FUNCTRACE
- printk(KERN_INFO "%s:%s Exit on line %d\n",
- dev->name,__FUNCTION__,__LINE__);
-#endif
- CLAW_DBF_TEXT(4,trace,"PCI_read");
- return;
- }
- if(!((p_ch->irb->scsw.stctl & SCSW_STCTL_SEC_STATUS) ||
- (p_ch->irb->scsw.stctl == SCSW_STCTL_STATUS_PEND) ||
- (p_ch->irb->scsw.stctl ==
- (SCSW_STCTL_ALERT_STATUS | SCSW_STCTL_STATUS_PEND)))) {
-#ifdef FUNCTRACE
- printk(KERN_INFO "%s:%s Exit on line %d\n",
- dev->name,__FUNCTION__,__LINE__);
-#endif
- CLAW_DBF_TEXT(4,trace,"SPend_rd");
- return;
- }
- clear_bit(0, (void *)&p_ch->IO_active);
- claw_clearbit_busy(TB_RETRY,dev);
- if (test_and_set_bit(CLAW_BH_ACTIVE,
- (void *)&p_ch->flag_a) == 0) {
- tasklet_schedule(&p_ch->tasklet);
- }
- else {
- CLAW_DBF_TEXT(4,trace,"RdBHAct");
- }
-
-#ifdef DEBUGMSG
- printk(KERN_INFO "%s: process CLAW_START_READ exit\n",
+ CLAW_DBF_TEXT(4, trace, "PCINoBH");
+ CLAW_DBF_TEXT(4, trace, "PCI_read");
+ return;
+ }
+ if (!((p_ch->irb->scsw.cmd.stctl & SCSW_STCTL_SEC_STATUS) ||
+ (p_ch->irb->scsw.cmd.stctl == SCSW_STCTL_STATUS_PEND) ||
+ (p_ch->irb->scsw.cmd.stctl ==
+ (SCSW_STCTL_ALERT_STATUS | SCSW_STCTL_STATUS_PEND)))) {
+ CLAW_DBF_TEXT(4, trace, "SPend_rd");
+ return;
+ }
+ clear_bit(0, (void *)&p_ch->IO_active);
+ claw_clearbit_busy(TB_RETRY, dev);
+ if (test_and_set_bit(CLAW_BH_ACTIVE,
+ (void *)&p_ch->flag_a) == 0)
+ tasklet_schedule(&p_ch->tasklet);
+ else
+ CLAW_DBF_TEXT(4, trace, "RdBHAct");
+ CLAW_DBF_TEXT(4, trace, "RdIRQXit");
+ return;
+ case CLAW_START_WRITE:
+ if (p_ch->irb->scsw.cmd.dstat & DEV_STAT_UNIT_CHECK) {
+ dev_info(&cdev->dev,
+ "%s: Unit Check Occurred in "
+ "write channel\n", dev->name);
+ clear_bit(0, (void *)&p_ch->IO_active);
+ if (p_ch->irb->ecw[0] & 0x80) {
+ dev_info(&cdev->dev,
+ "%s: Resetting Event "
+ "occurred:\n", dev->name);
+ init_timer(&p_ch->timer);
+ p_ch->timer.function =
+ (void *)claw_write_retry;
+ p_ch->timer.data = (unsigned long)p_ch;
+ p_ch->timer.expires = jiffies + 10*HZ;
+ add_timer(&p_ch->timer);
+ dev_info(&cdev->dev,
+ "%s: write connection "
+ "restarting\n", dev->name);
+ }
+ CLAW_DBF_TEXT(4, trace, "rstrtwrt");
+ return;
+ }
+ if (p_ch->irb->scsw.cmd.dstat & DEV_STAT_UNIT_EXCEP) {
+ clear_bit(0, (void *)&p_ch->IO_active);
+ dev_info(&cdev->dev,
+ "%s: Unit Exception "
+ "occurred in write channel\n",
dev->name);
-#endif
-#ifdef FUNCTRACE
- printk(KERN_INFO "%s:%s Exit on line %d\n",
- dev->name,__FUNCTION__,__LINE__);
-#endif
- CLAW_DBF_TEXT(4,trace,"RdIRQXit");
- return;
- case CLAW_START_WRITE:
- if (p_ch->irb->scsw.dstat & DEV_STAT_UNIT_CHECK) {
- printk(KERN_INFO "%s: Unit Check Occured in "
- "write channel\n",dev->name);
- clear_bit(0, (void *)&p_ch->IO_active);
- if (p_ch->irb->ecw[0] & 0x80 ) {
- printk(KERN_INFO "%s: Resetting Event "
- "occurred:\n",dev->name);
- init_timer(&p_ch->timer);
- p_ch->timer.function =
- (void *)claw_write_retry;
- p_ch->timer.data = (unsigned long)p_ch;
- p_ch->timer.expires = jiffies + 10*HZ;
- add_timer(&p_ch->timer);
- printk(KERN_INFO "%s: write connection "
- "restarting\n",dev->name);
- }
-#ifdef FUNCTRACE
- printk(KERN_INFO "%s:%s Exit on line %d\n",
- dev->name,__FUNCTION__,__LINE__);
-#endif
- CLAW_DBF_TEXT(4,trace,"rstrtwrt");
- return;
- }
- if (p_ch->irb->scsw.dstat & DEV_STAT_UNIT_EXCEP) {
- clear_bit(0, (void *)&p_ch->IO_active);
- printk(KERN_INFO "%s: Unit Exception "
- "Occured in write channel\n",
- dev->name);
- }
- if(!((p_ch->irb->scsw.stctl & SCSW_STCTL_SEC_STATUS) ||
- (p_ch->irb->scsw.stctl == SCSW_STCTL_STATUS_PEND) ||
- (p_ch->irb->scsw.stctl ==
- (SCSW_STCTL_ALERT_STATUS | SCSW_STCTL_STATUS_PEND)))) {
-#ifdef FUNCTRACE
- printk(KERN_INFO "%s:%s Exit on line %d\n",
- dev->name,__FUNCTION__,__LINE__);
-#endif
- CLAW_DBF_TEXT(4,trace,"writeUE");
- return;
- }
- clear_bit(0, (void *)&p_ch->IO_active);
- if (claw_test_and_setbit_busy(TB_TX,dev)==0) {
- claw_write_next(p_ch);
- claw_clearbit_busy(TB_TX,dev);
- claw_clear_busy(dev);
- }
- p_ch_r=(struct chbk *)&privptr->channel[READ];
- if (test_and_set_bit(CLAW_BH_ACTIVE,
- (void *)&p_ch_r->flag_a) == 0) {
- tasklet_schedule(&p_ch_r->tasklet);
- }
-
-#ifdef DEBUGMSG
- printk(KERN_INFO "%s: process CLAW_START_WRITE exit\n",
- dev->name);
-#endif
-#ifdef FUNCTRACE
- printk(KERN_INFO "%s:%s Exit on line %d\n",
- dev->name,__FUNCTION__,__LINE__);
-#endif
- CLAW_DBF_TEXT(4,trace,"StWtExit");
- return;
- default:
- printk(KERN_WARNING "%s: wrong selection code - irq "
- "state=%d\n",dev->name,p_ch->claw_state);
-#ifdef FUNCTRACE
- printk(KERN_INFO "%s:%s Exit on line %d\n",
- dev->name,__FUNCTION__,__LINE__);
-#endif
- CLAW_DBF_TEXT(2,trace,"badIRQ");
- return;
+ }
+ if (!((p_ch->irb->scsw.cmd.stctl & SCSW_STCTL_SEC_STATUS) ||
+ (p_ch->irb->scsw.cmd.stctl == SCSW_STCTL_STATUS_PEND) ||
+ (p_ch->irb->scsw.cmd.stctl ==
+ (SCSW_STCTL_ALERT_STATUS | SCSW_STCTL_STATUS_PEND)))) {
+ CLAW_DBF_TEXT(4, trace, "writeUE");
+ return;
+ }
+ clear_bit(0, (void *)&p_ch->IO_active);
+ if (claw_test_and_setbit_busy(TB_TX, dev) == 0) {
+ claw_write_next(p_ch);
+ claw_clearbit_busy(TB_TX, dev);
+ claw_clear_busy(dev);
+ }
+ p_ch_r = (struct chbk *)&privptr->channel[READ_CHANNEL];
+ if (test_and_set_bit(CLAW_BH_ACTIVE,
+ (void *)&p_ch_r->flag_a) == 0)
+ tasklet_schedule(&p_ch_r->tasklet);
+ CLAW_DBF_TEXT(4, trace, "StWtExit");
+ return;
+ default:
+ dev_warn(&cdev->dev,
+ "The CLAW device for %s received an unexpected IRQ\n",
+ dev->name);
+ CLAW_DBF_TEXT(2, trace, "badIRQ");
+ return;
}
} /* end of claw_irq_handler */
@@ -996,33 +776,13 @@ claw_irq_tasklet ( unsigned long data )
{
struct chbk * p_ch;
struct net_device *dev;
- struct claw_privbk * privptr;
p_ch = (struct chbk *) data;
dev = (struct net_device *)p_ch->ndev;
-#ifdef FUNCTRACE
- printk(KERN_INFO "%s:%s Enter \n",dev->name,__FUNCTION__);
-#endif
-#ifdef DEBUGMSG
- printk(KERN_INFO "%s: variable p_ch =\n",dev->name);
- dumpit((char *) p_ch, sizeof(struct chbk));
-#endif
- CLAW_DBF_TEXT(4,trace,"IRQtask");
-
- privptr = (struct claw_privbk *) dev->priv;
-
-#ifdef DEBUGMSG
- printk(KERN_INFO "%s: bh routine - state-%02x\n" ,
- dev->name, p_ch->claw_state);
-#endif
-
+ CLAW_DBF_TEXT(4, trace, "IRQtask");
unpack_read(dev);
clear_bit(CLAW_BH_ACTIVE, (void *)&p_ch->flag_a);
- CLAW_DBF_TEXT(4,trace,"TskletXt");
-#ifdef FUNCTRACE
- printk(KERN_INFO "%s:%s Exit on line %d\n",
- dev->name,__FUNCTION__,__LINE__);
-#endif
+ CLAW_DBF_TEXT(4, trace, "TskletXt");
return;
} /* end of claw_irq_bh */
@@ -1044,31 +804,22 @@ claw_release(struct net_device *dev)
if (!dev)
return 0;
- privptr = (struct claw_privbk *) dev->priv;
+ privptr = (struct claw_privbk *)dev->ml_priv;
if (!privptr)
return 0;
-#ifdef FUNCTRACE
- printk(KERN_INFO "%s:%s Enter \n",dev->name,__FUNCTION__);
-#endif
- CLAW_DBF_TEXT(4,trace,"release");
-#ifdef DEBUGMSG
- printk(KERN_INFO "%s: variable dev =\n",dev->name);
- dumpit((char *) dev, sizeof(struct net_device));
- printk(KERN_INFO "Priv Buffalloc %d\n",privptr->buffs_alloc);
- printk(KERN_INFO "Priv p_buff_ccw = %p\n",&privptr->p_buff_ccw);
-#endif
+ CLAW_DBF_TEXT(4, trace, "release");
privptr->release_pend=1;
claw_setbit_busy(TB_STOP,dev);
for ( i = 1; i >=0 ; i--) {
spin_lock_irqsave(
get_ccwdev_lock(privptr->channel[i].cdev), saveflags);
- /* del_timer(&privptr->channel[READ].timer); */
+ /* del_timer(&privptr->channel[READ_CHANNEL].timer); */
privptr->channel[i].claw_state = CLAW_STOP;
privptr->channel[i].IO_active = 0;
parm = (unsigned long) &privptr->channel[i];
- if (i == WRITE)
+ if (i == WRITE_CHANNEL)
claw_purge_skb_queue(
- &privptr->channel[WRITE].collect_queue);
+ &privptr->channel[WRITE_CHANNEL].collect_queue);
rc = ccw_device_halt (privptr->channel[i].cdev, parm);
if (privptr->system_validate_comp==0x00) /* never opened? */
init_waitqueue_head(&privptr->channel[i].wait);
@@ -1088,19 +839,15 @@ claw_release(struct net_device *dev)
privptr->pk_skb = NULL;
}
if(privptr->buffs_alloc != 1) {
-#ifdef FUNCTRACE
- printk(KERN_INFO "%s:%s Exit on line %d\n",
- dev->name,__FUNCTION__,__LINE__);
-#endif
- CLAW_DBF_TEXT(4,trace,"none2fre");
+ CLAW_DBF_TEXT(4, trace, "none2fre");
return 0;
}
- CLAW_DBF_TEXT(4,trace,"freebufs");
+ CLAW_DBF_TEXT(4, trace, "freebufs");
if (privptr->p_buff_ccw != NULL) {
free_pages((unsigned long)privptr->p_buff_ccw,
(int)pages_to_order_of_mag(privptr->p_buff_ccw_num));
}
- CLAW_DBF_TEXT(4,trace,"freeread");
+ CLAW_DBF_TEXT(4, trace, "freeread");
if (privptr->p_env->read_size < PAGE_SIZE) {
if (privptr->p_buff_read != NULL) {
free_pages((unsigned long)privptr->p_buff_read,
@@ -1116,7 +863,7 @@ claw_release(struct net_device *dev)
p_buf=p_buf->next;
}
}
- CLAW_DBF_TEXT(4,trace,"freewrit");
+ CLAW_DBF_TEXT(4, trace, "freewrit");
if (privptr->p_env->write_size < PAGE_SIZE ) {
free_pages((unsigned long)privptr->p_buff_write,
(int)pages_to_order_of_mag(privptr->p_buff_write_num));
@@ -1130,7 +877,7 @@ claw_release(struct net_device *dev)
p_buf=p_buf->next;
}
}
- CLAW_DBF_TEXT(4,trace,"clearptr");
+ CLAW_DBF_TEXT(4, trace, "clearptr");
privptr->buffs_alloc = 0;
privptr->p_buff_ccw=NULL;
privptr->p_buff_read=NULL;
@@ -1159,26 +906,22 @@ claw_release(struct net_device *dev)
privptr->mtc_skipping = 1;
privptr->mtc_offset=0;
- if (((privptr->channel[READ].last_dstat |
- privptr->channel[WRITE].last_dstat) &
+ if (((privptr->channel[READ_CHANNEL].last_dstat |
+ privptr->channel[WRITE_CHANNEL].last_dstat) &
~(DEV_STAT_CHN_END | DEV_STAT_DEV_END)) != 0x00) {
- printk(KERN_WARNING "%s: channel problems during close - "
- "read: %02x - write: %02x\n",
+ dev_warn(&privptr->channel[READ_CHANNEL].cdev->dev,
+ "Deactivating %s completed with incorrect"
+ " subchannel status "
+ "(read %02x, write %02x)\n",
dev->name,
- privptr->channel[READ].last_dstat,
- privptr->channel[WRITE].last_dstat);
- CLAW_DBF_TEXT(2,trace,"badclose");
+ privptr->channel[READ_CHANNEL].last_dstat,
+ privptr->channel[WRITE_CHANNEL].last_dstat);
+ CLAW_DBF_TEXT(2, trace, "badclose");
}
-#ifdef FUNCTRACE
- printk(KERN_INFO "%s:%s Exit on line %d\n",
- dev->name,__FUNCTION__,__LINE__);
-#endif
- CLAW_DBF_TEXT(4,trace,"rlsexit");
+ CLAW_DBF_TEXT(4, trace, "rlsexit");
return 0;
} /* end of claw_release */
-
-
/*-------------------------------------------------------------------*
* claw_write_retry *
* *
@@ -1190,32 +933,12 @@ claw_write_retry ( struct chbk *p_ch )
struct net_device *dev=p_ch->ndev;
-
-#ifdef FUNCTRACE
- printk(KERN_INFO "%s:%s Enter\n",dev->name,__FUNCTION__);
- printk(KERN_INFO "claw: variable p_ch =\n");
- dumpit((char *) p_ch, sizeof(struct chbk));
-#endif
- CLAW_DBF_TEXT(4,trace,"w_retry");
+ CLAW_DBF_TEXT(4, trace, "w_retry");
if (p_ch->claw_state == CLAW_STOP) {
-#ifdef FUNCTRACE
- printk(KERN_INFO "%s:%s Exit on line %d\n",
- dev->name,__FUNCTION__,__LINE__);
-#endif
return;
}
-#ifdef DEBUGMSG
- printk( KERN_INFO "%s:%s state-%02x\n" ,
- dev->name,
- __FUNCTION__,
- p_ch->claw_state);
-#endif
claw_strt_out_IO( dev );
-#ifdef FUNCTRACE
- printk(KERN_INFO "%s:%s Exit on line %d\n",
- dev->name,__FUNCTION__,__LINE__);
-#endif
- CLAW_DBF_TEXT(4,trace,"rtry_xit");
+ CLAW_DBF_TEXT(4, trace, "rtry_xit");
return;
} /* end of claw_write_retry */
@@ -1232,24 +955,18 @@ claw_write_next ( struct chbk * p_ch )
struct net_device *dev;
struct claw_privbk *privptr=NULL;
struct sk_buff *pk_skb;
- int rc;
-#ifdef FUNCTRACE
- printk(KERN_INFO "%s:%s Enter \n",p_ch->ndev->name,__FUNCTION__);
- printk(KERN_INFO "%s: variable p_ch =\n",p_ch->ndev->name);
- dumpit((char *) p_ch, sizeof(struct chbk));
-#endif
- CLAW_DBF_TEXT(4,trace,"claw_wrt");
+ CLAW_DBF_TEXT(4, trace, "claw_wrt");
if (p_ch->claw_state == CLAW_STOP)
return;
dev = (struct net_device *) p_ch->ndev;
- privptr = (struct claw_privbk *) dev->priv;
+ privptr = (struct claw_privbk *) dev->ml_priv;
claw_free_wrt_buf( dev );
if ((privptr->write_free_count > 0) &&
!skb_queue_empty(&p_ch->collect_queue)) {
pk_skb = claw_pack_skb(privptr);
while (pk_skb != NULL) {
- rc = claw_hw_tx( pk_skb, dev,1);
+ claw_hw_tx(pk_skb, dev, 1);
if (privptr->write_free_count > 0) {
pk_skb = claw_pack_skb(privptr);
} else
@@ -1259,11 +976,6 @@ claw_write_next ( struct chbk * p_ch )
if (privptr->p_write_active_first!=NULL) {
claw_strt_out_IO(dev);
}
-
-#ifdef FUNCTRACE
- printk(KERN_INFO "%s:%s Exit on line %d\n",
- dev->name,__FUNCTION__,__LINE__);
-#endif
return;
} /* end of claw_write_next */
@@ -1275,22 +987,12 @@ claw_write_next ( struct chbk * p_ch )
static void
claw_timer ( struct chbk * p_ch )
{
-#ifdef FUNCTRACE
- printk(KERN_INFO "%s:%s Entry\n",p_ch->ndev->name,__FUNCTION__);
- printk(KERN_INFO "%s: variable p_ch =\n",p_ch->ndev->name);
- dumpit((char *) p_ch, sizeof(struct chbk));
-#endif
- CLAW_DBF_TEXT(4,trace,"timer");
+ CLAW_DBF_TEXT(4, trace, "timer");
p_ch->flag |= CLAW_TIMER;
wake_up(&p_ch->wait);
-#ifdef FUNCTRACE
- printk(KERN_INFO "%s:%s Exit on line %d\n",
- p_ch->ndev->name,__FUNCTION__,__LINE__);
-#endif
return;
} /* end of claw_timer */
-
/*
*
* functions
@@ -1310,11 +1012,9 @@ static int
pages_to_order_of_mag(int num_of_pages)
{
int order_of_mag=1; /* assume 2 pages */
- int nump=2;
-#ifdef FUNCTRACE
- printk(KERN_INFO "%s Enter pages = %d \n",__FUNCTION__,num_of_pages);
-#endif
- CLAW_DBF_TEXT_(5,trace,"pages%d",num_of_pages);
+ int nump;
+
+ CLAW_DBF_TEXT_(5, trace, "pages%d", num_of_pages);
if (num_of_pages == 1) {return 0; } /* magnitude of 0 = 1 page */
/* 512 pages = 2Meg on 4k page systems */
if (num_of_pages >= 512) {return 9; }
@@ -1325,11 +1025,7 @@ pages_to_order_of_mag(int num_of_pages)
order_of_mag +=1;
}
if (order_of_mag > 9) { order_of_mag = 9; } /* I know it's paranoid */
-#ifdef FUNCTRACE
- printk(KERN_INFO "%s Exit on line %d, order = %d\n",
- __FUNCTION__,__LINE__, order_of_mag);
-#endif
- CLAW_DBF_TEXT_(5,trace,"mag%d",order_of_mag);
+ CLAW_DBF_TEXT_(5, trace, "mag%d", order_of_mag);
return order_of_mag;
}
@@ -1345,33 +1041,15 @@ add_claw_reads(struct net_device *dev, struct ccwbk* p_first,
struct claw_privbk *privptr;
struct ccw1 temp_ccw;
struct endccw * p_end;
-#ifdef IOTRACE
- struct ccwbk* p_buf;
-#endif
-#ifdef FUNCTRACE
- printk(KERN_INFO "%s:%s Enter \n",dev->name,__FUNCTION__);
-#endif
-#ifdef DEBUGMSG
- printk(KERN_INFO "dev\n");
- dumpit((char *) dev, sizeof(struct net_device));
- printk(KERN_INFO "p_first\n");
- dumpit((char *) p_first, sizeof(struct ccwbk));
- printk(KERN_INFO "p_last\n");
- dumpit((char *) p_last, sizeof(struct ccwbk));
-#endif
- CLAW_DBF_TEXT(4,trace,"addreads");
- privptr = dev->priv;
+ CLAW_DBF_TEXT(4, trace, "addreads");
+ privptr = dev->ml_priv;
p_end = privptr->p_end_ccw;
/* first CCW and last CCW contains a new set of read channel programs
* to apend the running channel programs
*/
if ( p_first==NULL) {
-#ifdef FUNCTRACE
- printk(KERN_INFO "%s:%s Exit on line %d\n",
- dev->name,__FUNCTION__,__LINE__);
-#endif
- CLAW_DBF_TEXT(4,trace,"addexit");
+ CLAW_DBF_TEXT(4, trace, "addexit");
return 0;
}
@@ -1398,21 +1076,11 @@ add_claw_reads(struct net_device *dev, struct ccwbk* p_first,
}
if ( privptr-> p_read_active_first ==NULL ) {
-#ifdef DEBUGMSG
- printk(KERN_INFO "%s:%s p_read_active_first == NULL \n",
- dev->name,__FUNCTION__);
- printk(KERN_INFO "%s:%s Read active first/last changed \n",
- dev->name,__FUNCTION__);
-#endif
- privptr-> p_read_active_first= p_first; /* set new first */
- privptr-> p_read_active_last = p_last; /* set new last */
+ privptr->p_read_active_first = p_first; /* set new first */
+ privptr->p_read_active_last = p_last; /* set new last */
}
else {
-#ifdef DEBUGMSG
- printk(KERN_INFO "%s:%s Read in progress \n",
- dev->name,__FUNCTION__);
-#endif
/* set up TIC ccw */
temp_ccw.cda= (__u32)__pa(&p_first->read);
temp_ccw.count=0;
@@ -1445,31 +1113,11 @@ add_claw_reads(struct net_device *dev, struct ccwbk* p_first,
privptr->p_read_active_last->r_TIC_2.cda=
(__u32)__pa(&p_first->read);
}
- /* chain in new set of blocks */
+ /* chain in new set of blocks */
privptr->p_read_active_last->next = p_first;
privptr->p_read_active_last=p_last;
} /* end of if ( privptr-> p_read_active_first ==NULL) */
-#ifdef IOTRACE
- printk(KERN_INFO "%s:%s dump p_last CCW BK \n",dev->name,__FUNCTION__);
- dumpit((char *)p_last, sizeof(struct ccwbk));
- printk(KERN_INFO "%s:%s dump p_end CCW BK \n",dev->name,__FUNCTION__);
- dumpit((char *)p_end, sizeof(struct endccw));
-
- printk(KERN_INFO "%s:%s dump p_first CCW BK \n",dev->name,__FUNCTION__);
- dumpit((char *)p_first, sizeof(struct ccwbk));
- printk(KERN_INFO "%s:%s Dump Active CCW chain \n",
- dev->name,__FUNCTION__);
- p_buf=privptr->p_read_active_first;
- while (p_buf!=NULL) {
- dumpit((char *)p_buf, sizeof(struct ccwbk));
- p_buf=p_buf->next;
- }
-#endif
-#ifdef FUNCTRACE
- printk(KERN_INFO "%s:%s Exit on line %d\n",
- dev->name,__FUNCTION__,__LINE__);
-#endif
- CLAW_DBF_TEXT(4,trace,"addexit");
+ CLAW_DBF_TEXT(4, trace, "addexit");
return 0;
} /* end of add_claw_reads */
@@ -1481,44 +1129,27 @@ add_claw_reads(struct net_device *dev, struct ccwbk* p_first,
static void
ccw_check_return_code(struct ccw_device *cdev, int return_code)
{
-#ifdef FUNCTRACE
- printk(KERN_INFO "%s: %s() > enter \n",
- cdev->dev.bus_id,__FUNCTION__);
-#endif
- CLAW_DBF_TEXT(4,trace,"ccwret");
-#ifdef DEBUGMSG
- printk(KERN_INFO "variable cdev =\n");
- dumpit((char *) cdev, sizeof(struct ccw_device));
- printk(KERN_INFO "variable return_code = %d\n",return_code);
-#endif
+ CLAW_DBF_TEXT(4, trace, "ccwret");
if (return_code != 0) {
switch (return_code) {
- case -EBUSY:
- printk(KERN_INFO "%s: Busy !\n",
- cdev->dev.bus_id);
- break;
- case -ENODEV:
- printk(KERN_EMERG "%s: Missing device called "
- "for IO ENODEV\n", cdev->dev.bus_id);
- break;
- case -EIO:
- printk(KERN_EMERG "%s: Status pending... EIO \n",
- cdev->dev.bus_id);
- break;
- case -EINVAL:
- printk(KERN_EMERG "%s: Invalid Dev State EINVAL \n",
- cdev->dev.bus_id);
- break;
- default:
- printk(KERN_EMERG "%s: Unknown error in "
- "Do_IO %d\n",cdev->dev.bus_id, return_code);
- }
- }
-#ifdef FUNCTRACE
- printk(KERN_INFO "%s: %s() > exit on line %d\n",
- cdev->dev.bus_id,__FUNCTION__,__LINE__);
-#endif
- CLAW_DBF_TEXT(4,trace,"ccwret");
+ case -EBUSY: /* BUSY is a transient state no action needed */
+ break;
+ case -ENODEV:
+ dev_err(&cdev->dev, "The remote channel adapter is not"
+ " available\n");
+ break;
+ case -EINVAL:
+ dev_err(&cdev->dev,
+ "The status of the remote channel adapter"
+ " is not valid\n");
+ break;
+ default:
+ dev_err(&cdev->dev, "The common device layer"
+ " returned error code %d\n",
+ return_code);
+ }
+ }
+ CLAW_DBF_TEXT(4, trace, "ccwret");
} /* end of ccw_check_return_code */
/*-------------------------------------------------------------------*
@@ -1528,173 +1159,41 @@ ccw_check_return_code(struct ccw_device *cdev, int return_code)
static void
ccw_check_unit_check(struct chbk * p_ch, unsigned char sense )
{
- struct net_device *dev = p_ch->ndev;
-
-#ifdef FUNCTRACE
- printk(KERN_INFO "%s: %s() > enter\n",dev->name,__FUNCTION__);
-#endif
-#ifdef DEBUGMSG
- printk(KERN_INFO "%s: variable dev =\n",dev->name);
- dumpit((char *)dev, sizeof(struct net_device));
- printk(KERN_INFO "%s: variable sense =\n",dev->name);
- dumpit((char *)&sense, 2);
-#endif
- CLAW_DBF_TEXT(4,trace,"unitchek");
-
- printk(KERN_INFO "%s: Unit Check with sense byte:0x%04x\n",
- dev->name, sense);
-
- if (sense & 0x40) {
- if (sense & 0x01) {
- printk(KERN_WARNING "%s: Interface disconnect or "
- "Selective reset "
- "occurred (remote side)\n", dev->name);
- }
- else {
- printk(KERN_WARNING "%s: System reset occured"
- " (remote side)\n", dev->name);
- }
- }
- else if (sense & 0x20) {
- if (sense & 0x04) {
- printk(KERN_WARNING "%s: Data-streaming "
- "timeout)\n", dev->name);
- }
- else {
- printk(KERN_WARNING "%s: Data-transfer parity"
- " error\n", dev->name);
- }
- }
- else if (sense & 0x10) {
- if (sense & 0x20) {
- printk(KERN_WARNING "%s: Hardware malfunction "
- "(remote side)\n", dev->name);
- }
- else {
- printk(KERN_WARNING "%s: read-data parity error "
- "(remote side)\n", dev->name);
- }
- }
+ struct net_device *ndev = p_ch->ndev;
+ struct device *dev = &p_ch->cdev->dev;
+
+ CLAW_DBF_TEXT(4, trace, "unitchek");
+ dev_warn(dev, "The communication peer of %s disconnected\n",
+ ndev->name);
+
+ if (sense & 0x40) {
+ if (sense & 0x01) {
+ dev_warn(dev, "The remote channel adapter for"
+ " %s has been reset\n",
+ ndev->name);
+ }
+ } else if (sense & 0x20) {
+ if (sense & 0x04) {
+ dev_warn(dev, "A data streaming timeout occurred"
+ " for %s\n",
+ ndev->name);
+ } else if (sense & 0x10) {
+ dev_warn(dev, "The remote channel adapter for %s"
+ " is faulty\n",
+ ndev->name);
+ } else {
+ dev_warn(dev, "A data transfer parity error occurred"
+ " for %s\n",
+ ndev->name);
+ }
+ } else if (sense & 0x10) {
+ dev_warn(dev, "A read data parity error occurred"
+ " for %s\n",
+ ndev->name);
+ }
-#ifdef FUNCTRACE
- printk(KERN_INFO "%s: %s() exit on line %d\n",
- dev->name,__FUNCTION__,__LINE__);
-#endif
} /* end of ccw_check_unit_check */
-
-
-/*-------------------------------------------------------------------*
-* Dump buffer format *
-* *
-*--------------------------------------------------------------------*/
-#ifdef DEBUG
-static void
-dumpit(char* buf, int len)
-{
-
- __u32 ct, sw, rm, dup;
- char *ptr, *rptr;
- char tbuf[82], tdup[82];
-#if (CONFIG_64BIT)
- char addr[22];
-#else
- char addr[12];
-#endif
- char boff[12];
- char bhex[82], duphex[82];
- char basc[40];
-
- sw = 0;
- rptr =ptr=buf;
- rm = 16;
- duphex[0] = 0x00;
- dup = 0;
- for ( ct=0; ct < len; ct++, ptr++, rptr++ ) {
- if (sw == 0) {
-#if (CONFIG_64BIT)
- sprintf(addr, "%16.16lX",(unsigned long)rptr);
-#else
- sprintf(addr, "%8.8X",(__u32)rptr);
-#endif
- sprintf(boff, "%4.4X", (__u32)ct);
- bhex[0] = '\0';
- basc[0] = '\0';
- }
- if ((sw == 4) || (sw == 12)) {
- strcat(bhex, " ");
- }
- if (sw == 8) {
- strcat(bhex, " ");
- }
-#if (CONFIG_64BIT)
- sprintf(tbuf,"%2.2lX", (unsigned long)*ptr);
-#else
- sprintf(tbuf,"%2.2X", (__u32)*ptr);
-#endif
- tbuf[2] = '\0';
- strcat(bhex, tbuf);
- if ((0!=isprint(*ptr)) && (*ptr >= 0x20)) {
- basc[sw] = *ptr;
- }
- else {
- basc[sw] = '.';
- }
- basc[sw+1] = '\0';
- sw++;
- rm--;
- if (sw==16) {
- if ((strcmp(duphex, bhex)) !=0) {
- if (dup !=0) {
- sprintf(tdup,"Duplicate as above to"
- " %s", addr);
- printk( KERN_INFO " "
- " --- %s ---\n",tdup);
- }
- printk( KERN_INFO " %s (+%s) : %s [%s]\n",
- addr, boff, bhex, basc);
- dup = 0;
- strcpy(duphex, bhex);
- }
- else {
- dup++;
- }
- sw = 0;
- rm = 16;
- }
- } /* endfor */
-
- if (sw != 0) {
- for ( ; rm > 0; rm--, sw++ ) {
- if ((sw==4) || (sw==12)) strcat(bhex, " ");
- if (sw==8) strcat(bhex, " ");
- strcat(bhex, " ");
- strcat(basc, " ");
- }
- if (dup !=0) {
- sprintf(tdup,"Duplicate as above to %s", addr);
- printk( KERN_INFO " --- %s ---\n",
- tdup);
- }
- printk( KERN_INFO " %s (+%s) : %s [%s]\n",
- addr, boff, bhex, basc);
- }
- else {
- if (dup >=1) {
- sprintf(tdup,"Duplicate as above to %s", addr);
- printk( KERN_INFO " --- %s ---\n",
- tdup);
- }
- if (dup !=0) {
- printk( KERN_INFO " %s (+%s) : %s [%s]\n",
- addr, boff, bhex, basc);
- }
- }
- return;
-
-} /* end of dumpit */
-#endif
-
/*-------------------------------------------------------------------*
* find_link *
*--------------------------------------------------------------------*/
@@ -1705,17 +1204,8 @@ find_link(struct net_device *dev, char *host_name, char *ws_name )
struct claw_env *p_env;
int rc=0;
-#ifdef FUNCTRACE
- printk(KERN_INFO "%s:%s > enter \n",dev->name,__FUNCTION__);
-#endif
- CLAW_DBF_TEXT(2,setup,"findlink");
-#ifdef DEBUGMSG
- printk(KERN_INFO "%s: variable dev = \n",dev->name);
- dumpit((char *) dev, sizeof(struct net_device));
- printk(KERN_INFO "%s: variable host_name = %s\n",dev->name, host_name);
- printk(KERN_INFO "%s: variable ws_name = %s\n",dev->name, ws_name);
-#endif
- privptr=dev->priv;
+ CLAW_DBF_TEXT(2, setup, "findlink");
+ privptr = dev->ml_priv;
p_env=privptr->p_env;
switch (p_env->packing)
{
@@ -1737,11 +1227,7 @@ find_link(struct net_device *dev, char *host_name, char *ws_name )
break;
}
-#ifdef FUNCTRACE
- printk(KERN_INFO "%s:%s Exit on line %d\n",
- dev->name,__FUNCTION__,__LINE__);
-#endif
- return 0;
+ return rc;
} /* end of find_link */
/*-------------------------------------------------------------------*
@@ -1764,32 +1250,13 @@ claw_hw_tx(struct sk_buff *skb, struct net_device *dev, long linkid)
unsigned char *pDataAddress;
struct endccw *pEnd;
struct ccw1 tempCCW;
- struct chbk *p_ch;
struct claw_env *p_env;
- int lock;
struct clawph *pk_head;
struct chbk *ch;
-#ifdef IOTRACE
- struct ccwbk *p_buf;
-#endif
-#ifdef FUNCTRACE
- printk(KERN_INFO "%s: %s() > enter\n",dev->name,__FUNCTION__);
-#endif
- CLAW_DBF_TEXT(4,trace,"hw_tx");
-#ifdef DEBUGMSG
- printk(KERN_INFO "%s: variable dev skb =\n",dev->name);
- dumpit((char *) skb, sizeof(struct sk_buff));
- printk(KERN_INFO "%s: variable dev =\n",dev->name);
- dumpit((char *) dev, sizeof(struct net_device));
- printk(KERN_INFO "%s: variable linkid = %ld\n",dev->name,linkid);
-#endif
- privptr = (struct claw_privbk *) (dev->priv);
- p_ch=(struct chbk *)&privptr->channel[WRITE];
+
+ CLAW_DBF_TEXT(4, trace, "hw_tx");
+ privptr = (struct claw_privbk *)(dev->ml_priv);
p_env =privptr->p_env;
-#ifdef IOTRACE
- printk(KERN_INFO "%s: %s() dump sk_buff \n",dev->name,__FUNCTION__);
- dumpit((char *)skb ,sizeof(struct sk_buff));
-#endif
claw_free_wrt_buf(dev); /* Clean up free chain if posible */
/* scan the write queue to free any completed write packets */
p_first_ccw=NULL;
@@ -1821,12 +1288,7 @@ claw_hw_tx(struct sk_buff *skb, struct net_device *dev, long linkid)
claw_strt_out_IO(dev );
claw_free_wrt_buf( dev );
if (privptr->write_free_count==0) {
-#ifdef IOTRACE
- printk(KERN_INFO "%s: "
- "(claw_check_busy) no free write "
- "buffers\n", dev->name);
-#endif
- ch = &privptr->channel[WRITE];
+ ch = &privptr->channel[WRITE_CHANNEL];
atomic_inc(&skb->users);
skb_queue_tail(&ch->collect_queue, skb);
goto Done;
@@ -1838,11 +1300,7 @@ claw_hw_tx(struct sk_buff *skb, struct net_device *dev, long linkid)
}
/* tx lock */
if (claw_test_and_setbit_busy(TB_TX,dev)) { /* set to busy */
-#ifdef DEBUGMSG
- printk(KERN_INFO "%s: busy (claw_test_and_setbit_"
- "busy)\n", dev->name);
-#endif
- ch = &privptr->channel[WRITE];
+ ch = &privptr->channel[WRITE_CHANNEL];
atomic_inc(&skb->users);
skb_queue_tail(&ch->collect_queue, skb);
claw_strt_out_IO(dev );
@@ -1858,31 +1316,19 @@ claw_hw_tx(struct sk_buff *skb, struct net_device *dev, long linkid)
privptr->p_write_free_chain == NULL ) {
claw_setbit_busy(TB_NOBUFFER,dev);
-
-#ifdef DEBUGMSG
- printk(KERN_INFO "%s: busy (claw_setbit_busy"
- "(TB_NOBUFFER))\n", dev->name);
- printk(KERN_INFO " free_count: %d, numBuffers : %d\n",
- (int)privptr->write_free_count,(int) numBuffers );
-#endif
- ch = &privptr->channel[WRITE];
+ ch = &privptr->channel[WRITE_CHANNEL];
atomic_inc(&skb->users);
skb_queue_tail(&ch->collect_queue, skb);
- CLAW_DBF_TEXT(2,trace,"clawbusy");
+ CLAW_DBF_TEXT(2, trace, "clawbusy");
goto Done2;
}
pDataAddress=skb->data;
len_of_data=skb->len;
while (len_of_data > 0) {
-#ifdef DEBUGMSG
- printk(KERN_INFO "%s: %s() length-of-data is %ld \n",
- dev->name ,__FUNCTION__,len_of_data);
- dumpit((char *)pDataAddress ,64);
-#endif
p_this_ccw=privptr->p_write_free_chain; /* get a block */
if (p_this_ccw == NULL) { /* lost the race */
- ch = &privptr->channel[WRITE];
+ ch = &privptr->channel[WRITE_CHANNEL];
atomic_inc(&skb->users);
skb_queue_tail(&ch->collect_queue, skb);
goto Done2;
@@ -1890,7 +1336,10 @@ claw_hw_tx(struct sk_buff *skb, struct net_device *dev, long linkid)
privptr->p_write_free_chain=p_this_ccw->next;
p_this_ccw->next=NULL;
--privptr->write_free_count; /* -1 */
- bytesInThisBuffer=len_of_data;
+ if (len_of_data >= privptr->p_env->write_size)
+ bytesInThisBuffer = privptr->p_env->write_size;
+ else
+ bytesInThisBuffer = len_of_data;
memcpy( p_this_ccw->p_buffer,pDataAddress, bytesInThisBuffer);
len_of_data-=bytesInThisBuffer;
pDataAddress+=(unsigned long)bytesInThisBuffer;
@@ -1911,12 +1360,6 @@ claw_hw_tx(struct sk_buff *skb, struct net_device *dev, long linkid)
(__u32)__pa(&p_this_ccw->write);
}
p_last_ccw=p_this_ccw; /* save new last block */
-#ifdef IOTRACE
- printk(KERN_INFO "%s: %s() > CCW and Buffer %ld bytes long \n",
- dev->name,__FUNCTION__,bytesInThisBuffer);
- dumpit((char *)p_this_ccw, sizeof(struct ccwbk));
- dumpit((char *)p_this_ccw->p_buffer, 64);
-#endif
}
/* FirstCCW and LastCCW now contain a new set of write channel
@@ -1924,7 +1367,7 @@ claw_hw_tx(struct sk_buff *skb, struct net_device *dev, long linkid)
*/
if (p_first_ccw!=NULL) {
- /* setup ending ccw sequence for this segment */
+ /* setup ending ccw sequence for this segment */
pEnd=privptr->p_end_ccw;
if (pEnd->write1) {
pEnd->write1=0x00; /* second end ccw is now active */
@@ -1949,13 +1392,11 @@ claw_hw_tx(struct sk_buff *skb, struct net_device *dev, long linkid)
pEnd->write1_nop2.count=1;
} /* end if if (pEnd->write1) */
-
if (privptr->p_write_active_first==NULL ) {
privptr->p_write_active_first=p_first_ccw;
privptr->p_write_active_last=p_last_ccw;
}
else {
-
/* set up Tic CCWs */
tempCCW.cda=(__u32)__pa(&p_first_ccw->write);
@@ -1994,43 +1435,15 @@ claw_hw_tx(struct sk_buff *skb, struct net_device *dev, long linkid)
}
} /* endif (p_first_ccw!=NULL) */
-
-
-#ifdef IOTRACE
- printk(KERN_INFO "%s: %s() > Dump Active CCW chain \n",
- dev->name,__FUNCTION__);
- p_buf=privptr->p_write_active_first;
- while (p_buf!=NULL) {
- dumpit((char *)p_buf, sizeof(struct ccwbk));
- p_buf=p_buf->next;
- }
- p_buf=(struct ccwbk*)privptr->p_end_ccw;
- dumpit((char *)p_buf, sizeof(struct endccw));
-#endif
dev_kfree_skb_any(skb);
- if (linkid==0) {
- lock=LOCK_NO;
- }
- else {
- lock=LOCK_YES;
- }
claw_strt_out_IO(dev );
/* if write free count is zero , set NOBUFFER */
-#ifdef DEBUGMSG
- printk(KERN_INFO "%s: %s() > free_count is %d\n",
- dev->name,__FUNCTION__,
- (int) privptr->write_free_count );
-#endif
if (privptr->write_free_count==0) {
claw_setbit_busy(TB_NOBUFFER,dev);
}
Done2:
claw_clearbit_busy(TB_TX,dev);
Done:
-#ifdef FUNCTRACE
- printk(KERN_INFO "%s: %s() > exit on line %d, rc = %d \n",
- dev->name,__FUNCTION__,__LINE__, rc);
-#endif
return(rc);
} /* end of claw_hw_tx */
@@ -2057,19 +1470,12 @@ init_ccw_bk(struct net_device *dev)
struct ccwbk*p_last_CCWB;
struct ccwbk*p_first_CCWB;
struct endccw *p_endccw=NULL;
- addr_t real_address;
- struct claw_privbk *privptr=dev->priv;
+ addr_t real_address;
+ struct claw_privbk *privptr = dev->ml_priv;
struct clawh *pClawH=NULL;
addr_t real_TIC_address;
int i,j;
-#ifdef FUNCTRACE
- printk(KERN_INFO "%s: %s() enter \n",dev->name,__FUNCTION__);
-#endif
- CLAW_DBF_TEXT(4,trace,"init_ccw");
-#ifdef DEBUGMSG
- printk(KERN_INFO "%s: variable dev =\n",dev->name);
- dumpit((char *) dev, sizeof(struct net_device));
-#endif
+ CLAW_DBF_TEXT(4, trace, "init_ccw");
/* initialize statistics field */
privptr->active_link_ID=0;
@@ -2094,20 +1500,6 @@ init_ccw_bk(struct net_device *dev)
*/
ccw_blocks_required =
privptr->p_env->read_buffers+privptr->p_env->write_buffers+1;
-#ifdef DEBUGMSG
- printk(KERN_INFO "%s: %s() "
- "ccw_blocks_required=%d\n",
- dev->name,__FUNCTION__,
- ccw_blocks_required);
- printk(KERN_INFO "%s: %s() "
- "PAGE_SIZE=0x%x\n",
- dev->name,__FUNCTION__,
- (unsigned int)PAGE_SIZE);
- printk(KERN_INFO "%s: %s() > "
- "PAGE_MASK=0x%x\n",
- dev->name,__FUNCTION__,
- (unsigned int)PAGE_MASK);
-#endif
/*
* compute number of CCW blocks that will fit in a page
*/
@@ -2115,14 +1507,6 @@ init_ccw_bk(struct net_device *dev)
ccw_pages_required=
DIV_ROUND_UP(ccw_blocks_required, ccw_blocks_perpage);
-#ifdef DEBUGMSG
- printk(KERN_INFO "%s: %s() > ccw_blocks_perpage=%d\n",
- dev->name,__FUNCTION__,
- ccw_blocks_perpage);
- printk(KERN_INFO "%s: %s() > ccw_pages_required=%d\n",
- dev->name,__FUNCTION__,
- ccw_pages_required);
-#endif
/*
* read and write sizes are set by 2 constants in claw.h
* 4k and 32k. Unpacked values other than 4k are not going to
@@ -2153,36 +1537,6 @@ init_ccw_bk(struct net_device *dev)
claw_write_pages = privptr->p_env->write_buffers *
privptr->p_buff_pages_perwrite;
}
-#ifdef DEBUGMSG
- if (privptr->p_env->read_size < PAGE_SIZE) {
- printk(KERN_INFO "%s: %s() reads_perpage=%d\n",
- dev->name,__FUNCTION__,
- claw_reads_perpage);
- }
- else {
- printk(KERN_INFO "%s: %s() pages_perread=%d\n",
- dev->name,__FUNCTION__,
- privptr->p_buff_pages_perread);
- }
- printk(KERN_INFO "%s: %s() read_pages=%d\n",
- dev->name,__FUNCTION__,
- claw_read_pages);
- if (privptr->p_env->write_size < PAGE_SIZE) {
- printk(KERN_INFO "%s: %s() writes_perpage=%d\n",
- dev->name,__FUNCTION__,
- claw_writes_perpage);
- }
- else {
- printk(KERN_INFO "%s: %s() pages_perwrite=%d\n",
- dev->name,__FUNCTION__,
- privptr->p_buff_pages_perwrite);
- }
- printk(KERN_INFO "%s: %s() write_pages=%d\n",
- dev->name,__FUNCTION__,
- claw_write_pages);
-#endif
-
-
/*
* allocate ccw_pages_required
*/
@@ -2191,17 +1545,6 @@ init_ccw_bk(struct net_device *dev)
(void *)__get_free_pages(__GFP_DMA,
(int)pages_to_order_of_mag(ccw_pages_required ));
if (privptr->p_buff_ccw==NULL) {
- printk(KERN_INFO "%s: %s() "
- "__get_free_pages for CCWs failed : "
- "pages is %d\n",
- dev->name,__FUNCTION__,
- ccw_pages_required );
-#ifdef FUNCTRACE
- printk(KERN_INFO "%s: %s() > "
- "exit on line %d, rc = ENOMEM\n",
- dev->name,__FUNCTION__,
- __LINE__);
-#endif
return -ENOMEM;
}
privptr->p_buff_ccw_num=ccw_pages_required;
@@ -2216,11 +1559,6 @@ init_ccw_bk(struct net_device *dev)
privptr->p_end_ccw = (struct endccw *)&privptr->end_ccw;
real_address = (__u32)__pa(privptr->p_end_ccw);
/* Initialize ending CCW block */
-#ifdef DEBUGMSG
- printk(KERN_INFO "%s: %s() begin initialize ending CCW blocks\n",
- dev->name,__FUNCTION__);
-#endif
-
p_endccw=privptr->p_end_ccw;
p_endccw->real=real_address;
p_endccw->write1=0x00;
@@ -2274,21 +1612,10 @@ init_ccw_bk(struct net_device *dev)
p_endccw->read2_nop2.count = 1;
p_endccw->read2_nop2.cda = 0;
-#ifdef IOTRACE
- printk(KERN_INFO "%s: %s() dump claw ending CCW BK \n",
- dev->name,__FUNCTION__);
- dumpit((char *)p_endccw, sizeof(struct endccw));
-#endif
-
/*
* Build a chain of CCWs
*
*/
-
-#ifdef DEBUGMSG
- printk(KERN_INFO "%s: %s() Begin build a chain of CCW buffer \n",
- dev->name,__FUNCTION__);
-#endif
p_buff=privptr->p_buff_ccw;
p_free_chain=NULL;
@@ -2303,26 +1630,10 @@ init_ccw_bk(struct net_device *dev)
}
p_buff+=PAGE_SIZE;
}
-#ifdef DEBUGMSG
- printk(KERN_INFO "%s: %s() "
- "End build a chain of CCW buffer \n",
- dev->name,__FUNCTION__);
- p_buf=p_free_chain;
- while (p_buf!=NULL) {
- dumpit((char *)p_buf, sizeof(struct ccwbk));
- p_buf=p_buf->next;
- }
-#endif
-
/*
* Initialize ClawSignalBlock
*
*/
-#ifdef DEBUGMSG
- printk(KERN_INFO "%s: %s() "
- "Begin initialize ClawSignalBlock \n",
- dev->name,__FUNCTION__);
-#endif
if (privptr->p_claw_signal_blk==NULL) {
privptr->p_claw_signal_blk=p_free_chain;
p_free_chain=p_free_chain->next;
@@ -2331,12 +1642,6 @@ init_ccw_bk(struct net_device *dev)
pClawH->opcode=0xff;
pClawH->flag=CLAW_BUSY;
}
-#ifdef DEBUGMSG
- printk(KERN_INFO "%s: %s() > End initialize "
- "ClawSignalBlock\n",
- dev->name,__FUNCTION__);
- dumpit((char *)privptr->p_claw_signal_blk, sizeof(struct ccwbk));
-#endif
/*
* allocate write_pages_required and add to free chain
@@ -2347,17 +1652,7 @@ init_ccw_bk(struct net_device *dev)
(void *)__get_free_pages(__GFP_DMA,
(int)pages_to_order_of_mag(claw_write_pages ));
if (privptr->p_buff_write==NULL) {
- printk(KERN_INFO "%s: %s() __get_free_pages for write"
- " bufs failed : get is for %d pages\n",
- dev->name,__FUNCTION__,claw_write_pages );
- free_pages((unsigned long)privptr->p_buff_ccw,
- (int)pages_to_order_of_mag(privptr->p_buff_ccw_num));
privptr->p_buff_ccw=NULL;
-#ifdef FUNCTRACE
- printk(KERN_INFO "%s: %s() > exit on line %d,"
- "rc = ENOMEM\n",
- dev->name,__FUNCTION__,__LINE__);
-#endif
return -ENOMEM;
}
/*
@@ -2367,10 +1662,6 @@ init_ccw_bk(struct net_device *dev)
memset(privptr->p_buff_write, 0x00,
ccw_pages_required * PAGE_SIZE);
-#ifdef DEBUGMSG
- printk(KERN_INFO "%s: %s() Begin build claw write free "
- "chain \n",dev->name,__FUNCTION__);
-#endif
privptr->p_write_free_chain=NULL;
p_buff=privptr->p_buff_write;
@@ -2392,10 +1683,11 @@ init_ccw_bk(struct net_device *dev)
p_buf-> w_TIC_1.flags = 0;
p_buf-> w_TIC_1.count = 0;
- if (((unsigned long)p_buff+privptr->p_env->write_size) >=
+ if (((unsigned long)p_buff +
+ privptr->p_env->write_size) >=
((unsigned long)(p_buff+2*
- (privptr->p_env->write_size) -1) & PAGE_MASK)) {
- p_buff= p_buff+privptr->p_env->write_size;
+ (privptr->p_env->write_size) - 1) & PAGE_MASK)) {
+ p_buff = p_buff+privptr->p_env->write_size;
}
}
}
@@ -2406,18 +1698,7 @@ init_ccw_bk(struct net_device *dev)
p_buff=(void *)__get_free_pages(__GFP_DMA,
(int)pages_to_order_of_mag(
privptr->p_buff_pages_perwrite) );
-#ifdef IOTRACE
- printk(KERN_INFO "%s:%s __get_free_pages "
- "for writes buf: get for %d pages\n",
- dev->name,__FUNCTION__,
- privptr->p_buff_pages_perwrite);
-#endif
if (p_buff==NULL) {
- printk(KERN_INFO "%s:%s __get_free_pages "
- "for writes buf failed : get is for %d pages\n",
- dev->name,
- __FUNCTION__,
- privptr->p_buff_pages_perwrite );
free_pages((unsigned long)privptr->p_buff_ccw,
(int)pages_to_order_of_mag(
privptr->p_buff_ccw_num));
@@ -2430,12 +1711,6 @@ init_ccw_bk(struct net_device *dev)
privptr->p_buff_pages_perwrite));
p_buf=p_buf->next;
}
-#ifdef FUNCTRACE
- printk(KERN_INFO "%s: %s exit on line %d, rc = ENOMEM\n",
- dev->name,
- __FUNCTION__,
- __LINE__);
-#endif
return -ENOMEM;
} /* Error on get_pages */
memset(p_buff, 0x00, privptr->p_env->write_size );
@@ -2464,15 +1739,6 @@ init_ccw_bk(struct net_device *dev)
privptr->write_free_count=privptr->p_env->write_buffers;
-#ifdef DEBUGMSG
- printk(KERN_INFO "%s:%s End build claw write free chain \n",
- dev->name,__FUNCTION__);
- p_buf=privptr->p_write_free_chain;
- while (p_buf!=NULL) {
- dumpit((char *)p_buf, sizeof(struct ccwbk));
- p_buf=p_buf->next;
- }
-#endif
/*
* allocate read_pages_required and chain to free chain
*/
@@ -2482,10 +1748,6 @@ init_ccw_bk(struct net_device *dev)
(void *)__get_free_pages(__GFP_DMA,
(int)pages_to_order_of_mag(claw_read_pages) );
if (privptr->p_buff_read==NULL) {
- printk(KERN_INFO "%s: %s() "
- "__get_free_pages for read buf failed : "
- "get is for %d pages\n",
- dev->name,__FUNCTION__,claw_read_pages );
free_pages((unsigned long)privptr->p_buff_ccw,
(int)pages_to_order_of_mag(
privptr->p_buff_ccw_num));
@@ -2495,10 +1757,6 @@ init_ccw_bk(struct net_device *dev)
privptr->p_buff_write_num));
privptr->p_buff_ccw=NULL;
privptr->p_buff_write=NULL;
-#ifdef FUNCTRACE
- printk(KERN_INFO "%s: %s() > exit on line %d, rc ="
- " ENOMEM\n",dev->name,__FUNCTION__,__LINE__);
-#endif
return -ENOMEM;
}
memset(privptr->p_buff_read, 0x00, claw_read_pages * PAGE_SIZE);
@@ -2507,10 +1765,6 @@ init_ccw_bk(struct net_device *dev)
* Build CLAW read free chain
*
*/
-#ifdef DEBUGMSG
- printk(KERN_INFO "%s: %s() Begin build claw read free chain \n",
- dev->name,__FUNCTION__);
-#endif
p_buff=privptr->p_buff_read;
for (i=0 ; i< privptr->p_env->read_buffers ; i++) {
p_buf = p_free_chain;
@@ -2573,58 +1827,49 @@ init_ccw_bk(struct net_device *dev)
p_buf->header.opcode=0xff;
p_buf->header.flag=CLAW_PENDING;
- if (((unsigned long)p_buff+privptr->p_env->read_size) >=
- ((unsigned long)(p_buff+2*(privptr->p_env->read_size) -1)
- & PAGE_MASK) ) {
+ if (((unsigned long)p_buff+privptr->p_env->read_size) >=
+ ((unsigned long)(p_buff+2*(privptr->p_env->read_size)
+ -1)
+ & PAGE_MASK)) {
p_buff= p_buff+privptr->p_env->read_size;
}
else {
p_buff=
(void *)((unsigned long)
- (p_buff+2*(privptr->p_env->read_size) -1)
+ (p_buff+2*(privptr->p_env->read_size)-1)
& PAGE_MASK) ;
}
} /* for read_buffers */
} /* read_size < PAGE_SIZE */
else { /* read Size >= PAGE_SIZE */
-
-#ifdef DEBUGMSG
- printk(KERN_INFO "%s: %s() Begin build claw read free chain \n",
- dev->name,__FUNCTION__);
-#endif
for (i=0 ; i< privptr->p_env->read_buffers ; i++) {
p_buff = (void *)__get_free_pages(__GFP_DMA,
- (int)pages_to_order_of_mag(privptr->p_buff_pages_perread) );
+ (int)pages_to_order_of_mag(
+ privptr->p_buff_pages_perread));
if (p_buff==NULL) {
- printk(KERN_INFO "%s: %s() __get_free_pages for read "
- "buf failed : get is for %d pages\n",
- dev->name,__FUNCTION__,
- privptr->p_buff_pages_perread );
free_pages((unsigned long)privptr->p_buff_ccw,
- (int)pages_to_order_of_mag(privptr->p_buff_ccw_num));
+ (int)pages_to_order_of_mag(privptr->
+ p_buff_ccw_num));
/* free the write pages */
p_buf=privptr->p_buff_write;
while (p_buf!=NULL) {
- free_pages((unsigned long)p_buf->p_buffer,
- (int)pages_to_order_of_mag(
- privptr->p_buff_pages_perwrite ));
+ free_pages(
+ (unsigned long)p_buf->p_buffer,
+ (int)pages_to_order_of_mag(
+ privptr->p_buff_pages_perwrite));
p_buf=p_buf->next;
}
/* free any read pages already alloc */
p_buf=privptr->p_buff_read;
while (p_buf!=NULL) {
- free_pages((unsigned long)p_buf->p_buffer,
- (int)pages_to_order_of_mag(
- privptr->p_buff_pages_perread ));
+ free_pages(
+ (unsigned long)p_buf->p_buffer,
+ (int)pages_to_order_of_mag(
+ privptr->p_buff_pages_perread));
p_buf=p_buf->next;
}
privptr->p_buff_ccw=NULL;
privptr->p_buff_write=NULL;
-#ifdef FUNCTRACE
- printk(KERN_INFO "%s: %s() exit on line %d, rc = ENOMEM\n",
- dev->name,__FUNCTION__,
- __LINE__);
-#endif
return -ENOMEM;
}
memset(p_buff, 0x00, privptr->p_env->read_size);
@@ -2693,22 +1938,9 @@ init_ccw_bk(struct net_device *dev)
} /* For read_buffers */
} /* read_size >= PAGE_SIZE */
} /* pBuffread = NULL */
-#ifdef DEBUGMSG
- printk(KERN_INFO "%s: %s() > End build claw read free chain \n",
- dev->name,__FUNCTION__);
- p_buf=p_first_CCWB;
- while (p_buf!=NULL) {
- dumpit((char *)p_buf, sizeof(struct ccwbk));
- p_buf=p_buf->next;
- }
-
-#endif
add_claw_reads( dev ,p_first_CCWB , p_last_CCWB);
privptr->buffs_alloc = 1;
-#ifdef FUNCTRACE
- printk(KERN_INFO "%s: %s() exit on line %d\n",
- dev->name,__FUNCTION__,__LINE__);
-#endif
+
return 0;
} /* end of init_ccw_bk */
@@ -2721,34 +1953,18 @@ init_ccw_bk(struct net_device *dev)
static void
probe_error( struct ccwgroup_device *cgdev)
{
- struct claw_privbk *privptr;
-#ifdef FUNCTRACE
- printk(KERN_INFO "%s enter \n",__FUNCTION__);
-#endif
- CLAW_DBF_TEXT(4,trace,"proberr");
-#ifdef DEBUGMSG
- printk(KERN_INFO "%s variable cgdev =\n",__FUNCTION__);
- dumpit((char *) cgdev, sizeof(struct ccwgroup_device));
-#endif
- privptr=(struct claw_privbk *)cgdev->dev.driver_data;
- if (privptr!=NULL) {
- kfree(privptr->p_env);
- privptr->p_env=NULL;
- kfree(privptr->p_mtc_envelope);
- privptr->p_mtc_envelope=NULL;
- kfree(privptr);
- privptr=NULL;
- }
-#ifdef FUNCTRACE
- printk(KERN_INFO "%s > exit on line %d\n",
- __FUNCTION__,__LINE__);
-#endif
+ struct claw_privbk *privptr;
- return;
+ CLAW_DBF_TEXT(4, trace, "proberr");
+ privptr = dev_get_drvdata(&cgdev->dev);
+ if (privptr != NULL) {
+ dev_set_drvdata(&cgdev->dev, NULL);
+ kfree(privptr->p_env);
+ kfree(privptr->p_mtc_envelope);
+ kfree(privptr);
+ }
} /* probe_error */
-
-
/*-------------------------------------------------------------------*
* claw_process_control *
* *
@@ -2770,32 +1986,19 @@ claw_process_control( struct net_device *dev, struct ccwbk * p_ccw)
struct conncmd *p_connect=NULL;
int rc;
struct chbk *p_ch = NULL;
-#ifdef FUNCTRACE
- printk(KERN_INFO "%s: %s() > enter \n",
- dev->name,__FUNCTION__);
-#endif
- CLAW_DBF_TEXT(2,setup,"clw_cntl");
-#ifdef DEBUGMSG
- printk(KERN_INFO "%s: variable dev =\n",dev->name);
- dumpit((char *) dev, sizeof(struct net_device));
- printk(KERN_INFO "%s: variable p_ccw =\n",dev->name);
- dumpit((char *) p_ccw, sizeof(struct ccwbk *));
-#endif
+ struct device *tdev;
+ CLAW_DBF_TEXT(2, setup, "clw_cntl");
udelay(1000); /* Wait a ms for the control packets to
*catch up to each other */
- privptr=dev->priv;
+ privptr = dev->ml_priv;
p_env=privptr->p_env;
+ tdev = &privptr->channel[READ_CHANNEL].cdev->dev;
memcpy( &temp_host_name, p_env->host_name, 8);
memcpy( &temp_ws_name, p_env->adapter_name , 8);
- printk(KERN_INFO "%s: CLAW device %.8s: "
+ dev_info(tdev, "%s: CLAW device %.8s: "
"Received Control Packet\n",
dev->name, temp_ws_name);
if (privptr->release_pend==1) {
-#ifdef FUNCTRACE
- printk(KERN_INFO "%s: %s() > "
- "exit on line %d, rc=0\n",
- dev->name,__FUNCTION__,__LINE__);
-#endif
return 0;
}
p_buf=p_ccw->p_buffer;
@@ -2805,261 +2008,251 @@ claw_process_control( struct net_device *dev, struct ccwbk * p_ccw)
} else {
memcpy(p_ctlbk, p_buf, sizeof(struct clawctl));
}
-#ifdef IOTRACE
- printk(KERN_INFO "%s: dump claw control data inbound\n",dev->name);
- dumpit((char *)p_ctlbk, sizeof(struct clawctl));
-#endif
switch (p_ctlbk->command)
{
- case SYSTEM_VALIDATE_REQUEST:
- if (p_ctlbk->version!=CLAW_VERSION_ID) {
- claw_snd_sys_validate_rsp(dev, p_ctlbk,
- CLAW_RC_WRONG_VERSION );
- printk("%s: %d is wrong version id. "
- "Expected %d\n",
- dev->name, p_ctlbk->version,
- CLAW_VERSION_ID);
- }
- p_sysval=(struct sysval *)&(p_ctlbk->data);
- printk( "%s: Recv Sys Validate Request: "
- "Vers=%d,link_id=%d,Corr=%d,WS name=%."
- "8s,Host name=%.8s\n",
- dev->name, p_ctlbk->version,
- p_ctlbk->linkid,
- p_ctlbk->correlator,
+ case SYSTEM_VALIDATE_REQUEST:
+ if (p_ctlbk->version != CLAW_VERSION_ID) {
+ claw_snd_sys_validate_rsp(dev, p_ctlbk,
+ CLAW_RC_WRONG_VERSION);
+ dev_warn(tdev, "The communication peer of %s"
+ " uses an incorrect API version %d\n",
+ dev->name, p_ctlbk->version);
+ }
+ p_sysval = (struct sysval *)&(p_ctlbk->data);
+ dev_info(tdev, "%s: Recv Sys Validate Request: "
+ "Vers=%d,link_id=%d,Corr=%d,WS name=%.8s,"
+ "Host name=%.8s\n",
+ dev->name, p_ctlbk->version,
+ p_ctlbk->linkid,
+ p_ctlbk->correlator,
+ p_sysval->WS_name,
+ p_sysval->host_name);
+ if (memcmp(temp_host_name, p_sysval->host_name, 8)) {
+ claw_snd_sys_validate_rsp(dev, p_ctlbk,
+ CLAW_RC_NAME_MISMATCH);
+ CLAW_DBF_TEXT(2, setup, "HSTBAD");
+ CLAW_DBF_TEXT_(2, setup, "%s", p_sysval->host_name);
+ CLAW_DBF_TEXT_(2, setup, "%s", temp_host_name);
+ dev_warn(tdev,
+ "Host name %s for %s does not match the"
+ " remote adapter name %s\n",
+ p_sysval->host_name,
+ dev->name,
+ temp_host_name);
+ }
+ if (memcmp(temp_ws_name, p_sysval->WS_name, 8)) {
+ claw_snd_sys_validate_rsp(dev, p_ctlbk,
+ CLAW_RC_NAME_MISMATCH);
+ CLAW_DBF_TEXT(2, setup, "WSNBAD");
+ CLAW_DBF_TEXT_(2, setup, "%s", p_sysval->WS_name);
+ CLAW_DBF_TEXT_(2, setup, "%s", temp_ws_name);
+ dev_warn(tdev, "Adapter name %s for %s does not match"
+ " the remote host name %s\n",
p_sysval->WS_name,
- p_sysval->host_name);
- if (0!=memcmp(temp_host_name,p_sysval->host_name,8)) {
- claw_snd_sys_validate_rsp(dev, p_ctlbk,
- CLAW_RC_NAME_MISMATCH );
- CLAW_DBF_TEXT(2,setup,"HSTBAD");
- CLAW_DBF_TEXT_(2,setup,"%s",p_sysval->host_name);
- CLAW_DBF_TEXT_(2,setup,"%s",temp_host_name);
- printk(KERN_INFO "%s: Host name mismatch\n",
- dev->name);
- printk(KERN_INFO "%s: Received :%s: "
- "expected :%s: \n",
- dev->name,
- p_sysval->host_name,
- temp_host_name);
- }
- if (0!=memcmp(temp_ws_name,p_sysval->WS_name,8)) {
- claw_snd_sys_validate_rsp(dev, p_ctlbk,
- CLAW_RC_NAME_MISMATCH );
- CLAW_DBF_TEXT(2,setup,"WSNBAD");
- CLAW_DBF_TEXT_(2,setup,"%s",p_sysval->WS_name);
- CLAW_DBF_TEXT_(2,setup,"%s",temp_ws_name);
- printk(KERN_INFO "%s: WS name mismatch\n",
- dev->name);
- printk(KERN_INFO "%s: Received :%s: "
- "expected :%s: \n",
- dev->name,
- p_sysval->WS_name,
- temp_ws_name);
- }
- if (( p_sysval->write_frame_size < p_env->write_size) &&
- ( p_env->packing == 0)) {
- claw_snd_sys_validate_rsp(dev, p_ctlbk,
- CLAW_RC_HOST_RCV_TOO_SMALL );
- printk(KERN_INFO "%s: host write size is too "
- "small\n", dev->name);
- CLAW_DBF_TEXT(2,setup,"wrtszbad");
- }
- if (( p_sysval->read_frame_size < p_env->read_size) &&
- ( p_env->packing == 0)) {
- claw_snd_sys_validate_rsp(dev, p_ctlbk,
- CLAW_RC_HOST_RCV_TOO_SMALL );
- printk(KERN_INFO "%s: host read size is too "
- "small\n", dev->name);
- CLAW_DBF_TEXT(2,setup,"rdsizbad");
- }
- claw_snd_sys_validate_rsp(dev, p_ctlbk, 0 );
- printk("%s: CLAW device %.8s: System validate"
- " completed.\n",dev->name, temp_ws_name);
- printk("%s: sys Validate Rsize:%d Wsize:%d\n",dev->name,
- p_sysval->read_frame_size,p_sysval->write_frame_size);
- privptr->system_validate_comp=1;
- if(strncmp(p_env->api_type,WS_APPL_NAME_PACKED,6) == 0) {
- p_env->packing = PACKING_ASK;
- }
- claw_strt_conn_req(dev);
- break;
-
- case SYSTEM_VALIDATE_RESPONSE:
- p_sysval=(struct sysval *)&(p_ctlbk->data);
- printk("%s: Recv Sys Validate Resp: Vers=%d,Corr=%d,RC=%d,"
- "WS name=%.8s,Host name=%.8s\n",
- dev->name,
- p_ctlbk->version,
- p_ctlbk->correlator,
- p_ctlbk->rc,
- p_sysval->WS_name,
- p_sysval->host_name);
- switch (p_ctlbk->rc)
- {
- case 0:
- printk(KERN_INFO "%s: CLAW device "
- "%.8s: System validate "
- "completed.\n",
- dev->name, temp_ws_name);
- if (privptr->system_validate_comp == 0)
- claw_strt_conn_req(dev);
- privptr->system_validate_comp=1;
- break;
- case CLAW_RC_NAME_MISMATCH:
- printk(KERN_INFO "%s: Sys Validate "
- "Resp : Host, WS name is "
- "mismatch\n",
- dev->name);
- break;
- case CLAW_RC_WRONG_VERSION:
- printk(KERN_INFO "%s: Sys Validate "
- "Resp : Wrong version\n",
- dev->name);
- break;
- case CLAW_RC_HOST_RCV_TOO_SMALL:
- printk(KERN_INFO "%s: Sys Validate "
- "Resp : bad frame size\n",
- dev->name);
- break;
- default:
- printk(KERN_INFO "%s: Sys Validate "
- "error code=%d \n",
- dev->name, p_ctlbk->rc );
- break;
- }
- break;
+ dev->name,
+ temp_ws_name);
+ }
+ if ((p_sysval->write_frame_size < p_env->write_size) &&
+ (p_env->packing == 0)) {
+ claw_snd_sys_validate_rsp(dev, p_ctlbk,
+ CLAW_RC_HOST_RCV_TOO_SMALL);
+ dev_warn(tdev,
+ "The local write buffer is smaller than the"
+ " remote read buffer\n");
+ CLAW_DBF_TEXT(2, setup, "wrtszbad");
+ }
+ if ((p_sysval->read_frame_size < p_env->read_size) &&
+ (p_env->packing == 0)) {
+ claw_snd_sys_validate_rsp(dev, p_ctlbk,
+ CLAW_RC_HOST_RCV_TOO_SMALL);
+ dev_warn(tdev,
+ "The local read buffer is smaller than the"
+ " remote write buffer\n");
+ CLAW_DBF_TEXT(2, setup, "rdsizbad");
+ }
+ claw_snd_sys_validate_rsp(dev, p_ctlbk, 0);
+ dev_info(tdev,
+ "CLAW device %.8s: System validate"
+ " completed.\n", temp_ws_name);
+ dev_info(tdev,
+ "%s: sys Validate Rsize:%d Wsize:%d\n",
+ dev->name, p_sysval->read_frame_size,
+ p_sysval->write_frame_size);
+ privptr->system_validate_comp = 1;
+ if (strncmp(p_env->api_type, WS_APPL_NAME_PACKED, 6) == 0)
+ p_env->packing = PACKING_ASK;
+ claw_strt_conn_req(dev);
+ break;
+ case SYSTEM_VALIDATE_RESPONSE:
+ p_sysval = (struct sysval *)&(p_ctlbk->data);
+ dev_info(tdev,
+ "Settings for %s validated (version=%d, "
+ "remote device=%d, rc=%d, adapter name=%.8s, "
+ "host name=%.8s)\n",
+ dev->name,
+ p_ctlbk->version,
+ p_ctlbk->correlator,
+ p_ctlbk->rc,
+ p_sysval->WS_name,
+ p_sysval->host_name);
+ switch (p_ctlbk->rc) {
+ case 0:
+ dev_info(tdev, "%s: CLAW device "
+ "%.8s: System validate completed.\n",
+ dev->name, temp_ws_name);
+ if (privptr->system_validate_comp == 0)
+ claw_strt_conn_req(dev);
+ privptr->system_validate_comp = 1;
+ break;
+ case CLAW_RC_NAME_MISMATCH:
+ dev_warn(tdev, "Validating %s failed because of"
+ " a host or adapter name mismatch\n",
+ dev->name);
+ break;
+ case CLAW_RC_WRONG_VERSION:
+ dev_warn(tdev, "Validating %s failed because of a"
+ " version conflict\n",
+ dev->name);
+ break;
+ case CLAW_RC_HOST_RCV_TOO_SMALL:
+ dev_warn(tdev, "Validating %s failed because of a"
+ " frame size conflict\n",
+ dev->name);
+ break;
+ default:
+ dev_warn(tdev, "The communication peer of %s rejected"
+ " the connection\n",
+ dev->name);
+ break;
+ }
+ break;
- case CONNECTION_REQUEST:
- p_connect=(struct conncmd *)&(p_ctlbk->data);
- printk(KERN_INFO "%s: Recv Conn Req: Vers=%d,link_id=%d,"
- "Corr=%d,HOST appl=%.8s,WS appl=%.8s\n",
- dev->name,
- p_ctlbk->version,
- p_ctlbk->linkid,
- p_ctlbk->correlator,
- p_connect->host_name,
- p_connect->WS_name);
- if (privptr->active_link_ID!=0 ) {
- claw_snd_disc(dev, p_ctlbk);
- printk(KERN_INFO "%s: Conn Req error : "
- "already logical link is active \n",
- dev->name);
- }
- if (p_ctlbk->linkid!=1 ) {
- claw_snd_disc(dev, p_ctlbk);
- printk(KERN_INFO "%s: Conn Req error : "
- "req logical link id is not 1\n",
- dev->name);
- }
- rc=find_link(dev,
- p_connect->host_name, p_connect->WS_name);
- if (rc!=0) {
- claw_snd_disc(dev, p_ctlbk);
- printk(KERN_INFO "%s: Conn Req error : "
- "req appl name does not match\n",
- dev->name);
- }
- claw_send_control(dev,
- CONNECTION_CONFIRM, p_ctlbk->linkid,
- p_ctlbk->correlator,
- 0, p_connect->host_name,
- p_connect->WS_name);
- if (p_env->packing == PACKING_ASK) {
- printk("%s: Now Pack ask\n",dev->name);
- p_env->packing = PACK_SEND;
- claw_snd_conn_req(dev,0);
- }
- printk(KERN_INFO "%s: CLAW device %.8s: Connection "
- "completed link_id=%d.\n",
- dev->name, temp_ws_name,
- p_ctlbk->linkid);
- privptr->active_link_ID=p_ctlbk->linkid;
- p_ch=&privptr->channel[WRITE];
- wake_up(&p_ch->wait); /* wake up claw_open ( WRITE) */
- break;
- case CONNECTION_RESPONSE:
- p_connect=(struct conncmd *)&(p_ctlbk->data);
- printk(KERN_INFO "%s: Revc Conn Resp: Vers=%d,link_id=%d,"
- "Corr=%d,RC=%d,Host appl=%.8s, WS appl=%.8s\n",
- dev->name,
- p_ctlbk->version,
- p_ctlbk->linkid,
- p_ctlbk->correlator,
- p_ctlbk->rc,
- p_connect->host_name,
- p_connect->WS_name);
-
- if (p_ctlbk->rc !=0 ) {
- printk(KERN_INFO "%s: Conn Resp error: rc=%d \n",
- dev->name, p_ctlbk->rc);
- return 1;
- }
- rc=find_link(dev,
- p_connect->host_name, p_connect->WS_name);
- if (rc!=0) {
- claw_snd_disc(dev, p_ctlbk);
- printk(KERN_INFO "%s: Conn Resp error: "
- "req appl name does not match\n",
- dev->name);
- }
- /* should be until CONNECTION_CONFIRM */
- privptr->active_link_ID = - (p_ctlbk->linkid);
- break;
- case CONNECTION_CONFIRM:
- p_connect=(struct conncmd *)&(p_ctlbk->data);
- printk(KERN_INFO "%s: Recv Conn Confirm:Vers=%d,link_id=%d,"
- "Corr=%d,Host appl=%.8s,WS appl=%.8s\n",
- dev->name,
- p_ctlbk->version,
- p_ctlbk->linkid,
- p_ctlbk->correlator,
- p_connect->host_name,
- p_connect->WS_name);
- if (p_ctlbk->linkid== -(privptr->active_link_ID)) {
- privptr->active_link_ID=p_ctlbk->linkid;
- if (p_env->packing > PACKING_ASK) {
- printk(KERN_INFO "%s: Confirmed Now packing\n",dev->name);
- p_env->packing = DO_PACKED;
- }
- p_ch=&privptr->channel[WRITE];
- wake_up(&p_ch->wait);
- }
- else {
- printk(KERN_INFO "%s: Conn confirm: "
- "unexpected linkid=%d \n",
- dev->name, p_ctlbk->linkid);
- claw_snd_disc(dev, p_ctlbk);
- }
- break;
- case DISCONNECT:
- printk(KERN_INFO "%s: Disconnect: "
- "Vers=%d,link_id=%d,Corr=%d\n",
- dev->name, p_ctlbk->version,
- p_ctlbk->linkid, p_ctlbk->correlator);
- if ((p_ctlbk->linkid == 2) &&
- (p_env->packing == PACK_SEND)) {
- privptr->active_link_ID = 1;
+ case CONNECTION_REQUEST:
+ p_connect = (struct conncmd *)&(p_ctlbk->data);
+ dev_info(tdev, "%s: Recv Conn Req: Vers=%d,link_id=%d,"
+ "Corr=%d,HOST appl=%.8s,WS appl=%.8s\n",
+ dev->name,
+ p_ctlbk->version,
+ p_ctlbk->linkid,
+ p_ctlbk->correlator,
+ p_connect->host_name,
+ p_connect->WS_name);
+ if (privptr->active_link_ID != 0) {
+ claw_snd_disc(dev, p_ctlbk);
+ dev_info(tdev, "%s rejected a connection request"
+ " because it is already active\n",
+ dev->name);
+ }
+ if (p_ctlbk->linkid != 1) {
+ claw_snd_disc(dev, p_ctlbk);
+ dev_info(tdev, "%s rejected a request to open multiple"
+ " connections\n",
+ dev->name);
+ }
+ rc = find_link(dev, p_connect->host_name, p_connect->WS_name);
+ if (rc != 0) {
+ claw_snd_disc(dev, p_ctlbk);
+ dev_info(tdev, "%s rejected a connection request"
+ " because of a type mismatch\n",
+ dev->name);
+ }
+ claw_send_control(dev,
+ CONNECTION_CONFIRM, p_ctlbk->linkid,
+ p_ctlbk->correlator,
+ 0, p_connect->host_name,
+ p_connect->WS_name);
+ if (p_env->packing == PACKING_ASK) {
+ p_env->packing = PACK_SEND;
+ claw_snd_conn_req(dev, 0);
+ }
+ dev_info(tdev, "%s: CLAW device %.8s: Connection "
+ "completed link_id=%d.\n",
+ dev->name, temp_ws_name,
+ p_ctlbk->linkid);
+ privptr->active_link_ID = p_ctlbk->linkid;
+ p_ch = &privptr->channel[WRITE_CHANNEL];
+ wake_up(&p_ch->wait); /* wake up claw_open ( WRITE) */
+ break;
+ case CONNECTION_RESPONSE:
+ p_connect = (struct conncmd *)&(p_ctlbk->data);
+ dev_info(tdev, "%s: Recv Conn Resp: Vers=%d,link_id=%d,"
+ "Corr=%d,RC=%d,Host appl=%.8s, WS appl=%.8s\n",
+ dev->name,
+ p_ctlbk->version,
+ p_ctlbk->linkid,
+ p_ctlbk->correlator,
+ p_ctlbk->rc,
+ p_connect->host_name,
+ p_connect->WS_name);
+
+ if (p_ctlbk->rc != 0) {
+ dev_warn(tdev, "The communication peer of %s rejected"
+ " a connection request\n",
+ dev->name);
+ return 1;
+ }
+ rc = find_link(dev,
+ p_connect->host_name, p_connect->WS_name);
+ if (rc != 0) {
+ claw_snd_disc(dev, p_ctlbk);
+ dev_warn(tdev, "The communication peer of %s"
+ " rejected a connection "
+ "request because of a type mismatch\n",
+ dev->name);
+ }
+ /* should be until CONNECTION_CONFIRM */
+ privptr->active_link_ID = -(p_ctlbk->linkid);
+ break;
+ case CONNECTION_CONFIRM:
+ p_connect = (struct conncmd *)&(p_ctlbk->data);
+ dev_info(tdev,
+ "%s: Recv Conn Confirm:Vers=%d,link_id=%d,"
+ "Corr=%d,Host appl=%.8s,WS appl=%.8s\n",
+ dev->name,
+ p_ctlbk->version,
+ p_ctlbk->linkid,
+ p_ctlbk->correlator,
+ p_connect->host_name,
+ p_connect->WS_name);
+ if (p_ctlbk->linkid == -(privptr->active_link_ID)) {
+ privptr->active_link_ID = p_ctlbk->linkid;
+ if (p_env->packing > PACKING_ASK) {
+ dev_info(tdev,
+ "%s: Confirmed Now packing\n", dev->name);
p_env->packing = DO_PACKED;
}
- else
- privptr->active_link_ID=0;
- break;
- case CLAW_ERROR:
- printk(KERN_INFO "%s: CLAW ERROR detected\n",
- dev->name);
- break;
- default:
- printk(KERN_INFO "%s: Unexpected command code=%d \n",
- dev->name, p_ctlbk->command);
- break;
+ p_ch = &privptr->channel[WRITE_CHANNEL];
+ wake_up(&p_ch->wait);
+ } else {
+ dev_warn(tdev, "Activating %s failed because of"
+ " an incorrect link ID=%d\n",
+ dev->name, p_ctlbk->linkid);
+ claw_snd_disc(dev, p_ctlbk);
+ }
+ break;
+ case DISCONNECT:
+ dev_info(tdev, "%s: Disconnect: "
+ "Vers=%d,link_id=%d,Corr=%d\n",
+ dev->name, p_ctlbk->version,
+ p_ctlbk->linkid, p_ctlbk->correlator);
+ if ((p_ctlbk->linkid == 2) &&
+ (p_env->packing == PACK_SEND)) {
+ privptr->active_link_ID = 1;
+ p_env->packing = DO_PACKED;
+ } else
+ privptr->active_link_ID = 0;
+ break;
+ case CLAW_ERROR:
+ dev_warn(tdev, "The communication peer of %s failed\n",
+ dev->name);
+ break;
+ default:
+ dev_warn(tdev, "The communication peer of %s sent"
+ " an unknown command code\n",
+ dev->name);
+ break;
}
-#ifdef FUNCTRACE
- printk(KERN_INFO "%s: %s() exit on line %d, rc = 0\n",
- dev->name,__FUNCTION__,__LINE__);
-#endif
-
return 0;
} /* end of claw_process_control */
@@ -3079,19 +2272,8 @@ claw_send_control(struct net_device *dev, __u8 type, __u8 link,
struct conncmd *p_connect;
struct sk_buff *skb;
-#ifdef FUNCTRACE
- printk(KERN_INFO "%s:%s > enter \n",dev->name,__FUNCTION__);
-#endif
- CLAW_DBF_TEXT(2,setup,"sndcntl");
-#ifdef DEBUGMSG
- printk(KERN_INFO "%s: Sending Control Packet \n",dev->name);
- printk(KERN_INFO "%s: variable type = 0x%X, link = "
- "%d, correlator = %d, rc = %d\n",
- dev->name,type, link, correlator, rc);
- printk(KERN_INFO "%s: variable local_name = %s, "
- "remote_name = %s\n",dev->name, local_name, remote_name);
-#endif
- privptr=dev->priv;
+ CLAW_DBF_TEXT(2, setup, "sndcntl");
+ privptr = dev->ml_priv;
p_ctl=(struct clawctl *)&privptr->ctl_bk;
p_ctl->command=type;
@@ -3109,12 +2291,14 @@ claw_send_control(struct net_device *dev, __u8 type, __u8 link,
memcpy(&p_sysval->host_name, local_name, 8);
memcpy(&p_sysval->WS_name, remote_name, 8);
if (privptr->p_env->packing > 0) {
- p_sysval->read_frame_size=DEF_PACK_BUFSIZE;
- p_sysval->write_frame_size=DEF_PACK_BUFSIZE;
+ p_sysval->read_frame_size = DEF_PACK_BUFSIZE;
+ p_sysval->write_frame_size = DEF_PACK_BUFSIZE;
} else {
- /* how big is the piggest group of packets */
- p_sysval->read_frame_size=privptr->p_env->read_size;
- p_sysval->write_frame_size=privptr->p_env->write_size;
+ /* how big is the biggest group of packets */
+ p_sysval->read_frame_size =
+ privptr->p_env->read_size;
+ p_sysval->write_frame_size =
+ privptr->p_env->write_size;
}
memset(&p_sysval->reserved, 0x00, 4);
break;
@@ -3142,29 +2326,14 @@ claw_send_control(struct net_device *dev, __u8 type, __u8 link,
skb = dev_alloc_skb(sizeof(struct clawctl));
if (!skb) {
- printk( "%s:%s low on mem, returning...\n",
- dev->name,__FUNCTION__);
-#ifdef DEBUG
- printk(KERN_INFO "%s:%s Exit, rc = ENOMEM\n",
- dev->name,__FUNCTION__);
-#endif
return -ENOMEM;
}
memcpy(skb_put(skb, sizeof(struct clawctl)),
p_ctl, sizeof(struct clawctl));
-#ifdef IOTRACE
- printk(KERN_INFO "%s: outbnd claw cntl data \n",dev->name);
- dumpit((char *)p_ctl,sizeof(struct clawctl));
-#endif
if (privptr->p_env->packing >= PACK_SEND)
claw_hw_tx(skb, dev, 1);
else
claw_hw_tx(skb, dev, 0);
-#ifdef FUNCTRACE
- printk(KERN_INFO "%s:%s Exit on line %d\n",
- dev->name,__FUNCTION__,__LINE__);
-#endif
-
return 0;
} /* end of claw_send_control */
@@ -3176,25 +2345,14 @@ static int
claw_snd_conn_req(struct net_device *dev, __u8 link)
{
int rc;
- struct claw_privbk *privptr=dev->priv;
+ struct claw_privbk *privptr = dev->ml_priv;
struct clawctl *p_ctl;
-#ifdef FUNCTRACE
- printk(KERN_INFO "%s:%s Enter \n",dev->name,__FUNCTION__);
-#endif
- CLAW_DBF_TEXT(2,setup,"snd_conn");
-#ifdef DEBUGMSG
- printk(KERN_INFO "%s: variable link = %X, dev =\n",dev->name, link);
- dumpit((char *) dev, sizeof(struct net_device));
-#endif
+ CLAW_DBF_TEXT(2, setup, "snd_conn");
rc = 1;
p_ctl=(struct clawctl *)&privptr->ctl_bk;
p_ctl->linkid = link;
if ( privptr->system_validate_comp==0x00 ) {
-#ifdef FUNCTRACE
- printk(KERN_INFO "%s:%s Exit on line %d, rc = 1\n",
- dev->name,__FUNCTION__,__LINE__);
-#endif
return rc;
}
if (privptr->p_env->packing == PACKING_ASK )
@@ -3207,10 +2365,6 @@ claw_snd_conn_req(struct net_device *dev, __u8 link)
if (privptr->p_env->packing == 0)
rc=claw_send_control(dev, CONNECTION_REQUEST,0,0,0,
HOST_APPL_NAME, privptr->p_env->api_type);
-#ifdef FUNCTRACE
- printk(KERN_INFO "%s:%s Exit on line %d, rc = %d\n",
- dev->name,__FUNCTION__,__LINE__, rc);
-#endif
return rc;
} /* end of claw_snd_conn_req */
@@ -3227,25 +2381,12 @@ claw_snd_disc(struct net_device *dev, struct clawctl * p_ctl)
int rc;
struct conncmd * p_connect;
-#ifdef FUNCTRACE
- printk(KERN_INFO "%s:%s Enter\n",dev->name,__FUNCTION__);
-#endif
- CLAW_DBF_TEXT(2,setup,"snd_dsc");
-#ifdef DEBUGMSG
- printk(KERN_INFO "%s: variable dev =\n",dev->name);
- dumpit((char *) dev, sizeof(struct net_device));
- printk(KERN_INFO "%s: variable p_ctl",dev->name);
- dumpit((char *) p_ctl, sizeof(struct clawctl));
-#endif
+ CLAW_DBF_TEXT(2, setup, "snd_dsc");
p_connect=(struct conncmd *)&p_ctl->data;
rc=claw_send_control(dev, DISCONNECT, p_ctl->linkid,
p_ctl->correlator, 0,
p_connect->host_name, p_connect->WS_name);
-#ifdef FUNCTRACE
- printk(KERN_INFO "%s:%s Exit on line %d, rc = %d\n",
- dev->name,__FUNCTION__, __LINE__, rc);
-#endif
return rc;
} /* end of claw_snd_disc */
@@ -3263,19 +2404,8 @@ claw_snd_sys_validate_rsp(struct net_device *dev,
struct claw_privbk *privptr;
int rc;
-#ifdef FUNCTRACE
- printk(KERN_INFO "%s:%s Enter\n",
- dev->name,__FUNCTION__);
-#endif
- CLAW_DBF_TEXT(2,setup,"chkresp");
-#ifdef DEBUGMSG
- printk(KERN_INFO "%s: variable return_code = %d, dev =\n",
- dev->name, return_code);
- dumpit((char *) dev, sizeof(struct net_device));
- printk(KERN_INFO "%s: variable p_ctl =\n",dev->name);
- dumpit((char *) p_ctl, sizeof(struct clawctl));
-#endif
- privptr = dev->priv;
+ CLAW_DBF_TEXT(2, setup, "chkresp");
+ privptr = dev->ml_priv;
p_env=privptr->p_env;
rc=claw_send_control(dev, SYSTEM_VALIDATE_RESPONSE,
p_ctl->linkid,
@@ -3283,10 +2413,6 @@ claw_snd_sys_validate_rsp(struct net_device *dev,
return_code,
p_env->host_name,
p_env->adapter_name );
-#ifdef FUNCTRACE
- printk(KERN_INFO "%s:%s Exit on line %d, rc = %d\n",
- dev->name,__FUNCTION__,__LINE__, rc);
-#endif
return rc;
} /* end of claw_snd_sys_validate_rsp */
@@ -3300,19 +2426,8 @@ claw_strt_conn_req(struct net_device *dev )
{
int rc;
-#ifdef FUNCTRACE
- printk(KERN_INFO "%s:%s Enter\n",dev->name,__FUNCTION__);
-#endif
- CLAW_DBF_TEXT(2,setup,"conn_req");
-#ifdef DEBUGMSG
- printk(KERN_INFO "%s: variable dev =\n",dev->name);
- dumpit((char *) dev, sizeof(struct net_device));
-#endif
+ CLAW_DBF_TEXT(2, setup, "conn_req");
rc=claw_snd_conn_req(dev, 1);
-#ifdef FUNCTRACE
- printk(KERN_INFO "%s:%s Exit on line %d, rc = %d\n",
- dev->name,__FUNCTION__,__LINE__, rc);
-#endif
return rc;
} /* end of claw_strt_conn_req */
@@ -3326,15 +2441,9 @@ static struct
net_device_stats *claw_stats(struct net_device *dev)
{
struct claw_privbk *privptr;
-#ifdef FUNCTRACE
- printk(KERN_INFO "%s:%s Enter\n",dev->name,__FUNCTION__);
-#endif
- CLAW_DBF_TEXT(4,trace,"stats");
- privptr = dev->priv;
-#ifdef FUNCTRACE
- printk(KERN_INFO "%s:%s Exit on line %d\n",
- dev->name,__FUNCTION__,__LINE__);
-#endif
+
+ CLAW_DBF_TEXT(4, trace, "stats");
+ privptr = dev->ml_priv;
return &privptr->stats;
} /* end of claw_stats */
@@ -3355,36 +2464,27 @@ unpack_read(struct net_device *dev )
struct clawph *p_packh;
void *p_packd;
struct clawctl *p_ctlrec=NULL;
+ struct device *p_dev;
__u32 len_of_data;
__u32 pack_off;
__u8 link_num;
__u8 mtc_this_frm=0;
__u32 bytes_to_mov;
- struct chbk *p_ch = NULL;
int i=0;
int p=0;
-#ifdef FUNCTRACE
- printk(KERN_INFO "%s:%s enter \n",dev->name,__FUNCTION__);
-#endif
- CLAW_DBF_TEXT(4,trace,"unpkread");
+ CLAW_DBF_TEXT(4, trace, "unpkread");
p_first_ccw=NULL;
p_last_ccw=NULL;
p_packh=NULL;
p_packd=NULL;
- privptr=dev->priv;
+ privptr = dev->ml_priv;
+
+ p_dev = &privptr->channel[READ_CHANNEL].cdev->dev;
p_env = privptr->p_env;
p_this_ccw=privptr->p_read_active_first;
- i=0;
while (p_this_ccw!=NULL && p_this_ccw->header.flag!=CLAW_PENDING) {
-#ifdef IOTRACE
- printk(KERN_INFO "%s p_this_ccw \n",dev->name);
- dumpit((char*)p_this_ccw, sizeof(struct ccwbk));
- printk(KERN_INFO "%s Inbound p_this_ccw->p_buffer(64)"
- " pk=%d \n",dev->name,p_env->packing);
- dumpit((char *)p_this_ccw->p_buffer, 64 );
-#endif
pack_off = 0;
p = 0;
p_this_ccw->header.flag=CLAW_PENDING;
@@ -3406,15 +2506,13 @@ unpack_read(struct net_device *dev )
else
link_num=p_this_ccw->header.opcode / 8;
if ((p_this_ccw->header.opcode & MORE_to_COME_FLAG)!=0) {
-#ifdef DEBUGMSG
- printk(KERN_INFO "%s: %s > More_to_come is ON\n",
- dev->name,__FUNCTION__);
-#endif
mtc_this_frm=1;
if (p_this_ccw->header.length!=
privptr->p_env->read_size ) {
- printk(KERN_INFO " %s: Invalid frame detected "
- "length is %02x\n" ,
+ dev_warn(p_dev,
+ "The communication peer of %s"
+ " sent a faulty"
+ " frame of length %02x\n",
dev->name, p_this_ccw->header.length);
}
}
@@ -3432,22 +2530,12 @@ unpack_read(struct net_device *dev )
privptr->mtc_skipping=0; /* Ok, the end */
privptr->mtc_logical_link=-1;
}
-#ifdef DEBUGMSG
- printk(KERN_INFO "%s:%s goto next "
- "frame from MoretoComeSkip \n",
- dev->name,__FUNCTION__);
-#endif
goto NextFrame;
}
if (link_num==0) {
claw_process_control(dev, p_this_ccw);
-#ifdef DEBUGMSG
- printk(KERN_INFO "%s:%s goto next "
- "frame from claw_process_control \n",
- dev->name,__FUNCTION__);
-#endif
- CLAW_DBF_TEXT(4,trace,"UnpkCntl");
+ CLAW_DBF_TEXT(4, trace, "UnpkCntl");
goto NextFrame;
}
unpack_next:
@@ -3456,7 +2544,7 @@ unpack_next:
goto NextFrame;
p_packd = p_this_ccw->p_buffer+pack_off;
p_packh = (struct clawph *) p_packd;
- if ((p_packh->len == 0) || /* all done with this frame? */
+ if ((p_packh->len == 0) || /* done with this frame? */
(p_packh->flag != 0))
goto NextFrame;
bytes_to_mov = p_packh->len;
@@ -3466,10 +2554,6 @@ unpack_next:
bytes_to_mov=p_this_ccw->header.length;
}
if (privptr->mtc_logical_link<0) {
-#ifdef DEBUGMSG
- printk(KERN_INFO "%s: %s mtc_logical_link < 0 \n",
- dev->name,__FUNCTION__);
-#endif
/*
* if More-To-Come is set in this frame then we don't know
@@ -3483,15 +2567,6 @@ unpack_next:
if (bytes_to_mov > (MAX_ENVELOPE_SIZE- privptr->mtc_offset) ) {
/* error */
-#ifdef DEBUGMSG
- printk(KERN_INFO "%s: %s > goto next "
- "frame from MoretoComeSkip \n",
- dev->name,
- __FUNCTION__);
- printk(KERN_INFO " bytes_to_mov %d > (MAX_ENVELOPE_"
- "SIZE-privptr->mtc_offset %d)\n",
- bytes_to_mov,(MAX_ENVELOPE_SIZE- privptr->mtc_offset));
-#endif
privptr->stats.rx_frame_errors++;
goto NextFrame;
}
@@ -3503,16 +2578,6 @@ unpack_next:
memcpy( privptr->p_mtc_envelope+ privptr->mtc_offset,
p_this_ccw->p_buffer, bytes_to_mov);
}
-#ifdef DEBUGMSG
- printk(KERN_INFO "%s: %s() received data \n",
- dev->name,__FUNCTION__);
- if (p_env->packing == DO_PACKED)
- dumpit((char *)p_packd+sizeof(struct clawph),32);
- else
- dumpit((char *)p_this_ccw->p_buffer, 32);
- printk(KERN_INFO "%s: %s() bytelength %d \n",
- dev->name,__FUNCTION__,bytes_to_mov);
-#endif
if (mtc_this_frm==0) {
len_of_data=privptr->mtc_offset+bytes_to_mov;
skb=dev_alloc_skb(len_of_data);
@@ -3527,16 +2592,11 @@ unpack_next:
privptr->stats.rx_packets++;
privptr->stats.rx_bytes+=len_of_data;
netif_rx(skb);
-#ifdef DEBUGMSG
- printk(KERN_INFO "%s: %s() netif_"
- "rx(skb) completed \n",
- dev->name,__FUNCTION__);
-#endif
}
else {
+ dev_info(p_dev, "Allocating a buffer for"
+ " incoming data failed\n");
privptr->stats.rx_dropped++;
- printk(KERN_WARNING "%s: %s() low on memory\n",
- dev->name,__FUNCTION__);
}
privptr->mtc_offset=0;
privptr->mtc_logical_link=-1;
@@ -3568,28 +2628,14 @@ NextFrame:
* chain to next block on active read queue
*/
p_this_ccw = privptr->p_read_active_first;
- CLAW_DBF_TEXT_(4,trace,"rxpkt %d",p);
+ CLAW_DBF_TEXT_(4, trace, "rxpkt %d", p);
} /* end of while */
/* check validity */
-#ifdef IOTRACE
- printk(KERN_INFO "%s:%s processed frame is %d \n",
- dev->name,__FUNCTION__,i);
- printk(KERN_INFO "%s:%s F:%lx L:%lx\n",
- dev->name,
- __FUNCTION__,
- (unsigned long)p_first_ccw,
- (unsigned long)p_last_ccw);
-#endif
- CLAW_DBF_TEXT_(4,trace,"rxfrm %d",i);
+ CLAW_DBF_TEXT_(4, trace, "rxfrm %d", i);
add_claw_reads(dev, p_first_ccw, p_last_ccw);
- p_ch=&privptr->channel[READ];
claw_strt_read(dev, LOCK_YES);
-#ifdef FUNCTRACE
- printk(KERN_INFO "%s: %s exit on line %d\n",
- dev->name, __FUNCTION__, __LINE__);
-#endif
return;
} /* end of unpack_read */
@@ -3603,18 +2649,13 @@ claw_strt_read (struct net_device *dev, int lock )
int rc = 0;
__u32 parm;
unsigned long saveflags = 0;
- struct claw_privbk *privptr=dev->priv;
+ struct claw_privbk *privptr = dev->ml_priv;
struct ccwbk*p_ccwbk;
struct chbk *p_ch;
struct clawh *p_clawh;
- p_ch=&privptr->channel[READ];
+ p_ch = &privptr->channel[READ_CHANNEL];
-#ifdef FUNCTRACE
- printk(KERN_INFO "%s:%s Enter \n",dev->name,__FUNCTION__);
- printk(KERN_INFO "%s: variable lock = %d, dev =\n",dev->name, lock);
- dumpit((char *) dev, sizeof(struct net_device));
-#endif
- CLAW_DBF_TEXT(4,trace,"StRdNter");
+ CLAW_DBF_TEXT(4, trace, "StRdNter");
p_clawh=(struct clawh *)privptr->p_claw_signal_blk;
p_clawh->flag=CLAW_IDLE; /* 0x00 */
@@ -3624,21 +2665,11 @@ claw_strt_read (struct net_device *dev, int lock )
privptr->p_read_active_first->header.flag!=CLAW_PENDING )) {
p_clawh->flag=CLAW_BUSY; /* 0xff */
}
-#ifdef DEBUGMSG
- printk(KERN_INFO "%s:%s state-%02x\n" ,
- dev->name,__FUNCTION__, p_ch->claw_state);
-#endif
if (lock==LOCK_YES) {
spin_lock_irqsave(get_ccwdev_lock(p_ch->cdev), saveflags);
}
if (test_and_set_bit(0, (void *)&p_ch->IO_active) == 0) {
-#ifdef DEBUGMSG
- printk(KERN_INFO "%s: HOT READ started in %s\n" ,
- dev->name,__FUNCTION__);
- p_clawh=(struct clawh *)privptr->p_claw_signal_blk;
- dumpit((char *)&p_clawh->flag , 1);
-#endif
- CLAW_DBF_TEXT(4,trace,"HotRead");
+ CLAW_DBF_TEXT(4, trace, "HotRead");
p_ccwbk=privptr->p_read_active_first;
parm = (unsigned long) p_ch;
rc = ccw_device_start (p_ch->cdev, &p_ccwbk->read, parm,
@@ -3648,21 +2679,13 @@ claw_strt_read (struct net_device *dev, int lock )
}
}
else {
-#ifdef DEBUGMSG
- printk(KERN_INFO "%s: No READ started by %s() In progress\n" ,
- dev->name,__FUNCTION__);
-#endif
- CLAW_DBF_TEXT(2,trace,"ReadAct");
+ CLAW_DBF_TEXT(2, trace, "ReadAct");
}
if (lock==LOCK_YES) {
spin_unlock_irqrestore(get_ccwdev_lock(p_ch->cdev), saveflags);
}
-#ifdef FUNCTRACE
- printk(KERN_INFO "%s:%s Exit on line %d\n",
- dev->name,__FUNCTION__,__LINE__);
-#endif
- CLAW_DBF_TEXT(4,trace,"StRdExit");
+ CLAW_DBF_TEXT(4, trace, "StRdExit");
return;
} /* end of claw_strt_read */
@@ -3680,50 +2703,30 @@ claw_strt_out_IO( struct net_device *dev )
struct chbk *p_ch;
struct ccwbk *p_first_ccw;
-#ifdef FUNCTRACE
- printk(KERN_INFO "%s:%s Enter\n",dev->name,__FUNCTION__);
-#endif
if (!dev) {
return;
}
- privptr=(struct claw_privbk *)dev->priv;
- p_ch=&privptr->channel[WRITE];
+ privptr = (struct claw_privbk *)dev->ml_priv;
+ p_ch = &privptr->channel[WRITE_CHANNEL];
-#ifdef DEBUGMSG
- printk(KERN_INFO "%s:%s state-%02x\n" ,
- dev->name,__FUNCTION__,p_ch->claw_state);
-#endif
- CLAW_DBF_TEXT(4,trace,"strt_io");
+ CLAW_DBF_TEXT(4, trace, "strt_io");
p_first_ccw=privptr->p_write_active_first;
if (p_ch->claw_state == CLAW_STOP)
return;
if (p_first_ccw == NULL) {
-#ifdef FUNCTRACE
- printk(KERN_INFO "%s:%s Exit on line %d\n",
- dev->name,__FUNCTION__,__LINE__);
-#endif
return;
}
if (test_and_set_bit(0, (void *)&p_ch->IO_active) == 0) {
parm = (unsigned long) p_ch;
-#ifdef DEBUGMSG
- printk(KERN_INFO "%s:%s do_io \n" ,dev->name,__FUNCTION__);
- dumpit((char *)p_first_ccw, sizeof(struct ccwbk));
-#endif
- CLAW_DBF_TEXT(2,trace,"StWrtIO");
- rc = ccw_device_start (p_ch->cdev,&p_first_ccw->write, parm,
- 0xff, 0);
+ CLAW_DBF_TEXT(2, trace, "StWrtIO");
+ rc = ccw_device_start(p_ch->cdev, &p_first_ccw->write, parm,
+ 0xff, 0);
if (rc != 0) {
ccw_check_return_code(p_ch->cdev, rc);
}
}
dev->trans_start = jiffies;
-#ifdef FUNCTRACE
- printk(KERN_INFO "%s:%s Exit on line %d\n",
- dev->name,__FUNCTION__,__LINE__);
-#endif
-
return;
} /* end of claw_strt_out_IO */
@@ -3736,37 +2739,12 @@ static void
claw_free_wrt_buf( struct net_device *dev )
{
- struct claw_privbk *privptr=(struct claw_privbk *)dev->priv;
- struct ccwbk*p_first_ccw;
- struct ccwbk*p_last_ccw;
+ struct claw_privbk *privptr = (struct claw_privbk *)dev->ml_priv;
struct ccwbk*p_this_ccw;
struct ccwbk*p_next_ccw;
-#ifdef IOTRACE
- struct ccwbk*p_buf;
-#endif
-#ifdef FUNCTRACE
- printk(KERN_INFO "%s:%s Enter\n",dev->name,__FUNCTION__);
- printk(KERN_INFO "%s: free count = %d variable dev =\n",
- dev->name,privptr->write_free_count);
-#endif
- CLAW_DBF_TEXT(4,trace,"freewrtb");
+
+ CLAW_DBF_TEXT(4, trace, "freewrtb");
/* scan the write queue to free any completed write packets */
- p_first_ccw=NULL;
- p_last_ccw=NULL;
-#ifdef IOTRACE
- printk(KERN_INFO "%s: Dump current CCW chain \n",dev->name );
- p_buf=privptr->p_write_active_first;
- while (p_buf!=NULL) {
- dumpit((char *)p_buf, sizeof(struct ccwbk));
- p_buf=p_buf->next;
- }
- if (p_buf==NULL) {
- printk(KERN_INFO "%s: privptr->p_write_"
- "active_first==NULL\n",dev->name );
- }
- p_buf=(struct ccwbk*)privptr->p_end_ccw;
- dumpit((char *)p_buf, sizeof(struct endccw));
-#endif
p_this_ccw=privptr->p_write_active_first;
while ( (p_this_ccw!=NULL) && (p_this_ccw->header.flag!=CLAW_PENDING))
{
@@ -3796,31 +2774,8 @@ claw_free_wrt_buf( struct net_device *dev )
/* whole chain removed? */
if (privptr->p_write_active_first==NULL) {
privptr->p_write_active_last=NULL;
-#ifdef DEBUGMSG
- printk(KERN_INFO "%s:%s p_write_"
- "active_first==NULL\n",dev->name,__FUNCTION__);
-#endif
- }
-#ifdef IOTRACE
- printk(KERN_INFO "%s: Dump arranged CCW chain \n",dev->name );
- p_buf=privptr->p_write_active_first;
- while (p_buf!=NULL) {
- dumpit((char *)p_buf, sizeof(struct ccwbk));
- p_buf=p_buf->next;
}
- if (p_buf==NULL) {
- printk(KERN_INFO "%s: privptr->p_write_active_"
- "first==NULL\n",dev->name );
- }
- p_buf=(struct ccwbk*)privptr->p_end_ccw;
- dumpit((char *)p_buf, sizeof(struct endccw));
-#endif
-
- CLAW_DBF_TEXT_(4,trace,"FWC=%d",privptr->write_free_count);
-#ifdef FUNCTRACE
- printk(KERN_INFO "%s:%s Exit on line %d free_count =%d\n",
- dev->name,__FUNCTION__, __LINE__,privptr->write_free_count);
-#endif
+ CLAW_DBF_TEXT_(4, trace, "FWC=%d", privptr->write_free_count);
return;
}
@@ -3832,30 +2787,24 @@ static void
claw_free_netdevice(struct net_device * dev, int free_dev)
{
struct claw_privbk *privptr;
-#ifdef FUNCTRACE
- printk(KERN_INFO "%s:%s Enter\n",dev->name,__FUNCTION__);
-#endif
- CLAW_DBF_TEXT(2,setup,"free_dev");
+ CLAW_DBF_TEXT(2, setup, "free_dev");
if (!dev)
return;
- CLAW_DBF_TEXT_(2,setup,"%s",dev->name);
- privptr = dev->priv;
+ CLAW_DBF_TEXT_(2, setup, "%s", dev->name);
+ privptr = dev->ml_priv;
if (dev->flags & IFF_RUNNING)
claw_release(dev);
if (privptr) {
- privptr->channel[READ].ndev = NULL; /* say it's free */
+ privptr->channel[READ_CHANNEL].ndev = NULL; /* say it's free */
}
- dev->priv=NULL;
+ dev->ml_priv = NULL;
#ifdef MODULE
if (free_dev) {
free_netdev(dev);
}
#endif
- CLAW_DBF_TEXT(2,setup,"feee_ok");
-#ifdef FUNCTRACE
- printk(KERN_INFO "%s:%s Exit\n",dev->name,__FUNCTION__);
-#endif
+ CLAW_DBF_TEXT(2, setup, "free_ok");
}
/**
@@ -3863,35 +2812,27 @@ claw_free_netdevice(struct net_device * dev, int free_dev)
* Initialize everything of the net device except the name and the
* channel structs.
*/
+static const struct net_device_ops claw_netdev_ops = {
+ .ndo_open = claw_open,
+ .ndo_stop = claw_release,
+ .ndo_get_stats = claw_stats,
+ .ndo_start_xmit = claw_tx,
+ .ndo_change_mtu = claw_change_mtu,
+};
+
static void
claw_init_netdevice(struct net_device * dev)
{
-#ifdef FUNCTRACE
- printk(KERN_INFO "%s:%s Enter\n",dev->name,__FUNCTION__);
-#endif
- CLAW_DBF_TEXT(2,setup,"init_dev");
- CLAW_DBF_TEXT_(2,setup,"%s",dev->name);
- if (!dev) {
- printk(KERN_WARNING "claw:%s BAD Device exit line %d\n",
- __FUNCTION__,__LINE__);
- CLAW_DBF_TEXT(2,setup,"baddev");
- return;
- }
+ CLAW_DBF_TEXT(2, setup, "init_dev");
+ CLAW_DBF_TEXT_(2, setup, "%s", dev->name);
dev->mtu = CLAW_DEFAULT_MTU_SIZE;
- dev->hard_start_xmit = claw_tx;
- dev->open = claw_open;
- dev->stop = claw_release;
- dev->get_stats = claw_stats;
- dev->change_mtu = claw_change_mtu;
dev->hard_header_len = 0;
dev->addr_len = 0;
dev->type = ARPHRD_SLIP;
dev->tx_queue_len = 1300;
dev->flags = IFF_POINTOPOINT | IFF_NOARP;
-#ifdef FUNCTRACE
- printk(KERN_INFO "%s:%s Exit\n",dev->name,__FUNCTION__);
-#endif
- CLAW_DBF_TEXT(2,setup,"initok");
+ dev->netdev_ops = &claw_netdev_ops;
+ CLAW_DBF_TEXT(2, setup, "initok");
return;
}
@@ -3908,29 +2849,16 @@ add_channel(struct ccw_device *cdev,int i,struct claw_privbk *privptr)
struct chbk *p_ch;
struct ccw_dev_id dev_id;
-#ifdef FUNCTRACE
- printk(KERN_INFO "%s:%s Enter\n",cdev->dev.bus_id,__FUNCTION__);
-#endif
- CLAW_DBF_TEXT_(2,setup,"%s",cdev->dev.bus_id);
+ CLAW_DBF_TEXT_(2, setup, "%s", dev_name(&cdev->dev));
privptr->channel[i].flag = i+1; /* Read is 1 Write is 2 */
p_ch = &privptr->channel[i];
p_ch->cdev = cdev;
- snprintf(p_ch->id, CLAW_ID_SIZE, "cl-%s", cdev->dev.bus_id);
+ snprintf(p_ch->id, CLAW_ID_SIZE, "cl-%s", dev_name(&cdev->dev));
ccw_device_get_id(cdev, &dev_id);
p_ch->devno = dev_id.devno;
if ((p_ch->irb = kzalloc(sizeof (struct irb),GFP_KERNEL)) == NULL) {
- printk(KERN_WARNING "%s Out of memory in %s for irb\n",
- p_ch->id,__FUNCTION__);
-#ifdef FUNCTRACE
- printk(KERN_INFO "%s:%s Exit on line %d\n",
- p_ch->id,__FUNCTION__,__LINE__);
-#endif
return -ENOMEM;
}
-#ifdef FUNCTRACE
- printk(KERN_INFO "%s:%s Exit on line %d\n",
- cdev->dev.bus_id,__FUNCTION__,__LINE__);
-#endif
return 0;
}
@@ -3952,81 +2880,79 @@ claw_new_device(struct ccwgroup_device *cgdev)
int ret;
struct ccw_dev_id dev_id;
- pr_debug("%s() called\n", __FUNCTION__);
- printk(KERN_INFO "claw: add for %s\n",cgdev->cdev[READ]->dev.bus_id);
- CLAW_DBF_TEXT(2,setup,"new_dev");
- privptr = cgdev->dev.driver_data;
- cgdev->cdev[READ]->dev.driver_data = privptr;
- cgdev->cdev[WRITE]->dev.driver_data = privptr;
+ dev_info(&cgdev->dev, "add for %s\n",
+ dev_name(&cgdev->cdev[READ_CHANNEL]->dev));
+ CLAW_DBF_TEXT(2, setup, "new_dev");
+ privptr = dev_get_drvdata(&cgdev->dev);
+ dev_set_drvdata(&cgdev->cdev[READ_CHANNEL]->dev, privptr);
+ dev_set_drvdata(&cgdev->cdev[WRITE_CHANNEL]->dev, privptr);
if (!privptr)
return -ENODEV;
p_env = privptr->p_env;
- ccw_device_get_id(cgdev->cdev[READ], &dev_id);
- p_env->devno[READ] = dev_id.devno;
- ccw_device_get_id(cgdev->cdev[WRITE], &dev_id);
- p_env->devno[WRITE] = dev_id.devno;
+ ccw_device_get_id(cgdev->cdev[READ_CHANNEL], &dev_id);
+ p_env->devno[READ_CHANNEL] = dev_id.devno;
+ ccw_device_get_id(cgdev->cdev[WRITE_CHANNEL], &dev_id);
+ p_env->devno[WRITE_CHANNEL] = dev_id.devno;
ret = add_channel(cgdev->cdev[0],0,privptr);
if (ret == 0)
ret = add_channel(cgdev->cdev[1],1,privptr);
if (ret != 0) {
- printk(KERN_WARNING
- "add channel failed "
- "with ret = %d\n", ret);
- goto out;
+ dev_warn(&cgdev->dev, "Creating a CLAW group device"
+ " failed with error code %d\n", ret);
+ goto out;
}
- ret = ccw_device_set_online(cgdev->cdev[READ]);
+ ret = ccw_device_set_online(cgdev->cdev[READ_CHANNEL]);
if (ret != 0) {
- printk(KERN_WARNING
- "claw: ccw_device_set_online %s READ failed "
- "with ret = %d\n",cgdev->cdev[READ]->dev.bus_id,ret);
+ dev_warn(&cgdev->dev,
+ "Setting the read subchannel online"
+ " failed with error code %d\n", ret);
goto out;
}
- ret = ccw_device_set_online(cgdev->cdev[WRITE]);
+ ret = ccw_device_set_online(cgdev->cdev[WRITE_CHANNEL]);
if (ret != 0) {
- printk(KERN_WARNING
- "claw: ccw_device_set_online %s WRITE failed "
- "with ret = %d\n",cgdev->cdev[WRITE]->dev.bus_id, ret);
+ dev_warn(&cgdev->dev,
+ "Setting the write subchannel online "
+ "failed with error code %d\n", ret);
goto out;
}
dev = alloc_netdev(0,"claw%d",claw_init_netdevice);
if (!dev) {
- printk(KERN_WARNING "%s:alloc_netdev failed\n",__FUNCTION__);
+ dev_warn(&cgdev->dev,
+ "Activating the CLAW device failed\n");
goto out;
}
- dev->priv = privptr;
- cgdev->dev.driver_data = privptr;
- cgdev->cdev[READ]->dev.driver_data = privptr;
- cgdev->cdev[WRITE]->dev.driver_data = privptr;
+ dev->ml_priv = privptr;
+ dev_set_drvdata(&cgdev->dev, privptr);
+ dev_set_drvdata(&cgdev->cdev[READ_CHANNEL]->dev, privptr);
+ dev_set_drvdata(&cgdev->cdev[WRITE_CHANNEL]->dev, privptr);
/* sysfs magic */
SET_NETDEV_DEV(dev, &cgdev->dev);
if (register_netdev(dev) != 0) {
claw_free_netdevice(dev, 1);
- CLAW_DBF_TEXT(2,trace,"regfail");
+ CLAW_DBF_TEXT(2, trace, "regfail");
goto out;
}
dev->flags &=~IFF_RUNNING;
if (privptr->buffs_alloc == 0) {
ret=init_ccw_bk(dev);
if (ret !=0) {
- printk(KERN_WARNING
- "claw: init_ccw_bk failed with ret=%d\n", ret);
unregister_netdev(dev);
claw_free_netdevice(dev,1);
- CLAW_DBF_TEXT(2,trace,"ccwmem");
+ CLAW_DBF_TEXT(2, trace, "ccwmem");
goto out;
}
}
- privptr->channel[READ].ndev = dev;
- privptr->channel[WRITE].ndev = dev;
+ privptr->channel[READ_CHANNEL].ndev = dev;
+ privptr->channel[WRITE_CHANNEL].ndev = dev;
privptr->p_env->ndev = dev;
- printk(KERN_INFO "%s:readsize=%d writesize=%d "
+ dev_info(&cgdev->dev, "%s:readsize=%d writesize=%d "
"readbuffer=%d writebuffer=%d read=0x%04x write=0x%04x\n",
dev->name, p_env->read_size,
p_env->write_size, p_env->read_buffers,
- p_env->write_buffers, p_env->devno[READ],
- p_env->devno[WRITE]);
- printk(KERN_INFO "%s:host_name:%.8s, adapter_name "
+ p_env->write_buffers, p_env->devno[READ_CHANNEL],
+ p_env->devno[WRITE_CHANNEL]);
+ dev_info(&cgdev->dev, "%s:host_name:%.8s, adapter_name "
":%.8s api_type: %.8s\n",
dev->name, p_env->host_name,
p_env->adapter_name , p_env->api_type);
@@ -4034,7 +2960,6 @@ claw_new_device(struct ccwgroup_device *cgdev)
out:
ccw_device_set_offline(cgdev->cdev[1]);
ccw_device_set_offline(cgdev->cdev[0]);
-
return -ENODEV;
}
@@ -4043,8 +2968,7 @@ claw_purge_skb_queue(struct sk_buff_head *q)
{
struct sk_buff *skb;
- CLAW_DBF_TEXT(4,trace,"purgque");
-
+ CLAW_DBF_TEXT(4, trace, "purgque");
while ((skb = skb_dequeue(q))) {
atomic_dec(&skb->users);
dev_kfree_skb_any(skb);
@@ -4063,31 +2987,30 @@ claw_shutdown_device(struct ccwgroup_device *cgdev)
{
struct claw_privbk *priv;
struct net_device *ndev;
- int ret;
+ int ret = 0;
- pr_debug("%s() called\n", __FUNCTION__);
- CLAW_DBF_TEXT_(2,setup,"%s",cgdev->dev.bus_id);
- priv = cgdev->dev.driver_data;
+ CLAW_DBF_TEXT_(2, setup, "%s", dev_name(&cgdev->dev));
+ priv = dev_get_drvdata(&cgdev->dev);
if (!priv)
return -ENODEV;
- ndev = priv->channel[READ].ndev;
+ ndev = priv->channel[READ_CHANNEL].ndev;
if (ndev) {
/* Close the device */
- printk(KERN_INFO
- "%s: shuting down \n",ndev->name);
+ dev_info(&cgdev->dev, "%s: shutting down\n",
+ ndev->name);
if (ndev->flags & IFF_RUNNING)
ret = claw_release(ndev);
ndev->flags &=~IFF_RUNNING;
unregister_netdev(ndev);
- ndev->priv = NULL; /* cgdev data, not ndev's to free */
+ ndev->ml_priv = NULL; /* cgdev data, not ndev's to free */
claw_free_netdevice(ndev, 1);
- priv->channel[READ].ndev = NULL;
- priv->channel[WRITE].ndev = NULL;
+ priv->channel[READ_CHANNEL].ndev = NULL;
+ priv->channel[WRITE_CHANNEL].ndev = NULL;
priv->p_env->ndev = NULL;
}
ccw_device_set_offline(cgdev->cdev[1]);
ccw_device_set_offline(cgdev->cdev[0]);
- return 0;
+ return ret;
}
static void
@@ -4095,18 +3018,11 @@ claw_remove_device(struct ccwgroup_device *cgdev)
{
struct claw_privbk *priv;
- pr_debug("%s() called\n", __FUNCTION__);
- CLAW_DBF_TEXT_(2,setup,"%s",cgdev->dev.bus_id);
- priv = cgdev->dev.driver_data;
- if (!priv) {
- printk(KERN_WARNING "claw: %s() no Priv exiting\n",__FUNCTION__);
- return;
- }
- printk(KERN_INFO "claw: %s() called %s will be removed.\n",
- __FUNCTION__,cgdev->cdev[0]->dev.bus_id);
+ CLAW_DBF_TEXT_(2, setup, "%s", dev_name(&cgdev->dev));
+ priv = dev_get_drvdata(&cgdev->dev);
+ dev_info(&cgdev->dev, " will be removed.\n");
if (cgdev->state == CCWGROUP_ONLINE)
claw_shutdown_device(cgdev);
- claw_remove_files(&cgdev->dev);
kfree(priv->p_mtc_envelope);
priv->p_mtc_envelope=NULL;
kfree(priv->p_env);
@@ -4116,10 +3032,12 @@ claw_remove_device(struct ccwgroup_device *cgdev)
kfree(priv->channel[1].irb);
priv->channel[1].irb=NULL;
kfree(priv);
- cgdev->dev.driver_data=NULL;
- cgdev->cdev[READ]->dev.driver_data = NULL;
- cgdev->cdev[WRITE]->dev.driver_data = NULL;
+ dev_set_drvdata(&cgdev->dev, NULL);
+ dev_set_drvdata(&cgdev->cdev[READ_CHANNEL]->dev, NULL);
+ dev_set_drvdata(&cgdev->cdev[WRITE_CHANNEL]->dev, NULL);
put_device(&cgdev->dev);
+
+ return;
}
@@ -4132,7 +3050,7 @@ claw_hname_show(struct device *dev, struct device_attribute *attr, char *buf)
struct claw_privbk *priv;
struct claw_env * p_env;
- priv = dev->driver_data;
+ priv = dev_get_drvdata(dev);
if (!priv)
return -ENODEV;
p_env = priv->p_env;
@@ -4140,12 +3058,13 @@ claw_hname_show(struct device *dev, struct device_attribute *attr, char *buf)
}
static ssize_t
-claw_hname_write(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
+claw_hname_write(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t count)
{
struct claw_privbk *priv;
struct claw_env * p_env;
- priv = dev->driver_data;
+ priv = dev_get_drvdata(dev);
if (!priv)
return -ENODEV;
p_env = priv->p_env;
@@ -4155,8 +3074,8 @@ claw_hname_write(struct device *dev, struct device_attribute *attr, const char *
strncpy(p_env->host_name,buf, count);
p_env->host_name[count-1] = 0x20; /* clear extra 0x0a */
p_env->host_name[MAX_NAME_LEN] = 0x00;
- CLAW_DBF_TEXT(2,setup,"HstnSet");
- CLAW_DBF_TEXT_(2,setup,"%s",p_env->host_name);
+ CLAW_DBF_TEXT(2, setup, "HstnSet");
+ CLAW_DBF_TEXT_(2, setup, "%s", p_env->host_name);
return count;
}
@@ -4169,20 +3088,21 @@ claw_adname_show(struct device *dev, struct device_attribute *attr, char *buf)
struct claw_privbk *priv;
struct claw_env * p_env;
- priv = dev->driver_data;
+ priv = dev_get_drvdata(dev);
if (!priv)
return -ENODEV;
p_env = priv->p_env;
- return sprintf(buf, "%s\n",p_env->adapter_name);
+ return sprintf(buf, "%s\n", p_env->adapter_name);
}
static ssize_t
-claw_adname_write(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
+claw_adname_write(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t count)
{
struct claw_privbk *priv;
struct claw_env * p_env;
- priv = dev->driver_data;
+ priv = dev_get_drvdata(dev);
if (!priv)
return -ENODEV;
p_env = priv->p_env;
@@ -4192,8 +3112,8 @@ claw_adname_write(struct device *dev, struct device_attribute *attr, const char
strncpy(p_env->adapter_name,buf, count);
p_env->adapter_name[count-1] = 0x20; /* clear extra 0x0a */
p_env->adapter_name[MAX_NAME_LEN] = 0x00;
- CLAW_DBF_TEXT(2,setup,"AdnSet");
- CLAW_DBF_TEXT_(2,setup,"%s",p_env->adapter_name);
+ CLAW_DBF_TEXT(2, setup, "AdnSet");
+ CLAW_DBF_TEXT_(2, setup, "%s", p_env->adapter_name);
return count;
}
@@ -4206,7 +3126,7 @@ claw_apname_show(struct device *dev, struct device_attribute *attr, char *buf)
struct claw_privbk *priv;
struct claw_env * p_env;
- priv = dev->driver_data;
+ priv = dev_get_drvdata(dev);
if (!priv)
return -ENODEV;
p_env = priv->p_env;
@@ -4215,12 +3135,13 @@ claw_apname_show(struct device *dev, struct device_attribute *attr, char *buf)
}
static ssize_t
-claw_apname_write(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
+claw_apname_write(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t count)
{
struct claw_privbk *priv;
struct claw_env * p_env;
- priv = dev->driver_data;
+ priv = dev_get_drvdata(dev);
if (!priv)
return -ENODEV;
p_env = priv->p_env;
@@ -4234,15 +3155,15 @@ claw_apname_write(struct device *dev, struct device_attribute *attr, const char
p_env->read_size=DEF_PACK_BUFSIZE;
p_env->write_size=DEF_PACK_BUFSIZE;
p_env->packing=PACKING_ASK;
- CLAW_DBF_TEXT(2,setup,"PACKING");
+ CLAW_DBF_TEXT(2, setup, "PACKING");
}
else {
p_env->packing=0;
p_env->read_size=CLAW_FRAME_SIZE;
p_env->write_size=CLAW_FRAME_SIZE;
- CLAW_DBF_TEXT(2,setup,"ApiSet");
+ CLAW_DBF_TEXT(2, setup, "ApiSet");
}
- CLAW_DBF_TEXT_(2,setup,"%s",p_env->api_type);
+ CLAW_DBF_TEXT_(2, setup, "%s", p_env->api_type);
return count;
}
@@ -4254,7 +3175,7 @@ claw_wbuff_show(struct device *dev, struct device_attribute *attr, char *buf)
struct claw_privbk *priv;
struct claw_env * p_env;
- priv = dev->driver_data;
+ priv = dev_get_drvdata(dev);
if (!priv)
return -ENODEV;
p_env = priv->p_env;
@@ -4262,13 +3183,14 @@ claw_wbuff_show(struct device *dev, struct device_attribute *attr, char *buf)
}
static ssize_t
-claw_wbuff_write(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
+claw_wbuff_write(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t count)
{
struct claw_privbk *priv;
struct claw_env * p_env;
int nnn,max;
- priv = dev->driver_data;
+ priv = dev_get_drvdata(dev);
if (!priv)
return -ENODEV;
p_env = priv->p_env;
@@ -4282,8 +3204,8 @@ claw_wbuff_write(struct device *dev, struct device_attribute *attr, const char *
if ((nnn > max ) || (nnn < 2))
return -EINVAL;
p_env->write_buffers = nnn;
- CLAW_DBF_TEXT(2,setup,"Wbufset");
- CLAW_DBF_TEXT_(2,setup,"WB=%d",p_env->write_buffers);
+ CLAW_DBF_TEXT(2, setup, "Wbufset");
+ CLAW_DBF_TEXT_(2, setup, "WB=%d", p_env->write_buffers);
return count;
}
@@ -4295,7 +3217,7 @@ claw_rbuff_show(struct device *dev, struct device_attribute *attr, char *buf)
struct claw_privbk *priv;
struct claw_env * p_env;
- priv = dev->driver_data;
+ priv = dev_get_drvdata(dev);
if (!priv)
return -ENODEV;
p_env = priv->p_env;
@@ -4303,13 +3225,14 @@ claw_rbuff_show(struct device *dev, struct device_attribute *attr, char *buf)
}
static ssize_t
-claw_rbuff_write(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
+claw_rbuff_write(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t count)
{
struct claw_privbk *priv;
struct claw_env *p_env;
int nnn,max;
- priv = dev->driver_data;
+ priv = dev_get_drvdata(dev);
if (!priv)
return -ENODEV;
p_env = priv->p_env;
@@ -4323,11 +3246,10 @@ claw_rbuff_write(struct device *dev, struct device_attribute *attr, const char *
if ((nnn > max ) || (nnn < 2))
return -EINVAL;
p_env->read_buffers = nnn;
- CLAW_DBF_TEXT(2,setup,"Rbufset");
- CLAW_DBF_TEXT_(2,setup,"RB=%d",p_env->read_buffers);
+ CLAW_DBF_TEXT(2, setup, "Rbufset");
+ CLAW_DBF_TEXT_(2, setup, "RB=%d", p_env->read_buffers);
return count;
}
-
static DEVICE_ATTR(read_buffer, 0644, claw_rbuff_show, claw_rbuff_write);
static struct attribute *claw_attr[] = {
@@ -4338,38 +3260,73 @@ static struct attribute *claw_attr[] = {
&dev_attr_host_name.attr,
NULL,
};
-
static struct attribute_group claw_attr_group = {
.attrs = claw_attr,
};
+static const struct attribute_group *claw_attr_groups[] = {
+ &claw_attr_group,
+ NULL,
+};
+static const struct device_type claw_devtype = {
+ .name = "claw",
+ .groups = claw_attr_groups,
+};
-static int
-claw_add_files(struct device *dev)
+/*----------------------------------------------------------------*
+ * claw_probe *
+ * this function is called for each CLAW device. *
+ *----------------------------------------------------------------*/
+static int claw_probe(struct ccwgroup_device *cgdev)
{
- pr_debug("%s() called\n", __FUNCTION__);
- CLAW_DBF_TEXT(2,setup,"add_file");
- return sysfs_create_group(&dev->kobj, &claw_attr_group);
-}
+ struct claw_privbk *privptr = NULL;
-static void
-claw_remove_files(struct device *dev)
-{
- pr_debug("%s() called\n", __FUNCTION__);
- CLAW_DBF_TEXT(2,setup,"rem_file");
- sysfs_remove_group(&dev->kobj, &claw_attr_group);
-}
+ CLAW_DBF_TEXT(2, setup, "probe");
+ if (!get_device(&cgdev->dev))
+ return -ENODEV;
+ privptr = kzalloc(sizeof(struct claw_privbk), GFP_KERNEL);
+ dev_set_drvdata(&cgdev->dev, privptr);
+ if (privptr == NULL) {
+ probe_error(cgdev);
+ put_device(&cgdev->dev);
+ CLAW_DBF_TEXT_(2, setup, "probex%d", -ENOMEM);
+ return -ENOMEM;
+ }
+ privptr->p_mtc_envelope = kzalloc(MAX_ENVELOPE_SIZE, GFP_KERNEL);
+ privptr->p_env = kzalloc(sizeof(struct claw_env), GFP_KERNEL);
+ if ((privptr->p_mtc_envelope == NULL) || (privptr->p_env == NULL)) {
+ probe_error(cgdev);
+ put_device(&cgdev->dev);
+ CLAW_DBF_TEXT_(2, setup, "probex%d", -ENOMEM);
+ return -ENOMEM;
+ }
+ memcpy(privptr->p_env->adapter_name, WS_NAME_NOT_DEF, 8);
+ memcpy(privptr->p_env->host_name, WS_NAME_NOT_DEF, 8);
+ memcpy(privptr->p_env->api_type, WS_NAME_NOT_DEF, 8);
+ privptr->p_env->packing = 0;
+ privptr->p_env->write_buffers = 5;
+ privptr->p_env->read_buffers = 5;
+ privptr->p_env->read_size = CLAW_FRAME_SIZE;
+ privptr->p_env->write_size = CLAW_FRAME_SIZE;
+ privptr->p_env->p_priv = privptr;
+ cgdev->cdev[0]->handler = claw_irq_handler;
+ cgdev->cdev[1]->handler = claw_irq_handler;
+ cgdev->dev.type = &claw_devtype;
+ CLAW_DBF_TEXT(2, setup, "prbext 0");
+
+ return 0;
+} /* end of claw_probe */
/*--------------------------------------------------------------------*
* claw_init and cleanup *
*---------------------------------------------------------------------*/
-static void __exit
-claw_cleanup(void)
+static void __exit claw_cleanup(void)
{
- unregister_cu3088_discipline(&claw_group_driver);
+ ccwgroup_driver_unregister(&claw_group_driver);
+ ccw_driver_unregister(&claw_ccw_driver);
+ root_device_unregister(claw_root_dev);
claw_unregister_debug_facility();
- printk(KERN_INFO "claw: Driver unloaded\n");
-
+ pr_info("Driver unloaded\n");
}
/**
@@ -4378,41 +3335,47 @@ claw_cleanup(void)
*
* @return 0 on success, !0 on error.
*/
-static int __init
-claw_init(void)
+static int __init claw_init(void)
{
int ret = 0;
- printk(KERN_INFO "claw: starting driver\n");
-#ifdef FUNCTRACE
- printk(KERN_INFO "claw: %s() enter \n",__FUNCTION__);
-#endif
+ pr_info("Loading %s\n", version);
ret = claw_register_debug_facility();
if (ret) {
- printk(KERN_WARNING "claw: %s() debug_register failed %d\n",
- __FUNCTION__,ret);
- return ret;
- }
- CLAW_DBF_TEXT(2,setup,"init_mod");
- ret = register_cu3088_discipline(&claw_group_driver);
- if (ret) {
- claw_unregister_debug_facility();
- printk(KERN_WARNING "claw; %s() cu3088 register failed %d\n",
- __FUNCTION__,ret);
+ pr_err("Registering with the S/390 debug feature"
+ " failed with error code %d\n", ret);
+ goto out_err;
}
-#ifdef FUNCTRACE
- printk(KERN_INFO "claw: %s() exit \n",__FUNCTION__);
-#endif
+ CLAW_DBF_TEXT(2, setup, "init_mod");
+ claw_root_dev = root_device_register("claw");
+ ret = PTR_ERR_OR_ZERO(claw_root_dev);
+ if (ret)
+ goto register_err;
+ ret = ccw_driver_register(&claw_ccw_driver);
+ if (ret)
+ goto ccw_err;
+ claw_group_driver.driver.groups = claw_drv_attr_groups;
+ ret = ccwgroup_driver_register(&claw_group_driver);
+ if (ret)
+ goto ccwgroup_err;
+ return 0;
+
+ccwgroup_err:
+ ccw_driver_unregister(&claw_ccw_driver);
+ccw_err:
+ root_device_unregister(claw_root_dev);
+register_err:
+ CLAW_DBF_TEXT(2, setup, "init_bad");
+ claw_unregister_debug_facility();
+out_err:
+ pr_err("Initializing the claw device driver failed\n");
return ret;
}
module_init(claw_init);
module_exit(claw_cleanup);
-
-
-/*--------------------------------------------------------------------*
-* End of File *
-*---------------------------------------------------------------------*/
-
-
+MODULE_AUTHOR("Andy Richter <richtera@us.ibm.com>");
+MODULE_DESCRIPTION("Linux for System z CLAW Driver\n" \
+ "Copyright IBM Corp. 2000, 2008\n");
+MODULE_LICENSE("GPL");
diff --git a/drivers/s390/net/claw.h b/drivers/s390/net/claw.h
index 1a89d989f34..3339b9b607b 100644
--- a/drivers/s390/net/claw.h
+++ b/drivers/s390/net/claw.h
@@ -74,8 +74,8 @@
#define MAX_ENVELOPE_SIZE 65536
#define CLAW_DEFAULT_MTU_SIZE 4096
#define DEF_PACK_BUFSIZE 32768
-#define READ 0
-#define WRITE 1
+#define READ_CHANNEL 0
+#define WRITE_CHANNEL 1
#define TB_TX 0 /* sk buffer handling in process */
#define TB_STOP 1 /* network device stop in process */
@@ -85,7 +85,7 @@
#define CLAW_MAX_DEV 256 /* max claw devices */
#define MAX_NAME_LEN 8 /* host name, adapter name length */
#define CLAW_FRAME_SIZE 4096
-#define CLAW_ID_SIZE BUS_ID_SIZE+3
+#define CLAW_ID_SIZE 20+3
/* state machine codes used in claw_irq_handler */
@@ -114,21 +114,27 @@ do { \
debug_event(claw_dbf_##name,level,(void*)(addr),len); \
} while (0)
-/* Allow to sort out low debug levels early to avoid wasted sprints */
-static inline int claw_dbf_passes(debug_info_t *dbf_grp, int level)
-{
- return (level <= dbf_grp->level);
-}
-
#define CLAW_DBF_TEXT_(level,name,text...) \
do { \
- if (claw_dbf_passes(claw_dbf_##name, level)) { \
+ if (debug_level_enabled(claw_dbf_##name, level)) { \
sprintf(debug_buffer, text); \
debug_text_event(claw_dbf_##name, level, \
debug_buffer); \
} \
} while (0)
+/**
+ * Enum for classifying detected devices.
+ */
+enum claw_channel_types {
+ /* Device is not a channel */
+ claw_channel_type_none,
+
+ /* Device is a CLAW channel device */
+ claw_channel_type_claw
+};
+
+
/*******************************************************
* Define Control Blocks *
* *
diff --git a/drivers/s390/net/ctcdbug.c b/drivers/s390/net/ctcdbug.c
deleted file mode 100644
index e6e72deb36b..00000000000
--- a/drivers/s390/net/ctcdbug.c
+++ /dev/null
@@ -1,80 +0,0 @@
-/*
- *
- * linux/drivers/s390/net/ctcdbug.c
- *
- * CTC / ESCON network driver - s390 dbf exploit.
- *
- * Copyright 2000,2003 IBM Corporation
- *
- * Author(s): Original Code written by
- * Peter Tiedemann (ptiedem@de.ibm.com)
- *
- * 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.
- */
-
-#include "ctcdbug.h"
-
-/**
- * Debug Facility Stuff
- */
-debug_info_t *ctc_dbf_setup = NULL;
-debug_info_t *ctc_dbf_data = NULL;
-debug_info_t *ctc_dbf_trace = NULL;
-
-DEFINE_PER_CPU(char[256], ctc_dbf_txt_buf);
-
-void
-ctc_unregister_dbf_views(void)
-{
- if (ctc_dbf_setup)
- debug_unregister(ctc_dbf_setup);
- if (ctc_dbf_data)
- debug_unregister(ctc_dbf_data);
- if (ctc_dbf_trace)
- debug_unregister(ctc_dbf_trace);
-}
-int
-ctc_register_dbf_views(void)
-{
- ctc_dbf_setup = debug_register(CTC_DBF_SETUP_NAME,
- CTC_DBF_SETUP_PAGES,
- CTC_DBF_SETUP_NR_AREAS,
- CTC_DBF_SETUP_LEN);
- ctc_dbf_data = debug_register(CTC_DBF_DATA_NAME,
- CTC_DBF_DATA_PAGES,
- CTC_DBF_DATA_NR_AREAS,
- CTC_DBF_DATA_LEN);
- ctc_dbf_trace = debug_register(CTC_DBF_TRACE_NAME,
- CTC_DBF_TRACE_PAGES,
- CTC_DBF_TRACE_NR_AREAS,
- CTC_DBF_TRACE_LEN);
-
- if ((ctc_dbf_setup == NULL) || (ctc_dbf_data == NULL) ||
- (ctc_dbf_trace == NULL)) {
- ctc_unregister_dbf_views();
- return -ENOMEM;
- }
- debug_register_view(ctc_dbf_setup, &debug_hex_ascii_view);
- debug_set_level(ctc_dbf_setup, CTC_DBF_SETUP_LEVEL);
-
- debug_register_view(ctc_dbf_data, &debug_hex_ascii_view);
- debug_set_level(ctc_dbf_data, CTC_DBF_DATA_LEVEL);
-
- debug_register_view(ctc_dbf_trace, &debug_hex_ascii_view);
- debug_set_level(ctc_dbf_trace, CTC_DBF_TRACE_LEVEL);
-
- return 0;
-}
-
diff --git a/drivers/s390/net/ctcdbug.h b/drivers/s390/net/ctcdbug.h
deleted file mode 100644
index 413925ee23d..00000000000
--- a/drivers/s390/net/ctcdbug.h
+++ /dev/null
@@ -1,125 +0,0 @@
-/*
- *
- * linux/drivers/s390/net/ctcdbug.h
- *
- * CTC / ESCON network driver - s390 dbf exploit.
- *
- * Copyright 2000,2003 IBM Corporation
- *
- * Author(s): Original Code written by
- * Peter Tiedemann (ptiedem@de.ibm.com)
- *
- * 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.
- */
-#ifndef _CTCDBUG_H_
-#define _CTCDBUG_H_
-
-#include <asm/debug.h>
-#include "ctcmain.h"
-/**
- * Debug Facility stuff
- */
-#define CTC_DBF_SETUP_NAME "ctc_setup"
-#define CTC_DBF_SETUP_LEN 16
-#define CTC_DBF_SETUP_PAGES 8
-#define CTC_DBF_SETUP_NR_AREAS 1
-#define CTC_DBF_SETUP_LEVEL 3
-
-#define CTC_DBF_DATA_NAME "ctc_data"
-#define CTC_DBF_DATA_LEN 128
-#define CTC_DBF_DATA_PAGES 8
-#define CTC_DBF_DATA_NR_AREAS 1
-#define CTC_DBF_DATA_LEVEL 3
-
-#define CTC_DBF_TRACE_NAME "ctc_trace"
-#define CTC_DBF_TRACE_LEN 16
-#define CTC_DBF_TRACE_PAGES 4
-#define CTC_DBF_TRACE_NR_AREAS 2
-#define CTC_DBF_TRACE_LEVEL 3
-
-#define DBF_TEXT(name,level,text) \
- do { \
- debug_text_event(ctc_dbf_##name,level,text); \
- } while (0)
-
-#define DBF_HEX(name,level,addr,len) \
- do { \
- debug_event(ctc_dbf_##name,level,(void*)(addr),len); \
- } while (0)
-
-DECLARE_PER_CPU(char[256], ctc_dbf_txt_buf);
-extern debug_info_t *ctc_dbf_setup;
-extern debug_info_t *ctc_dbf_data;
-extern debug_info_t *ctc_dbf_trace;
-
-
-#define DBF_TEXT_(name,level,text...) \
- do { \
- char* ctc_dbf_txt_buf = get_cpu_var(ctc_dbf_txt_buf); \
- sprintf(ctc_dbf_txt_buf, text); \
- debug_text_event(ctc_dbf_##name,level,ctc_dbf_txt_buf); \
- put_cpu_var(ctc_dbf_txt_buf); \
- } while (0)
-
-#define DBF_SPRINTF(name,level,text...) \
- do { \
- debug_sprintf_event(ctc_dbf_trace, level, ##text ); \
- debug_sprintf_event(ctc_dbf_trace, level, text ); \
- } while (0)
-
-
-int ctc_register_dbf_views(void);
-
-void ctc_unregister_dbf_views(void);
-
-/**
- * some more debug stuff
- */
-
-#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));
-
-static inline void
-hex_dump(unsigned char *buf, size_t len)
-{
- size_t i;
-
- for (i = 0; i < len; i++) {
- if (i && !(i % 16))
- printk("\n");
- printk("%02x ", *(buf + i));
- }
- printk("\n");
-}
-
-
-#endif
diff --git a/drivers/s390/net/ctcm_dbug.c b/drivers/s390/net/ctcm_dbug.c
new file mode 100644
index 00000000000..8363f1c966e
--- /dev/null
+++ b/drivers/s390/net/ctcm_dbug.c
@@ -0,0 +1,77 @@
+/*
+ * Copyright IBM Corp. 2001, 2007
+ * Authors: Peter Tiedemann (ptiedem@de.ibm.com)
+ *
+ */
+
+#include <linux/stddef.h>
+#include <linux/string.h>
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/ctype.h>
+#include <linux/sysctl.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/fs.h>
+#include <linux/debugfs.h>
+#include "ctcm_dbug.h"
+
+/*
+ * Debug Facility Stuff
+ */
+
+struct ctcm_dbf_info ctcm_dbf[CTCM_DBF_INFOS] = {
+ [CTCM_DBF_SETUP] = {"ctc_setup", 8, 1, 64, CTC_DBF_INFO, NULL},
+ [CTCM_DBF_ERROR] = {"ctc_error", 8, 1, 64, CTC_DBF_ERROR, NULL},
+ [CTCM_DBF_TRACE] = {"ctc_trace", 8, 1, 64, CTC_DBF_ERROR, NULL},
+ [CTCM_DBF_MPC_SETUP] = {"mpc_setup", 8, 1, 80, CTC_DBF_INFO, NULL},
+ [CTCM_DBF_MPC_ERROR] = {"mpc_error", 8, 1, 80, CTC_DBF_ERROR, NULL},
+ [CTCM_DBF_MPC_TRACE] = {"mpc_trace", 8, 1, 80, CTC_DBF_ERROR, NULL},
+};
+
+void ctcm_unregister_dbf_views(void)
+{
+ int x;
+ for (x = 0; x < CTCM_DBF_INFOS; x++) {
+ debug_unregister(ctcm_dbf[x].id);
+ ctcm_dbf[x].id = NULL;
+ }
+}
+
+int ctcm_register_dbf_views(void)
+{
+ int x;
+ for (x = 0; x < CTCM_DBF_INFOS; x++) {
+ /* register the areas */
+ ctcm_dbf[x].id = debug_register(ctcm_dbf[x].name,
+ ctcm_dbf[x].pages,
+ ctcm_dbf[x].areas,
+ ctcm_dbf[x].len);
+ if (ctcm_dbf[x].id == NULL) {
+ ctcm_unregister_dbf_views();
+ return -ENOMEM;
+ }
+
+ /* register a view */
+ debug_register_view(ctcm_dbf[x].id, &debug_hex_ascii_view);
+ /* set a passing level */
+ debug_set_level(ctcm_dbf[x].id, ctcm_dbf[x].level);
+ }
+
+ return 0;
+}
+
+void ctcm_dbf_longtext(enum ctcm_dbf_names dbf_nix, int level, char *fmt, ...)
+{
+ char dbf_txt_buf[64];
+ va_list args;
+
+ if (!debug_level_enabled(ctcm_dbf[dbf_nix].id, level))
+ return;
+ va_start(args, fmt);
+ vsnprintf(dbf_txt_buf, sizeof(dbf_txt_buf), fmt, args);
+ va_end(args);
+
+ debug_text_event(ctcm_dbf[dbf_nix].id, level, dbf_txt_buf);
+}
+
diff --git a/drivers/s390/net/ctcm_dbug.h b/drivers/s390/net/ctcm_dbug.h
new file mode 100644
index 00000000000..47bf0501995
--- /dev/null
+++ b/drivers/s390/net/ctcm_dbug.h
@@ -0,0 +1,141 @@
+/*
+ * Copyright IBM Corp. 2001, 2007
+ * Authors: Peter Tiedemann (ptiedem@de.ibm.com)
+ *
+ */
+
+#ifndef _CTCM_DBUG_H_
+#define _CTCM_DBUG_H_
+
+/*
+ * Debug Facility stuff
+ */
+
+#include <asm/debug.h>
+
+#ifdef DEBUG
+ #define do_debug 1
+#else
+ #define do_debug 0
+#endif
+#ifdef DEBUGCCW
+ #define do_debug_ccw 1
+ #define DEBUGDATA 1
+#else
+ #define do_debug_ccw 0
+#endif
+#ifdef DEBUGDATA
+ #define do_debug_data 1
+#else
+ #define do_debug_data 0
+#endif
+
+/* define dbf debug levels similar to kernel msg levels */
+#define CTC_DBF_ALWAYS 0 /* always print this */
+#define CTC_DBF_EMERG 0 /* system is unusable */
+#define CTC_DBF_ALERT 1 /* action must be taken immediately */
+#define CTC_DBF_CRIT 2 /* critical conditions */
+#define CTC_DBF_ERROR 3 /* error conditions */
+#define CTC_DBF_WARN 4 /* warning conditions */
+#define CTC_DBF_NOTICE 5 /* normal but significant condition */
+#define CTC_DBF_INFO 5 /* informational */
+#define CTC_DBF_DEBUG 6 /* debug-level messages */
+
+enum ctcm_dbf_names {
+ CTCM_DBF_SETUP,
+ CTCM_DBF_ERROR,
+ CTCM_DBF_TRACE,
+ CTCM_DBF_MPC_SETUP,
+ CTCM_DBF_MPC_ERROR,
+ CTCM_DBF_MPC_TRACE,
+ CTCM_DBF_INFOS /* must be last element */
+};
+
+struct ctcm_dbf_info {
+ char name[DEBUG_MAX_NAME_LEN];
+ int pages;
+ int areas;
+ int len;
+ int level;
+ debug_info_t *id;
+};
+
+extern struct ctcm_dbf_info ctcm_dbf[CTCM_DBF_INFOS];
+
+int ctcm_register_dbf_views(void);
+void ctcm_unregister_dbf_views(void);
+void ctcm_dbf_longtext(enum ctcm_dbf_names dbf_nix, int level, char *text, ...);
+
+static inline const char *strtail(const char *s, int n)
+{
+ int l = strlen(s);
+ return (l > n) ? s + (l - n) : s;
+}
+
+#define CTCM_FUNTAIL strtail((char *)__func__, 16)
+
+#define CTCM_DBF_TEXT(name, level, text) \
+ do { \
+ debug_text_event(ctcm_dbf[CTCM_DBF_##name].id, level, text); \
+ } while (0)
+
+#define CTCM_DBF_HEX(name, level, addr, len) \
+ do { \
+ debug_event(ctcm_dbf[CTCM_DBF_##name].id, \
+ level, (void *)(addr), len); \
+ } while (0)
+
+#define CTCM_DBF_TEXT_(name, level, text...) \
+ ctcm_dbf_longtext(CTCM_DBF_##name, level, text)
+
+/*
+ * cat : one of {setup, mpc_setup, trace, mpc_trace, error, mpc_error}.
+ * dev : netdevice with valid name field.
+ * text: any text string.
+ */
+#define CTCM_DBF_DEV_NAME(cat, dev, text) \
+ do { \
+ CTCM_DBF_TEXT_(cat, CTC_DBF_INFO, "%s(%s) :- %s", \
+ CTCM_FUNTAIL, dev->name, text); \
+ } while (0)
+
+#define MPC_DBF_DEV_NAME(cat, dev, text) \
+ do { \
+ CTCM_DBF_TEXT_(MPC_##cat, CTC_DBF_INFO, "%s(%s) := %s", \
+ CTCM_FUNTAIL, dev->name, text); \
+ } while (0)
+
+#define CTCMY_DBF_DEV_NAME(cat, dev, text) \
+ do { \
+ if (IS_MPCDEV(dev)) \
+ MPC_DBF_DEV_NAME(cat, dev, text); \
+ else \
+ CTCM_DBF_DEV_NAME(cat, dev, text); \
+ } while (0)
+
+/*
+ * cat : one of {setup, mpc_setup, trace, mpc_trace, error, mpc_error}.
+ * dev : netdevice.
+ * text: any text string.
+ */
+#define CTCM_DBF_DEV(cat, dev, text) \
+ do { \
+ CTCM_DBF_TEXT_(cat, CTC_DBF_INFO, "%s(%p) :-: %s", \
+ CTCM_FUNTAIL, dev, text); \
+ } while (0)
+
+#define MPC_DBF_DEV(cat, dev, text) \
+ do { \
+ CTCM_DBF_TEXT_(MPC_##cat, CTC_DBF_INFO, "%s(%p) :=: %s", \
+ CTCM_FUNTAIL, dev, text); \
+ } while (0)
+
+#define CTCMY_DBF_DEV(cat, dev, text) \
+ do { \
+ if (IS_MPCDEV(dev)) \
+ MPC_DBF_DEV(cat, dev, text); \
+ else \
+ CTCM_DBF_DEV(cat, dev, text); \
+ } while (0)
+
+#endif
diff --git a/drivers/s390/net/ctcm_fsms.c b/drivers/s390/net/ctcm_fsms.c
new file mode 100644
index 00000000000..fb92524d24e
--- /dev/null
+++ b/drivers/s390/net/ctcm_fsms.c
@@ -0,0 +1,2304 @@
+/*
+ * Copyright IBM Corp. 2001, 2007
+ * Authors: Fritz Elfert (felfert@millenux.com)
+ * Peter Tiedemann (ptiedem@de.ibm.com)
+ * MPC additions :
+ * Belinda Thompson (belindat@us.ibm.com)
+ * Andy Richter (richtera@us.ibm.com)
+ */
+
+#undef DEBUG
+#undef DEBUGDATA
+#undef DEBUGCCW
+
+#define KMSG_COMPONENT "ctcm"
+#define pr_fmt(fmt) KMSG_COMPONENT ": " fmt
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/errno.h>
+#include <linux/types.h>
+#include <linux/interrupt.h>
+#include <linux/timer.h>
+#include <linux/bitops.h>
+
+#include <linux/signal.h>
+#include <linux/string.h>
+
+#include <linux/ip.h>
+#include <linux/if_arp.h>
+#include <linux/tcp.h>
+#include <linux/skbuff.h>
+#include <linux/ctype.h>
+#include <net/dst.h>
+
+#include <linux/io.h>
+#include <asm/ccwdev.h>
+#include <asm/ccwgroup.h>
+#include <linux/uaccess.h>
+
+#include <asm/idals.h>
+
+#include "fsm.h"
+
+#include "ctcm_dbug.h"
+#include "ctcm_main.h"
+#include "ctcm_fsms.h"
+
+const char *dev_state_names[] = {
+ [DEV_STATE_STOPPED] = "Stopped",
+ [DEV_STATE_STARTWAIT_RXTX] = "StartWait RXTX",
+ [DEV_STATE_STARTWAIT_RX] = "StartWait RX",
+ [DEV_STATE_STARTWAIT_TX] = "StartWait TX",
+ [DEV_STATE_STOPWAIT_RXTX] = "StopWait RXTX",
+ [DEV_STATE_STOPWAIT_RX] = "StopWait RX",
+ [DEV_STATE_STOPWAIT_TX] = "StopWait TX",
+ [DEV_STATE_RUNNING] = "Running",
+};
+
+const char *dev_event_names[] = {
+ [DEV_EVENT_START] = "Start",
+ [DEV_EVENT_STOP] = "Stop",
+ [DEV_EVENT_RXUP] = "RX up",
+ [DEV_EVENT_TXUP] = "TX up",
+ [DEV_EVENT_RXDOWN] = "RX down",
+ [DEV_EVENT_TXDOWN] = "TX down",
+ [DEV_EVENT_RESTART] = "Restart",
+};
+
+const char *ctc_ch_event_names[] = {
+ [CTC_EVENT_IO_SUCCESS] = "ccw_device success",
+ [CTC_EVENT_IO_EBUSY] = "ccw_device busy",
+ [CTC_EVENT_IO_ENODEV] = "ccw_device enodev",
+ [CTC_EVENT_IO_UNKNOWN] = "ccw_device unknown",
+ [CTC_EVENT_ATTNBUSY] = "Status ATTN & BUSY",
+ [CTC_EVENT_ATTN] = "Status ATTN",
+ [CTC_EVENT_BUSY] = "Status BUSY",
+ [CTC_EVENT_UC_RCRESET] = "Unit check remote reset",
+ [CTC_EVENT_UC_RSRESET] = "Unit check remote system reset",
+ [CTC_EVENT_UC_TXTIMEOUT] = "Unit check TX timeout",
+ [CTC_EVENT_UC_TXPARITY] = "Unit check TX parity",
+ [CTC_EVENT_UC_HWFAIL] = "Unit check Hardware failure",
+ [CTC_EVENT_UC_RXPARITY] = "Unit check RX parity",
+ [CTC_EVENT_UC_ZERO] = "Unit check ZERO",
+ [CTC_EVENT_UC_UNKNOWN] = "Unit check Unknown",
+ [CTC_EVENT_SC_UNKNOWN] = "SubChannel check Unknown",
+ [CTC_EVENT_MC_FAIL] = "Machine check failure",
+ [CTC_EVENT_MC_GOOD] = "Machine check operational",
+ [CTC_EVENT_IRQ] = "IRQ normal",
+ [CTC_EVENT_FINSTAT] = "IRQ final",
+ [CTC_EVENT_TIMER] = "Timer",
+ [CTC_EVENT_START] = "Start",
+ [CTC_EVENT_STOP] = "Stop",
+ /*
+ * additional MPC events
+ */
+ [CTC_EVENT_SEND_XID] = "XID Exchange",
+ [CTC_EVENT_RSWEEP_TIMER] = "MPC Group Sweep Timer",
+};
+
+const char *ctc_ch_state_names[] = {
+ [CTC_STATE_IDLE] = "Idle",
+ [CTC_STATE_STOPPED] = "Stopped",
+ [CTC_STATE_STARTWAIT] = "StartWait",
+ [CTC_STATE_STARTRETRY] = "StartRetry",
+ [CTC_STATE_SETUPWAIT] = "SetupWait",
+ [CTC_STATE_RXINIT] = "RX init",
+ [CTC_STATE_TXINIT] = "TX init",
+ [CTC_STATE_RX] = "RX",
+ [CTC_STATE_TX] = "TX",
+ [CTC_STATE_RXIDLE] = "RX idle",
+ [CTC_STATE_TXIDLE] = "TX idle",
+ [CTC_STATE_RXERR] = "RX error",
+ [CTC_STATE_TXERR] = "TX error",
+ [CTC_STATE_TERM] = "Terminating",
+ [CTC_STATE_DTERM] = "Restarting",
+ [CTC_STATE_NOTOP] = "Not operational",
+ /*
+ * additional MPC states
+ */
+ [CH_XID0_PENDING] = "Pending XID0 Start",
+ [CH_XID0_INPROGRESS] = "In XID0 Negotiations ",
+ [CH_XID7_PENDING] = "Pending XID7 P1 Start",
+ [CH_XID7_PENDING1] = "Active XID7 P1 Exchange ",
+ [CH_XID7_PENDING2] = "Pending XID7 P2 Start ",
+ [CH_XID7_PENDING3] = "Active XID7 P2 Exchange ",
+ [CH_XID7_PENDING4] = "XID7 Complete - Pending READY ",
+};
+
+static void ctcm_action_nop(fsm_instance *fi, int event, void *arg);
+
+/*
+ * ----- static ctcm actions for channel statemachine -----
+ *
+*/
+static void chx_txdone(fsm_instance *fi, int event, void *arg);
+static void chx_rx(fsm_instance *fi, int event, void *arg);
+static void chx_rxidle(fsm_instance *fi, int event, void *arg);
+static void chx_firstio(fsm_instance *fi, int event, void *arg);
+static void ctcm_chx_setmode(fsm_instance *fi, int event, void *arg);
+static void ctcm_chx_start(fsm_instance *fi, int event, void *arg);
+static void ctcm_chx_haltio(fsm_instance *fi, int event, void *arg);
+static void ctcm_chx_stopped(fsm_instance *fi, int event, void *arg);
+static void ctcm_chx_stop(fsm_instance *fi, int event, void *arg);
+static void ctcm_chx_fail(fsm_instance *fi, int event, void *arg);
+static void ctcm_chx_setuperr(fsm_instance *fi, int event, void *arg);
+static void ctcm_chx_restart(fsm_instance *fi, int event, void *arg);
+static void ctcm_chx_rxiniterr(fsm_instance *fi, int event, void *arg);
+static void ctcm_chx_rxinitfail(fsm_instance *fi, int event, void *arg);
+static void ctcm_chx_rxdisc(fsm_instance *fi, int event, void *arg);
+static void ctcm_chx_txiniterr(fsm_instance *fi, int event, void *arg);
+static void ctcm_chx_txretry(fsm_instance *fi, int event, void *arg);
+static void ctcm_chx_iofatal(fsm_instance *fi, int event, void *arg);
+
+/*
+ * ----- static ctcmpc actions for ctcmpc channel statemachine -----
+ *
+*/
+static void ctcmpc_chx_txdone(fsm_instance *fi, int event, void *arg);
+static void ctcmpc_chx_rx(fsm_instance *fi, int event, void *arg);
+static void ctcmpc_chx_firstio(fsm_instance *fi, int event, void *arg);
+/* shared :
+static void ctcm_chx_setmode(fsm_instance *fi, int event, void *arg);
+static void ctcm_chx_start(fsm_instance *fi, int event, void *arg);
+static void ctcm_chx_haltio(fsm_instance *fi, int event, void *arg);
+static void ctcm_chx_stopped(fsm_instance *fi, int event, void *arg);
+static void ctcm_chx_stop(fsm_instance *fi, int event, void *arg);
+static void ctcm_chx_fail(fsm_instance *fi, int event, void *arg);
+static void ctcm_chx_setuperr(fsm_instance *fi, int event, void *arg);
+static void ctcm_chx_restart(fsm_instance *fi, int event, void *arg);
+static void ctcm_chx_rxiniterr(fsm_instance *fi, int event, void *arg);
+static void ctcm_chx_rxinitfail(fsm_instance *fi, int event, void *arg);
+static void ctcm_chx_rxdisc(fsm_instance *fi, int event, void *arg);
+static void ctcm_chx_txiniterr(fsm_instance *fi, int event, void *arg);
+static void ctcm_chx_txretry(fsm_instance *fi, int event, void *arg);
+static void ctcm_chx_iofatal(fsm_instance *fi, int event, void *arg);
+*/
+static void ctcmpc_chx_attn(fsm_instance *fsm, int event, void *arg);
+static void ctcmpc_chx_attnbusy(fsm_instance *, int, void *);
+static void ctcmpc_chx_resend(fsm_instance *, int, void *);
+static void ctcmpc_chx_send_sweep(fsm_instance *fsm, int event, void *arg);
+
+/**
+ * Check return code of a preceding ccw_device call, halt_IO etc...
+ *
+ * ch : The channel, the error belongs to.
+ * Returns the error code (!= 0) to inspect.
+ */
+void ctcm_ccw_check_rc(struct channel *ch, int rc, char *msg)
+{
+ CTCM_DBF_TEXT_(ERROR, CTC_DBF_ERROR,
+ "%s(%s): %s: %04x\n",
+ CTCM_FUNTAIL, ch->id, msg, rc);
+ switch (rc) {
+ case -EBUSY:
+ pr_info("%s: The communication peer is busy\n",
+ ch->id);
+ fsm_event(ch->fsm, CTC_EVENT_IO_EBUSY, ch);
+ break;
+ case -ENODEV:
+ pr_err("%s: The specified target device is not valid\n",
+ ch->id);
+ fsm_event(ch->fsm, CTC_EVENT_IO_ENODEV, ch);
+ break;
+ default:
+ pr_err("An I/O operation resulted in error %04x\n",
+ rc);
+ fsm_event(ch->fsm, CTC_EVENT_IO_UNKNOWN, ch);
+ }
+}
+
+void ctcm_purge_skb_queue(struct sk_buff_head *q)
+{
+ struct sk_buff *skb;
+
+ CTCM_DBF_TEXT(TRACE, CTC_DBF_DEBUG, __func__);
+
+ while ((skb = skb_dequeue(q))) {
+ atomic_dec(&skb->users);
+ dev_kfree_skb_any(skb);
+ }
+}
+
+/**
+ * NOP action for statemachines
+ */
+static void ctcm_action_nop(fsm_instance *fi, int event, void *arg)
+{
+}
+
+/*
+ * Actions for channel - statemachines.
+ */
+
+/**
+ * Normal data has been send. Free the corresponding
+ * skb (it's in io_queue), reset dev->tbusy and
+ * revert to idle state.
+ *
+ * fi An instance of a channel statemachine.
+ * event The event, just happened.
+ * arg Generic pointer, casted from channel * upon call.
+ */
+static void chx_txdone(fsm_instance *fi, int event, void *arg)
+{
+ struct channel *ch = arg;
+ struct net_device *dev = ch->netdev;
+ struct ctcm_priv *priv = dev->ml_priv;
+ struct sk_buff *skb;
+ int first = 1;
+ int i;
+ unsigned long duration;
+ struct timespec done_stamp = current_kernel_time(); /* xtime */
+
+ CTCM_PR_DEBUG("%s(%s): %s\n", __func__, ch->id, dev->name);
+
+ duration =
+ (done_stamp.tv_sec - ch->prof.send_stamp.tv_sec) * 1000000 +
+ (done_stamp.tv_nsec - ch->prof.send_stamp.tv_nsec) / 1000;
+ if (duration > ch->prof.tx_time)
+ ch->prof.tx_time = duration;
+
+ if (ch->irb->scsw.cmd.count != 0)
+ CTCM_DBF_TEXT_(TRACE, CTC_DBF_DEBUG,
+ "%s(%s): TX not complete, remaining %d bytes",
+ CTCM_FUNTAIL, dev->name, ch->irb->scsw.cmd.count);
+ fsm_deltimer(&ch->timer);
+ while ((skb = skb_dequeue(&ch->io_queue))) {
+ priv->stats.tx_packets++;
+ priv->stats.tx_bytes += skb->len - LL_HEADER_LENGTH;
+ if (first) {
+ priv->stats.tx_bytes += 2;
+ first = 0;
+ }
+ atomic_dec(&skb->users);
+ dev_kfree_skb_irq(skb);
+ }
+ spin_lock(&ch->collect_lock);
+ clear_normalized_cda(&ch->ccw[4]);
+ if (ch->collect_len > 0) {
+ int rc;
+
+ if (ctcm_checkalloc_buffer(ch)) {
+ spin_unlock(&ch->collect_lock);
+ return;
+ }
+ ch->trans_skb->data = ch->trans_skb_data;
+ skb_reset_tail_pointer(ch->trans_skb);
+ ch->trans_skb->len = 0;
+ if (ch->prof.maxmulti < (ch->collect_len + 2))
+ ch->prof.maxmulti = ch->collect_len + 2;
+ if (ch->prof.maxcqueue < skb_queue_len(&ch->collect_queue))
+ ch->prof.maxcqueue = skb_queue_len(&ch->collect_queue);
+ *((__u16 *)skb_put(ch->trans_skb, 2)) = ch->collect_len + 2;
+ i = 0;
+ while ((skb = skb_dequeue(&ch->collect_queue))) {
+ skb_copy_from_linear_data(skb,
+ skb_put(ch->trans_skb, skb->len), skb->len);
+ priv->stats.tx_packets++;
+ priv->stats.tx_bytes += skb->len - LL_HEADER_LENGTH;
+ atomic_dec(&skb->users);
+ dev_kfree_skb_irq(skb);
+ i++;
+ }
+ ch->collect_len = 0;
+ spin_unlock(&ch->collect_lock);
+ ch->ccw[1].count = ch->trans_skb->len;
+ fsm_addtimer(&ch->timer, CTCM_TIME_5_SEC, CTC_EVENT_TIMER, ch);
+ ch->prof.send_stamp = current_kernel_time(); /* xtime */
+ rc = ccw_device_start(ch->cdev, &ch->ccw[0],
+ (unsigned long)ch, 0xff, 0);
+ ch->prof.doios_multi++;
+ if (rc != 0) {
+ priv->stats.tx_dropped += i;
+ priv->stats.tx_errors += i;
+ fsm_deltimer(&ch->timer);
+ ctcm_ccw_check_rc(ch, rc, "chained TX");
+ }
+ } else {
+ spin_unlock(&ch->collect_lock);
+ fsm_newstate(fi, CTC_STATE_TXIDLE);
+ }
+ ctcm_clear_busy_do(dev);
+}
+
+/**
+ * Initial data is sent.
+ * Notify device statemachine that we are up and
+ * running.
+ *
+ * fi An instance of a channel statemachine.
+ * event The event, just happened.
+ * arg Generic pointer, casted from channel * upon call.
+ */
+void ctcm_chx_txidle(fsm_instance *fi, int event, void *arg)
+{
+ struct channel *ch = arg;
+ struct net_device *dev = ch->netdev;
+ struct ctcm_priv *priv = dev->ml_priv;
+
+ CTCM_PR_DEBUG("%s(%s): %s\n", __func__, ch->id, dev->name);
+
+ fsm_deltimer(&ch->timer);
+ fsm_newstate(fi, CTC_STATE_TXIDLE);
+ fsm_event(priv->fsm, DEV_EVENT_TXUP, ch->netdev);
+}
+
+/**
+ * Got normal data, check for sanity, queue it up, allocate new buffer
+ * trigger bottom half, and initiate next read.
+ *
+ * fi An instance of a channel statemachine.
+ * event The event, just happened.
+ * arg Generic pointer, casted from channel * upon call.
+ */
+static void chx_rx(fsm_instance *fi, int event, void *arg)
+{
+ struct channel *ch = arg;
+ struct net_device *dev = ch->netdev;
+ struct ctcm_priv *priv = dev->ml_priv;
+ int len = ch->max_bufsize - ch->irb->scsw.cmd.count;
+ struct sk_buff *skb = ch->trans_skb;
+ __u16 block_len = *((__u16 *)skb->data);
+ int check_len;
+ int rc;
+
+ fsm_deltimer(&ch->timer);
+ if (len < 8) {
+ CTCM_DBF_TEXT_(TRACE, CTC_DBF_NOTICE,
+ "%s(%s): got packet with length %d < 8\n",
+ CTCM_FUNTAIL, dev->name, len);
+ priv->stats.rx_dropped++;
+ priv->stats.rx_length_errors++;
+ goto again;
+ }
+ if (len > ch->max_bufsize) {
+ CTCM_DBF_TEXT_(TRACE, CTC_DBF_NOTICE,
+ "%s(%s): got packet with length %d > %d\n",
+ CTCM_FUNTAIL, dev->name, len, ch->max_bufsize);
+ priv->stats.rx_dropped++;
+ priv->stats.rx_length_errors++;
+ goto again;
+ }
+
+ /*
+ * VM TCP seems to have a bug sending 2 trailing bytes of garbage.
+ */
+ switch (ch->protocol) {
+ case CTCM_PROTO_S390:
+ case CTCM_PROTO_OS390:
+ check_len = block_len + 2;
+ break;
+ default:
+ check_len = block_len;
+ break;
+ }
+ if ((len < block_len) || (len > check_len)) {
+ CTCM_DBF_TEXT_(TRACE, CTC_DBF_NOTICE,
+ "%s(%s): got block length %d != rx length %d\n",
+ CTCM_FUNTAIL, dev->name, block_len, len);
+ if (do_debug)
+ ctcmpc_dump_skb(skb, 0);
+
+ *((__u16 *)skb->data) = len;
+ priv->stats.rx_dropped++;
+ priv->stats.rx_length_errors++;
+ goto again;
+ }
+ if (block_len > 2) {
+ *((__u16 *)skb->data) = block_len - 2;
+ ctcm_unpack_skb(ch, skb);
+ }
+ again:
+ skb->data = ch->trans_skb_data;
+ skb_reset_tail_pointer(skb);
+ skb->len = 0;
+ if (ctcm_checkalloc_buffer(ch))
+ return;
+ ch->ccw[1].count = ch->max_bufsize;
+ rc = ccw_device_start(ch->cdev, &ch->ccw[0],
+ (unsigned long)ch, 0xff, 0);
+ if (rc != 0)
+ ctcm_ccw_check_rc(ch, rc, "normal RX");
+}
+
+/**
+ * Initialize connection by sending a __u16 of value 0.
+ *
+ * fi An instance of a channel statemachine.
+ * event The event, just happened.
+ * arg Generic pointer, casted from channel * upon call.
+ */
+static void chx_firstio(fsm_instance *fi, int event, void *arg)
+{
+ int rc;
+ struct channel *ch = arg;
+ int fsmstate = fsm_getstate(fi);
+
+ CTCM_DBF_TEXT_(TRACE, CTC_DBF_NOTICE,
+ "%s(%s) : %02x",
+ CTCM_FUNTAIL, ch->id, fsmstate);
+
+ ch->sense_rc = 0; /* reset unit check report control */
+ if (fsmstate == CTC_STATE_TXIDLE)
+ CTCM_DBF_TEXT_(TRACE, CTC_DBF_DEBUG,
+ "%s(%s): remote side issued READ?, init.\n",
+ CTCM_FUNTAIL, ch->id);
+ fsm_deltimer(&ch->timer);
+ if (ctcm_checkalloc_buffer(ch))
+ return;
+ if ((fsmstate == CTC_STATE_SETUPWAIT) &&
+ (ch->protocol == CTCM_PROTO_OS390)) {
+ /* OS/390 resp. z/OS */
+ if (CHANNEL_DIRECTION(ch->flags) == CTCM_READ) {
+ *((__u16 *)ch->trans_skb->data) = CTCM_INITIAL_BLOCKLEN;
+ fsm_addtimer(&ch->timer, CTCM_TIME_5_SEC,
+ CTC_EVENT_TIMER, ch);
+ chx_rxidle(fi, event, arg);
+ } else {
+ struct net_device *dev = ch->netdev;
+ struct ctcm_priv *priv = dev->ml_priv;
+ fsm_newstate(fi, CTC_STATE_TXIDLE);
+ fsm_event(priv->fsm, DEV_EVENT_TXUP, dev);
+ }
+ return;
+ }
+ /*
+ * Don't setup a timer for receiving the initial RX frame
+ * if in compatibility mode, since VM TCP delays the initial
+ * frame until it has some data to send.
+ */
+ if ((CHANNEL_DIRECTION(ch->flags) == CTCM_WRITE) ||
+ (ch->protocol != CTCM_PROTO_S390))
+ fsm_addtimer(&ch->timer, CTCM_TIME_5_SEC, CTC_EVENT_TIMER, ch);
+
+ *((__u16 *)ch->trans_skb->data) = CTCM_INITIAL_BLOCKLEN;
+ ch->ccw[1].count = 2; /* Transfer only length */
+
+ fsm_newstate(fi, (CHANNEL_DIRECTION(ch->flags) == CTCM_READ)
+ ? CTC_STATE_RXINIT : CTC_STATE_TXINIT);
+ rc = ccw_device_start(ch->cdev, &ch->ccw[0],
+ (unsigned long)ch, 0xff, 0);
+ if (rc != 0) {
+ fsm_deltimer(&ch->timer);
+ fsm_newstate(fi, CTC_STATE_SETUPWAIT);
+ ctcm_ccw_check_rc(ch, rc, "init IO");
+ }
+ /*
+ * If in compatibility mode since we don't setup a timer, we
+ * also signal RX channel up immediately. This enables us
+ * to send packets early which in turn usually triggers some
+ * reply from VM TCP which brings up the RX channel to it's
+ * final state.
+ */
+ if ((CHANNEL_DIRECTION(ch->flags) == CTCM_READ) &&
+ (ch->protocol == CTCM_PROTO_S390)) {
+ struct net_device *dev = ch->netdev;
+ struct ctcm_priv *priv = dev->ml_priv;
+ fsm_event(priv->fsm, DEV_EVENT_RXUP, dev);
+ }
+}
+
+/**
+ * Got initial data, check it. If OK,
+ * notify device statemachine that we are up and
+ * running.
+ *
+ * fi An instance of a channel statemachine.
+ * event The event, just happened.
+ * arg Generic pointer, casted from channel * upon call.
+ */
+static void chx_rxidle(fsm_instance *fi, int event, void *arg)
+{
+ struct channel *ch = arg;
+ struct net_device *dev = ch->netdev;
+ struct ctcm_priv *priv = dev->ml_priv;
+ __u16 buflen;
+ int rc;
+
+ fsm_deltimer(&ch->timer);
+ buflen = *((__u16 *)ch->trans_skb->data);
+ CTCM_PR_DEBUG("%s: %s: Initial RX count = %d\n",
+ __func__, dev->name, buflen);
+
+ if (buflen >= CTCM_INITIAL_BLOCKLEN) {
+ if (ctcm_checkalloc_buffer(ch))
+ return;
+ ch->ccw[1].count = ch->max_bufsize;
+ fsm_newstate(fi, CTC_STATE_RXIDLE);
+ rc = ccw_device_start(ch->cdev, &ch->ccw[0],
+ (unsigned long)ch, 0xff, 0);
+ if (rc != 0) {
+ fsm_newstate(fi, CTC_STATE_RXINIT);
+ ctcm_ccw_check_rc(ch, rc, "initial RX");
+ } else
+ fsm_event(priv->fsm, DEV_EVENT_RXUP, dev);
+ } else {
+ CTCM_PR_DEBUG("%s: %s: Initial RX count %d not %d\n",
+ __func__, dev->name,
+ buflen, CTCM_INITIAL_BLOCKLEN);
+ chx_firstio(fi, event, arg);
+ }
+}
+
+/**
+ * Set channel into extended mode.
+ *
+ * fi An instance of a channel statemachine.
+ * event The event, just happened.
+ * arg Generic pointer, casted from channel * upon call.
+ */
+static void ctcm_chx_setmode(fsm_instance *fi, int event, void *arg)
+{
+ struct channel *ch = arg;
+ int rc;
+ unsigned long saveflags = 0;
+ int timeout = CTCM_TIME_5_SEC;
+
+ fsm_deltimer(&ch->timer);
+ if (IS_MPC(ch)) {
+ timeout = 1500;
+ CTCM_PR_DEBUG("enter %s: cp=%i ch=0x%p id=%s\n",
+ __func__, smp_processor_id(), ch, ch->id);
+ }
+ fsm_addtimer(&ch->timer, timeout, CTC_EVENT_TIMER, ch);
+ fsm_newstate(fi, CTC_STATE_SETUPWAIT);
+ CTCM_CCW_DUMP((char *)&ch->ccw[6], sizeof(struct ccw1) * 2);
+
+ if (event == CTC_EVENT_TIMER) /* only for timer not yet locked */
+ spin_lock_irqsave(get_ccwdev_lock(ch->cdev), saveflags);
+ /* Such conditional locking is undeterministic in
+ * static view. => ignore sparse warnings here. */
+
+ rc = ccw_device_start(ch->cdev, &ch->ccw[6],
+ (unsigned long)ch, 0xff, 0);
+ if (event == CTC_EVENT_TIMER) /* see above comments */
+ spin_unlock_irqrestore(get_ccwdev_lock(ch->cdev), saveflags);
+ if (rc != 0) {
+ fsm_deltimer(&ch->timer);
+ fsm_newstate(fi, CTC_STATE_STARTWAIT);
+ ctcm_ccw_check_rc(ch, rc, "set Mode");
+ } else
+ ch->retry = 0;
+}
+
+/**
+ * Setup channel.
+ *
+ * fi An instance of a channel statemachine.
+ * event The event, just happened.
+ * arg Generic pointer, casted from channel * upon call.
+ */
+static void ctcm_chx_start(fsm_instance *fi, int event, void *arg)
+{
+ struct channel *ch = arg;
+ unsigned long saveflags;
+ int rc;
+
+ CTCM_DBF_TEXT_(SETUP, CTC_DBF_INFO, "%s(%s): %s",
+ CTCM_FUNTAIL, ch->id,
+ (CHANNEL_DIRECTION(ch->flags) == CTCM_READ) ? "RX" : "TX");
+
+ if (ch->trans_skb != NULL) {
+ clear_normalized_cda(&ch->ccw[1]);
+ dev_kfree_skb(ch->trans_skb);
+ ch->trans_skb = NULL;
+ }
+ if (CHANNEL_DIRECTION(ch->flags) == CTCM_READ) {
+ ch->ccw[1].cmd_code = CCW_CMD_READ;
+ ch->ccw[1].flags = CCW_FLAG_SLI;
+ ch->ccw[1].count = 0;
+ } else {
+ ch->ccw[1].cmd_code = CCW_CMD_WRITE;
+ ch->ccw[1].flags = CCW_FLAG_SLI | CCW_FLAG_CC;
+ ch->ccw[1].count = 0;
+ }
+ if (ctcm_checkalloc_buffer(ch)) {
+ CTCM_DBF_TEXT_(TRACE, CTC_DBF_DEBUG,
+ "%s(%s): %s trans_skb alloc delayed "
+ "until first transfer",
+ CTCM_FUNTAIL, ch->id,
+ (CHANNEL_DIRECTION(ch->flags) == CTCM_READ) ?
+ "RX" : "TX");
+ }
+ ch->ccw[0].cmd_code = CCW_CMD_PREPARE;
+ ch->ccw[0].flags = CCW_FLAG_SLI | CCW_FLAG_CC;
+ ch->ccw[0].count = 0;
+ ch->ccw[0].cda = 0;
+ ch->ccw[2].cmd_code = CCW_CMD_NOOP; /* jointed CE + DE */
+ ch->ccw[2].flags = CCW_FLAG_SLI;
+ ch->ccw[2].count = 0;
+ ch->ccw[2].cda = 0;
+ memcpy(&ch->ccw[3], &ch->ccw[0], sizeof(struct ccw1) * 3);
+ ch->ccw[4].cda = 0;
+ ch->ccw[4].flags &= ~CCW_FLAG_IDA;
+
+ fsm_newstate(fi, CTC_STATE_STARTWAIT);
+ fsm_addtimer(&ch->timer, 1000, CTC_EVENT_TIMER, ch);
+ spin_lock_irqsave(get_ccwdev_lock(ch->cdev), saveflags);
+ rc = ccw_device_halt(ch->cdev, (unsigned long)ch);
+ spin_unlock_irqrestore(get_ccwdev_lock(ch->cdev), saveflags);
+ if (rc != 0) {
+ if (rc != -EBUSY)
+ fsm_deltimer(&ch->timer);
+ ctcm_ccw_check_rc(ch, rc, "initial HaltIO");
+ }
+}
+
+/**
+ * Shutdown a channel.
+ *
+ * fi An instance of a channel statemachine.
+ * event The event, just happened.
+ * arg Generic pointer, casted from channel * upon call.
+ */
+static void ctcm_chx_haltio(fsm_instance *fi, int event, void *arg)
+{
+ struct channel *ch = arg;
+ unsigned long saveflags = 0;
+ int rc;
+ int oldstate;
+
+ fsm_deltimer(&ch->timer);
+ if (IS_MPC(ch))
+ fsm_deltimer(&ch->sweep_timer);
+
+ fsm_addtimer(&ch->timer, CTCM_TIME_5_SEC, CTC_EVENT_TIMER, ch);
+
+ if (event == CTC_EVENT_STOP) /* only for STOP not yet locked */
+ spin_lock_irqsave(get_ccwdev_lock(ch->cdev), saveflags);
+ /* Such conditional locking is undeterministic in
+ * static view. => ignore sparse warnings here. */
+ oldstate = fsm_getstate(fi);
+ fsm_newstate(fi, CTC_STATE_TERM);
+ rc = ccw_device_halt(ch->cdev, (unsigned long)ch);
+
+ if (event == CTC_EVENT_STOP)
+ spin_unlock_irqrestore(get_ccwdev_lock(ch->cdev), saveflags);
+ /* see remark above about conditional locking */
+
+ if (rc != 0 && rc != -EBUSY) {
+ fsm_deltimer(&ch->timer);
+ if (event != CTC_EVENT_STOP) {
+ fsm_newstate(fi, oldstate);
+ ctcm_ccw_check_rc(ch, rc, (char *)__func__);
+ }
+ }
+}
+
+/**
+ * Cleanup helper for chx_fail and chx_stopped
+ * cleanup channels queue and notify interface statemachine.
+ *
+ * fi An instance of a channel statemachine.
+ * state The next state (depending on caller).
+ * ch The channel to operate on.
+ */
+static void ctcm_chx_cleanup(fsm_instance *fi, int state,
+ struct channel *ch)
+{
+ struct net_device *dev = ch->netdev;
+ struct ctcm_priv *priv = dev->ml_priv;
+
+ CTCM_DBF_TEXT_(SETUP, CTC_DBF_NOTICE,
+ "%s(%s): %s[%d]\n",
+ CTCM_FUNTAIL, dev->name, ch->id, state);
+
+ fsm_deltimer(&ch->timer);
+ if (IS_MPC(ch))
+ fsm_deltimer(&ch->sweep_timer);
+
+ fsm_newstate(fi, state);
+ if (state == CTC_STATE_STOPPED && ch->trans_skb != NULL) {
+ clear_normalized_cda(&ch->ccw[1]);
+ dev_kfree_skb_any(ch->trans_skb);
+ ch->trans_skb = NULL;
+ }
+
+ ch->th_seg = 0x00;
+ ch->th_seq_num = 0x00;
+ if (CHANNEL_DIRECTION(ch->flags) == CTCM_READ) {
+ skb_queue_purge(&ch->io_queue);
+ fsm_event(priv->fsm, DEV_EVENT_RXDOWN, dev);
+ } else {
+ ctcm_purge_skb_queue(&ch->io_queue);
+ if (IS_MPC(ch))
+ ctcm_purge_skb_queue(&ch->sweep_queue);
+ spin_lock(&ch->collect_lock);
+ ctcm_purge_skb_queue(&ch->collect_queue);
+ ch->collect_len = 0;
+ spin_unlock(&ch->collect_lock);
+ fsm_event(priv->fsm, DEV_EVENT_TXDOWN, dev);
+ }
+}
+
+/**
+ * A channel has successfully been halted.
+ * Cleanup it's queue and notify interface statemachine.
+ *
+ * fi An instance of a channel statemachine.
+ * event The event, just happened.
+ * arg Generic pointer, casted from channel * upon call.
+ */
+static void ctcm_chx_stopped(fsm_instance *fi, int event, void *arg)
+{
+ ctcm_chx_cleanup(fi, CTC_STATE_STOPPED, arg);
+}
+
+/**
+ * A stop command from device statemachine arrived and we are in
+ * not operational mode. Set state to stopped.
+ *
+ * fi An instance of a channel statemachine.
+ * event The event, just happened.
+ * arg Generic pointer, casted from channel * upon call.
+ */
+static void ctcm_chx_stop(fsm_instance *fi, int event, void *arg)
+{
+ fsm_newstate(fi, CTC_STATE_STOPPED);
+}
+
+/**
+ * A machine check for no path, not operational status or gone device has
+ * happened.
+ * Cleanup queue and notify interface statemachine.
+ *
+ * fi An instance of a channel statemachine.
+ * event The event, just happened.
+ * arg Generic pointer, casted from channel * upon call.
+ */
+static void ctcm_chx_fail(fsm_instance *fi, int event, void *arg)
+{
+ ctcm_chx_cleanup(fi, CTC_STATE_NOTOP, arg);
+}
+
+/**
+ * Handle error during setup of channel.
+ *
+ * fi An instance of a channel statemachine.
+ * event The event, just happened.
+ * arg Generic pointer, casted from channel * upon call.
+ */
+static void ctcm_chx_setuperr(fsm_instance *fi, int event, void *arg)
+{
+ struct channel *ch = arg;
+ struct net_device *dev = ch->netdev;
+ struct ctcm_priv *priv = dev->ml_priv;
+
+ /*
+ * Special case: Got UC_RCRESET on setmode.
+ * This means that remote side isn't setup. In this case
+ * simply retry after some 10 secs...
+ */
+ if ((fsm_getstate(fi) == CTC_STATE_SETUPWAIT) &&
+ ((event == CTC_EVENT_UC_RCRESET) ||
+ (event == CTC_EVENT_UC_RSRESET))) {
+ fsm_newstate(fi, CTC_STATE_STARTRETRY);
+ fsm_deltimer(&ch->timer);
+ fsm_addtimer(&ch->timer, CTCM_TIME_5_SEC, CTC_EVENT_TIMER, ch);
+ if (!IS_MPC(ch) &&
+ (CHANNEL_DIRECTION(ch->flags) == CTCM_READ)) {
+ int rc = ccw_device_halt(ch->cdev, (unsigned long)ch);
+ if (rc != 0)
+ ctcm_ccw_check_rc(ch, rc,
+ "HaltIO in chx_setuperr");
+ }
+ return;
+ }
+
+ CTCM_DBF_TEXT_(ERROR, CTC_DBF_CRIT,
+ "%s(%s) : %s error during %s channel setup state=%s\n",
+ CTCM_FUNTAIL, dev->name, ctc_ch_event_names[event],
+ (CHANNEL_DIRECTION(ch->flags) == CTCM_READ) ? "RX" : "TX",
+ fsm_getstate_str(fi));
+
+ if (CHANNEL_DIRECTION(ch->flags) == CTCM_READ) {
+ fsm_newstate(fi, CTC_STATE_RXERR);
+ fsm_event(priv->fsm, DEV_EVENT_RXDOWN, dev);
+ } else {
+ fsm_newstate(fi, CTC_STATE_TXERR);
+ fsm_event(priv->fsm, DEV_EVENT_TXDOWN, dev);
+ }
+}
+
+/**
+ * Restart a channel after an error.
+ *
+ * fi An instance of a channel statemachine.
+ * event The event, just happened.
+ * arg Generic pointer, casted from channel * upon call.
+ */
+static void ctcm_chx_restart(fsm_instance *fi, int event, void *arg)
+{
+ struct channel *ch = arg;
+ struct net_device *dev = ch->netdev;
+ unsigned long saveflags = 0;
+ int oldstate;
+ int rc;
+
+ CTCM_DBF_TEXT_(TRACE, CTC_DBF_NOTICE,
+ "%s: %s[%d] of %s\n",
+ CTCM_FUNTAIL, ch->id, event, dev->name);
+
+ fsm_deltimer(&ch->timer);
+
+ fsm_addtimer(&ch->timer, CTCM_TIME_5_SEC, CTC_EVENT_TIMER, ch);
+ oldstate = fsm_getstate(fi);
+ fsm_newstate(fi, CTC_STATE_STARTWAIT);
+ if (event == CTC_EVENT_TIMER) /* only for timer not yet locked */
+ spin_lock_irqsave(get_ccwdev_lock(ch->cdev), saveflags);
+ /* Such conditional locking is a known problem for
+ * sparse because its undeterministic in static view.
+ * Warnings should be ignored here. */
+ rc = ccw_device_halt(ch->cdev, (unsigned long)ch);
+ if (event == CTC_EVENT_TIMER)
+ spin_unlock_irqrestore(get_ccwdev_lock(ch->cdev), saveflags);
+ if (rc != 0) {
+ if (rc != -EBUSY) {
+ fsm_deltimer(&ch->timer);
+ fsm_newstate(fi, oldstate);
+ }
+ ctcm_ccw_check_rc(ch, rc, "HaltIO in ctcm_chx_restart");
+ }
+}
+
+/**
+ * Handle error during RX initial handshake (exchange of
+ * 0-length block header)
+ *
+ * fi An instance of a channel statemachine.
+ * event The event, just happened.
+ * arg Generic pointer, casted from channel * upon call.
+ */
+static void ctcm_chx_rxiniterr(fsm_instance *fi, int event, void *arg)
+{
+ struct channel *ch = arg;
+ struct net_device *dev = ch->netdev;
+ struct ctcm_priv *priv = dev->ml_priv;
+
+ if (event == CTC_EVENT_TIMER) {
+ if (!IS_MPCDEV(dev))
+ /* TODO : check if MPC deletes timer somewhere */
+ fsm_deltimer(&ch->timer);
+ if (ch->retry++ < 3)
+ ctcm_chx_restart(fi, event, arg);
+ else {
+ fsm_newstate(fi, CTC_STATE_RXERR);
+ fsm_event(priv->fsm, DEV_EVENT_RXDOWN, dev);
+ }
+ } else {
+ CTCM_DBF_TEXT_(ERROR, CTC_DBF_ERROR,
+ "%s(%s): %s in %s", CTCM_FUNTAIL, ch->id,
+ ctc_ch_event_names[event], fsm_getstate_str(fi));
+
+ dev_warn(&dev->dev,
+ "Initialization failed with RX/TX init handshake "
+ "error %s\n", ctc_ch_event_names[event]);
+ }
+}
+
+/**
+ * Notify device statemachine if we gave up initialization
+ * of RX channel.
+ *
+ * fi An instance of a channel statemachine.
+ * event The event, just happened.
+ * arg Generic pointer, casted from channel * upon call.
+ */
+static void ctcm_chx_rxinitfail(fsm_instance *fi, int event, void *arg)
+{
+ struct channel *ch = arg;
+ struct net_device *dev = ch->netdev;
+ struct ctcm_priv *priv = dev->ml_priv;
+
+ CTCM_DBF_TEXT_(ERROR, CTC_DBF_ERROR,
+ "%s(%s): RX %s busy, init. fail",
+ CTCM_FUNTAIL, dev->name, ch->id);
+ fsm_newstate(fi, CTC_STATE_RXERR);
+ fsm_event(priv->fsm, DEV_EVENT_RXDOWN, dev);
+}
+
+/**
+ * Handle RX Unit check remote reset (remote disconnected)
+ *
+ * fi An instance of a channel statemachine.
+ * event The event, just happened.
+ * arg Generic pointer, casted from channel * upon call.
+ */
+static void ctcm_chx_rxdisc(fsm_instance *fi, int event, void *arg)
+{
+ struct channel *ch = arg;
+ struct channel *ch2;
+ struct net_device *dev = ch->netdev;
+ struct ctcm_priv *priv = dev->ml_priv;
+
+ CTCM_DBF_TEXT_(TRACE, CTC_DBF_NOTICE,
+ "%s: %s: remote disconnect - re-init ...",
+ CTCM_FUNTAIL, dev->name);
+ fsm_deltimer(&ch->timer);
+ /*
+ * Notify device statemachine
+ */
+ fsm_event(priv->fsm, DEV_EVENT_RXDOWN, dev);
+ fsm_event(priv->fsm, DEV_EVENT_TXDOWN, dev);
+
+ fsm_newstate(fi, CTC_STATE_DTERM);
+ ch2 = priv->channel[CTCM_WRITE];
+ fsm_newstate(ch2->fsm, CTC_STATE_DTERM);
+
+ ccw_device_halt(ch->cdev, (unsigned long)ch);
+ ccw_device_halt(ch2->cdev, (unsigned long)ch2);
+}
+
+/**
+ * Handle error during TX channel initialization.
+ *
+ * fi An instance of a channel statemachine.
+ * event The event, just happened.
+ * arg Generic pointer, casted from channel * upon call.
+ */
+static void ctcm_chx_txiniterr(fsm_instance *fi, int event, void *arg)
+{
+ struct channel *ch = arg;
+ struct net_device *dev = ch->netdev;
+ struct ctcm_priv *priv = dev->ml_priv;
+
+ if (event == CTC_EVENT_TIMER) {
+ fsm_deltimer(&ch->timer);
+ if (ch->retry++ < 3)
+ ctcm_chx_restart(fi, event, arg);
+ else {
+ fsm_newstate(fi, CTC_STATE_TXERR);
+ fsm_event(priv->fsm, DEV_EVENT_TXDOWN, dev);
+ }
+ } else {
+ CTCM_DBF_TEXT_(ERROR, CTC_DBF_ERROR,
+ "%s(%s): %s in %s", CTCM_FUNTAIL, ch->id,
+ ctc_ch_event_names[event], fsm_getstate_str(fi));
+
+ dev_warn(&dev->dev,
+ "Initialization failed with RX/TX init handshake "
+ "error %s\n", ctc_ch_event_names[event]);
+ }
+}
+
+/**
+ * Handle TX timeout by retrying operation.
+ *
+ * fi An instance of a channel statemachine.
+ * event The event, just happened.
+ * arg Generic pointer, casted from channel * upon call.
+ */
+static void ctcm_chx_txretry(fsm_instance *fi, int event, void *arg)
+{
+ struct channel *ch = arg;
+ struct net_device *dev = ch->netdev;
+ struct ctcm_priv *priv = dev->ml_priv;
+ struct sk_buff *skb;
+
+ CTCM_PR_DEBUG("Enter: %s: cp=%i ch=0x%p id=%s\n",
+ __func__, smp_processor_id(), ch, ch->id);
+
+ fsm_deltimer(&ch->timer);
+ if (ch->retry++ > 3) {
+ struct mpc_group *gptr = priv->mpcg;
+ CTCM_DBF_TEXT_(TRACE, CTC_DBF_INFO,
+ "%s: %s: retries exceeded",
+ CTCM_FUNTAIL, ch->id);
+ fsm_event(priv->fsm, DEV_EVENT_TXDOWN, dev);
+ /* call restart if not MPC or if MPC and mpcg fsm is ready.
+ use gptr as mpc indicator */
+ if (!(gptr && (fsm_getstate(gptr->fsm) != MPCG_STATE_READY)))
+ ctcm_chx_restart(fi, event, arg);
+ goto done;
+ }
+
+ CTCM_DBF_TEXT_(TRACE, CTC_DBF_DEBUG,
+ "%s : %s: retry %d",
+ CTCM_FUNTAIL, ch->id, ch->retry);
+ skb = skb_peek(&ch->io_queue);
+ if (skb) {
+ int rc = 0;
+ unsigned long saveflags = 0;
+ clear_normalized_cda(&ch->ccw[4]);
+ ch->ccw[4].count = skb->len;
+ if (set_normalized_cda(&ch->ccw[4], skb->data)) {
+ CTCM_DBF_TEXT_(TRACE, CTC_DBF_INFO,
+ "%s: %s: IDAL alloc failed",
+ CTCM_FUNTAIL, ch->id);
+ fsm_event(priv->fsm, DEV_EVENT_TXDOWN, dev);
+ ctcm_chx_restart(fi, event, arg);
+ goto done;
+ }
+ fsm_addtimer(&ch->timer, 1000, CTC_EVENT_TIMER, ch);
+ if (event == CTC_EVENT_TIMER) /* for TIMER not yet locked */
+ spin_lock_irqsave(get_ccwdev_lock(ch->cdev), saveflags);
+ /* Such conditional locking is a known problem for
+ * sparse because its undeterministic in static view.
+ * Warnings should be ignored here. */
+ if (do_debug_ccw)
+ ctcmpc_dumpit((char *)&ch->ccw[3],
+ sizeof(struct ccw1) * 3);
+
+ rc = ccw_device_start(ch->cdev, &ch->ccw[3],
+ (unsigned long)ch, 0xff, 0);
+ if (event == CTC_EVENT_TIMER)
+ spin_unlock_irqrestore(get_ccwdev_lock(ch->cdev),
+ saveflags);
+ if (rc != 0) {
+ fsm_deltimer(&ch->timer);
+ ctcm_ccw_check_rc(ch, rc, "TX in chx_txretry");
+ ctcm_purge_skb_queue(&ch->io_queue);
+ }
+ }
+done:
+ return;
+}
+
+/**
+ * Handle fatal errors during an I/O command.
+ *
+ * fi An instance of a channel statemachine.
+ * event The event, just happened.
+ * arg Generic pointer, casted from channel * upon call.
+ */
+static void ctcm_chx_iofatal(fsm_instance *fi, int event, void *arg)
+{
+ struct channel *ch = arg;
+ struct net_device *dev = ch->netdev;
+ struct ctcm_priv *priv = dev->ml_priv;
+ int rd = CHANNEL_DIRECTION(ch->flags);
+
+ fsm_deltimer(&ch->timer);
+ CTCM_DBF_TEXT_(ERROR, CTC_DBF_ERROR,
+ "%s: %s: %s unrecoverable channel error",
+ CTCM_FUNTAIL, ch->id, rd == CTCM_READ ? "RX" : "TX");
+
+ if (IS_MPC(ch)) {
+ priv->stats.tx_dropped++;
+ priv->stats.tx_errors++;
+ }
+ if (rd == CTCM_READ) {
+ fsm_newstate(fi, CTC_STATE_RXERR);
+ fsm_event(priv->fsm, DEV_EVENT_RXDOWN, dev);
+ } else {
+ fsm_newstate(fi, CTC_STATE_TXERR);
+ fsm_event(priv->fsm, DEV_EVENT_TXDOWN, dev);
+ }
+}
+
+/*
+ * The ctcm statemachine for a channel.
+ */
+const fsm_node ch_fsm[] = {
+ { CTC_STATE_STOPPED, CTC_EVENT_STOP, ctcm_action_nop },
+ { CTC_STATE_STOPPED, CTC_EVENT_START, ctcm_chx_start },
+ { CTC_STATE_STOPPED, CTC_EVENT_FINSTAT, ctcm_action_nop },
+ { CTC_STATE_STOPPED, CTC_EVENT_MC_FAIL, ctcm_action_nop },
+
+ { CTC_STATE_NOTOP, CTC_EVENT_STOP, ctcm_chx_stop },
+ { CTC_STATE_NOTOP, CTC_EVENT_START, ctcm_action_nop },
+ { CTC_STATE_NOTOP, CTC_EVENT_FINSTAT, ctcm_action_nop },
+ { CTC_STATE_NOTOP, CTC_EVENT_MC_FAIL, ctcm_action_nop },
+ { CTC_STATE_NOTOP, CTC_EVENT_MC_GOOD, ctcm_chx_start },
+
+ { CTC_STATE_STARTWAIT, CTC_EVENT_STOP, ctcm_chx_haltio },
+ { CTC_STATE_STARTWAIT, CTC_EVENT_START, ctcm_action_nop },
+ { CTC_STATE_STARTWAIT, CTC_EVENT_FINSTAT, ctcm_chx_setmode },
+ { CTC_STATE_STARTWAIT, CTC_EVENT_TIMER, ctcm_chx_setuperr },
+ { CTC_STATE_STARTWAIT, CTC_EVENT_IO_ENODEV, ctcm_chx_iofatal },
+ { CTC_STATE_STARTWAIT, CTC_EVENT_MC_FAIL, ctcm_chx_fail },
+
+ { CTC_STATE_STARTRETRY, CTC_EVENT_STOP, ctcm_chx_haltio },
+ { CTC_STATE_STARTRETRY, CTC_EVENT_TIMER, ctcm_chx_setmode },
+ { CTC_STATE_STARTRETRY, CTC_EVENT_FINSTAT, ctcm_action_nop },
+ { CTC_STATE_STARTRETRY, CTC_EVENT_MC_FAIL, ctcm_chx_fail },
+
+ { CTC_STATE_SETUPWAIT, CTC_EVENT_STOP, ctcm_chx_haltio },
+ { CTC_STATE_SETUPWAIT, CTC_EVENT_START, ctcm_action_nop },
+ { CTC_STATE_SETUPWAIT, CTC_EVENT_FINSTAT, chx_firstio },
+ { CTC_STATE_SETUPWAIT, CTC_EVENT_UC_RCRESET, ctcm_chx_setuperr },
+ { CTC_STATE_SETUPWAIT, CTC_EVENT_UC_RSRESET, ctcm_chx_setuperr },
+ { CTC_STATE_SETUPWAIT, CTC_EVENT_TIMER, ctcm_chx_setmode },
+ { CTC_STATE_SETUPWAIT, CTC_EVENT_IO_ENODEV, ctcm_chx_iofatal },
+ { CTC_STATE_SETUPWAIT, CTC_EVENT_MC_FAIL, ctcm_chx_fail },
+
+ { CTC_STATE_RXINIT, CTC_EVENT_STOP, ctcm_chx_haltio },
+ { CTC_STATE_RXINIT, CTC_EVENT_START, ctcm_action_nop },
+ { CTC_STATE_RXINIT, CTC_EVENT_FINSTAT, chx_rxidle },
+ { CTC_STATE_RXINIT, CTC_EVENT_UC_RCRESET, ctcm_chx_rxiniterr },
+ { CTC_STATE_RXINIT, CTC_EVENT_UC_RSRESET, ctcm_chx_rxiniterr },
+ { CTC_STATE_RXINIT, CTC_EVENT_TIMER, ctcm_chx_rxiniterr },
+ { CTC_STATE_RXINIT, CTC_EVENT_ATTNBUSY, ctcm_chx_rxinitfail },
+ { CTC_STATE_RXINIT, CTC_EVENT_IO_ENODEV, ctcm_chx_iofatal },
+ { CTC_STATE_RXINIT, CTC_EVENT_UC_ZERO, chx_firstio },
+ { CTC_STATE_RXINIT, CTC_EVENT_MC_FAIL, ctcm_chx_fail },
+
+ { CTC_STATE_RXIDLE, CTC_EVENT_STOP, ctcm_chx_haltio },
+ { CTC_STATE_RXIDLE, CTC_EVENT_START, ctcm_action_nop },
+ { CTC_STATE_RXIDLE, CTC_EVENT_FINSTAT, chx_rx },
+ { CTC_STATE_RXIDLE, CTC_EVENT_UC_RCRESET, ctcm_chx_rxdisc },
+ { CTC_STATE_RXIDLE, CTC_EVENT_IO_ENODEV, ctcm_chx_iofatal },
+ { CTC_STATE_RXIDLE, CTC_EVENT_MC_FAIL, ctcm_chx_fail },
+ { CTC_STATE_RXIDLE, CTC_EVENT_UC_ZERO, chx_rx },
+
+ { CTC_STATE_TXINIT, CTC_EVENT_STOP, ctcm_chx_haltio },
+ { CTC_STATE_TXINIT, CTC_EVENT_START, ctcm_action_nop },
+ { CTC_STATE_TXINIT, CTC_EVENT_FINSTAT, ctcm_chx_txidle },
+ { CTC_STATE_TXINIT, CTC_EVENT_UC_RCRESET, ctcm_chx_txiniterr },
+ { CTC_STATE_TXINIT, CTC_EVENT_UC_RSRESET, ctcm_chx_txiniterr },
+ { CTC_STATE_TXINIT, CTC_EVENT_TIMER, ctcm_chx_txiniterr },
+ { CTC_STATE_TXINIT, CTC_EVENT_IO_ENODEV, ctcm_chx_iofatal },
+ { CTC_STATE_TXINIT, CTC_EVENT_MC_FAIL, ctcm_chx_fail },
+
+ { CTC_STATE_TXIDLE, CTC_EVENT_STOP, ctcm_chx_haltio },
+ { CTC_STATE_TXIDLE, CTC_EVENT_START, ctcm_action_nop },
+ { CTC_STATE_TXIDLE, CTC_EVENT_FINSTAT, chx_firstio },
+ { CTC_STATE_TXIDLE, CTC_EVENT_UC_RCRESET, ctcm_action_nop },
+ { CTC_STATE_TXIDLE, CTC_EVENT_UC_RSRESET, ctcm_action_nop },
+ { CTC_STATE_TXIDLE, CTC_EVENT_IO_ENODEV, ctcm_chx_iofatal },
+ { CTC_STATE_TXIDLE, CTC_EVENT_MC_FAIL, ctcm_chx_fail },
+
+ { CTC_STATE_TERM, CTC_EVENT_STOP, ctcm_action_nop },
+ { CTC_STATE_TERM, CTC_EVENT_START, ctcm_chx_restart },
+ { CTC_STATE_TERM, CTC_EVENT_FINSTAT, ctcm_chx_stopped },
+ { CTC_STATE_TERM, CTC_EVENT_UC_RCRESET, ctcm_action_nop },
+ { CTC_STATE_TERM, CTC_EVENT_UC_RSRESET, ctcm_action_nop },
+ { CTC_STATE_TERM, CTC_EVENT_MC_FAIL, ctcm_chx_fail },
+
+ { CTC_STATE_DTERM, CTC_EVENT_STOP, ctcm_chx_haltio },
+ { CTC_STATE_DTERM, CTC_EVENT_START, ctcm_chx_restart },
+ { CTC_STATE_DTERM, CTC_EVENT_FINSTAT, ctcm_chx_setmode },
+ { CTC_STATE_DTERM, CTC_EVENT_UC_RCRESET, ctcm_action_nop },
+ { CTC_STATE_DTERM, CTC_EVENT_UC_RSRESET, ctcm_action_nop },
+ { CTC_STATE_DTERM, CTC_EVENT_MC_FAIL, ctcm_chx_fail },
+
+ { CTC_STATE_TX, CTC_EVENT_STOP, ctcm_chx_haltio },
+ { CTC_STATE_TX, CTC_EVENT_START, ctcm_action_nop },
+ { CTC_STATE_TX, CTC_EVENT_FINSTAT, chx_txdone },
+ { CTC_STATE_TX, CTC_EVENT_UC_RCRESET, ctcm_chx_txretry },
+ { CTC_STATE_TX, CTC_EVENT_UC_RSRESET, ctcm_chx_txretry },
+ { CTC_STATE_TX, CTC_EVENT_TIMER, ctcm_chx_txretry },
+ { CTC_STATE_TX, CTC_EVENT_IO_ENODEV, ctcm_chx_iofatal },
+ { CTC_STATE_TX, CTC_EVENT_MC_FAIL, ctcm_chx_fail },
+
+ { CTC_STATE_RXERR, CTC_EVENT_STOP, ctcm_chx_haltio },
+ { CTC_STATE_TXERR, CTC_EVENT_STOP, ctcm_chx_haltio },
+ { CTC_STATE_TXERR, CTC_EVENT_MC_FAIL, ctcm_chx_fail },
+ { CTC_STATE_RXERR, CTC_EVENT_MC_FAIL, ctcm_chx_fail },
+};
+
+int ch_fsm_len = ARRAY_SIZE(ch_fsm);
+
+/*
+ * MPC actions for mpc channel statemachine
+ * handling of MPC protocol requires extra
+ * statemachine and actions which are prefixed ctcmpc_ .
+ * The ctc_ch_states and ctc_ch_state_names,
+ * ctc_ch_events and ctc_ch_event_names share the ctcm definitions
+ * which are expanded by some elements.
+ */
+
+/*
+ * Actions for mpc channel statemachine.
+ */
+
+/**
+ * Normal data has been send. Free the corresponding
+ * skb (it's in io_queue), reset dev->tbusy and
+ * revert to idle state.
+ *
+ * fi An instance of a channel statemachine.
+ * event The event, just happened.
+ * arg Generic pointer, casted from channel * upon call.
+ */
+static void ctcmpc_chx_txdone(fsm_instance *fi, int event, void *arg)
+{
+ struct channel *ch = arg;
+ struct net_device *dev = ch->netdev;
+ struct ctcm_priv *priv = dev->ml_priv;
+ struct mpc_group *grp = priv->mpcg;
+ struct sk_buff *skb;
+ int first = 1;
+ int i;
+ __u32 data_space;
+ unsigned long duration;
+ struct sk_buff *peekskb;
+ int rc;
+ struct th_header *header;
+ struct pdu *p_header;
+ struct timespec done_stamp = current_kernel_time(); /* xtime */
+
+ CTCM_PR_DEBUG("Enter %s: %s cp:%i\n",
+ __func__, dev->name, smp_processor_id());
+
+ duration =
+ (done_stamp.tv_sec - ch->prof.send_stamp.tv_sec) * 1000000 +
+ (done_stamp.tv_nsec - ch->prof.send_stamp.tv_nsec) / 1000;
+ if (duration > ch->prof.tx_time)
+ ch->prof.tx_time = duration;
+
+ if (ch->irb->scsw.cmd.count != 0)
+ CTCM_DBF_TEXT_(MPC_TRACE, CTC_DBF_DEBUG,
+ "%s(%s): TX not complete, remaining %d bytes",
+ CTCM_FUNTAIL, dev->name, ch->irb->scsw.cmd.count);
+ fsm_deltimer(&ch->timer);
+ while ((skb = skb_dequeue(&ch->io_queue))) {
+ priv->stats.tx_packets++;
+ priv->stats.tx_bytes += skb->len - TH_HEADER_LENGTH;
+ if (first) {
+ priv->stats.tx_bytes += 2;
+ first = 0;
+ }
+ atomic_dec(&skb->users);
+ dev_kfree_skb_irq(skb);
+ }
+ spin_lock(&ch->collect_lock);
+ clear_normalized_cda(&ch->ccw[4]);
+ if ((ch->collect_len <= 0) || (grp->in_sweep != 0)) {
+ spin_unlock(&ch->collect_lock);
+ fsm_newstate(fi, CTC_STATE_TXIDLE);
+ goto done;
+ }
+
+ if (ctcm_checkalloc_buffer(ch)) {
+ spin_unlock(&ch->collect_lock);
+ goto done;
+ }
+ ch->trans_skb->data = ch->trans_skb_data;
+ skb_reset_tail_pointer(ch->trans_skb);
+ ch->trans_skb->len = 0;
+ if (ch->prof.maxmulti < (ch->collect_len + TH_HEADER_LENGTH))
+ ch->prof.maxmulti = ch->collect_len + TH_HEADER_LENGTH;
+ if (ch->prof.maxcqueue < skb_queue_len(&ch->collect_queue))
+ ch->prof.maxcqueue = skb_queue_len(&ch->collect_queue);
+ i = 0;
+ p_header = NULL;
+ data_space = grp->group_max_buflen - TH_HEADER_LENGTH;
+
+ CTCM_PR_DBGDATA("%s: building trans_skb from collect_q"
+ " data_space:%04x\n",
+ __func__, data_space);
+
+ while ((skb = skb_dequeue(&ch->collect_queue))) {
+ memcpy(skb_put(ch->trans_skb, skb->len), skb->data, skb->len);
+ p_header = (struct pdu *)
+ (skb_tail_pointer(ch->trans_skb) - skb->len);
+ p_header->pdu_flag = 0x00;
+ if (skb->protocol == ntohs(ETH_P_SNAP))
+ p_header->pdu_flag |= 0x60;
+ else
+ p_header->pdu_flag |= 0x20;
+
+ CTCM_PR_DBGDATA("%s: trans_skb len:%04x \n",
+ __func__, ch->trans_skb->len);
+ CTCM_PR_DBGDATA("%s: pdu header and data for up"
+ " to 32 bytes sent to vtam\n", __func__);
+ CTCM_D3_DUMP((char *)p_header, min_t(int, skb->len, 32));
+
+ ch->collect_len -= skb->len;
+ data_space -= skb->len;
+ priv->stats.tx_packets++;
+ priv->stats.tx_bytes += skb->len;
+ atomic_dec(&skb->users);
+ dev_kfree_skb_any(skb);
+ peekskb = skb_peek(&ch->collect_queue);
+ if (peekskb->len > data_space)
+ break;
+ i++;
+ }
+ /* p_header points to the last one we handled */
+ if (p_header)
+ p_header->pdu_flag |= PDU_LAST; /*Say it's the last one*/
+ header = kzalloc(TH_HEADER_LENGTH, gfp_type());
+ if (!header) {
+ spin_unlock(&ch->collect_lock);
+ fsm_event(priv->mpcg->fsm, MPCG_EVENT_INOP, dev);
+ goto done;
+ }
+ header->th_ch_flag = TH_HAS_PDU; /* Normal data */
+ ch->th_seq_num++;
+ header->th_seq_num = ch->th_seq_num;
+
+ CTCM_PR_DBGDATA("%s: ToVTAM_th_seq= %08x\n" ,
+ __func__, ch->th_seq_num);
+
+ memcpy(skb_push(ch->trans_skb, TH_HEADER_LENGTH), header,
+ TH_HEADER_LENGTH); /* put the TH on the packet */
+
+ kfree(header);
+
+ CTCM_PR_DBGDATA("%s: trans_skb len:%04x \n",
+ __func__, ch->trans_skb->len);
+ CTCM_PR_DBGDATA("%s: up-to-50 bytes of trans_skb "
+ "data to vtam from collect_q\n", __func__);
+ CTCM_D3_DUMP((char *)ch->trans_skb->data,
+ min_t(int, ch->trans_skb->len, 50));
+
+ spin_unlock(&ch->collect_lock);
+ clear_normalized_cda(&ch->ccw[1]);
+
+ CTCM_PR_DBGDATA("ccwcda=0x%p data=0x%p\n",
+ (void *)(unsigned long)ch->ccw[1].cda,
+ ch->trans_skb->data);
+ ch->ccw[1].count = ch->max_bufsize;
+
+ if (set_normalized_cda(&ch->ccw[1], ch->trans_skb->data)) {
+ dev_kfree_skb_any(ch->trans_skb);
+ ch->trans_skb = NULL;
+ CTCM_DBF_TEXT_(MPC_TRACE, CTC_DBF_ERROR,
+ "%s: %s: IDAL alloc failed",
+ CTCM_FUNTAIL, ch->id);
+ fsm_event(priv->mpcg->fsm, MPCG_EVENT_INOP, dev);
+ return;
+ }
+
+ CTCM_PR_DBGDATA("ccwcda=0x%p data=0x%p\n",
+ (void *)(unsigned long)ch->ccw[1].cda,
+ ch->trans_skb->data);
+
+ ch->ccw[1].count = ch->trans_skb->len;
+ fsm_addtimer(&ch->timer, CTCM_TIME_5_SEC, CTC_EVENT_TIMER, ch);
+ ch->prof.send_stamp = current_kernel_time(); /* xtime */
+ if (do_debug_ccw)
+ ctcmpc_dumpit((char *)&ch->ccw[0], sizeof(struct ccw1) * 3);
+ rc = ccw_device_start(ch->cdev, &ch->ccw[0],
+ (unsigned long)ch, 0xff, 0);
+ ch->prof.doios_multi++;
+ if (rc != 0) {
+ priv->stats.tx_dropped += i;
+ priv->stats.tx_errors += i;
+ fsm_deltimer(&ch->timer);
+ ctcm_ccw_check_rc(ch, rc, "chained TX");
+ }
+done:
+ ctcm_clear_busy(dev);
+ return;
+}
+
+/**
+ * Got normal data, check for sanity, queue it up, allocate new buffer
+ * trigger bottom half, and initiate next read.
+ *
+ * fi An instance of a channel statemachine.
+ * event The event, just happened.
+ * arg Generic pointer, casted from channel * upon call.
+ */
+static void ctcmpc_chx_rx(fsm_instance *fi, int event, void *arg)
+{
+ struct channel *ch = arg;
+ struct net_device *dev = ch->netdev;
+ struct ctcm_priv *priv = dev->ml_priv;
+ struct mpc_group *grp = priv->mpcg;
+ struct sk_buff *skb = ch->trans_skb;
+ struct sk_buff *new_skb;
+ unsigned long saveflags = 0; /* avoids compiler warning */
+ int len = ch->max_bufsize - ch->irb->scsw.cmd.count;
+
+ CTCM_PR_DEBUG("%s: %s: cp:%i %s maxbuf : %04x, len: %04x\n",
+ CTCM_FUNTAIL, dev->name, smp_processor_id(),
+ ch->id, ch->max_bufsize, len);
+ fsm_deltimer(&ch->timer);
+
+ if (skb == NULL) {
+ CTCM_DBF_TEXT_(MPC_ERROR, CTC_DBF_ERROR,
+ "%s(%s): TRANS_SKB = NULL",
+ CTCM_FUNTAIL, dev->name);
+ goto again;
+ }
+
+ if (len < TH_HEADER_LENGTH) {
+ CTCM_DBF_TEXT_(MPC_ERROR, CTC_DBF_ERROR,
+ "%s(%s): packet length %d to short",
+ CTCM_FUNTAIL, dev->name, len);
+ priv->stats.rx_dropped++;
+ priv->stats.rx_length_errors++;
+ } else {
+ /* must have valid th header or game over */
+ __u32 block_len = len;
+ len = TH_HEADER_LENGTH + XID2_LENGTH + 4;
+ new_skb = __dev_alloc_skb(ch->max_bufsize, GFP_ATOMIC);
+
+ if (new_skb == NULL) {
+ CTCM_DBF_TEXT_(MPC_ERROR, CTC_DBF_ERROR,
+ "%s(%d): skb allocation failed",
+ CTCM_FUNTAIL, dev->name);
+ fsm_event(priv->mpcg->fsm, MPCG_EVENT_INOP, dev);
+ goto again;
+ }
+ switch (fsm_getstate(grp->fsm)) {
+ case MPCG_STATE_RESET:
+ case MPCG_STATE_INOP:
+ dev_kfree_skb_any(new_skb);
+ break;
+ case MPCG_STATE_FLOWC:
+ case MPCG_STATE_READY:
+ memcpy(skb_put(new_skb, block_len),
+ skb->data, block_len);
+ skb_queue_tail(&ch->io_queue, new_skb);
+ tasklet_schedule(&ch->ch_tasklet);
+ break;
+ default:
+ memcpy(skb_put(new_skb, len), skb->data, len);
+ skb_queue_tail(&ch->io_queue, new_skb);
+ tasklet_hi_schedule(&ch->ch_tasklet);
+ break;
+ }
+ }
+
+again:
+ switch (fsm_getstate(grp->fsm)) {
+ int rc, dolock;
+ case MPCG_STATE_FLOWC:
+ case MPCG_STATE_READY:
+ if (ctcm_checkalloc_buffer(ch))
+ break;
+ ch->trans_skb->data = ch->trans_skb_data;
+ skb_reset_tail_pointer(ch->trans_skb);
+ ch->trans_skb->len = 0;
+ ch->ccw[1].count = ch->max_bufsize;
+ if (do_debug_ccw)
+ ctcmpc_dumpit((char *)&ch->ccw[0],
+ sizeof(struct ccw1) * 3);
+ dolock = !in_irq();
+ if (dolock)
+ spin_lock_irqsave(
+ get_ccwdev_lock(ch->cdev), saveflags);
+ rc = ccw_device_start(ch->cdev, &ch->ccw[0],
+ (unsigned long)ch, 0xff, 0);
+ if (dolock) /* see remark about conditional locking */
+ spin_unlock_irqrestore(
+ get_ccwdev_lock(ch->cdev), saveflags);
+ if (rc != 0)
+ ctcm_ccw_check_rc(ch, rc, "normal RX");
+ default:
+ break;
+ }
+
+ CTCM_PR_DEBUG("Exit %s: %s, ch=0x%p, id=%s\n",
+ __func__, dev->name, ch, ch->id);
+
+}
+
+/**
+ * Initialize connection by sending a __u16 of value 0.
+ *
+ * fi An instance of a channel statemachine.
+ * event The event, just happened.
+ * arg Generic pointer, casted from channel * upon call.
+ */
+static void ctcmpc_chx_firstio(fsm_instance *fi, int event, void *arg)
+{
+ struct channel *ch = arg;
+ struct net_device *dev = ch->netdev;
+ struct ctcm_priv *priv = dev->ml_priv;
+ struct mpc_group *gptr = priv->mpcg;
+
+ CTCM_PR_DEBUG("Enter %s: id=%s, ch=0x%p\n",
+ __func__, ch->id, ch);
+
+ CTCM_DBF_TEXT_(MPC_TRACE, CTC_DBF_INFO,
+ "%s: %s: chstate:%i, grpstate:%i, prot:%i\n",
+ CTCM_FUNTAIL, ch->id, fsm_getstate(fi),
+ fsm_getstate(gptr->fsm), ch->protocol);
+
+ if (fsm_getstate(fi) == CTC_STATE_TXIDLE)
+ MPC_DBF_DEV_NAME(TRACE, dev, "remote side issued READ? ");
+
+ fsm_deltimer(&ch->timer);
+ if (ctcm_checkalloc_buffer(ch))
+ goto done;
+
+ switch (fsm_getstate(fi)) {
+ case CTC_STATE_STARTRETRY:
+ case CTC_STATE_SETUPWAIT:
+ if (CHANNEL_DIRECTION(ch->flags) == CTCM_READ) {
+ ctcmpc_chx_rxidle(fi, event, arg);
+ } else {
+ fsm_newstate(fi, CTC_STATE_TXIDLE);
+ fsm_event(priv->fsm, DEV_EVENT_TXUP, dev);
+ }
+ goto done;
+ default:
+ break;
+ }
+
+ fsm_newstate(fi, (CHANNEL_DIRECTION(ch->flags) == CTCM_READ)
+ ? CTC_STATE_RXINIT : CTC_STATE_TXINIT);
+
+done:
+ CTCM_PR_DEBUG("Exit %s: id=%s, ch=0x%p\n",
+ __func__, ch->id, ch);
+ return;
+}
+
+/**
+ * Got initial data, check it. If OK,
+ * notify device statemachine that we are up and
+ * running.
+ *
+ * fi An instance of a channel statemachine.
+ * event The event, just happened.
+ * arg Generic pointer, casted from channel * upon call.
+ */
+void ctcmpc_chx_rxidle(fsm_instance *fi, int event, void *arg)
+{
+ struct channel *ch = arg;
+ struct net_device *dev = ch->netdev;
+ struct ctcm_priv *priv = dev->ml_priv;
+ struct mpc_group *grp = priv->mpcg;
+ int rc;
+ unsigned long saveflags = 0; /* avoids compiler warning */
+
+ fsm_deltimer(&ch->timer);
+ CTCM_PR_DEBUG("%s: %s: %s: cp:%i, chstate:%i grpstate:%i\n",
+ __func__, ch->id, dev->name, smp_processor_id(),
+ fsm_getstate(fi), fsm_getstate(grp->fsm));
+
+ fsm_newstate(fi, CTC_STATE_RXIDLE);
+ /* XID processing complete */
+
+ switch (fsm_getstate(grp->fsm)) {
+ case MPCG_STATE_FLOWC:
+ case MPCG_STATE_READY:
+ if (ctcm_checkalloc_buffer(ch))
+ goto done;
+ ch->trans_skb->data = ch->trans_skb_data;
+ skb_reset_tail_pointer(ch->trans_skb);
+ ch->trans_skb->len = 0;
+ ch->ccw[1].count = ch->max_bufsize;
+ CTCM_CCW_DUMP((char *)&ch->ccw[0], sizeof(struct ccw1) * 3);
+ if (event == CTC_EVENT_START)
+ /* see remark about conditional locking */
+ spin_lock_irqsave(get_ccwdev_lock(ch->cdev), saveflags);
+ rc = ccw_device_start(ch->cdev, &ch->ccw[0],
+ (unsigned long)ch, 0xff, 0);
+ if (event == CTC_EVENT_START)
+ spin_unlock_irqrestore(
+ get_ccwdev_lock(ch->cdev), saveflags);
+ if (rc != 0) {
+ fsm_newstate(fi, CTC_STATE_RXINIT);
+ ctcm_ccw_check_rc(ch, rc, "initial RX");
+ goto done;
+ }
+ break;
+ default:
+ break;
+ }
+
+ fsm_event(priv->fsm, DEV_EVENT_RXUP, dev);
+done:
+ return;
+}
+
+/*
+ * ctcmpc channel FSM action
+ * called from several points in ctcmpc_ch_fsm
+ * ctcmpc only
+ */
+static void ctcmpc_chx_attn(fsm_instance *fsm, int event, void *arg)
+{
+ struct channel *ch = arg;
+ struct net_device *dev = ch->netdev;
+ struct ctcm_priv *priv = dev->ml_priv;
+ struct mpc_group *grp = priv->mpcg;
+
+ CTCM_PR_DEBUG("%s(%s): %s(ch=0x%p), cp=%i, ChStat:%s, GrpStat:%s\n",
+ __func__, dev->name, ch->id, ch, smp_processor_id(),
+ fsm_getstate_str(ch->fsm), fsm_getstate_str(grp->fsm));
+
+ switch (fsm_getstate(grp->fsm)) {
+ case MPCG_STATE_XID2INITW:
+ /* ok..start yside xid exchanges */
+ if (!ch->in_mpcgroup)
+ break;
+ if (fsm_getstate(ch->fsm) == CH_XID0_PENDING) {
+ fsm_deltimer(&grp->timer);
+ fsm_addtimer(&grp->timer,
+ MPC_XID_TIMEOUT_VALUE,
+ MPCG_EVENT_TIMER, dev);
+ fsm_event(grp->fsm, MPCG_EVENT_XID0DO, ch);
+
+ } else if (fsm_getstate(ch->fsm) < CH_XID7_PENDING1)
+ /* attn rcvd before xid0 processed via bh */
+ fsm_newstate(ch->fsm, CH_XID7_PENDING1);
+ break;
+ case MPCG_STATE_XID2INITX:
+ case MPCG_STATE_XID0IOWAIT:
+ case MPCG_STATE_XID0IOWAIX:
+ /* attn rcvd before xid0 processed on ch
+ but mid-xid0 processing for group */
+ if (fsm_getstate(ch->fsm) < CH_XID7_PENDING1)
+ fsm_newstate(ch->fsm, CH_XID7_PENDING1);
+ break;
+ case MPCG_STATE_XID7INITW:
+ case MPCG_STATE_XID7INITX:
+ case MPCG_STATE_XID7INITI:
+ case MPCG_STATE_XID7INITZ:
+ switch (fsm_getstate(ch->fsm)) {
+ case CH_XID7_PENDING:
+ fsm_newstate(ch->fsm, CH_XID7_PENDING1);
+ break;
+ case CH_XID7_PENDING2:
+ fsm_newstate(ch->fsm, CH_XID7_PENDING3);
+ break;
+ }
+ fsm_event(grp->fsm, MPCG_EVENT_XID7DONE, dev);
+ break;
+ }
+
+ return;
+}
+
+/*
+ * ctcmpc channel FSM action
+ * called from one point in ctcmpc_ch_fsm
+ * ctcmpc only
+ */
+static void ctcmpc_chx_attnbusy(fsm_instance *fsm, int event, void *arg)
+{
+ struct channel *ch = arg;
+ struct net_device *dev = ch->netdev;
+ struct ctcm_priv *priv = dev->ml_priv;
+ struct mpc_group *grp = priv->mpcg;
+
+ CTCM_PR_DEBUG("%s(%s): %s\n ChState:%s GrpState:%s\n",
+ __func__, dev->name, ch->id,
+ fsm_getstate_str(ch->fsm), fsm_getstate_str(grp->fsm));
+
+ fsm_deltimer(&ch->timer);
+
+ switch (fsm_getstate(grp->fsm)) {
+ case MPCG_STATE_XID0IOWAIT:
+ /* vtam wants to be primary.start yside xid exchanges*/
+ /* only receive one attn-busy at a time so must not */
+ /* change state each time */
+ grp->changed_side = 1;
+ fsm_newstate(grp->fsm, MPCG_STATE_XID2INITW);
+ break;
+ case MPCG_STATE_XID2INITW:
+ if (grp->changed_side == 1) {
+ grp->changed_side = 2;
+ break;
+ }
+ /* process began via call to establish_conn */
+ /* so must report failure instead of reverting */
+ /* back to ready-for-xid passive state */
+ if (grp->estconnfunc)
+ goto done;
+ /* this attnbusy is NOT the result of xside xid */
+ /* collisions so yside must have been triggered */
+ /* by an ATTN that was not intended to start XID */
+ /* processing. Revert back to ready-for-xid and */
+ /* wait for ATTN interrupt to signal xid start */
+ if (fsm_getstate(ch->fsm) == CH_XID0_INPROGRESS) {
+ fsm_newstate(ch->fsm, CH_XID0_PENDING) ;
+ fsm_deltimer(&grp->timer);
+ goto done;
+ }
+ fsm_event(grp->fsm, MPCG_EVENT_INOP, dev);
+ goto done;
+ case MPCG_STATE_XID2INITX:
+ /* XID2 was received before ATTN Busy for second
+ channel.Send yside xid for second channel.
+ */
+ if (grp->changed_side == 1) {
+ grp->changed_side = 2;
+ break;
+ }
+ case MPCG_STATE_XID0IOWAIX:
+ case MPCG_STATE_XID7INITW:
+ case MPCG_STATE_XID7INITX:
+ case MPCG_STATE_XID7INITI:
+ case MPCG_STATE_XID7INITZ:
+ default:
+ /* multiple attn-busy indicates too out-of-sync */
+ /* and they are certainly not being received as part */
+ /* of valid mpc group negotiations.. */
+ fsm_event(grp->fsm, MPCG_EVENT_INOP, dev);
+ goto done;
+ }
+
+ if (grp->changed_side == 1) {
+ fsm_deltimer(&grp->timer);
+ fsm_addtimer(&grp->timer, MPC_XID_TIMEOUT_VALUE,
+ MPCG_EVENT_TIMER, dev);
+ }
+ if (ch->in_mpcgroup)
+ fsm_event(grp->fsm, MPCG_EVENT_XID0DO, ch);
+ else
+ CTCM_DBF_TEXT_(MPC_ERROR, CTC_DBF_ERROR,
+ "%s(%s): channel %s not added to group",
+ CTCM_FUNTAIL, dev->name, ch->id);
+
+done:
+ return;
+}
+
+/*
+ * ctcmpc channel FSM action
+ * called from several points in ctcmpc_ch_fsm
+ * ctcmpc only
+ */
+static void ctcmpc_chx_resend(fsm_instance *fsm, int event, void *arg)
+{
+ struct channel *ch = arg;
+ struct net_device *dev = ch->netdev;
+ struct ctcm_priv *priv = dev->ml_priv;
+ struct mpc_group *grp = priv->mpcg;
+
+ fsm_event(grp->fsm, MPCG_EVENT_XID0DO, ch);
+ return;
+}
+
+/*
+ * ctcmpc channel FSM action
+ * called from several points in ctcmpc_ch_fsm
+ * ctcmpc only
+ */
+static void ctcmpc_chx_send_sweep(fsm_instance *fsm, int event, void *arg)
+{
+ struct channel *ach = arg;
+ struct net_device *dev = ach->netdev;
+ struct ctcm_priv *priv = dev->ml_priv;
+ struct mpc_group *grp = priv->mpcg;
+ struct channel *wch = priv->channel[CTCM_WRITE];
+ struct channel *rch = priv->channel[CTCM_READ];
+ struct sk_buff *skb;
+ struct th_sweep *header;
+ int rc = 0;
+ unsigned long saveflags = 0;
+
+ CTCM_PR_DEBUG("ctcmpc enter: %s(): cp=%i ch=0x%p id=%s\n",
+ __func__, smp_processor_id(), ach, ach->id);
+
+ if (grp->in_sweep == 0)
+ goto done;
+
+ CTCM_PR_DBGDATA("%s: 1: ToVTAM_th_seq= %08x\n" ,
+ __func__, wch->th_seq_num);
+ CTCM_PR_DBGDATA("%s: 1: FromVTAM_th_seq= %08x\n" ,
+ __func__, rch->th_seq_num);
+
+ if (fsm_getstate(wch->fsm) != CTC_STATE_TXIDLE) {
+ /* give the previous IO time to complete */
+ fsm_addtimer(&wch->sweep_timer,
+ 200, CTC_EVENT_RSWEEP_TIMER, wch);
+ goto done;
+ }
+
+ skb = skb_dequeue(&wch->sweep_queue);
+ if (!skb)
+ goto done;
+
+ if (set_normalized_cda(&wch->ccw[4], skb->data)) {
+ grp->in_sweep = 0;
+ ctcm_clear_busy_do(dev);
+ dev_kfree_skb_any(skb);
+ fsm_event(grp->fsm, MPCG_EVENT_INOP, dev);
+ goto done;
+ } else {
+ atomic_inc(&skb->users);
+ skb_queue_tail(&wch->io_queue, skb);
+ }
+
+ /* send out the sweep */
+ wch->ccw[4].count = skb->len;
+
+ header = (struct th_sweep *)skb->data;
+ switch (header->th.th_ch_flag) {
+ case TH_SWEEP_REQ:
+ grp->sweep_req_pend_num--;
+ break;
+ case TH_SWEEP_RESP:
+ grp->sweep_rsp_pend_num--;
+ break;
+ }
+
+ header->sw.th_last_seq = wch->th_seq_num;
+
+ CTCM_CCW_DUMP((char *)&wch->ccw[3], sizeof(struct ccw1) * 3);
+ CTCM_PR_DBGDATA("%s: sweep packet\n", __func__);
+ CTCM_D3_DUMP((char *)header, TH_SWEEP_LENGTH);
+
+ fsm_addtimer(&wch->timer, CTCM_TIME_5_SEC, CTC_EVENT_TIMER, wch);
+ fsm_newstate(wch->fsm, CTC_STATE_TX);
+
+ spin_lock_irqsave(get_ccwdev_lock(wch->cdev), saveflags);
+ wch->prof.send_stamp = current_kernel_time(); /* xtime */
+ rc = ccw_device_start(wch->cdev, &wch->ccw[3],
+ (unsigned long) wch, 0xff, 0);
+ spin_unlock_irqrestore(get_ccwdev_lock(wch->cdev), saveflags);
+
+ if ((grp->sweep_req_pend_num == 0) &&
+ (grp->sweep_rsp_pend_num == 0)) {
+ grp->in_sweep = 0;
+ rch->th_seq_num = 0x00;
+ wch->th_seq_num = 0x00;
+ ctcm_clear_busy_do(dev);
+ }
+
+ CTCM_PR_DBGDATA("%s: To-/From-VTAM_th_seq = %08x/%08x\n" ,
+ __func__, wch->th_seq_num, rch->th_seq_num);
+
+ if (rc != 0)
+ ctcm_ccw_check_rc(wch, rc, "send sweep");
+
+done:
+ return;
+}
+
+
+/*
+ * The ctcmpc statemachine for a channel.
+ */
+
+const fsm_node ctcmpc_ch_fsm[] = {
+ { CTC_STATE_STOPPED, CTC_EVENT_STOP, ctcm_action_nop },
+ { CTC_STATE_STOPPED, CTC_EVENT_START, ctcm_chx_start },
+ { CTC_STATE_STOPPED, CTC_EVENT_IO_ENODEV, ctcm_chx_iofatal },
+ { CTC_STATE_STOPPED, CTC_EVENT_FINSTAT, ctcm_action_nop },
+ { CTC_STATE_STOPPED, CTC_EVENT_MC_FAIL, ctcm_action_nop },
+
+ { CTC_STATE_NOTOP, CTC_EVENT_STOP, ctcm_chx_stop },
+ { CTC_STATE_NOTOP, CTC_EVENT_START, ctcm_action_nop },
+ { CTC_STATE_NOTOP, CTC_EVENT_FINSTAT, ctcm_action_nop },
+ { CTC_STATE_NOTOP, CTC_EVENT_MC_FAIL, ctcm_action_nop },
+ { CTC_STATE_NOTOP, CTC_EVENT_MC_GOOD, ctcm_chx_start },
+ { CTC_STATE_NOTOP, CTC_EVENT_UC_RCRESET, ctcm_chx_stop },
+ { CTC_STATE_NOTOP, CTC_EVENT_UC_RSRESET, ctcm_chx_stop },
+ { CTC_STATE_NOTOP, CTC_EVENT_IO_ENODEV, ctcm_chx_iofatal },
+
+ { CTC_STATE_STARTWAIT, CTC_EVENT_STOP, ctcm_chx_haltio },
+ { CTC_STATE_STARTWAIT, CTC_EVENT_START, ctcm_action_nop },
+ { CTC_STATE_STARTWAIT, CTC_EVENT_FINSTAT, ctcm_chx_setmode },
+ { CTC_STATE_STARTWAIT, CTC_EVENT_TIMER, ctcm_chx_setuperr },
+ { CTC_STATE_STARTWAIT, CTC_EVENT_IO_ENODEV, ctcm_chx_iofatal },
+ { CTC_STATE_STARTWAIT, CTC_EVENT_MC_FAIL, ctcm_chx_fail },
+
+ { CTC_STATE_STARTRETRY, CTC_EVENT_STOP, ctcm_chx_haltio },
+ { CTC_STATE_STARTRETRY, CTC_EVENT_TIMER, ctcm_chx_setmode },
+ { CTC_STATE_STARTRETRY, CTC_EVENT_FINSTAT, ctcm_chx_setmode },
+ { CTC_STATE_STARTRETRY, CTC_EVENT_MC_FAIL, ctcm_chx_fail },
+ { CTC_STATE_STARTRETRY, CTC_EVENT_IO_ENODEV, ctcm_chx_iofatal },
+
+ { CTC_STATE_SETUPWAIT, CTC_EVENT_STOP, ctcm_chx_haltio },
+ { CTC_STATE_SETUPWAIT, CTC_EVENT_START, ctcm_action_nop },
+ { CTC_STATE_SETUPWAIT, CTC_EVENT_FINSTAT, ctcmpc_chx_firstio },
+ { CTC_STATE_SETUPWAIT, CTC_EVENT_UC_RCRESET, ctcm_chx_setuperr },
+ { CTC_STATE_SETUPWAIT, CTC_EVENT_UC_RSRESET, ctcm_chx_setuperr },
+ { CTC_STATE_SETUPWAIT, CTC_EVENT_TIMER, ctcm_chx_setmode },
+ { CTC_STATE_SETUPWAIT, CTC_EVENT_IO_ENODEV, ctcm_chx_iofatal },
+ { CTC_STATE_SETUPWAIT, CTC_EVENT_MC_FAIL, ctcm_chx_fail },
+
+ { CTC_STATE_RXINIT, CTC_EVENT_STOP, ctcm_chx_haltio },
+ { CTC_STATE_RXINIT, CTC_EVENT_START, ctcm_action_nop },
+ { CTC_STATE_RXINIT, CTC_EVENT_FINSTAT, ctcmpc_chx_rxidle },
+ { CTC_STATE_RXINIT, CTC_EVENT_UC_RCRESET, ctcm_chx_rxiniterr },
+ { CTC_STATE_RXINIT, CTC_EVENT_UC_RSRESET, ctcm_chx_rxiniterr },
+ { CTC_STATE_RXINIT, CTC_EVENT_TIMER, ctcm_chx_rxiniterr },
+ { CTC_STATE_RXINIT, CTC_EVENT_ATTNBUSY, ctcm_chx_rxinitfail },
+ { CTC_STATE_RXINIT, CTC_EVENT_IO_ENODEV, ctcm_chx_iofatal },
+ { CTC_STATE_RXINIT, CTC_EVENT_UC_ZERO, ctcmpc_chx_firstio },
+ { CTC_STATE_RXINIT, CTC_EVENT_MC_FAIL, ctcm_chx_fail },
+
+ { CH_XID0_PENDING, CTC_EVENT_FINSTAT, ctcm_action_nop },
+ { CH_XID0_PENDING, CTC_EVENT_ATTN, ctcmpc_chx_attn },
+ { CH_XID0_PENDING, CTC_EVENT_STOP, ctcm_chx_haltio },
+ { CH_XID0_PENDING, CTC_EVENT_START, ctcm_action_nop },
+ { CH_XID0_PENDING, CTC_EVENT_IO_ENODEV, ctcm_chx_iofatal },
+ { CH_XID0_PENDING, CTC_EVENT_MC_FAIL, ctcm_chx_fail },
+ { CH_XID0_PENDING, CTC_EVENT_UC_RCRESET, ctcm_chx_setuperr },
+ { CH_XID0_PENDING, CTC_EVENT_UC_RSRESET, ctcm_chx_setuperr },
+ { CH_XID0_PENDING, CTC_EVENT_UC_RSRESET, ctcm_chx_setuperr },
+ { CH_XID0_PENDING, CTC_EVENT_ATTNBUSY, ctcm_chx_iofatal },
+
+ { CH_XID0_INPROGRESS, CTC_EVENT_FINSTAT, ctcmpc_chx_rx },
+ { CH_XID0_INPROGRESS, CTC_EVENT_ATTN, ctcmpc_chx_attn },
+ { CH_XID0_INPROGRESS, CTC_EVENT_STOP, ctcm_chx_haltio },
+ { CH_XID0_INPROGRESS, CTC_EVENT_START, ctcm_action_nop },
+ { CH_XID0_INPROGRESS, CTC_EVENT_IO_ENODEV, ctcm_chx_iofatal },
+ { CH_XID0_INPROGRESS, CTC_EVENT_MC_FAIL, ctcm_chx_fail },
+ { CH_XID0_INPROGRESS, CTC_EVENT_UC_ZERO, ctcmpc_chx_rx },
+ { CH_XID0_INPROGRESS, CTC_EVENT_UC_RCRESET, ctcm_chx_setuperr },
+ { CH_XID0_INPROGRESS, CTC_EVENT_ATTNBUSY, ctcmpc_chx_attnbusy },
+ { CH_XID0_INPROGRESS, CTC_EVENT_TIMER, ctcmpc_chx_resend },
+ { CH_XID0_INPROGRESS, CTC_EVENT_IO_EBUSY, ctcm_chx_fail },
+
+ { CH_XID7_PENDING, CTC_EVENT_FINSTAT, ctcmpc_chx_rx },
+ { CH_XID7_PENDING, CTC_EVENT_ATTN, ctcmpc_chx_attn },
+ { CH_XID7_PENDING, CTC_EVENT_STOP, ctcm_chx_haltio },
+ { CH_XID7_PENDING, CTC_EVENT_START, ctcm_action_nop },
+ { CH_XID7_PENDING, CTC_EVENT_IO_ENODEV, ctcm_chx_iofatal },
+ { CH_XID7_PENDING, CTC_EVENT_MC_FAIL, ctcm_chx_fail },
+ { CH_XID7_PENDING, CTC_EVENT_UC_ZERO, ctcmpc_chx_rx },
+ { CH_XID7_PENDING, CTC_EVENT_UC_RCRESET, ctcm_chx_setuperr },
+ { CH_XID7_PENDING, CTC_EVENT_UC_RSRESET, ctcm_chx_setuperr },
+ { CH_XID7_PENDING, CTC_EVENT_UC_RSRESET, ctcm_chx_setuperr },
+ { CH_XID7_PENDING, CTC_EVENT_ATTNBUSY, ctcm_chx_iofatal },
+ { CH_XID7_PENDING, CTC_EVENT_TIMER, ctcmpc_chx_resend },
+ { CH_XID7_PENDING, CTC_EVENT_IO_EBUSY, ctcm_chx_fail },
+
+ { CH_XID7_PENDING1, CTC_EVENT_FINSTAT, ctcmpc_chx_rx },
+ { CH_XID7_PENDING1, CTC_EVENT_ATTN, ctcmpc_chx_attn },
+ { CH_XID7_PENDING1, CTC_EVENT_STOP, ctcm_chx_haltio },
+ { CH_XID7_PENDING1, CTC_EVENT_START, ctcm_action_nop },
+ { CH_XID7_PENDING1, CTC_EVENT_IO_ENODEV, ctcm_chx_iofatal },
+ { CH_XID7_PENDING1, CTC_EVENT_MC_FAIL, ctcm_chx_fail },
+ { CH_XID7_PENDING1, CTC_EVENT_UC_ZERO, ctcmpc_chx_rx },
+ { CH_XID7_PENDING1, CTC_EVENT_UC_RCRESET, ctcm_chx_setuperr },
+ { CH_XID7_PENDING1, CTC_EVENT_UC_RSRESET, ctcm_chx_setuperr },
+ { CH_XID7_PENDING1, CTC_EVENT_ATTNBUSY, ctcm_chx_iofatal },
+ { CH_XID7_PENDING1, CTC_EVENT_TIMER, ctcmpc_chx_resend },
+ { CH_XID7_PENDING1, CTC_EVENT_IO_EBUSY, ctcm_chx_fail },
+
+ { CH_XID7_PENDING2, CTC_EVENT_FINSTAT, ctcmpc_chx_rx },
+ { CH_XID7_PENDING2, CTC_EVENT_ATTN, ctcmpc_chx_attn },
+ { CH_XID7_PENDING2, CTC_EVENT_STOP, ctcm_chx_haltio },
+ { CH_XID7_PENDING2, CTC_EVENT_START, ctcm_action_nop },
+ { CH_XID7_PENDING2, CTC_EVENT_IO_ENODEV, ctcm_chx_iofatal },
+ { CH_XID7_PENDING2, CTC_EVENT_MC_FAIL, ctcm_chx_fail },
+ { CH_XID7_PENDING2, CTC_EVENT_UC_ZERO, ctcmpc_chx_rx },
+ { CH_XID7_PENDING2, CTC_EVENT_UC_RCRESET, ctcm_chx_setuperr },
+ { CH_XID7_PENDING2, CTC_EVENT_UC_RSRESET, ctcm_chx_setuperr },
+ { CH_XID7_PENDING2, CTC_EVENT_ATTNBUSY, ctcm_chx_iofatal },
+ { CH_XID7_PENDING2, CTC_EVENT_TIMER, ctcmpc_chx_resend },
+ { CH_XID7_PENDING2, CTC_EVENT_IO_EBUSY, ctcm_chx_fail },
+
+ { CH_XID7_PENDING3, CTC_EVENT_FINSTAT, ctcmpc_chx_rx },
+ { CH_XID7_PENDING3, CTC_EVENT_ATTN, ctcmpc_chx_attn },
+ { CH_XID7_PENDING3, CTC_EVENT_STOP, ctcm_chx_haltio },
+ { CH_XID7_PENDING3, CTC_EVENT_START, ctcm_action_nop },
+ { CH_XID7_PENDING3, CTC_EVENT_IO_ENODEV, ctcm_chx_iofatal },
+ { CH_XID7_PENDING3, CTC_EVENT_MC_FAIL, ctcm_chx_fail },
+ { CH_XID7_PENDING3, CTC_EVENT_UC_ZERO, ctcmpc_chx_rx },
+ { CH_XID7_PENDING3, CTC_EVENT_UC_RCRESET, ctcm_chx_setuperr },
+ { CH_XID7_PENDING3, CTC_EVENT_UC_RSRESET, ctcm_chx_setuperr },
+ { CH_XID7_PENDING3, CTC_EVENT_ATTNBUSY, ctcm_chx_iofatal },
+ { CH_XID7_PENDING3, CTC_EVENT_TIMER, ctcmpc_chx_resend },
+ { CH_XID7_PENDING3, CTC_EVENT_IO_EBUSY, ctcm_chx_fail },
+
+ { CH_XID7_PENDING4, CTC_EVENT_FINSTAT, ctcmpc_chx_rx },
+ { CH_XID7_PENDING4, CTC_EVENT_ATTN, ctcmpc_chx_attn },
+ { CH_XID7_PENDING4, CTC_EVENT_STOP, ctcm_chx_haltio },
+ { CH_XID7_PENDING4, CTC_EVENT_START, ctcm_action_nop },
+ { CH_XID7_PENDING4, CTC_EVENT_IO_ENODEV, ctcm_chx_iofatal },
+ { CH_XID7_PENDING4, CTC_EVENT_MC_FAIL, ctcm_chx_fail },
+ { CH_XID7_PENDING4, CTC_EVENT_UC_ZERO, ctcmpc_chx_rx },
+ { CH_XID7_PENDING4, CTC_EVENT_UC_RCRESET, ctcm_chx_setuperr },
+ { CH_XID7_PENDING4, CTC_EVENT_UC_RSRESET, ctcm_chx_setuperr },
+ { CH_XID7_PENDING4, CTC_EVENT_ATTNBUSY, ctcm_chx_iofatal },
+ { CH_XID7_PENDING4, CTC_EVENT_TIMER, ctcmpc_chx_resend },
+ { CH_XID7_PENDING4, CTC_EVENT_IO_EBUSY, ctcm_chx_fail },
+
+ { CTC_STATE_RXIDLE, CTC_EVENT_STOP, ctcm_chx_haltio },
+ { CTC_STATE_RXIDLE, CTC_EVENT_START, ctcm_action_nop },
+ { CTC_STATE_RXIDLE, CTC_EVENT_FINSTAT, ctcmpc_chx_rx },
+ { CTC_STATE_RXIDLE, CTC_EVENT_UC_RCRESET, ctcm_chx_rxdisc },
+ { CTC_STATE_RXIDLE, CTC_EVENT_UC_RSRESET, ctcm_chx_fail },
+ { CTC_STATE_RXIDLE, CTC_EVENT_IO_ENODEV, ctcm_chx_iofatal },
+ { CTC_STATE_RXIDLE, CTC_EVENT_MC_FAIL, ctcm_chx_fail },
+ { CTC_STATE_RXIDLE, CTC_EVENT_UC_ZERO, ctcmpc_chx_rx },
+
+ { CTC_STATE_TXINIT, CTC_EVENT_STOP, ctcm_chx_haltio },
+ { CTC_STATE_TXINIT, CTC_EVENT_START, ctcm_action_nop },
+ { CTC_STATE_TXINIT, CTC_EVENT_FINSTAT, ctcm_chx_txidle },
+ { CTC_STATE_TXINIT, CTC_EVENT_UC_RCRESET, ctcm_chx_txiniterr },
+ { CTC_STATE_TXINIT, CTC_EVENT_UC_RSRESET, ctcm_chx_txiniterr },
+ { CTC_STATE_TXINIT, CTC_EVENT_TIMER, ctcm_chx_txiniterr },
+ { CTC_STATE_TXINIT, CTC_EVENT_IO_ENODEV, ctcm_chx_iofatal },
+ { CTC_STATE_TXINIT, CTC_EVENT_MC_FAIL, ctcm_chx_fail },
+ { CTC_STATE_TXINIT, CTC_EVENT_RSWEEP_TIMER, ctcmpc_chx_send_sweep },
+
+ { CTC_STATE_TXIDLE, CTC_EVENT_STOP, ctcm_chx_haltio },
+ { CTC_STATE_TXIDLE, CTC_EVENT_START, ctcm_action_nop },
+ { CTC_STATE_TXIDLE, CTC_EVENT_FINSTAT, ctcmpc_chx_firstio },
+ { CTC_STATE_TXIDLE, CTC_EVENT_UC_RCRESET, ctcm_chx_fail },
+ { CTC_STATE_TXIDLE, CTC_EVENT_UC_RSRESET, ctcm_chx_fail },
+ { CTC_STATE_TXIDLE, CTC_EVENT_IO_ENODEV, ctcm_chx_iofatal },
+ { CTC_STATE_TXIDLE, CTC_EVENT_MC_FAIL, ctcm_chx_fail },
+ { CTC_STATE_TXIDLE, CTC_EVENT_RSWEEP_TIMER, ctcmpc_chx_send_sweep },
+
+ { CTC_STATE_TERM, CTC_EVENT_STOP, ctcm_action_nop },
+ { CTC_STATE_TERM, CTC_EVENT_START, ctcm_chx_restart },
+ { CTC_STATE_TERM, CTC_EVENT_FINSTAT, ctcm_chx_stopped },
+ { CTC_STATE_TERM, CTC_EVENT_UC_RCRESET, ctcm_action_nop },
+ { CTC_STATE_TERM, CTC_EVENT_UC_RSRESET, ctcm_action_nop },
+ { CTC_STATE_TERM, CTC_EVENT_MC_FAIL, ctcm_chx_fail },
+ { CTC_STATE_TERM, CTC_EVENT_IO_EBUSY, ctcm_chx_fail },
+ { CTC_STATE_TERM, CTC_EVENT_IO_ENODEV, ctcm_chx_iofatal },
+
+ { CTC_STATE_DTERM, CTC_EVENT_STOP, ctcm_chx_haltio },
+ { CTC_STATE_DTERM, CTC_EVENT_START, ctcm_chx_restart },
+ { CTC_STATE_DTERM, CTC_EVENT_FINSTAT, ctcm_chx_setmode },
+ { CTC_STATE_DTERM, CTC_EVENT_UC_RCRESET, ctcm_action_nop },
+ { CTC_STATE_DTERM, CTC_EVENT_UC_RSRESET, ctcm_action_nop },
+ { CTC_STATE_DTERM, CTC_EVENT_MC_FAIL, ctcm_chx_fail },
+ { CTC_STATE_DTERM, CTC_EVENT_IO_ENODEV, ctcm_chx_iofatal },
+
+ { CTC_STATE_TX, CTC_EVENT_STOP, ctcm_chx_haltio },
+ { CTC_STATE_TX, CTC_EVENT_START, ctcm_action_nop },
+ { CTC_STATE_TX, CTC_EVENT_FINSTAT, ctcmpc_chx_txdone },
+ { CTC_STATE_TX, CTC_EVENT_UC_RCRESET, ctcm_chx_fail },
+ { CTC_STATE_TX, CTC_EVENT_UC_RSRESET, ctcm_chx_fail },
+ { CTC_STATE_TX, CTC_EVENT_TIMER, ctcm_chx_txretry },
+ { CTC_STATE_TX, CTC_EVENT_IO_ENODEV, ctcm_chx_iofatal },
+ { CTC_STATE_TX, CTC_EVENT_MC_FAIL, ctcm_chx_fail },
+ { CTC_STATE_TX, CTC_EVENT_RSWEEP_TIMER, ctcmpc_chx_send_sweep },
+ { CTC_STATE_TX, CTC_EVENT_IO_EBUSY, ctcm_chx_fail },
+
+ { CTC_STATE_RXERR, CTC_EVENT_STOP, ctcm_chx_haltio },
+ { CTC_STATE_TXERR, CTC_EVENT_STOP, ctcm_chx_haltio },
+ { CTC_STATE_TXERR, CTC_EVENT_IO_ENODEV, ctcm_chx_iofatal },
+ { CTC_STATE_TXERR, CTC_EVENT_MC_FAIL, ctcm_chx_fail },
+ { CTC_STATE_RXERR, CTC_EVENT_MC_FAIL, ctcm_chx_fail },
+};
+
+int mpc_ch_fsm_len = ARRAY_SIZE(ctcmpc_ch_fsm);
+
+/*
+ * Actions for interface - statemachine.
+ */
+
+/**
+ * Startup channels by sending CTC_EVENT_START to each channel.
+ *
+ * fi An instance of an interface statemachine.
+ * event The event, just happened.
+ * arg Generic pointer, casted from struct net_device * upon call.
+ */
+static void dev_action_start(fsm_instance *fi, int event, void *arg)
+{
+ struct net_device *dev = arg;
+ struct ctcm_priv *priv = dev->ml_priv;
+ int direction;
+
+ CTCMY_DBF_DEV_NAME(SETUP, dev, "");
+
+ fsm_deltimer(&priv->restart_timer);
+ fsm_newstate(fi, DEV_STATE_STARTWAIT_RXTX);
+ if (IS_MPC(priv))
+ priv->mpcg->channels_terminating = 0;
+ for (direction = CTCM_READ; direction <= CTCM_WRITE; direction++) {
+ struct channel *ch = priv->channel[direction];
+ fsm_event(ch->fsm, CTC_EVENT_START, ch);
+ }
+}
+
+/**
+ * Shutdown channels by sending CTC_EVENT_STOP to each channel.
+ *
+ * fi An instance of an interface statemachine.
+ * event The event, just happened.
+ * arg Generic pointer, casted from struct net_device * upon call.
+ */
+static void dev_action_stop(fsm_instance *fi, int event, void *arg)
+{
+ int direction;
+ struct net_device *dev = arg;
+ struct ctcm_priv *priv = dev->ml_priv;
+
+ CTCMY_DBF_DEV_NAME(SETUP, dev, "");
+
+ fsm_newstate(fi, DEV_STATE_STOPWAIT_RXTX);
+ for (direction = CTCM_READ; direction <= CTCM_WRITE; direction++) {
+ struct channel *ch = priv->channel[direction];
+ fsm_event(ch->fsm, CTC_EVENT_STOP, ch);
+ ch->th_seq_num = 0x00;
+ CTCM_PR_DEBUG("%s: CH_th_seq= %08x\n",
+ __func__, ch->th_seq_num);
+ }
+ if (IS_MPC(priv))
+ fsm_newstate(priv->mpcg->fsm, MPCG_STATE_RESET);
+}
+
+static void dev_action_restart(fsm_instance *fi, int event, void *arg)
+{
+ int restart_timer;
+ struct net_device *dev = arg;
+ struct ctcm_priv *priv = dev->ml_priv;
+
+ CTCMY_DBF_DEV_NAME(TRACE, dev, "");
+
+ if (IS_MPC(priv)) {
+ restart_timer = CTCM_TIME_1_SEC;
+ } else {
+ restart_timer = CTCM_TIME_5_SEC;
+ }
+ dev_info(&dev->dev, "Restarting device\n");
+
+ dev_action_stop(fi, event, arg);
+ fsm_event(priv->fsm, DEV_EVENT_STOP, dev);
+ if (IS_MPC(priv))
+ fsm_newstate(priv->mpcg->fsm, MPCG_STATE_RESET);
+
+ /* going back into start sequence too quickly can */
+ /* result in the other side becoming unreachable due */
+ /* to sense reported when IO is aborted */
+ fsm_addtimer(&priv->restart_timer, restart_timer,
+ DEV_EVENT_START, dev);
+}
+
+/**
+ * Called from channel statemachine
+ * when a channel is up and running.
+ *
+ * fi An instance of an interface statemachine.
+ * event The event, just happened.
+ * arg Generic pointer, casted from struct net_device * upon call.
+ */
+static void dev_action_chup(fsm_instance *fi, int event, void *arg)
+{
+ struct net_device *dev = arg;
+ struct ctcm_priv *priv = dev->ml_priv;
+ int dev_stat = fsm_getstate(fi);
+
+ CTCM_DBF_TEXT_(SETUP, CTC_DBF_NOTICE,
+ "%s(%s): priv = %p [%d,%d]\n ", CTCM_FUNTAIL,
+ dev->name, dev->ml_priv, dev_stat, event);
+
+ switch (fsm_getstate(fi)) {
+ case DEV_STATE_STARTWAIT_RXTX:
+ if (event == DEV_EVENT_RXUP)
+ fsm_newstate(fi, DEV_STATE_STARTWAIT_TX);
+ else
+ fsm_newstate(fi, DEV_STATE_STARTWAIT_RX);
+ break;
+ case DEV_STATE_STARTWAIT_RX:
+ if (event == DEV_EVENT_RXUP) {
+ fsm_newstate(fi, DEV_STATE_RUNNING);
+ dev_info(&dev->dev,
+ "Connected with remote side\n");
+ ctcm_clear_busy(dev);
+ }
+ break;
+ case DEV_STATE_STARTWAIT_TX:
+ if (event == DEV_EVENT_TXUP) {
+ fsm_newstate(fi, DEV_STATE_RUNNING);
+ dev_info(&dev->dev,
+ "Connected with remote side\n");
+ ctcm_clear_busy(dev);
+ }
+ break;
+ case DEV_STATE_STOPWAIT_TX:
+ if (event == DEV_EVENT_RXUP)
+ fsm_newstate(fi, DEV_STATE_STOPWAIT_RXTX);
+ break;
+ case DEV_STATE_STOPWAIT_RX:
+ if (event == DEV_EVENT_TXUP)
+ fsm_newstate(fi, DEV_STATE_STOPWAIT_RXTX);
+ break;
+ }
+
+ if (IS_MPC(priv)) {
+ if (event == DEV_EVENT_RXUP)
+ mpc_channel_action(priv->channel[CTCM_READ],
+ CTCM_READ, MPC_CHANNEL_ADD);
+ else
+ mpc_channel_action(priv->channel[CTCM_WRITE],
+ CTCM_WRITE, MPC_CHANNEL_ADD);
+ }
+}
+
+/**
+ * Called from device statemachine
+ * when a channel has been shutdown.
+ *
+ * fi An instance of an interface statemachine.
+ * event The event, just happened.
+ * arg Generic pointer, casted from struct net_device * upon call.
+ */
+static void dev_action_chdown(fsm_instance *fi, int event, void *arg)
+{
+
+ struct net_device *dev = arg;
+ struct ctcm_priv *priv = dev->ml_priv;
+
+ CTCMY_DBF_DEV_NAME(SETUP, dev, "");
+
+ switch (fsm_getstate(fi)) {
+ case DEV_STATE_RUNNING:
+ if (event == DEV_EVENT_TXDOWN)
+ fsm_newstate(fi, DEV_STATE_STARTWAIT_TX);
+ else
+ fsm_newstate(fi, DEV_STATE_STARTWAIT_RX);
+ break;
+ case DEV_STATE_STARTWAIT_RX:
+ if (event == DEV_EVENT_TXDOWN)
+ fsm_newstate(fi, DEV_STATE_STARTWAIT_RXTX);
+ break;
+ case DEV_STATE_STARTWAIT_TX:
+ if (event == DEV_EVENT_RXDOWN)
+ fsm_newstate(fi, DEV_STATE_STARTWAIT_RXTX);
+ break;
+ case DEV_STATE_STOPWAIT_RXTX:
+ if (event == DEV_EVENT_TXDOWN)
+ fsm_newstate(fi, DEV_STATE_STOPWAIT_RX);
+ else
+ fsm_newstate(fi, DEV_STATE_STOPWAIT_TX);
+ break;
+ case DEV_STATE_STOPWAIT_RX:
+ if (event == DEV_EVENT_RXDOWN)
+ fsm_newstate(fi, DEV_STATE_STOPPED);
+ break;
+ case DEV_STATE_STOPWAIT_TX:
+ if (event == DEV_EVENT_TXDOWN)
+ fsm_newstate(fi, DEV_STATE_STOPPED);
+ break;
+ }
+ if (IS_MPC(priv)) {
+ if (event == DEV_EVENT_RXDOWN)
+ mpc_channel_action(priv->channel[CTCM_READ],
+ CTCM_READ, MPC_CHANNEL_REMOVE);
+ else
+ mpc_channel_action(priv->channel[CTCM_WRITE],
+ CTCM_WRITE, MPC_CHANNEL_REMOVE);
+ }
+}
+
+const fsm_node dev_fsm[] = {
+ { DEV_STATE_STOPPED, DEV_EVENT_START, dev_action_start },
+ { DEV_STATE_STOPWAIT_RXTX, DEV_EVENT_START, dev_action_start },
+ { DEV_STATE_STOPWAIT_RXTX, DEV_EVENT_RXDOWN, dev_action_chdown },
+ { DEV_STATE_STOPWAIT_RXTX, DEV_EVENT_TXDOWN, dev_action_chdown },
+ { DEV_STATE_STOPWAIT_RXTX, DEV_EVENT_RESTART, dev_action_restart },
+ { DEV_STATE_STOPWAIT_RX, DEV_EVENT_START, dev_action_start },
+ { DEV_STATE_STOPWAIT_RX, DEV_EVENT_RXUP, dev_action_chup },
+ { DEV_STATE_STOPWAIT_RX, DEV_EVENT_TXUP, dev_action_chup },
+ { DEV_STATE_STOPWAIT_RX, DEV_EVENT_RXDOWN, dev_action_chdown },
+ { DEV_STATE_STOPWAIT_RX, DEV_EVENT_RESTART, dev_action_restart },
+ { DEV_STATE_STOPWAIT_TX, DEV_EVENT_START, dev_action_start },
+ { DEV_STATE_STOPWAIT_TX, DEV_EVENT_RXUP, dev_action_chup },
+ { DEV_STATE_STOPWAIT_TX, DEV_EVENT_TXUP, dev_action_chup },
+ { DEV_STATE_STOPWAIT_TX, DEV_EVENT_TXDOWN, dev_action_chdown },
+ { DEV_STATE_STOPWAIT_TX, DEV_EVENT_RESTART, dev_action_restart },
+ { DEV_STATE_STARTWAIT_RXTX, DEV_EVENT_STOP, dev_action_stop },
+ { DEV_STATE_STARTWAIT_RXTX, DEV_EVENT_RXUP, dev_action_chup },
+ { DEV_STATE_STARTWAIT_RXTX, DEV_EVENT_TXUP, dev_action_chup },
+ { DEV_STATE_STARTWAIT_RXTX, DEV_EVENT_RXDOWN, dev_action_chdown },
+ { DEV_STATE_STARTWAIT_RXTX, DEV_EVENT_TXDOWN, dev_action_chdown },
+ { DEV_STATE_STARTWAIT_RXTX, DEV_EVENT_RESTART, dev_action_restart },
+ { DEV_STATE_STARTWAIT_TX, DEV_EVENT_STOP, dev_action_stop },
+ { DEV_STATE_STARTWAIT_TX, DEV_EVENT_RXUP, dev_action_chup },
+ { DEV_STATE_STARTWAIT_TX, DEV_EVENT_TXUP, dev_action_chup },
+ { DEV_STATE_STARTWAIT_TX, DEV_EVENT_RXDOWN, dev_action_chdown },
+ { DEV_STATE_STARTWAIT_TX, DEV_EVENT_RESTART, dev_action_restart },
+ { DEV_STATE_STARTWAIT_RX, DEV_EVENT_STOP, dev_action_stop },
+ { DEV_STATE_STARTWAIT_RX, DEV_EVENT_RXUP, dev_action_chup },
+ { DEV_STATE_STARTWAIT_RX, DEV_EVENT_TXUP, dev_action_chup },
+ { DEV_STATE_STARTWAIT_RX, DEV_EVENT_TXDOWN, dev_action_chdown },
+ { DEV_STATE_STARTWAIT_RX, DEV_EVENT_RESTART, dev_action_restart },
+ { DEV_STATE_RUNNING, DEV_EVENT_STOP, dev_action_stop },
+ { DEV_STATE_RUNNING, DEV_EVENT_RXDOWN, dev_action_chdown },
+ { DEV_STATE_RUNNING, DEV_EVENT_TXDOWN, dev_action_chdown },
+ { DEV_STATE_RUNNING, DEV_EVENT_TXUP, ctcm_action_nop },
+ { DEV_STATE_RUNNING, DEV_EVENT_RXUP, ctcm_action_nop },
+ { DEV_STATE_RUNNING, DEV_EVENT_RESTART, dev_action_restart },
+};
+
+int dev_fsm_len = ARRAY_SIZE(dev_fsm);
+
+/* --- This is the END my friend --- */
+
diff --git a/drivers/s390/net/ctcm_fsms.h b/drivers/s390/net/ctcm_fsms.h
new file mode 100644
index 00000000000..c963d04799c
--- /dev/null
+++ b/drivers/s390/net/ctcm_fsms.h
@@ -0,0 +1,356 @@
+/*
+ * Copyright IBM Corp. 2001, 2007
+ * Authors: Fritz Elfert (felfert@millenux.com)
+ * Peter Tiedemann (ptiedem@de.ibm.com)
+ * MPC additions :
+ * Belinda Thompson (belindat@us.ibm.com)
+ * Andy Richter (richtera@us.ibm.com)
+ */
+#ifndef _CTCM_FSMS_H_
+#define _CTCM_FSMS_H_
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/errno.h>
+#include <linux/types.h>
+#include <linux/interrupt.h>
+#include <linux/timer.h>
+#include <linux/bitops.h>
+
+#include <linux/signal.h>
+#include <linux/string.h>
+
+#include <linux/ip.h>
+#include <linux/if_arp.h>
+#include <linux/tcp.h>
+#include <linux/skbuff.h>
+#include <linux/ctype.h>
+#include <net/dst.h>
+
+#include <linux/io.h>
+#include <asm/ccwdev.h>
+#include <asm/ccwgroup.h>
+#include <linux/uaccess.h>
+
+#include <asm/idals.h>
+
+#include "fsm.h"
+#include "ctcm_main.h"
+
+/*
+ * Definitions for the channel statemachine(s) for ctc and ctcmpc
+ *
+ * To allow better kerntyping, prefix-less definitions for channel states
+ * and channel events have been replaced :
+ * ch_event... -> ctc_ch_event...
+ * CH_EVENT... -> CTC_EVENT...
+ * ch_state... -> ctc_ch_state...
+ * CH_STATE... -> CTC_STATE...
+ */
+/*
+ * Events of the channel statemachine(s) for ctc and ctcmpc
+ */
+enum ctc_ch_events {
+ /*
+ * Events, representing return code of
+ * I/O operations (ccw_device_start, ccw_device_halt et al.)
+ */
+ CTC_EVENT_IO_SUCCESS,
+ CTC_EVENT_IO_EBUSY,
+ CTC_EVENT_IO_ENODEV,
+ CTC_EVENT_IO_UNKNOWN,
+
+ CTC_EVENT_ATTNBUSY,
+ CTC_EVENT_ATTN,
+ CTC_EVENT_BUSY,
+ /*
+ * Events, representing unit-check
+ */
+ CTC_EVENT_UC_RCRESET,
+ CTC_EVENT_UC_RSRESET,
+ CTC_EVENT_UC_TXTIMEOUT,
+ CTC_EVENT_UC_TXPARITY,
+ CTC_EVENT_UC_HWFAIL,
+ CTC_EVENT_UC_RXPARITY,
+ CTC_EVENT_UC_ZERO,
+ CTC_EVENT_UC_UNKNOWN,
+ /*
+ * Events, representing subchannel-check
+ */
+ CTC_EVENT_SC_UNKNOWN,
+ /*
+ * Events, representing machine checks
+ */
+ CTC_EVENT_MC_FAIL,
+ CTC_EVENT_MC_GOOD,
+ /*
+ * Event, representing normal IRQ
+ */
+ CTC_EVENT_IRQ,
+ CTC_EVENT_FINSTAT,
+ /*
+ * Event, representing timer expiry.
+ */
+ CTC_EVENT_TIMER,
+ /*
+ * Events, representing commands from upper levels.
+ */
+ CTC_EVENT_START,
+ CTC_EVENT_STOP,
+ CTC_NR_EVENTS,
+ /*
+ * additional MPC events
+ */
+ CTC_EVENT_SEND_XID = CTC_NR_EVENTS,
+ CTC_EVENT_RSWEEP_TIMER,
+ /*
+ * MUST be always the last element!!
+ */
+ CTC_MPC_NR_EVENTS,
+};
+
+/*
+ * States of the channel statemachine(s) for ctc and ctcmpc.
+ */
+enum ctc_ch_states {
+ /*
+ * Channel not assigned to any device,
+ * initial state, direction invalid
+ */
+ CTC_STATE_IDLE,
+ /*
+ * Channel assigned but not operating
+ */
+ CTC_STATE_STOPPED,
+ CTC_STATE_STARTWAIT,
+ CTC_STATE_STARTRETRY,
+ CTC_STATE_SETUPWAIT,
+ CTC_STATE_RXINIT,
+ CTC_STATE_TXINIT,
+ CTC_STATE_RX,
+ CTC_STATE_TX,
+ CTC_STATE_RXIDLE,
+ CTC_STATE_TXIDLE,
+ CTC_STATE_RXERR,
+ CTC_STATE_TXERR,
+ CTC_STATE_TERM,
+ CTC_STATE_DTERM,
+ CTC_STATE_NOTOP,
+ CTC_NR_STATES, /* MUST be the last element of non-expanded states */
+ /*
+ * additional MPC states
+ */
+ CH_XID0_PENDING = CTC_NR_STATES,
+ CH_XID0_INPROGRESS,
+ CH_XID7_PENDING,
+ CH_XID7_PENDING1,
+ CH_XID7_PENDING2,
+ CH_XID7_PENDING3,
+ CH_XID7_PENDING4,
+ CTC_MPC_NR_STATES, /* MUST be the last element of expanded mpc states */
+};
+
+extern const char *ctc_ch_event_names[];
+
+extern const char *ctc_ch_state_names[];
+
+void ctcm_ccw_check_rc(struct channel *ch, int rc, char *msg);
+void ctcm_purge_skb_queue(struct sk_buff_head *q);
+void fsm_action_nop(fsm_instance *fi, int event, void *arg);
+
+/*
+ * ----- non-static actions for ctcm channel statemachine -----
+ *
+ */
+void ctcm_chx_txidle(fsm_instance *fi, int event, void *arg);
+
+/*
+ * ----- FSM (state/event/action) of the ctcm channel statemachine -----
+ */
+extern const fsm_node ch_fsm[];
+extern int ch_fsm_len;
+
+
+/*
+ * ----- non-static actions for ctcmpc channel statemachine ----
+ *
+ */
+/* shared :
+void ctcm_chx_txidle(fsm_instance * fi, int event, void *arg);
+ */
+void ctcmpc_chx_rxidle(fsm_instance *fi, int event, void *arg);
+
+/*
+ * ----- FSM (state/event/action) of the ctcmpc channel statemachine -----
+ */
+extern const fsm_node ctcmpc_ch_fsm[];
+extern int mpc_ch_fsm_len;
+
+/*
+ * Definitions for the device interface statemachine for ctc and mpc
+ */
+
+/*
+ * States of the device interface statemachine.
+ */
+enum dev_states {
+ DEV_STATE_STOPPED,
+ DEV_STATE_STARTWAIT_RXTX,
+ DEV_STATE_STARTWAIT_RX,
+ DEV_STATE_STARTWAIT_TX,
+ DEV_STATE_STOPWAIT_RXTX,
+ DEV_STATE_STOPWAIT_RX,
+ DEV_STATE_STOPWAIT_TX,
+ DEV_STATE_RUNNING,
+ /*
+ * MUST be always the last element!!
+ */
+ CTCM_NR_DEV_STATES
+};
+
+extern const char *dev_state_names[];
+
+/*
+ * Events of the device interface statemachine.
+ * ctcm and ctcmpc
+ */
+enum dev_events {
+ DEV_EVENT_START,
+ DEV_EVENT_STOP,
+ DEV_EVENT_RXUP,
+ DEV_EVENT_TXUP,
+ DEV_EVENT_RXDOWN,
+ DEV_EVENT_TXDOWN,
+ DEV_EVENT_RESTART,
+ /*
+ * MUST be always the last element!!
+ */
+ CTCM_NR_DEV_EVENTS
+};
+
+extern const char *dev_event_names[];
+
+/*
+ * Actions for the device interface statemachine.
+ * ctc and ctcmpc
+ */
+/*
+static void dev_action_start(fsm_instance * fi, int event, void *arg);
+static void dev_action_stop(fsm_instance * fi, int event, void *arg);
+static void dev_action_restart(fsm_instance *fi, int event, void *arg);
+static void dev_action_chup(fsm_instance * fi, int event, void *arg);
+static void dev_action_chdown(fsm_instance * fi, int event, void *arg);
+*/
+
+/*
+ * The (state/event/action) fsm table of the device interface statemachine.
+ * ctcm and ctcmpc
+ */
+extern const fsm_node dev_fsm[];
+extern int dev_fsm_len;
+
+
+/*
+ * Definitions for the MPC Group statemachine
+ */
+
+/*
+ * MPC Group Station FSM States
+
+State Name When In This State
+====================== =======================================
+MPCG_STATE_RESET Initial State When Driver Loaded
+ We receive and send NOTHING
+
+MPCG_STATE_INOP INOP Received.
+ Group level non-recoverable error
+
+MPCG_STATE_READY XID exchanges for at least 1 write and
+ 1 read channel have completed.
+ Group is ready for data transfer.
+
+States from ctc_mpc_alloc_channel
+==============================================================
+MPCG_STATE_XID2INITW Awaiting XID2(0) Initiation
+ ATTN from other side will start
+ XID negotiations.
+ Y-side protocol only.
+
+MPCG_STATE_XID2INITX XID2(0) negotiations are in progress.
+ At least 1, but not all, XID2(0)'s
+ have been received from partner.
+
+MPCG_STATE_XID7INITW XID2(0) complete
+ No XID2(7)'s have yet been received.
+ XID2(7) negotiations pending.
+
+MPCG_STATE_XID7INITX XID2(7) negotiations in progress.
+ At least 1, but not all, XID2(7)'s
+ have been received from partner.
+
+MPCG_STATE_XID7INITF XID2(7) negotiations complete.
+ Transitioning to READY.
+
+MPCG_STATE_READY Ready for Data Transfer.
+
+
+States from ctc_mpc_establish_connectivity call
+==============================================================
+MPCG_STATE_XID0IOWAIT Initiating XID2(0) negotiations.
+ X-side protocol only.
+ ATTN-BUSY from other side will convert
+ this to Y-side protocol and the
+ ctc_mpc_alloc_channel flow will begin.
+
+MPCG_STATE_XID0IOWAIX XID2(0) negotiations are in progress.
+ At least 1, but not all, XID2(0)'s
+ have been received from partner.
+
+MPCG_STATE_XID7INITI XID2(0) complete
+ No XID2(7)'s have yet been received.
+ XID2(7) negotiations pending.
+
+MPCG_STATE_XID7INITZ XID2(7) negotiations in progress.
+ At least 1, but not all, XID2(7)'s
+ have been received from partner.
+
+MPCG_STATE_XID7INITF XID2(7) negotiations complete.
+ Transitioning to READY.
+
+MPCG_STATE_READY Ready for Data Transfer.
+
+*/
+
+enum mpcg_events {
+ MPCG_EVENT_INOP,
+ MPCG_EVENT_DISCONC,
+ MPCG_EVENT_XID0DO,
+ MPCG_EVENT_XID2,
+ MPCG_EVENT_XID2DONE,
+ MPCG_EVENT_XID7DONE,
+ MPCG_EVENT_TIMER,
+ MPCG_EVENT_DOIO,
+ MPCG_NR_EVENTS,
+};
+
+enum mpcg_states {
+ MPCG_STATE_RESET,
+ MPCG_STATE_INOP,
+ MPCG_STATE_XID2INITW,
+ MPCG_STATE_XID2INITX,
+ MPCG_STATE_XID7INITW,
+ MPCG_STATE_XID7INITX,
+ MPCG_STATE_XID0IOWAIT,
+ MPCG_STATE_XID0IOWAIX,
+ MPCG_STATE_XID7INITI,
+ MPCG_STATE_XID7INITZ,
+ MPCG_STATE_XID7INITF,
+ MPCG_STATE_FLOWC,
+ MPCG_STATE_READY,
+ MPCG_NR_STATES,
+};
+
+#endif
+/* --- This is the END my friend --- */
diff --git a/drivers/s390/net/ctcm_main.c b/drivers/s390/net/ctcm_main.c
new file mode 100644
index 00000000000..03b6ad03557
--- /dev/null
+++ b/drivers/s390/net/ctcm_main.c
@@ -0,0 +1,1871 @@
+/*
+ * Copyright IBM Corp. 2001, 2009
+ * Author(s):
+ * Original CTC driver(s):
+ * Fritz Elfert (felfert@millenux.com)
+ * Dieter Wellerdiek (wel@de.ibm.com)
+ * Martin Schwidefsky (schwidefsky@de.ibm.com)
+ * Denis Joseph Barrow (barrow_dj@yahoo.com)
+ * Jochen Roehrig (roehrig@de.ibm.com)
+ * Cornelia Huck <cornelia.huck@de.ibm.com>
+ * MPC additions:
+ * Belinda Thompson (belindat@us.ibm.com)
+ * Andy Richter (richtera@us.ibm.com)
+ * Revived by:
+ * Peter Tiedemann (ptiedem@de.ibm.com)
+ */
+
+#undef DEBUG
+#undef DEBUGDATA
+#undef DEBUGCCW
+
+#define KMSG_COMPONENT "ctcm"
+#define pr_fmt(fmt) KMSG_COMPONENT ": " fmt
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/errno.h>
+#include <linux/types.h>
+#include <linux/interrupt.h>
+#include <linux/timer.h>
+#include <linux/bitops.h>
+
+#include <linux/signal.h>
+#include <linux/string.h>
+
+#include <linux/ip.h>
+#include <linux/if_arp.h>
+#include <linux/tcp.h>
+#include <linux/skbuff.h>
+#include <linux/ctype.h>
+#include <net/dst.h>
+
+#include <linux/io.h>
+#include <asm/ccwdev.h>
+#include <asm/ccwgroup.h>
+#include <linux/uaccess.h>
+
+#include <asm/idals.h>
+
+#include "ctcm_fsms.h"
+#include "ctcm_main.h"
+
+/* Some common global variables */
+
+/**
+ * The root device for ctcm group devices
+ */
+static struct device *ctcm_root_dev;
+
+/*
+ * Linked list of all detected channels.
+ */
+struct channel *channels;
+
+/**
+ * Unpack a just received skb and hand it over to
+ * upper layers.
+ *
+ * ch The channel where this skb has been received.
+ * pskb The received skb.
+ */
+void ctcm_unpack_skb(struct channel *ch, struct sk_buff *pskb)
+{
+ struct net_device *dev = ch->netdev;
+ struct ctcm_priv *priv = dev->ml_priv;
+ __u16 len = *((__u16 *) pskb->data);
+
+ skb_put(pskb, 2 + LL_HEADER_LENGTH);
+ skb_pull(pskb, 2);
+ pskb->dev = dev;
+ pskb->ip_summed = CHECKSUM_UNNECESSARY;
+ while (len > 0) {
+ struct sk_buff *skb;
+ int skblen;
+ struct ll_header *header = (struct ll_header *)pskb->data;
+
+ skb_pull(pskb, LL_HEADER_LENGTH);
+ if ((ch->protocol == CTCM_PROTO_S390) &&
+ (header->type != ETH_P_IP)) {
+ if (!(ch->logflags & LOG_FLAG_ILLEGALPKT)) {
+ ch->logflags |= LOG_FLAG_ILLEGALPKT;
+ /*
+ * Check packet type only if we stick strictly
+ * to S/390's protocol of OS390. This only
+ * supports IP. Otherwise allow any packet
+ * type.
+ */
+ CTCM_DBF_TEXT_(ERROR, CTC_DBF_ERROR,
+ "%s(%s): Illegal packet type 0x%04x"
+ " - dropping",
+ CTCM_FUNTAIL, dev->name, header->type);
+ }
+ priv->stats.rx_dropped++;
+ priv->stats.rx_frame_errors++;
+ return;
+ }
+ pskb->protocol = ntohs(header->type);
+ if ((header->length <= LL_HEADER_LENGTH) ||
+ (len <= LL_HEADER_LENGTH)) {
+ if (!(ch->logflags & LOG_FLAG_ILLEGALSIZE)) {
+ CTCM_DBF_TEXT_(ERROR, CTC_DBF_ERROR,
+ "%s(%s): Illegal packet size %d(%d,%d)"
+ "- dropping",
+ CTCM_FUNTAIL, dev->name,
+ header->length, dev->mtu, len);
+ ch->logflags |= LOG_FLAG_ILLEGALSIZE;
+ }
+
+ priv->stats.rx_dropped++;
+ priv->stats.rx_length_errors++;
+ return;
+ }
+ header->length -= LL_HEADER_LENGTH;
+ len -= LL_HEADER_LENGTH;
+ if ((header->length > skb_tailroom(pskb)) ||
+ (header->length > len)) {
+ if (!(ch->logflags & LOG_FLAG_OVERRUN)) {
+ CTCM_DBF_TEXT_(ERROR, CTC_DBF_ERROR,
+ "%s(%s): Packet size %d (overrun)"
+ " - dropping", CTCM_FUNTAIL,
+ dev->name, header->length);
+ ch->logflags |= LOG_FLAG_OVERRUN;
+ }
+
+ priv->stats.rx_dropped++;
+ priv->stats.rx_length_errors++;
+ return;
+ }
+ skb_put(pskb, header->length);
+ skb_reset_mac_header(pskb);
+ len -= header->length;
+ skb = dev_alloc_skb(pskb->len);
+ if (!skb) {
+ if (!(ch->logflags & LOG_FLAG_NOMEM)) {
+ CTCM_DBF_TEXT_(ERROR, CTC_DBF_ERROR,
+ "%s(%s): MEMORY allocation error",
+ CTCM_FUNTAIL, dev->name);
+ ch->logflags |= LOG_FLAG_NOMEM;
+ }
+ priv->stats.rx_dropped++;
+ return;
+ }
+ skb_copy_from_linear_data(pskb, skb_put(skb, pskb->len),
+ pskb->len);
+ skb_reset_mac_header(skb);
+ skb->dev = pskb->dev;
+ skb->protocol = pskb->protocol;
+ pskb->ip_summed = CHECKSUM_UNNECESSARY;
+ skblen = skb->len;
+ /*
+ * reset logflags
+ */
+ ch->logflags = 0;
+ priv->stats.rx_packets++;
+ priv->stats.rx_bytes += skblen;
+ netif_rx_ni(skb);
+ if (len > 0) {
+ skb_pull(pskb, header->length);
+ if (skb_tailroom(pskb) < LL_HEADER_LENGTH) {
+ CTCM_DBF_DEV_NAME(TRACE, dev,
+ "Overrun in ctcm_unpack_skb");
+ ch->logflags |= LOG_FLAG_OVERRUN;
+ return;
+ }
+ skb_put(pskb, LL_HEADER_LENGTH);
+ }
+ }
+}
+
+/**
+ * Release a specific channel in the channel list.
+ *
+ * ch Pointer to channel struct to be released.
+ */
+static void channel_free(struct channel *ch)
+{
+ CTCM_DBF_TEXT_(SETUP, CTC_DBF_INFO, "%s(%s)", CTCM_FUNTAIL, ch->id);
+ ch->flags &= ~CHANNEL_FLAGS_INUSE;
+ fsm_newstate(ch->fsm, CTC_STATE_IDLE);
+}
+
+/**
+ * Remove a specific channel in the channel list.
+ *
+ * ch Pointer to channel struct to be released.
+ */
+static void channel_remove(struct channel *ch)
+{
+ struct channel **c = &channels;
+ char chid[CTCM_ID_SIZE+1];
+ int ok = 0;
+
+ if (ch == NULL)
+ return;
+ else
+ strncpy(chid, ch->id, CTCM_ID_SIZE);
+
+ channel_free(ch);
+ while (*c) {
+ if (*c == ch) {
+ *c = ch->next;
+ fsm_deltimer(&ch->timer);
+ if (IS_MPC(ch))
+ fsm_deltimer(&ch->sweep_timer);
+
+ kfree_fsm(ch->fsm);
+ clear_normalized_cda(&ch->ccw[4]);
+ if (ch->trans_skb != NULL) {
+ clear_normalized_cda(&ch->ccw[1]);
+ dev_kfree_skb_any(ch->trans_skb);
+ }
+ if (IS_MPC(ch)) {
+ tasklet_kill(&ch->ch_tasklet);
+ tasklet_kill(&ch->ch_disc_tasklet);
+ kfree(ch->discontact_th);
+ }
+ kfree(ch->ccw);
+ kfree(ch->irb);
+ kfree(ch);
+ ok = 1;
+ break;
+ }
+ c = &((*c)->next);
+ }
+
+ CTCM_DBF_TEXT_(SETUP, CTC_DBF_INFO, "%s(%s) %s", CTCM_FUNTAIL,
+ chid, ok ? "OK" : "failed");
+}
+
+/**
+ * Get a specific channel from the channel list.
+ *
+ * type Type of channel we are interested in.
+ * id Id of channel we are interested in.
+ * direction Direction we want to use this channel for.
+ *
+ * returns Pointer to a channel or NULL if no matching channel available.
+ */
+static struct channel *channel_get(enum ctcm_channel_types type,
+ char *id, int direction)
+{
+ struct channel *ch = channels;
+
+ while (ch && (strncmp(ch->id, id, CTCM_ID_SIZE) || (ch->type != type)))
+ ch = ch->next;
+ if (!ch) {
+ CTCM_DBF_TEXT_(ERROR, CTC_DBF_ERROR,
+ "%s(%d, %s, %d) not found in channel list\n",
+ CTCM_FUNTAIL, type, id, direction);
+ } else {
+ if (ch->flags & CHANNEL_FLAGS_INUSE)
+ ch = NULL;
+ else {
+ ch->flags |= CHANNEL_FLAGS_INUSE;
+ ch->flags &= ~CHANNEL_FLAGS_RWMASK;
+ ch->flags |= (direction == CTCM_WRITE)
+ ? CHANNEL_FLAGS_WRITE : CHANNEL_FLAGS_READ;
+ fsm_newstate(ch->fsm, CTC_STATE_STOPPED);
+ }
+ }
+ return ch;
+}
+
+static long ctcm_check_irb_error(struct ccw_device *cdev, struct irb *irb)
+{
+ if (!IS_ERR(irb))
+ return 0;
+
+ CTCM_DBF_TEXT_(ERROR, CTC_DBF_WARN,
+ "irb error %ld on device %s\n",
+ PTR_ERR(irb), dev_name(&cdev->dev));
+
+ switch (PTR_ERR(irb)) {
+ case -EIO:
+ dev_err(&cdev->dev,
+ "An I/O-error occurred on the CTCM device\n");
+ break;
+ case -ETIMEDOUT:
+ dev_err(&cdev->dev,
+ "An adapter hardware operation timed out\n");
+ break;
+ default:
+ dev_err(&cdev->dev,
+ "An error occurred on the adapter hardware\n");
+ }
+ return PTR_ERR(irb);
+}
+
+
+/**
+ * Check sense of a unit check.
+ *
+ * ch The channel, the sense code belongs to.
+ * sense The sense code to inspect.
+ */
+static inline void ccw_unit_check(struct channel *ch, __u8 sense)
+{
+ CTCM_DBF_TEXT_(TRACE, CTC_DBF_DEBUG,
+ "%s(%s): %02x",
+ CTCM_FUNTAIL, ch->id, sense);
+
+ if (sense & SNS0_INTERVENTION_REQ) {
+ if (sense & 0x01) {
+ if (ch->sense_rc != 0x01) {
+ pr_notice(
+ "%s: The communication peer has "
+ "disconnected\n", ch->id);
+ ch->sense_rc = 0x01;
+ }
+ fsm_event(ch->fsm, CTC_EVENT_UC_RCRESET, ch);
+ } else {
+ if (ch->sense_rc != SNS0_INTERVENTION_REQ) {
+ pr_notice(
+ "%s: The remote operating system is "
+ "not available\n", ch->id);
+ ch->sense_rc = SNS0_INTERVENTION_REQ;
+ }
+ fsm_event(ch->fsm, CTC_EVENT_UC_RSRESET, ch);
+ }
+ } else if (sense & SNS0_EQUIPMENT_CHECK) {
+ if (sense & SNS0_BUS_OUT_CHECK) {
+ if (ch->sense_rc != SNS0_BUS_OUT_CHECK) {
+ CTCM_DBF_TEXT_(TRACE, CTC_DBF_WARN,
+ "%s(%s): remote HW error %02x",
+ CTCM_FUNTAIL, ch->id, sense);
+ ch->sense_rc = SNS0_BUS_OUT_CHECK;
+ }
+ fsm_event(ch->fsm, CTC_EVENT_UC_HWFAIL, ch);
+ } else {
+ if (ch->sense_rc != SNS0_EQUIPMENT_CHECK) {
+ CTCM_DBF_TEXT_(TRACE, CTC_DBF_WARN,
+ "%s(%s): remote read parity error %02x",
+ CTCM_FUNTAIL, ch->id, sense);
+ ch->sense_rc = SNS0_EQUIPMENT_CHECK;
+ }
+ fsm_event(ch->fsm, CTC_EVENT_UC_RXPARITY, ch);
+ }
+ } else if (sense & SNS0_BUS_OUT_CHECK) {
+ if (ch->sense_rc != SNS0_BUS_OUT_CHECK) {
+ CTCM_DBF_TEXT_(TRACE, CTC_DBF_WARN,
+ "%s(%s): BUS OUT error %02x",
+ CTCM_FUNTAIL, ch->id, sense);
+ ch->sense_rc = SNS0_BUS_OUT_CHECK;
+ }
+ if (sense & 0x04) /* data-streaming timeout */
+ fsm_event(ch->fsm, CTC_EVENT_UC_TXTIMEOUT, ch);
+ else /* Data-transfer parity error */
+ fsm_event(ch->fsm, CTC_EVENT_UC_TXPARITY, ch);
+ } else if (sense & SNS0_CMD_REJECT) {
+ if (ch->sense_rc != SNS0_CMD_REJECT) {
+ CTCM_DBF_TEXT_(TRACE, CTC_DBF_WARN,
+ "%s(%s): Command rejected",
+ CTCM_FUNTAIL, ch->id);
+ ch->sense_rc = SNS0_CMD_REJECT;
+ }
+ } else if (sense == 0) {
+ CTCM_DBF_TEXT_(TRACE, CTC_DBF_WARN,
+ "%s(%s): Unit check ZERO",
+ CTCM_FUNTAIL, ch->id);
+ fsm_event(ch->fsm, CTC_EVENT_UC_ZERO, ch);
+ } else {
+ CTCM_DBF_TEXT_(TRACE, CTC_DBF_WARN,
+ "%s(%s): Unit check code %02x unknown",
+ CTCM_FUNTAIL, ch->id, sense);
+ fsm_event(ch->fsm, CTC_EVENT_UC_UNKNOWN, ch);
+ }
+}
+
+int ctcm_ch_alloc_buffer(struct channel *ch)
+{
+ clear_normalized_cda(&ch->ccw[1]);
+ ch->trans_skb = __dev_alloc_skb(ch->max_bufsize, GFP_ATOMIC | GFP_DMA);
+ if (ch->trans_skb == NULL) {
+ CTCM_DBF_TEXT_(ERROR, CTC_DBF_ERROR,
+ "%s(%s): %s trans_skb allocation error",
+ CTCM_FUNTAIL, ch->id,
+ (CHANNEL_DIRECTION(ch->flags) == CTCM_READ) ?
+ "RX" : "TX");
+ return -ENOMEM;
+ }
+
+ ch->ccw[1].count = ch->max_bufsize;
+ if (set_normalized_cda(&ch->ccw[1], ch->trans_skb->data)) {
+ dev_kfree_skb(ch->trans_skb);
+ ch->trans_skb = NULL;
+ CTCM_DBF_TEXT_(ERROR, CTC_DBF_ERROR,
+ "%s(%s): %s set norm_cda failed",
+ CTCM_FUNTAIL, ch->id,
+ (CHANNEL_DIRECTION(ch->flags) == CTCM_READ) ?
+ "RX" : "TX");
+ return -ENOMEM;
+ }
+
+ ch->ccw[1].count = 0;
+ ch->trans_skb_data = ch->trans_skb->data;
+ ch->flags &= ~CHANNEL_FLAGS_BUFSIZE_CHANGED;
+ return 0;
+}
+
+/*
+ * Interface API for upper network layers
+ */
+
+/**
+ * Open an interface.
+ * Called from generic network layer when ifconfig up is run.
+ *
+ * dev Pointer to interface struct.
+ *
+ * returns 0 on success, -ERRNO on failure. (Never fails.)
+ */
+int ctcm_open(struct net_device *dev)
+{
+ struct ctcm_priv *priv = dev->ml_priv;
+
+ CTCMY_DBF_DEV_NAME(SETUP, dev, "");
+ if (!IS_MPC(priv))
+ fsm_event(priv->fsm, DEV_EVENT_START, dev);
+ return 0;
+}
+
+/**
+ * Close an interface.
+ * Called from generic network layer when ifconfig down is run.
+ *
+ * dev Pointer to interface struct.
+ *
+ * returns 0 on success, -ERRNO on failure. (Never fails.)
+ */
+int ctcm_close(struct net_device *dev)
+{
+ struct ctcm_priv *priv = dev->ml_priv;
+
+ CTCMY_DBF_DEV_NAME(SETUP, dev, "");
+ if (!IS_MPC(priv))
+ fsm_event(priv->fsm, DEV_EVENT_STOP, dev);
+ return 0;
+}
+
+
+/**
+ * Transmit a packet.
+ * This is a helper function for ctcm_tx().
+ *
+ * ch Channel to be used for sending.
+ * skb Pointer to struct sk_buff of packet to send.
+ * The linklevel header has already been set up
+ * by ctcm_tx().
+ *
+ * returns 0 on success, -ERRNO on failure. (Never fails.)
+ */
+static int ctcm_transmit_skb(struct channel *ch, struct sk_buff *skb)
+{
+ unsigned long saveflags;
+ struct ll_header header;
+ int rc = 0;
+ __u16 block_len;
+ int ccw_idx;
+ struct sk_buff *nskb;
+ unsigned long hi;
+
+ /* we need to acquire the lock for testing the state
+ * otherwise we can have an IRQ changing the state to
+ * TXIDLE after the test but before acquiring the lock.
+ */
+ spin_lock_irqsave(&ch->collect_lock, saveflags);
+ if (fsm_getstate(ch->fsm) != CTC_STATE_TXIDLE) {
+ int l = skb->len + LL_HEADER_LENGTH;
+
+ if (ch->collect_len + l > ch->max_bufsize - 2) {
+ spin_unlock_irqrestore(&ch->collect_lock, saveflags);
+ return -EBUSY;
+ } else {
+ atomic_inc(&skb->users);
+ header.length = l;
+ header.type = skb->protocol;
+ header.unused = 0;
+ memcpy(skb_push(skb, LL_HEADER_LENGTH), &header,
+ LL_HEADER_LENGTH);
+ skb_queue_tail(&ch->collect_queue, skb);
+ ch->collect_len += l;
+ }
+ spin_unlock_irqrestore(&ch->collect_lock, saveflags);
+ goto done;
+ }
+ spin_unlock_irqrestore(&ch->collect_lock, saveflags);
+ /*
+ * Protect skb against beeing free'd by upper
+ * layers.
+ */
+ atomic_inc(&skb->users);
+ ch->prof.txlen += skb->len;
+ header.length = skb->len + LL_HEADER_LENGTH;
+ header.type = skb->protocol;
+ header.unused = 0;
+ memcpy(skb_push(skb, LL_HEADER_LENGTH), &header, LL_HEADER_LENGTH);
+ block_len = skb->len + 2;
+ *((__u16 *)skb_push(skb, 2)) = block_len;
+
+ /*
+ * IDAL support in CTCM is broken, so we have to
+ * care about skb's above 2G ourselves.
+ */
+ hi = ((unsigned long)skb_tail_pointer(skb) + LL_HEADER_LENGTH) >> 31;
+ if (hi) {
+ nskb = alloc_skb(skb->len, GFP_ATOMIC | GFP_DMA);
+ if (!nskb) {
+ atomic_dec(&skb->users);
+ skb_pull(skb, LL_HEADER_LENGTH + 2);
+ ctcm_clear_busy(ch->netdev);
+ return -ENOMEM;
+ } else {
+ memcpy(skb_put(nskb, skb->len), skb->data, skb->len);
+ atomic_inc(&nskb->users);
+ atomic_dec(&skb->users);
+ dev_kfree_skb_irq(skb);
+ skb = nskb;
+ }
+ }
+
+ ch->ccw[4].count = block_len;
+ if (set_normalized_cda(&ch->ccw[4], skb->data)) {
+ /*
+ * idal allocation failed, try via copying to
+ * trans_skb. trans_skb usually has a pre-allocated
+ * idal.
+ */
+ if (ctcm_checkalloc_buffer(ch)) {
+ /*
+ * Remove our header. It gets added
+ * again on retransmit.
+ */
+ atomic_dec(&skb->users);
+ skb_pull(skb, LL_HEADER_LENGTH + 2);
+ ctcm_clear_busy(ch->netdev);
+ return -ENOMEM;
+ }
+
+ skb_reset_tail_pointer(ch->trans_skb);
+ ch->trans_skb->len = 0;
+ ch->ccw[1].count = skb->len;
+ skb_copy_from_linear_data(skb,
+ skb_put(ch->trans_skb, skb->len), skb->len);
+ atomic_dec(&skb->users);
+ dev_kfree_skb_irq(skb);
+ ccw_idx = 0;
+ } else {
+ skb_queue_tail(&ch->io_queue, skb);
+ ccw_idx = 3;
+ }
+ if (do_debug_ccw)
+ ctcmpc_dumpit((char *)&ch->ccw[ccw_idx],
+ sizeof(struct ccw1) * 3);
+ ch->retry = 0;
+ fsm_newstate(ch->fsm, CTC_STATE_TX);
+ fsm_addtimer(&ch->timer, CTCM_TIME_5_SEC, CTC_EVENT_TIMER, ch);
+ spin_lock_irqsave(get_ccwdev_lock(ch->cdev), saveflags);
+ ch->prof.send_stamp = current_kernel_time(); /* xtime */
+ rc = ccw_device_start(ch->cdev, &ch->ccw[ccw_idx],
+ (unsigned long)ch, 0xff, 0);
+ spin_unlock_irqrestore(get_ccwdev_lock(ch->cdev), saveflags);
+ if (ccw_idx == 3)
+ ch->prof.doios_single++;
+ if (rc != 0) {
+ fsm_deltimer(&ch->timer);
+ ctcm_ccw_check_rc(ch, rc, "single skb TX");
+ if (ccw_idx == 3)
+ skb_dequeue_tail(&ch->io_queue);
+ /*
+ * Remove our header. It gets added
+ * again on retransmit.
+ */
+ skb_pull(skb, LL_HEADER_LENGTH + 2);
+ } else if (ccw_idx == 0) {
+ struct net_device *dev = ch->netdev;
+ struct ctcm_priv *priv = dev->ml_priv;
+ priv->stats.tx_packets++;
+ priv->stats.tx_bytes += skb->len - LL_HEADER_LENGTH;
+ }
+done:
+ ctcm_clear_busy(ch->netdev);
+ return rc;
+}
+
+static void ctcmpc_send_sweep_req(struct channel *rch)
+{
+ struct net_device *dev = rch->netdev;
+ struct ctcm_priv *priv;
+ struct mpc_group *grp;
+ struct th_sweep *header;
+ struct sk_buff *sweep_skb;
+ struct channel *ch;
+ /* int rc = 0; */
+
+ priv = dev->ml_priv;
+ grp = priv->mpcg;
+ ch = priv->channel[CTCM_WRITE];
+
+ /* sweep processing is not complete until response and request */
+ /* has completed for all read channels in group */
+ if (grp->in_sweep == 0) {
+ grp->in_sweep = 1;
+ grp->sweep_rsp_pend_num = grp->active_channels[CTCM_READ];
+ grp->sweep_req_pend_num = grp->active_channels[CTCM_READ];
+ }
+
+ sweep_skb = __dev_alloc_skb(MPC_BUFSIZE_DEFAULT, GFP_ATOMIC|GFP_DMA);
+
+ if (sweep_skb == NULL) {
+ /* rc = -ENOMEM; */
+ goto nomem;
+ }
+
+ header = kmalloc(TH_SWEEP_LENGTH, gfp_type());
+
+ if (!header) {
+ dev_kfree_skb_any(sweep_skb);
+ /* rc = -ENOMEM; */
+ goto nomem;
+ }
+
+ header->th.th_seg = 0x00 ;
+ header->th.th_ch_flag = TH_SWEEP_REQ; /* 0x0f */
+ header->th.th_blk_flag = 0x00;
+ header->th.th_is_xid = 0x00;
+ header->th.th_seq_num = 0x00;
+ header->sw.th_last_seq = ch->th_seq_num;
+
+ memcpy(skb_put(sweep_skb, TH_SWEEP_LENGTH), header, TH_SWEEP_LENGTH);
+
+ kfree(header);
+
+ dev->trans_start = jiffies;
+ skb_queue_tail(&ch->sweep_queue, sweep_skb);
+
+ fsm_addtimer(&ch->sweep_timer, 100, CTC_EVENT_RSWEEP_TIMER, ch);
+
+ return;
+
+nomem:
+ grp->in_sweep = 0;
+ ctcm_clear_busy(dev);
+ fsm_event(grp->fsm, MPCG_EVENT_INOP, dev);
+
+ return;
+}
+
+/*
+ * MPC mode version of transmit_skb
+ */
+static int ctcmpc_transmit_skb(struct channel *ch, struct sk_buff *skb)
+{
+ struct pdu *p_header;
+ struct net_device *dev = ch->netdev;
+ struct ctcm_priv *priv = dev->ml_priv;
+ struct mpc_group *grp = priv->mpcg;
+ struct th_header *header;
+ struct sk_buff *nskb;
+ int rc = 0;
+ int ccw_idx;
+ unsigned long hi;
+ unsigned long saveflags = 0; /* avoids compiler warning */
+
+ CTCM_PR_DEBUG("Enter %s: %s, cp=%i ch=0x%p id=%s state=%s\n",
+ __func__, dev->name, smp_processor_id(), ch,
+ ch->id, fsm_getstate_str(ch->fsm));
+
+ if ((fsm_getstate(ch->fsm) != CTC_STATE_TXIDLE) || grp->in_sweep) {
+ spin_lock_irqsave(&ch->collect_lock, saveflags);
+ atomic_inc(&skb->users);
+ p_header = kmalloc(PDU_HEADER_LENGTH, gfp_type());
+
+ if (!p_header) {
+ spin_unlock_irqrestore(&ch->collect_lock, saveflags);
+ goto nomem_exit;
+ }
+
+ p_header->pdu_offset = skb->len;
+ p_header->pdu_proto = 0x01;
+ p_header->pdu_flag = 0x00;
+ if (skb->protocol == ntohs(ETH_P_SNAP)) {
+ p_header->pdu_flag |= PDU_FIRST | PDU_CNTL;
+ } else {
+ p_header->pdu_flag |= PDU_FIRST;
+ }
+ p_header->pdu_seq = 0;
+ memcpy(skb_push(skb, PDU_HEADER_LENGTH), p_header,
+ PDU_HEADER_LENGTH);
+
+ CTCM_PR_DEBUG("%s(%s): Put on collect_q - skb len: %04x \n"
+ "pdu header and data for up to 32 bytes:\n",
+ __func__, dev->name, skb->len);
+ CTCM_D3_DUMP((char *)skb->data, min_t(int, 32, skb->len));
+
+ skb_queue_tail(&ch->collect_queue, skb);
+ ch->collect_len += skb->len;
+ kfree(p_header);
+
+ spin_unlock_irqrestore(&ch->collect_lock, saveflags);
+ goto done;
+ }
+
+ /*
+ * Protect skb against beeing free'd by upper
+ * layers.
+ */
+ atomic_inc(&skb->users);
+
+ /*
+ * IDAL support in CTCM is broken, so we have to
+ * care about skb's above 2G ourselves.
+ */
+ hi = ((unsigned long)skb->tail + TH_HEADER_LENGTH) >> 31;
+ if (hi) {
+ nskb = __dev_alloc_skb(skb->len, GFP_ATOMIC | GFP_DMA);
+ if (!nskb) {
+ goto nomem_exit;
+ } else {
+ memcpy(skb_put(nskb, skb->len), skb->data, skb->len);
+ atomic_inc(&nskb->users);
+ atomic_dec(&skb->users);
+ dev_kfree_skb_irq(skb);
+ skb = nskb;
+ }
+ }
+
+ p_header = kmalloc(PDU_HEADER_LENGTH, gfp_type());
+
+ if (!p_header)
+ goto nomem_exit;
+
+ p_header->pdu_offset = skb->len;
+ p_header->pdu_proto = 0x01;
+ p_header->pdu_flag = 0x00;
+ p_header->pdu_seq = 0;
+ if (skb->protocol == ntohs(ETH_P_SNAP)) {
+ p_header->pdu_flag |= PDU_FIRST | PDU_CNTL;
+ } else {
+ p_header->pdu_flag |= PDU_FIRST;
+ }
+ memcpy(skb_push(skb, PDU_HEADER_LENGTH), p_header, PDU_HEADER_LENGTH);
+
+ kfree(p_header);
+
+ if (ch->collect_len > 0) {
+ spin_lock_irqsave(&ch->collect_lock, saveflags);
+ skb_queue_tail(&ch->collect_queue, skb);
+ ch->collect_len += skb->len;
+ skb = skb_dequeue(&ch->collect_queue);
+ ch->collect_len -= skb->len;
+ spin_unlock_irqrestore(&ch->collect_lock, saveflags);
+ }
+
+ p_header = (struct pdu *)skb->data;
+ p_header->pdu_flag |= PDU_LAST;
+
+ ch->prof.txlen += skb->len - PDU_HEADER_LENGTH;
+
+ header = kmalloc(TH_HEADER_LENGTH, gfp_type());
+ if (!header)
+ goto nomem_exit;
+
+ header->th_seg = 0x00;
+ header->th_ch_flag = TH_HAS_PDU; /* Normal data */
+ header->th_blk_flag = 0x00;
+ header->th_is_xid = 0x00; /* Just data here */
+ ch->th_seq_num++;
+ header->th_seq_num = ch->th_seq_num;
+
+ CTCM_PR_DBGDATA("%s(%s) ToVTAM_th_seq= %08x\n" ,
+ __func__, dev->name, ch->th_seq_num);
+
+ /* put the TH on the packet */
+ memcpy(skb_push(skb, TH_HEADER_LENGTH), header, TH_HEADER_LENGTH);
+
+ kfree(header);
+
+ CTCM_PR_DBGDATA("%s(%s): skb len: %04x\n - pdu header and data for "
+ "up to 32 bytes sent to vtam:\n",
+ __func__, dev->name, skb->len);
+ CTCM_D3_DUMP((char *)skb->data, min_t(int, 32, skb->len));
+
+ ch->ccw[4].count = skb->len;
+ if (set_normalized_cda(&ch->ccw[4], skb->data)) {
+ /*
+ * idal allocation failed, try via copying to trans_skb.
+ * trans_skb usually has a pre-allocated idal.
+ */
+ if (ctcm_checkalloc_buffer(ch)) {
+ /*
+ * Remove our header.
+ * It gets added again on retransmit.
+ */
+ goto nomem_exit;
+ }
+
+ skb_reset_tail_pointer(ch->trans_skb);
+ ch->trans_skb->len = 0;
+ ch->ccw[1].count = skb->len;
+ memcpy(skb_put(ch->trans_skb, skb->len), skb->data, skb->len);
+ atomic_dec(&skb->users);
+ dev_kfree_skb_irq(skb);
+ ccw_idx = 0;
+ CTCM_PR_DBGDATA("%s(%s): trans_skb len: %04x\n"
+ "up to 32 bytes sent to vtam:\n",
+ __func__, dev->name, ch->trans_skb->len);
+ CTCM_D3_DUMP((char *)ch->trans_skb->data,
+ min_t(int, 32, ch->trans_skb->len));
+ } else {
+ skb_queue_tail(&ch->io_queue, skb);
+ ccw_idx = 3;
+ }
+ ch->retry = 0;
+ fsm_newstate(ch->fsm, CTC_STATE_TX);
+ fsm_addtimer(&ch->timer, CTCM_TIME_5_SEC, CTC_EVENT_TIMER, ch);
+
+ if (do_debug_ccw)
+ ctcmpc_dumpit((char *)&ch->ccw[ccw_idx],
+ sizeof(struct ccw1) * 3);
+
+ spin_lock_irqsave(get_ccwdev_lock(ch->cdev), saveflags);
+ ch->prof.send_stamp = current_kernel_time(); /* xtime */
+ rc = ccw_device_start(ch->cdev, &ch->ccw[ccw_idx],
+ (unsigned long)ch, 0xff, 0);
+ spin_unlock_irqrestore(get_ccwdev_lock(ch->cdev), saveflags);
+ if (ccw_idx == 3)
+ ch->prof.doios_single++;
+ if (rc != 0) {
+ fsm_deltimer(&ch->timer);
+ ctcm_ccw_check_rc(ch, rc, "single skb TX");
+ if (ccw_idx == 3)
+ skb_dequeue_tail(&ch->io_queue);
+ } else if (ccw_idx == 0) {
+ priv->stats.tx_packets++;
+ priv->stats.tx_bytes += skb->len - TH_HEADER_LENGTH;
+ }
+ if (ch->th_seq_num > 0xf0000000) /* Chose at random. */
+ ctcmpc_send_sweep_req(ch);
+
+ goto done;
+nomem_exit:
+ CTCM_DBF_TEXT_(MPC_ERROR, CTC_DBF_CRIT,
+ "%s(%s): MEMORY allocation ERROR\n",
+ CTCM_FUNTAIL, ch->id);
+ rc = -ENOMEM;
+ atomic_dec(&skb->users);
+ dev_kfree_skb_any(skb);
+ fsm_event(priv->mpcg->fsm, MPCG_EVENT_INOP, dev);
+done:
+ CTCM_PR_DEBUG("Exit %s(%s)\n", __func__, dev->name);
+ return rc;
+}
+
+/**
+ * Start transmission of a packet.
+ * Called from generic network device layer.
+ *
+ * skb Pointer to buffer containing the packet.
+ * dev Pointer to interface struct.
+ *
+ * returns 0 if packet consumed, !0 if packet rejected.
+ * Note: If we return !0, then the packet is free'd by
+ * the generic network layer.
+ */
+/* first merge version - leaving both functions separated */
+static int ctcm_tx(struct sk_buff *skb, struct net_device *dev)
+{
+ struct ctcm_priv *priv = dev->ml_priv;
+
+ if (skb == NULL) {
+ CTCM_DBF_TEXT_(ERROR, CTC_DBF_ERROR,
+ "%s(%s): NULL sk_buff passed",
+ CTCM_FUNTAIL, dev->name);
+ priv->stats.tx_dropped++;
+ return NETDEV_TX_OK;
+ }
+ if (skb_headroom(skb) < (LL_HEADER_LENGTH + 2)) {
+ CTCM_DBF_TEXT_(ERROR, CTC_DBF_ERROR,
+ "%s(%s): Got sk_buff with head room < %ld bytes",
+ CTCM_FUNTAIL, dev->name, LL_HEADER_LENGTH + 2);
+ dev_kfree_skb(skb);
+ priv->stats.tx_dropped++;
+ return NETDEV_TX_OK;
+ }
+
+ /*
+ * If channels are not running, try to restart them
+ * and throw away packet.
+ */
+ if (fsm_getstate(priv->fsm) != DEV_STATE_RUNNING) {
+ fsm_event(priv->fsm, DEV_EVENT_START, dev);
+ dev_kfree_skb(skb);
+ priv->stats.tx_dropped++;
+ priv->stats.tx_errors++;
+ priv->stats.tx_carrier_errors++;
+ return NETDEV_TX_OK;
+ }
+
+ if (ctcm_test_and_set_busy(dev))
+ return NETDEV_TX_BUSY;
+
+ dev->trans_start = jiffies;
+ if (ctcm_transmit_skb(priv->channel[CTCM_WRITE], skb) != 0)
+ return NETDEV_TX_BUSY;
+ return NETDEV_TX_OK;
+}
+
+/* unmerged MPC variant of ctcm_tx */
+static int ctcmpc_tx(struct sk_buff *skb, struct net_device *dev)
+{
+ int len = 0;
+ struct ctcm_priv *priv = dev->ml_priv;
+ struct mpc_group *grp = priv->mpcg;
+ struct sk_buff *newskb = NULL;
+
+ /*
+ * Some sanity checks ...
+ */
+ if (skb == NULL) {
+ CTCM_DBF_TEXT_(MPC_ERROR, CTC_DBF_ERROR,
+ "%s(%s): NULL sk_buff passed",
+ CTCM_FUNTAIL, dev->name);
+ priv->stats.tx_dropped++;
+ goto done;
+ }
+ if (skb_headroom(skb) < (TH_HEADER_LENGTH + PDU_HEADER_LENGTH)) {
+ CTCM_DBF_TEXT_(MPC_TRACE, CTC_DBF_ERROR,
+ "%s(%s): Got sk_buff with head room < %ld bytes",
+ CTCM_FUNTAIL, dev->name,
+ TH_HEADER_LENGTH + PDU_HEADER_LENGTH);
+
+ CTCM_D3_DUMP((char *)skb->data, min_t(int, 32, skb->len));
+
+ len = skb->len + TH_HEADER_LENGTH + PDU_HEADER_LENGTH;
+ newskb = __dev_alloc_skb(len, gfp_type() | GFP_DMA);
+
+ if (!newskb) {
+ CTCM_DBF_TEXT_(MPC_TRACE, CTC_DBF_ERROR,
+ "%s: %s: __dev_alloc_skb failed",
+ __func__, dev->name);
+
+ dev_kfree_skb_any(skb);
+ priv->stats.tx_dropped++;
+ priv->stats.tx_errors++;
+ priv->stats.tx_carrier_errors++;
+ fsm_event(grp->fsm, MPCG_EVENT_INOP, dev);
+ goto done;
+ }
+ newskb->protocol = skb->protocol;
+ skb_reserve(newskb, TH_HEADER_LENGTH + PDU_HEADER_LENGTH);
+ memcpy(skb_put(newskb, skb->len), skb->data, skb->len);
+ dev_kfree_skb_any(skb);
+ skb = newskb;
+ }
+
+ /*
+ * If channels are not running,
+ * notify anybody about a link failure and throw
+ * away packet.
+ */
+ if ((fsm_getstate(priv->fsm) != DEV_STATE_RUNNING) ||
+ (fsm_getstate(grp->fsm) < MPCG_STATE_XID2INITW)) {
+ dev_kfree_skb_any(skb);
+ CTCM_DBF_TEXT_(MPC_ERROR, CTC_DBF_ERROR,
+ "%s(%s): inactive MPCGROUP - dropped",
+ CTCM_FUNTAIL, dev->name);
+ priv->stats.tx_dropped++;
+ priv->stats.tx_errors++;
+ priv->stats.tx_carrier_errors++;
+ goto done;
+ }
+
+ if (ctcm_test_and_set_busy(dev)) {
+ CTCM_DBF_TEXT_(MPC_ERROR, CTC_DBF_ERROR,
+ "%s(%s): device busy - dropped",
+ CTCM_FUNTAIL, dev->name);
+ dev_kfree_skb_any(skb);
+ priv->stats.tx_dropped++;
+ priv->stats.tx_errors++;
+ priv->stats.tx_carrier_errors++;
+ fsm_event(grp->fsm, MPCG_EVENT_INOP, dev);
+ goto done;
+ }
+
+ dev->trans_start = jiffies;
+ if (ctcmpc_transmit_skb(priv->channel[CTCM_WRITE], skb) != 0) {
+ CTCM_DBF_TEXT_(MPC_ERROR, CTC_DBF_ERROR,
+ "%s(%s): device error - dropped",
+ CTCM_FUNTAIL, dev->name);
+ dev_kfree_skb_any(skb);
+ priv->stats.tx_dropped++;
+ priv->stats.tx_errors++;
+ priv->stats.tx_carrier_errors++;
+ ctcm_clear_busy(dev);
+ fsm_event(grp->fsm, MPCG_EVENT_INOP, dev);
+ goto done;
+ }
+ ctcm_clear_busy(dev);
+done:
+ if (do_debug)
+ MPC_DBF_DEV_NAME(TRACE, dev, "exit");
+
+ return NETDEV_TX_OK; /* handle freeing of skb here */
+}
+
+
+/**
+ * Sets MTU of an interface.
+ *
+ * dev Pointer to interface struct.
+ * new_mtu The new MTU to use for this interface.
+ *
+ * returns 0 on success, -EINVAL if MTU is out of valid range.
+ * (valid range is 576 .. 65527). If VM is on the
+ * remote side, maximum MTU is 32760, however this is
+ * not checked here.
+ */
+static int ctcm_change_mtu(struct net_device *dev, int new_mtu)
+{
+ struct ctcm_priv *priv;
+ int max_bufsize;
+
+ if (new_mtu < 576 || new_mtu > 65527)
+ return -EINVAL;
+
+ priv = dev->ml_priv;
+ max_bufsize = priv->channel[CTCM_READ]->max_bufsize;
+
+ if (IS_MPC(priv)) {
+ if (new_mtu > max_bufsize - TH_HEADER_LENGTH)
+ return -EINVAL;
+ dev->hard_header_len = TH_HEADER_LENGTH + PDU_HEADER_LENGTH;
+ } else {
+ if (new_mtu > max_bufsize - LL_HEADER_LENGTH - 2)
+ return -EINVAL;
+ dev->hard_header_len = LL_HEADER_LENGTH + 2;
+ }
+ dev->mtu = new_mtu;
+ return 0;
+}
+
+/**
+ * Returns interface statistics of a device.
+ *
+ * dev Pointer to interface struct.
+ *
+ * returns Pointer to stats struct of this interface.
+ */
+static struct net_device_stats *ctcm_stats(struct net_device *dev)
+{
+ return &((struct ctcm_priv *)dev->ml_priv)->stats;
+}
+
+static void ctcm_free_netdevice(struct net_device *dev)
+{
+ struct ctcm_priv *priv;
+ struct mpc_group *grp;
+
+ CTCM_DBF_TEXT_(SETUP, CTC_DBF_INFO,
+ "%s(%s)", CTCM_FUNTAIL, dev->name);
+ priv = dev->ml_priv;
+ if (priv) {
+ grp = priv->mpcg;
+ if (grp) {
+ if (grp->fsm)
+ kfree_fsm(grp->fsm);
+ if (grp->xid_skb)
+ dev_kfree_skb(grp->xid_skb);
+ if (grp->rcvd_xid_skb)
+ dev_kfree_skb(grp->rcvd_xid_skb);
+ tasklet_kill(&grp->mpc_tasklet2);
+ kfree(grp);
+ priv->mpcg = NULL;
+ }
+ if (priv->fsm) {
+ kfree_fsm(priv->fsm);
+ priv->fsm = NULL;
+ }
+ kfree(priv->xid);
+ priv->xid = NULL;
+ /*
+ * Note: kfree(priv); is done in "opposite" function of
+ * allocator function probe_device which is remove_device.
+ */
+ }
+#ifdef MODULE
+ free_netdev(dev);
+#endif
+}
+
+struct mpc_group *ctcmpc_init_mpc_group(struct ctcm_priv *priv);
+
+static const struct net_device_ops ctcm_netdev_ops = {
+ .ndo_open = ctcm_open,
+ .ndo_stop = ctcm_close,
+ .ndo_get_stats = ctcm_stats,
+ .ndo_change_mtu = ctcm_change_mtu,
+ .ndo_start_xmit = ctcm_tx,
+};
+
+static const struct net_device_ops ctcm_mpc_netdev_ops = {
+ .ndo_open = ctcm_open,
+ .ndo_stop = ctcm_close,
+ .ndo_get_stats = ctcm_stats,
+ .ndo_change_mtu = ctcm_change_mtu,
+ .ndo_start_xmit = ctcmpc_tx,
+};
+
+void static ctcm_dev_setup(struct net_device *dev)
+{
+ dev->type = ARPHRD_SLIP;
+ dev->tx_queue_len = 100;
+ dev->flags = IFF_POINTOPOINT | IFF_NOARP;
+}
+
+/*
+ * Initialize everything of the net device except the name and the
+ * channel structs.
+ */
+static struct net_device *ctcm_init_netdevice(struct ctcm_priv *priv)
+{
+ struct net_device *dev;
+ struct mpc_group *grp;
+ if (!priv)
+ return NULL;
+
+ if (IS_MPC(priv))
+ dev = alloc_netdev(0, MPC_DEVICE_GENE, ctcm_dev_setup);
+ else
+ dev = alloc_netdev(0, CTC_DEVICE_GENE, ctcm_dev_setup);
+
+ if (!dev) {
+ CTCM_DBF_TEXT_(ERROR, CTC_DBF_CRIT,
+ "%s: MEMORY allocation ERROR",
+ CTCM_FUNTAIL);
+ return NULL;
+ }
+ dev->ml_priv = priv;
+ priv->fsm = init_fsm("ctcmdev", dev_state_names, dev_event_names,
+ CTCM_NR_DEV_STATES, CTCM_NR_DEV_EVENTS,
+ dev_fsm, dev_fsm_len, GFP_KERNEL);
+ if (priv->fsm == NULL) {
+ CTCMY_DBF_DEV(SETUP, dev, "init_fsm error");
+ free_netdev(dev);
+ return NULL;
+ }
+ fsm_newstate(priv->fsm, DEV_STATE_STOPPED);
+ fsm_settimer(priv->fsm, &priv->restart_timer);
+
+ if (IS_MPC(priv)) {
+ /* MPC Group Initializations */
+ grp = ctcmpc_init_mpc_group(priv);
+ if (grp == NULL) {
+ MPC_DBF_DEV(SETUP, dev, "init_mpc_group error");
+ free_netdev(dev);
+ return NULL;
+ }
+ tasklet_init(&grp->mpc_tasklet2,
+ mpc_group_ready, (unsigned long)dev);
+ dev->mtu = MPC_BUFSIZE_DEFAULT -
+ TH_HEADER_LENGTH - PDU_HEADER_LENGTH;
+
+ dev->netdev_ops = &ctcm_mpc_netdev_ops;
+ dev->hard_header_len = TH_HEADER_LENGTH + PDU_HEADER_LENGTH;
+ priv->buffer_size = MPC_BUFSIZE_DEFAULT;
+ } else {
+ dev->mtu = CTCM_BUFSIZE_DEFAULT - LL_HEADER_LENGTH - 2;
+ dev->netdev_ops = &ctcm_netdev_ops;
+ dev->hard_header_len = LL_HEADER_LENGTH + 2;
+ }
+
+ CTCMY_DBF_DEV(SETUP, dev, "finished");
+
+ return dev;
+}
+
+/**
+ * Main IRQ handler.
+ *
+ * cdev The ccw_device the interrupt is for.
+ * intparm interruption parameter.
+ * irb interruption response block.
+ */
+static void ctcm_irq_handler(struct ccw_device *cdev,
+ unsigned long intparm, struct irb *irb)
+{
+ struct channel *ch;
+ struct net_device *dev;
+ struct ctcm_priv *priv;
+ struct ccwgroup_device *cgdev;
+ int cstat;
+ int dstat;
+
+ CTCM_DBF_TEXT_(TRACE, CTC_DBF_DEBUG,
+ "Enter %s(%s)", CTCM_FUNTAIL, dev_name(&cdev->dev));
+
+ if (ctcm_check_irb_error(cdev, irb))
+ return;
+
+ cgdev = dev_get_drvdata(&cdev->dev);
+
+ cstat = irb->scsw.cmd.cstat;
+ dstat = irb->scsw.cmd.dstat;
+
+ /* Check for unsolicited interrupts. */
+ if (cgdev == NULL) {
+ CTCM_DBF_TEXT_(TRACE, CTC_DBF_ERROR,
+ "%s(%s) unsolicited irq: c-%02x d-%02x\n",
+ CTCM_FUNTAIL, dev_name(&cdev->dev), cstat, dstat);
+ dev_warn(&cdev->dev,
+ "The adapter received a non-specific IRQ\n");
+ return;
+ }
+
+ priv = dev_get_drvdata(&cgdev->dev);
+
+ /* Try to extract channel from driver data. */
+ if (priv->channel[CTCM_READ]->cdev == cdev)
+ ch = priv->channel[CTCM_READ];
+ else if (priv->channel[CTCM_WRITE]->cdev == cdev)
+ ch = priv->channel[CTCM_WRITE];
+ else {
+ dev_err(&cdev->dev,
+ "%s: Internal error: Can't determine channel for "
+ "interrupt device %s\n",
+ __func__, dev_name(&cdev->dev));
+ /* Explain: inconsistent internal structures */
+ return;
+ }
+
+ dev = ch->netdev;
+ if (dev == NULL) {
+ dev_err(&cdev->dev,
+ "%s Internal error: net_device is NULL, ch = 0x%p\n",
+ __func__, ch);
+ /* Explain: inconsistent internal structures */
+ return;
+ }
+
+ /* Copy interruption response block. */
+ memcpy(ch->irb, irb, sizeof(struct irb));
+
+ /* Issue error message and return on subchannel error code */
+ if (irb->scsw.cmd.cstat) {
+ fsm_event(ch->fsm, CTC_EVENT_SC_UNKNOWN, ch);
+ CTCM_DBF_TEXT_(TRACE, CTC_DBF_WARN,
+ "%s(%s): sub-ch check %s: cs=%02x ds=%02x",
+ CTCM_FUNTAIL, dev->name, ch->id, cstat, dstat);
+ dev_warn(&cdev->dev,
+ "A check occurred on the subchannel\n");
+ return;
+ }
+
+ /* Check the reason-code of a unit check */
+ if (irb->scsw.cmd.dstat & DEV_STAT_UNIT_CHECK) {
+ if ((irb->ecw[0] & ch->sense_rc) == 0)
+ /* print it only once */
+ CTCM_DBF_TEXT_(TRACE, CTC_DBF_WARN,
+ "%s(%s): sense=%02x, ds=%02x",
+ CTCM_FUNTAIL, ch->id, irb->ecw[0], dstat);
+ ccw_unit_check(ch, irb->ecw[0]);
+ return;
+ }
+ if (irb->scsw.cmd.dstat & DEV_STAT_BUSY) {
+ if (irb->scsw.cmd.dstat & DEV_STAT_ATTENTION)
+ fsm_event(ch->fsm, CTC_EVENT_ATTNBUSY, ch);
+ else
+ fsm_event(ch->fsm, CTC_EVENT_BUSY, ch);
+ return;
+ }
+ if (irb->scsw.cmd.dstat & DEV_STAT_ATTENTION) {
+ fsm_event(ch->fsm, CTC_EVENT_ATTN, ch);
+ return;
+ }
+ if ((irb->scsw.cmd.stctl & SCSW_STCTL_SEC_STATUS) ||
+ (irb->scsw.cmd.stctl == SCSW_STCTL_STATUS_PEND) ||
+ (irb->scsw.cmd.stctl ==
+ (SCSW_STCTL_ALERT_STATUS | SCSW_STCTL_STATUS_PEND)))
+ fsm_event(ch->fsm, CTC_EVENT_FINSTAT, ch);
+ else
+ fsm_event(ch->fsm, CTC_EVENT_IRQ, ch);
+
+}
+
+static const struct device_type ctcm_devtype = {
+ .name = "ctcm",
+ .groups = ctcm_attr_groups,
+};
+
+/**
+ * Add ctcm specific attributes.
+ * Add ctcm private data.
+ *
+ * cgdev pointer to ccwgroup_device just added
+ *
+ * returns 0 on success, !0 on failure.
+ */
+static int ctcm_probe_device(struct ccwgroup_device *cgdev)
+{
+ struct ctcm_priv *priv;
+
+ CTCM_DBF_TEXT_(SETUP, CTC_DBF_INFO,
+ "%s %p",
+ __func__, cgdev);
+
+ if (!get_device(&cgdev->dev))
+ return -ENODEV;
+
+ priv = kzalloc(sizeof(struct ctcm_priv), GFP_KERNEL);
+ if (!priv) {
+ CTCM_DBF_TEXT_(ERROR, CTC_DBF_ERROR,
+ "%s: memory allocation failure",
+ CTCM_FUNTAIL);
+ put_device(&cgdev->dev);
+ return -ENOMEM;
+ }
+ priv->buffer_size = CTCM_BUFSIZE_DEFAULT;
+ cgdev->cdev[0]->handler = ctcm_irq_handler;
+ cgdev->cdev[1]->handler = ctcm_irq_handler;
+ dev_set_drvdata(&cgdev->dev, priv);
+ cgdev->dev.type = &ctcm_devtype;
+
+ return 0;
+}
+
+/**
+ * Add a new channel to the list of channels.
+ * Keeps the channel list sorted.
+ *
+ * cdev The ccw_device to be added.
+ * type The type class of the new channel.
+ * priv Points to the private data of the ccwgroup_device.
+ *
+ * returns 0 on success, !0 on error.
+ */
+static int add_channel(struct ccw_device *cdev, enum ctcm_channel_types type,
+ struct ctcm_priv *priv)
+{
+ struct channel **c = &channels;
+ struct channel *ch;
+ int ccw_num;
+ int rc = 0;
+
+ CTCM_DBF_TEXT_(SETUP, CTC_DBF_INFO,
+ "%s(%s), type %d, proto %d",
+ __func__, dev_name(&cdev->dev), type, priv->protocol);
+
+ ch = kzalloc(sizeof(struct channel), GFP_KERNEL);
+ if (ch == NULL)
+ return -ENOMEM;
+
+ ch->protocol = priv->protocol;
+ if (IS_MPC(priv)) {
+ ch->discontact_th = kzalloc(TH_HEADER_LENGTH, gfp_type());
+ if (ch->discontact_th == NULL)
+ goto nomem_return;
+
+ ch->discontact_th->th_blk_flag = TH_DISCONTACT;
+ tasklet_init(&ch->ch_disc_tasklet,
+ mpc_action_send_discontact, (unsigned long)ch);
+
+ tasklet_init(&ch->ch_tasklet, ctcmpc_bh, (unsigned long)ch);
+ ch->max_bufsize = (MPC_BUFSIZE_DEFAULT - 35);
+ ccw_num = 17;
+ } else
+ ccw_num = 8;
+
+ ch->ccw = kzalloc(ccw_num * sizeof(struct ccw1), GFP_KERNEL | GFP_DMA);
+ if (ch->ccw == NULL)
+ goto nomem_return;
+
+ ch->cdev = cdev;
+ snprintf(ch->id, CTCM_ID_SIZE, "ch-%s", dev_name(&cdev->dev));
+ ch->type = type;
+
+ /**
+ * "static" ccws are used in the following way:
+ *
+ * ccw[0..2] (Channel program for generic I/O):
+ * 0: prepare
+ * 1: read or write (depending on direction) with fixed
+ * buffer (idal allocated once when buffer is allocated)
+ * 2: nop
+ * ccw[3..5] (Channel program for direct write of packets)
+ * 3: prepare
+ * 4: write (idal allocated on every write).
+ * 5: nop
+ * ccw[6..7] (Channel program for initial channel setup):
+ * 6: set extended mode
+ * 7: nop
+ *
+ * ch->ccw[0..5] are initialized in ch_action_start because
+ * the channel's direction is yet unknown here.
+ *
+ * ccws used for xid2 negotiations
+ * ch-ccw[8-14] need to be used for the XID exchange either
+ * X side XID2 Processing
+ * 8: write control
+ * 9: write th
+ * 10: write XID
+ * 11: read th from secondary
+ * 12: read XID from secondary
+ * 13: read 4 byte ID
+ * 14: nop
+ * Y side XID Processing
+ * 8: sense
+ * 9: read th
+ * 10: read XID
+ * 11: write th
+ * 12: write XID
+ * 13: write 4 byte ID
+ * 14: nop
+ *
+ * ccws used for double noop due to VM timing issues
+ * which result in unrecoverable Busy on channel
+ * 15: nop
+ * 16: nop
+ */
+ ch->ccw[6].cmd_code = CCW_CMD_SET_EXTENDED;
+ ch->ccw[6].flags = CCW_FLAG_SLI;
+
+ ch->ccw[7].cmd_code = CCW_CMD_NOOP;
+ ch->ccw[7].flags = CCW_FLAG_SLI;
+
+ if (IS_MPC(priv)) {
+ ch->ccw[15].cmd_code = CCW_CMD_WRITE;
+ ch->ccw[15].flags = CCW_FLAG_SLI | CCW_FLAG_CC;
+ ch->ccw[15].count = TH_HEADER_LENGTH;
+ ch->ccw[15].cda = virt_to_phys(ch->discontact_th);
+
+ ch->ccw[16].cmd_code = CCW_CMD_NOOP;
+ ch->ccw[16].flags = CCW_FLAG_SLI;
+
+ ch->fsm = init_fsm(ch->id, ctc_ch_state_names,
+ ctc_ch_event_names, CTC_MPC_NR_STATES,
+ CTC_MPC_NR_EVENTS, ctcmpc_ch_fsm,
+ mpc_ch_fsm_len, GFP_KERNEL);
+ } else {
+ ch->fsm = init_fsm(ch->id, ctc_ch_state_names,
+ ctc_ch_event_names, CTC_NR_STATES,
+ CTC_NR_EVENTS, ch_fsm,
+ ch_fsm_len, GFP_KERNEL);
+ }
+ if (ch->fsm == NULL)
+ goto nomem_return;
+
+ fsm_newstate(ch->fsm, CTC_STATE_IDLE);
+
+ ch->irb = kzalloc(sizeof(struct irb), GFP_KERNEL);
+ if (ch->irb == NULL)
+ goto nomem_return;
+
+ while (*c && ctcm_less_than((*c)->id, ch->id))
+ c = &(*c)->next;
+
+ if (*c && (!strncmp((*c)->id, ch->id, CTCM_ID_SIZE))) {
+ CTCM_DBF_TEXT_(SETUP, CTC_DBF_INFO,
+ "%s (%s) already in list, using old entry",
+ __func__, (*c)->id);
+
+ goto free_return;
+ }
+
+ spin_lock_init(&ch->collect_lock);
+
+ fsm_settimer(ch->fsm, &ch->timer);
+ skb_queue_head_init(&ch->io_queue);
+ skb_queue_head_init(&ch->collect_queue);
+
+ if (IS_MPC(priv)) {
+ fsm_settimer(ch->fsm, &ch->sweep_timer);
+ skb_queue_head_init(&ch->sweep_queue);
+ }
+ ch->next = *c;
+ *c = ch;
+ return 0;
+
+nomem_return:
+ rc = -ENOMEM;
+
+free_return: /* note that all channel pointers are 0 or valid */
+ kfree(ch->ccw);
+ kfree(ch->discontact_th);
+ kfree_fsm(ch->fsm);
+ kfree(ch->irb);
+ kfree(ch);
+ return rc;
+}
+
+/*
+ * Return type of a detected device.
+ */
+static enum ctcm_channel_types get_channel_type(struct ccw_device_id *id)
+{
+ enum ctcm_channel_types type;
+ type = (enum ctcm_channel_types)id->driver_info;
+
+ if (type == ctcm_channel_type_ficon)
+ type = ctcm_channel_type_escon;
+
+ return type;
+}
+
+/**
+ *
+ * Setup an interface.
+ *
+ * cgdev Device to be setup.
+ *
+ * returns 0 on success, !0 on failure.
+ */
+static int ctcm_new_device(struct ccwgroup_device *cgdev)
+{
+ char read_id[CTCM_ID_SIZE];
+ char write_id[CTCM_ID_SIZE];
+ int direction;
+ enum ctcm_channel_types type;
+ struct ctcm_priv *priv;
+ struct net_device *dev;
+ struct ccw_device *cdev0;
+ struct ccw_device *cdev1;
+ struct channel *readc;
+ struct channel *writec;
+ int ret;
+ int result;
+
+ priv = dev_get_drvdata(&cgdev->dev);
+ if (!priv) {
+ result = -ENODEV;
+ goto out_err_result;
+ }
+
+ cdev0 = cgdev->cdev[0];
+ cdev1 = cgdev->cdev[1];
+
+ type = get_channel_type(&cdev0->id);
+
+ snprintf(read_id, CTCM_ID_SIZE, "ch-%s", dev_name(&cdev0->dev));
+ snprintf(write_id, CTCM_ID_SIZE, "ch-%s", dev_name(&cdev1->dev));
+
+ ret = add_channel(cdev0, type, priv);
+ if (ret) {
+ result = ret;
+ goto out_err_result;
+ }
+ ret = add_channel(cdev1, type, priv);
+ if (ret) {
+ result = ret;
+ goto out_remove_channel1;
+ }
+
+ ret = ccw_device_set_online(cdev0);
+ if (ret != 0) {
+ CTCM_DBF_TEXT_(TRACE, CTC_DBF_NOTICE,
+ "%s(%s) set_online rc=%d",
+ CTCM_FUNTAIL, read_id, ret);
+ result = -EIO;
+ goto out_remove_channel2;
+ }
+
+ ret = ccw_device_set_online(cdev1);
+ if (ret != 0) {
+ CTCM_DBF_TEXT_(TRACE, CTC_DBF_NOTICE,
+ "%s(%s) set_online rc=%d",
+ CTCM_FUNTAIL, write_id, ret);
+
+ result = -EIO;
+ goto out_ccw1;
+ }
+
+ dev = ctcm_init_netdevice(priv);
+ if (dev == NULL) {
+ result = -ENODEV;
+ goto out_ccw2;
+ }
+
+ for (direction = CTCM_READ; direction <= CTCM_WRITE; direction++) {
+ priv->channel[direction] =
+ channel_get(type, direction == CTCM_READ ?
+ read_id : write_id, direction);
+ if (priv->channel[direction] == NULL) {
+ if (direction == CTCM_WRITE)
+ channel_free(priv->channel[CTCM_READ]);
+ goto out_dev;
+ }
+ priv->channel[direction]->netdev = dev;
+ priv->channel[direction]->protocol = priv->protocol;
+ priv->channel[direction]->max_bufsize = priv->buffer_size;
+ }
+ /* sysfs magic */
+ SET_NETDEV_DEV(dev, &cgdev->dev);
+
+ if (register_netdev(dev)) {
+ result = -ENODEV;
+ goto out_dev;
+ }
+
+ strlcpy(priv->fsm->name, dev->name, sizeof(priv->fsm->name));
+
+ dev_info(&dev->dev,
+ "setup OK : r/w = %s/%s, protocol : %d\n",
+ priv->channel[CTCM_READ]->id,
+ priv->channel[CTCM_WRITE]->id, priv->protocol);
+
+ CTCM_DBF_TEXT_(SETUP, CTC_DBF_INFO,
+ "setup(%s) OK : r/w = %s/%s, protocol : %d", dev->name,
+ priv->channel[CTCM_READ]->id,
+ priv->channel[CTCM_WRITE]->id, priv->protocol);
+
+ return 0;
+out_dev:
+ ctcm_free_netdevice(dev);
+out_ccw2:
+ ccw_device_set_offline(cgdev->cdev[1]);
+out_ccw1:
+ ccw_device_set_offline(cgdev->cdev[0]);
+out_remove_channel2:
+ readc = channel_get(type, read_id, CTCM_READ);
+ channel_remove(readc);
+out_remove_channel1:
+ writec = channel_get(type, write_id, CTCM_WRITE);
+ channel_remove(writec);
+out_err_result:
+ return result;
+}
+
+/**
+ * Shutdown an interface.
+ *
+ * cgdev Device to be shut down.
+ *
+ * returns 0 on success, !0 on failure.
+ */
+static int ctcm_shutdown_device(struct ccwgroup_device *cgdev)
+{
+ struct ctcm_priv *priv;
+ struct net_device *dev;
+
+ priv = dev_get_drvdata(&cgdev->dev);
+ if (!priv)
+ return -ENODEV;
+
+ if (priv->channel[CTCM_READ]) {
+ dev = priv->channel[CTCM_READ]->netdev;
+ CTCM_DBF_DEV(SETUP, dev, "");
+ /* Close the device */
+ ctcm_close(dev);
+ dev->flags &= ~IFF_RUNNING;
+ channel_free(priv->channel[CTCM_READ]);
+ } else
+ dev = NULL;
+
+ if (priv->channel[CTCM_WRITE])
+ channel_free(priv->channel[CTCM_WRITE]);
+
+ if (dev) {
+ unregister_netdev(dev);
+ ctcm_free_netdevice(dev);
+ }
+
+ if (priv->fsm)
+ kfree_fsm(priv->fsm);
+
+ ccw_device_set_offline(cgdev->cdev[1]);
+ ccw_device_set_offline(cgdev->cdev[0]);
+
+ if (priv->channel[CTCM_READ])
+ channel_remove(priv->channel[CTCM_READ]);
+ if (priv->channel[CTCM_WRITE])
+ channel_remove(priv->channel[CTCM_WRITE]);
+ priv->channel[CTCM_READ] = priv->channel[CTCM_WRITE] = NULL;
+
+ return 0;
+
+}
+
+
+static void ctcm_remove_device(struct ccwgroup_device *cgdev)
+{
+ struct ctcm_priv *priv = dev_get_drvdata(&cgdev->dev);
+
+ CTCM_DBF_TEXT_(SETUP, CTC_DBF_INFO,
+ "removing device %p, proto : %d",
+ cgdev, priv->protocol);
+
+ if (cgdev->state == CCWGROUP_ONLINE)
+ ctcm_shutdown_device(cgdev);
+ dev_set_drvdata(&cgdev->dev, NULL);
+ kfree(priv);
+ put_device(&cgdev->dev);
+}
+
+static int ctcm_pm_suspend(struct ccwgroup_device *gdev)
+{
+ struct ctcm_priv *priv = dev_get_drvdata(&gdev->dev);
+
+ if (gdev->state == CCWGROUP_OFFLINE)
+ return 0;
+ netif_device_detach(priv->channel[CTCM_READ]->netdev);
+ ctcm_close(priv->channel[CTCM_READ]->netdev);
+ if (!wait_event_timeout(priv->fsm->wait_q,
+ fsm_getstate(priv->fsm) == DEV_STATE_STOPPED, CTCM_TIME_5_SEC)) {
+ netif_device_attach(priv->channel[CTCM_READ]->netdev);
+ return -EBUSY;
+ }
+ ccw_device_set_offline(gdev->cdev[1]);
+ ccw_device_set_offline(gdev->cdev[0]);
+ return 0;
+}
+
+static int ctcm_pm_resume(struct ccwgroup_device *gdev)
+{
+ struct ctcm_priv *priv = dev_get_drvdata(&gdev->dev);
+ int rc;
+
+ if (gdev->state == CCWGROUP_OFFLINE)
+ return 0;
+ rc = ccw_device_set_online(gdev->cdev[1]);
+ if (rc)
+ goto err_out;
+ rc = ccw_device_set_online(gdev->cdev[0]);
+ if (rc)
+ goto err_out;
+ ctcm_open(priv->channel[CTCM_READ]->netdev);
+err_out:
+ netif_device_attach(priv->channel[CTCM_READ]->netdev);
+ return rc;
+}
+
+static struct ccw_device_id ctcm_ids[] = {
+ {CCW_DEVICE(0x3088, 0x08), .driver_info = ctcm_channel_type_parallel},
+ {CCW_DEVICE(0x3088, 0x1e), .driver_info = ctcm_channel_type_ficon},
+ {CCW_DEVICE(0x3088, 0x1f), .driver_info = ctcm_channel_type_escon},
+ {},
+};
+MODULE_DEVICE_TABLE(ccw, ctcm_ids);
+
+static struct ccw_driver ctcm_ccw_driver = {
+ .driver = {
+ .owner = THIS_MODULE,
+ .name = "ctcm",
+ },
+ .ids = ctcm_ids,
+ .probe = ccwgroup_probe_ccwdev,
+ .remove = ccwgroup_remove_ccwdev,
+ .int_class = IRQIO_CTC,
+};
+
+static struct ccwgroup_driver ctcm_group_driver = {
+ .driver = {
+ .owner = THIS_MODULE,
+ .name = CTC_DRIVER_NAME,
+ },
+ .setup = ctcm_probe_device,
+ .remove = ctcm_remove_device,
+ .set_online = ctcm_new_device,
+ .set_offline = ctcm_shutdown_device,
+ .freeze = ctcm_pm_suspend,
+ .thaw = ctcm_pm_resume,
+ .restore = ctcm_pm_resume,
+};
+
+static ssize_t ctcm_driver_group_store(struct device_driver *ddrv,
+ const char *buf, size_t count)
+{
+ int err;
+
+ err = ccwgroup_create_dev(ctcm_root_dev, &ctcm_group_driver, 2, buf);
+ return err ? err : count;
+}
+static DRIVER_ATTR(group, 0200, NULL, ctcm_driver_group_store);
+
+static struct attribute *ctcm_drv_attrs[] = {
+ &driver_attr_group.attr,
+ NULL,
+};
+static struct attribute_group ctcm_drv_attr_group = {
+ .attrs = ctcm_drv_attrs,
+};
+static const struct attribute_group *ctcm_drv_attr_groups[] = {
+ &ctcm_drv_attr_group,
+ NULL,
+};
+
+/*
+ * Module related routines
+ */
+
+/*
+ * Prepare to be unloaded. Free IRQ's and release all resources.
+ * This is called just before this module is unloaded. It is
+ * not called, if the usage count is !0, so we don't need to check
+ * for that.
+ */
+static void __exit ctcm_exit(void)
+{
+ ccwgroup_driver_unregister(&ctcm_group_driver);
+ ccw_driver_unregister(&ctcm_ccw_driver);
+ root_device_unregister(ctcm_root_dev);
+ ctcm_unregister_dbf_views();
+ pr_info("CTCM driver unloaded\n");
+}
+
+/*
+ * Print Banner.
+ */
+static void print_banner(void)
+{
+ pr_info("CTCM driver initialized\n");
+}
+
+/**
+ * Initialize module.
+ * This is called just after the module is loaded.
+ *
+ * returns 0 on success, !0 on error.
+ */
+static int __init ctcm_init(void)
+{
+ int ret;
+
+ channels = NULL;
+
+ ret = ctcm_register_dbf_views();
+ if (ret)
+ goto out_err;
+ ctcm_root_dev = root_device_register("ctcm");
+ ret = PTR_ERR_OR_ZERO(ctcm_root_dev);
+ if (ret)
+ goto register_err;
+ ret = ccw_driver_register(&ctcm_ccw_driver);
+ if (ret)
+ goto ccw_err;
+ ctcm_group_driver.driver.groups = ctcm_drv_attr_groups;
+ ret = ccwgroup_driver_register(&ctcm_group_driver);
+ if (ret)
+ goto ccwgroup_err;
+ print_banner();
+ return 0;
+
+ccwgroup_err:
+ ccw_driver_unregister(&ctcm_ccw_driver);
+ccw_err:
+ root_device_unregister(ctcm_root_dev);
+register_err:
+ ctcm_unregister_dbf_views();
+out_err:
+ pr_err("%s / Initializing the ctcm device driver failed, ret = %d\n",
+ __func__, ret);
+ return ret;
+}
+
+module_init(ctcm_init);
+module_exit(ctcm_exit);
+
+MODULE_AUTHOR("Peter Tiedemann <ptiedem@de.ibm.com>");
+MODULE_DESCRIPTION("Network driver for S/390 CTC + CTCMPC (SNA)");
+MODULE_LICENSE("GPL");
+
diff --git a/drivers/s390/net/ctcm_main.h b/drivers/s390/net/ctcm_main.h
new file mode 100644
index 00000000000..477c933685f
--- /dev/null
+++ b/drivers/s390/net/ctcm_main.h
@@ -0,0 +1,315 @@
+/*
+ * Copyright IBM Corp. 2001, 2007
+ * Authors: Fritz Elfert (felfert@millenux.com)
+ * Peter Tiedemann (ptiedem@de.ibm.com)
+ */
+
+#ifndef _CTCM_MAIN_H_
+#define _CTCM_MAIN_H_
+
+#include <asm/ccwdev.h>
+#include <asm/ccwgroup.h>
+
+#include <linux/skbuff.h>
+#include <linux/netdevice.h>
+
+#include "fsm.h"
+#include "ctcm_dbug.h"
+#include "ctcm_mpc.h"
+
+#define CTC_DRIVER_NAME "ctcm"
+#define CTC_DEVICE_NAME "ctc"
+#define MPC_DEVICE_NAME "mpc"
+#define CTC_DEVICE_GENE CTC_DEVICE_NAME "%d"
+#define MPC_DEVICE_GENE MPC_DEVICE_NAME "%d"
+
+#define CHANNEL_FLAGS_READ 0
+#define CHANNEL_FLAGS_WRITE 1
+#define CHANNEL_FLAGS_INUSE 2
+#define CHANNEL_FLAGS_BUFSIZE_CHANGED 4
+#define CHANNEL_FLAGS_FAILED 8
+#define CHANNEL_FLAGS_WAITIRQ 16
+#define CHANNEL_FLAGS_RWMASK 1
+#define CHANNEL_DIRECTION(f) (f & CHANNEL_FLAGS_RWMASK)
+
+#define LOG_FLAG_ILLEGALPKT 1
+#define LOG_FLAG_ILLEGALSIZE 2
+#define LOG_FLAG_OVERRUN 4
+#define LOG_FLAG_NOMEM 8
+
+#define ctcm_pr_debug(fmt, arg...) printk(KERN_DEBUG fmt, ##arg)
+
+#define CTCM_PR_DEBUG(fmt, arg...) \
+ do { \
+ if (do_debug) \
+ printk(KERN_DEBUG fmt, ##arg); \
+ } while (0)
+
+#define CTCM_PR_DBGDATA(fmt, arg...) \
+ do { \
+ if (do_debug_data) \
+ printk(KERN_DEBUG fmt, ##arg); \
+ } while (0)
+
+#define CTCM_D3_DUMP(buf, len) \
+ do { \
+ if (do_debug_data) \
+ ctcmpc_dumpit(buf, len); \
+ } while (0)
+
+#define CTCM_CCW_DUMP(buf, len) \
+ do { \
+ if (do_debug_ccw) \
+ ctcmpc_dumpit(buf, len); \
+ } while (0)
+
+/**
+ * Enum for classifying detected devices
+ */
+enum ctcm_channel_types {
+ /* Device is not a channel */
+ ctcm_channel_type_none,
+
+ /* Device is a CTC/A */
+ ctcm_channel_type_parallel,
+
+ /* Device is a FICON channel */
+ ctcm_channel_type_ficon,
+
+ /* Device is a ESCON channel */
+ ctcm_channel_type_escon
+};
+
+/*
+ * CCW commands, used in this driver.
+ */
+#define CCW_CMD_WRITE 0x01
+#define CCW_CMD_READ 0x02
+#define CCW_CMD_NOOP 0x03
+#define CCW_CMD_TIC 0x08
+#define CCW_CMD_SENSE_CMD 0x14
+#define CCW_CMD_WRITE_CTL 0x17
+#define CCW_CMD_SET_EXTENDED 0xc3
+#define CCW_CMD_PREPARE 0xe3
+
+#define CTCM_PROTO_S390 0
+#define CTCM_PROTO_LINUX 1
+#define CTCM_PROTO_LINUX_TTY 2
+#define CTCM_PROTO_OS390 3
+#define CTCM_PROTO_MPC 4
+#define CTCM_PROTO_MAX 4
+
+#define CTCM_BUFSIZE_LIMIT 65535
+#define CTCM_BUFSIZE_DEFAULT 32768
+#define MPC_BUFSIZE_DEFAULT CTCM_BUFSIZE_LIMIT
+
+#define CTCM_TIME_1_SEC 1000
+#define CTCM_TIME_5_SEC 5000
+#define CTCM_TIME_10_SEC 10000
+
+#define CTCM_INITIAL_BLOCKLEN 2
+
+#define CTCM_READ 0
+#define CTCM_WRITE 1
+
+#define CTCM_ID_SIZE 20+3
+
+struct ctcm_profile {
+ unsigned long maxmulti;
+ unsigned long maxcqueue;
+ unsigned long doios_single;
+ unsigned long doios_multi;
+ unsigned long txlen;
+ unsigned long tx_time;
+ struct timespec send_stamp;
+};
+
+/*
+ * Definition of one channel
+ */
+struct channel {
+ struct channel *next;
+ char id[CTCM_ID_SIZE];
+ struct ccw_device *cdev;
+ /*
+ * Type of this channel.
+ * CTC/A or Escon for valid channels.
+ */
+ enum ctcm_channel_types type;
+ /*
+ * Misc. flags. See CHANNEL_FLAGS_... below
+ */
+ __u32 flags;
+ __u16 protocol; /* protocol of this channel (4 = MPC) */
+ /*
+ * I/O and irq related stuff
+ */
+ struct ccw1 *ccw;
+ struct irb *irb;
+ /*
+ * RX/TX buffer size
+ */
+ int max_bufsize;
+ struct sk_buff *trans_skb; /* transmit/receive buffer */
+ struct sk_buff_head io_queue; /* universal I/O queue */
+ struct tasklet_struct ch_tasklet; /* MPC ONLY */
+ /*
+ * TX queue for collecting skb's during busy.
+ */
+ struct sk_buff_head collect_queue;
+ /*
+ * Amount of data in collect_queue.
+ */
+ int collect_len;
+ /*
+ * spinlock for collect_queue and collect_len
+ */
+ spinlock_t collect_lock;
+ /*
+ * Timer for detecting unresposive
+ * I/O operations.
+ */
+ fsm_timer timer;
+ /* MPC ONLY section begin */
+ __u32 th_seq_num; /* SNA TH seq number */
+ __u8 th_seg;
+ __u32 pdu_seq;
+ struct sk_buff *xid_skb;
+ char *xid_skb_data;
+ struct th_header *xid_th;
+ struct xid2 *xid;
+ char *xid_id;
+ struct th_header *rcvd_xid_th;
+ struct xid2 *rcvd_xid;
+ char *rcvd_xid_id;
+ __u8 in_mpcgroup;
+ fsm_timer sweep_timer;
+ struct sk_buff_head sweep_queue;
+ struct th_header *discontact_th;
+ struct tasklet_struct ch_disc_tasklet;
+ /* MPC ONLY section end */
+
+ int retry; /* retry counter for misc. operations */
+ fsm_instance *fsm; /* finite state machine of this channel */
+ struct net_device *netdev; /* corresponding net_device */
+ struct ctcm_profile prof;
+ __u8 *trans_skb_data;
+ __u16 logflags;
+ __u8 sense_rc; /* last unit check sense code report control */
+};
+
+struct ctcm_priv {
+ struct net_device_stats stats;
+ unsigned long tbusy;
+
+ /* The MPC group struct of this interface */
+ struct mpc_group *mpcg; /* MPC only */
+ struct xid2 *xid; /* MPC only */
+
+ /* The finite state machine of this interface */
+ fsm_instance *fsm;
+
+ /* The protocol of this device */
+ __u16 protocol;
+
+ /* Timer for restarting after I/O Errors */
+ fsm_timer restart_timer;
+
+ int buffer_size; /* ctc only */
+
+ struct channel *channel[2];
+};
+
+int ctcm_open(struct net_device *dev);
+int ctcm_close(struct net_device *dev);
+
+extern const struct attribute_group *ctcm_attr_groups[];
+
+/*
+ * Compatibility macros for busy handling
+ * of network devices.
+ */
+static inline void ctcm_clear_busy_do(struct net_device *dev)
+{
+ clear_bit(0, &(((struct ctcm_priv *)dev->ml_priv)->tbusy));
+ netif_wake_queue(dev);
+}
+
+static inline void ctcm_clear_busy(struct net_device *dev)
+{
+ struct mpc_group *grp;
+ grp = ((struct ctcm_priv *)dev->ml_priv)->mpcg;
+
+ if (!(grp && grp->in_sweep))
+ ctcm_clear_busy_do(dev);
+}
+
+
+static inline int ctcm_test_and_set_busy(struct net_device *dev)
+{
+ netif_stop_queue(dev);
+ return test_and_set_bit(0,
+ &(((struct ctcm_priv *)dev->ml_priv)->tbusy));
+}
+
+extern int loglevel;
+extern struct channel *channels;
+
+void ctcm_unpack_skb(struct channel *ch, struct sk_buff *pskb);
+
+/*
+ * Functions related to setup and device detection.
+ */
+
+static inline int ctcm_less_than(char *id1, char *id2)
+{
+ unsigned long dev1, dev2;
+
+ id1 = id1 + 5;
+ id2 = id2 + 5;
+
+ dev1 = simple_strtoul(id1, &id1, 16);
+ dev2 = simple_strtoul(id2, &id2, 16);
+
+ return (dev1 < dev2);
+}
+
+int ctcm_ch_alloc_buffer(struct channel *ch);
+
+static inline int ctcm_checkalloc_buffer(struct channel *ch)
+{
+ if (ch->trans_skb == NULL)
+ return ctcm_ch_alloc_buffer(ch);
+ if (ch->flags & CHANNEL_FLAGS_BUFSIZE_CHANGED) {
+ dev_kfree_skb(ch->trans_skb);
+ return ctcm_ch_alloc_buffer(ch);
+ }
+ return 0;
+}
+
+struct mpc_group *ctcmpc_init_mpc_group(struct ctcm_priv *priv);
+
+/* test if protocol attribute (of struct ctcm_priv or struct channel)
+ * has MPC protocol setting. Type is not checked
+ */
+#define IS_MPC(p) ((p)->protocol == CTCM_PROTO_MPC)
+
+/* test if struct ctcm_priv of struct net_device has MPC protocol setting */
+#define IS_MPCDEV(dev) IS_MPC((struct ctcm_priv *)dev->ml_priv)
+
+static inline gfp_t gfp_type(void)
+{
+ return in_interrupt() ? GFP_ATOMIC : GFP_KERNEL;
+}
+
+/*
+ * Definition of our link level header.
+ */
+struct ll_header {
+ __u16 length;
+ __u16 type;
+ __u16 unused;
+};
+#define LL_HEADER_LENGTH (sizeof(struct ll_header))
+
+#endif
diff --git a/drivers/s390/net/ctcm_mpc.c b/drivers/s390/net/ctcm_mpc.c
new file mode 100644
index 00000000000..2dbc77b5137
--- /dev/null
+++ b/drivers/s390/net/ctcm_mpc.c
@@ -0,0 +1,2174 @@
+/*
+ * Copyright IBM Corp. 2004, 2007
+ * Authors: Belinda Thompson (belindat@us.ibm.com)
+ * Andy Richter (richtera@us.ibm.com)
+ * Peter Tiedemann (ptiedem@de.ibm.com)
+ */
+
+/*
+ This module exports functions to be used by CCS:
+ EXPORT_SYMBOL(ctc_mpc_alloc_channel);
+ EXPORT_SYMBOL(ctc_mpc_establish_connectivity);
+ EXPORT_SYMBOL(ctc_mpc_dealloc_ch);
+ EXPORT_SYMBOL(ctc_mpc_flow_control);
+*/
+
+#undef DEBUG
+#undef DEBUGDATA
+#undef DEBUGCCW
+
+#define KMSG_COMPONENT "ctcm"
+#define pr_fmt(fmt) KMSG_COMPONENT ": " fmt
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/errno.h>
+#include <linux/types.h>
+#include <linux/interrupt.h>
+#include <linux/timer.h>
+#include <linux/sched.h>
+
+#include <linux/signal.h>
+#include <linux/string.h>
+#include <linux/proc_fs.h>
+
+#include <linux/ip.h>
+#include <linux/if_arp.h>
+#include <linux/tcp.h>
+#include <linux/skbuff.h>
+#include <linux/ctype.h>
+#include <linux/netdevice.h>
+#include <net/dst.h>
+
+#include <linux/io.h> /* instead of <asm/io.h> ok ? */
+#include <asm/ccwdev.h>
+#include <asm/ccwgroup.h>
+#include <linux/bitops.h> /* instead of <asm/bitops.h> ok ? */
+#include <linux/uaccess.h> /* instead of <asm/uaccess.h> ok ? */
+#include <linux/wait.h>
+#include <linux/moduleparam.h>
+#include <asm/idals.h>
+
+#include "ctcm_main.h"
+#include "ctcm_mpc.h"
+#include "ctcm_fsms.h"
+
+static const struct xid2 init_xid = {
+ .xid2_type_id = XID_FM2,
+ .xid2_len = 0x45,
+ .xid2_adj_id = 0,
+ .xid2_rlen = 0x31,
+ .xid2_resv1 = 0,
+ .xid2_flag1 = 0,
+ .xid2_fmtt = 0,
+ .xid2_flag4 = 0x80,
+ .xid2_resv2 = 0,
+ .xid2_tgnum = 0,
+ .xid2_sender_id = 0,
+ .xid2_flag2 = 0,
+ .xid2_option = XID2_0,
+ .xid2_resv3 = "\x00",
+ .xid2_resv4 = 0,
+ .xid2_dlc_type = XID2_READ_SIDE,
+ .xid2_resv5 = 0,
+ .xid2_mpc_flag = 0,
+ .xid2_resv6 = 0,
+ .xid2_buf_len = (MPC_BUFSIZE_DEFAULT - 35),
+};
+
+static const struct th_header thnorm = {
+ .th_seg = 0x00,
+ .th_ch_flag = TH_IS_XID,
+ .th_blk_flag = TH_DATA_IS_XID,
+ .th_is_xid = 0x01,
+ .th_seq_num = 0x00000000,
+};
+
+static const struct th_header thdummy = {
+ .th_seg = 0x00,
+ .th_ch_flag = 0x00,
+ .th_blk_flag = TH_DATA_IS_XID,
+ .th_is_xid = 0x01,
+ .th_seq_num = 0x00000000,
+};
+
+/*
+ * Definition of one MPC group
+ */
+
+/*
+ * Compatibility macros for busy handling
+ * of network devices.
+ */
+
+static void ctcmpc_unpack_skb(struct channel *ch, struct sk_buff *pskb);
+
+/*
+ * MPC Group state machine actions (static prototypes)
+ */
+static void mpc_action_nop(fsm_instance *fsm, int event, void *arg);
+static void mpc_action_go_ready(fsm_instance *fsm, int event, void *arg);
+static void mpc_action_go_inop(fsm_instance *fi, int event, void *arg);
+static void mpc_action_timeout(fsm_instance *fi, int event, void *arg);
+static int mpc_validate_xid(struct mpcg_info *mpcginfo);
+static void mpc_action_yside_xid(fsm_instance *fsm, int event, void *arg);
+static void mpc_action_doxid0(fsm_instance *fsm, int event, void *arg);
+static void mpc_action_doxid7(fsm_instance *fsm, int event, void *arg);
+static void mpc_action_xside_xid(fsm_instance *fsm, int event, void *arg);
+static void mpc_action_rcvd_xid0(fsm_instance *fsm, int event, void *arg);
+static void mpc_action_rcvd_xid7(fsm_instance *fsm, int event, void *arg);
+
+#ifdef DEBUGDATA
+/*-------------------------------------------------------------------*
+* Dump buffer format *
+* *
+*--------------------------------------------------------------------*/
+void ctcmpc_dumpit(char *buf, int len)
+{
+ __u32 ct, sw, rm, dup;
+ char *ptr, *rptr;
+ char tbuf[82], tdup[82];
+ #ifdef CONFIG_64BIT
+ char addr[22];
+ #else
+ char addr[12];
+ #endif
+ char boff[12];
+ char bhex[82], duphex[82];
+ char basc[40];
+
+ sw = 0;
+ rptr = ptr = buf;
+ rm = 16;
+ duphex[0] = 0x00;
+ dup = 0;
+
+ for (ct = 0; ct < len; ct++, ptr++, rptr++) {
+ if (sw == 0) {
+ #ifdef CONFIG_64BIT
+ sprintf(addr, "%16.16llx", (__u64)rptr);
+ #else
+ sprintf(addr, "%8.8X", (__u32)rptr);
+ #endif
+
+ sprintf(boff, "%4.4X", (__u32)ct);
+ bhex[0] = '\0';
+ basc[0] = '\0';
+ }
+ if ((sw == 4) || (sw == 12))
+ strcat(bhex, " ");
+ if (sw == 8)
+ strcat(bhex, " ");
+
+ #if CONFIG_64BIT
+ sprintf(tbuf, "%2.2llX", (__u64)*ptr);
+ #else
+ sprintf(tbuf, "%2.2X", (__u32)*ptr);
+ #endif
+
+ tbuf[2] = '\0';
+ strcat(bhex, tbuf);
+ if ((0 != isprint(*ptr)) && (*ptr >= 0x20))
+ basc[sw] = *ptr;
+ else
+ basc[sw] = '.';
+
+ basc[sw+1] = '\0';
+ sw++;
+ rm--;
+ if (sw != 16)
+ continue;
+ if ((strcmp(duphex, bhex)) != 0) {
+ if (dup != 0) {
+ sprintf(tdup,
+ "Duplicate as above to %s", addr);
+ ctcm_pr_debug(" --- %s ---\n",
+ tdup);
+ }
+ ctcm_pr_debug(" %s (+%s) : %s [%s]\n",
+ addr, boff, bhex, basc);
+ dup = 0;
+ strcpy(duphex, bhex);
+ } else
+ dup++;
+
+ sw = 0;
+ rm = 16;
+ } /* endfor */
+
+ if (sw != 0) {
+ for ( ; rm > 0; rm--, sw++) {
+ if ((sw == 4) || (sw == 12))
+ strcat(bhex, " ");
+ if (sw == 8)
+ strcat(bhex, " ");
+ strcat(bhex, " ");
+ strcat(basc, " ");
+ }
+ if (dup != 0) {
+ sprintf(tdup, "Duplicate as above to %s", addr);
+ ctcm_pr_debug(" --- %s ---\n", tdup);
+ }
+ ctcm_pr_debug(" %s (+%s) : %s [%s]\n",
+ addr, boff, bhex, basc);
+ } else {
+ if (dup >= 1) {
+ sprintf(tdup, "Duplicate as above to %s", addr);
+ ctcm_pr_debug(" --- %s ---\n", tdup);
+ }
+ if (dup != 0) {
+ ctcm_pr_debug(" %s (+%s) : %s [%s]\n",
+ addr, boff, bhex, basc);
+ }
+ }
+
+ return;
+
+} /* end of ctcmpc_dumpit */
+#endif
+
+#ifdef DEBUGDATA
+/*
+ * Dump header and first 16 bytes of an sk_buff for debugging purposes.
+ *
+ * skb The sk_buff to dump.
+ * offset Offset relative to skb-data, where to start the dump.
+ */
+void ctcmpc_dump_skb(struct sk_buff *skb, int offset)
+{
+ __u8 *p = skb->data;
+ struct th_header *header;
+ struct pdu *pheader;
+ int bl = skb->len;
+ int i;
+
+ if (p == NULL)
+ return;
+
+ p += offset;
+ header = (struct th_header *)p;
+
+ ctcm_pr_debug("dump:\n");
+ ctcm_pr_debug("skb len=%d \n", skb->len);
+ if (skb->len > 2) {
+ switch (header->th_ch_flag) {
+ case TH_HAS_PDU:
+ break;
+ case 0x00:
+ case TH_IS_XID:
+ if ((header->th_blk_flag == TH_DATA_IS_XID) &&
+ (header->th_is_xid == 0x01))
+ goto dumpth;
+ case TH_SWEEP_REQ:
+ goto dumpth;
+ case TH_SWEEP_RESP:
+ goto dumpth;
+ default:
+ break;
+ }
+
+ pheader = (struct pdu *)p;
+ ctcm_pr_debug("pdu->offset: %d hex: %04x\n",
+ pheader->pdu_offset, pheader->pdu_offset);
+ ctcm_pr_debug("pdu->flag : %02x\n", pheader->pdu_flag);
+ ctcm_pr_debug("pdu->proto : %02x\n", pheader->pdu_proto);
+ ctcm_pr_debug("pdu->seq : %02x\n", pheader->pdu_seq);
+ goto dumpdata;
+
+dumpth:
+ ctcm_pr_debug("th->seg : %02x\n", header->th_seg);
+ ctcm_pr_debug("th->ch : %02x\n", header->th_ch_flag);
+ ctcm_pr_debug("th->blk_flag: %02x\n", header->th_blk_flag);
+ ctcm_pr_debug("th->type : %s\n",
+ (header->th_is_xid) ? "DATA" : "XID");
+ ctcm_pr_debug("th->seqnum : %04x\n", header->th_seq_num);
+
+ }
+dumpdata:
+ if (bl > 32)
+ bl = 32;
+ ctcm_pr_debug("data: ");
+ for (i = 0; i < bl; i++)
+ ctcm_pr_debug("%02x%s", *p++, (i % 16) ? " " : "\n");
+ ctcm_pr_debug("\n");
+}
+#endif
+
+static struct net_device *ctcmpc_get_dev(int port_num)
+{
+ char device[20];
+ struct net_device *dev;
+ struct ctcm_priv *priv;
+
+ sprintf(device, "%s%i", MPC_DEVICE_NAME, port_num);
+
+ dev = __dev_get_by_name(&init_net, device);
+
+ if (dev == NULL) {
+ CTCM_DBF_TEXT_(MPC_ERROR, CTC_DBF_ERROR,
+ "%s: Device not found by name: %s",
+ CTCM_FUNTAIL, device);
+ return NULL;
+ }
+ priv = dev->ml_priv;
+ if (priv == NULL) {
+ CTCM_DBF_TEXT_(MPC_ERROR, CTC_DBF_ERROR,
+ "%s(%s): dev->ml_priv is NULL",
+ CTCM_FUNTAIL, device);
+ return NULL;
+ }
+ if (priv->mpcg == NULL) {
+ CTCM_DBF_TEXT_(MPC_ERROR, CTC_DBF_ERROR,
+ "%s(%s): priv->mpcg is NULL",
+ CTCM_FUNTAIL, device);
+ return NULL;
+ }
+ return dev;
+}
+
+/*
+ * ctc_mpc_alloc_channel
+ * (exported interface)
+ *
+ * Device Initialization :
+ * ACTPATH driven IO operations
+ */
+int ctc_mpc_alloc_channel(int port_num, void (*callback)(int, int))
+{
+ struct net_device *dev;
+ struct mpc_group *grp;
+ struct ctcm_priv *priv;
+
+ dev = ctcmpc_get_dev(port_num);
+ if (dev == NULL)
+ return 1;
+ priv = dev->ml_priv;
+ grp = priv->mpcg;
+
+ grp->allochanfunc = callback;
+ grp->port_num = port_num;
+ grp->port_persist = 1;
+
+ CTCM_DBF_TEXT_(MPC_SETUP, CTC_DBF_INFO,
+ "%s(%s): state=%s",
+ CTCM_FUNTAIL, dev->name, fsm_getstate_str(grp->fsm));
+
+ switch (fsm_getstate(grp->fsm)) {
+ case MPCG_STATE_INOP:
+ /* Group is in the process of terminating */
+ grp->alloc_called = 1;
+ break;
+ case MPCG_STATE_RESET:
+ /* MPC Group will transition to state */
+ /* MPCG_STATE_XID2INITW iff the minimum number */
+ /* of 1 read and 1 write channel have successfully*/
+ /* activated */
+ /*fsm_newstate(grp->fsm, MPCG_STATE_XID2INITW);*/
+ if (callback)
+ grp->send_qllc_disc = 1;
+ case MPCG_STATE_XID0IOWAIT:
+ fsm_deltimer(&grp->timer);
+ grp->outstanding_xid2 = 0;
+ grp->outstanding_xid7 = 0;
+ grp->outstanding_xid7_p2 = 0;
+ grp->saved_xid2 = NULL;
+ if (callback)
+ ctcm_open(dev);
+ fsm_event(priv->fsm, DEV_EVENT_START, dev);
+ break;
+ case MPCG_STATE_READY:
+ /* XID exchanges completed after PORT was activated */
+ /* Link station already active */
+ /* Maybe timing issue...retry callback */
+ grp->allocchan_callback_retries++;
+ if (grp->allocchan_callback_retries < 4) {
+ if (grp->allochanfunc)
+ grp->allochanfunc(grp->port_num,
+ grp->group_max_buflen);
+ } else {
+ /* there are problems...bail out */
+ /* there may be a state mismatch so restart */
+ fsm_event(grp->fsm, MPCG_EVENT_INOP, dev);
+ grp->allocchan_callback_retries = 0;
+ }
+ break;
+ }
+
+ return 0;
+}
+EXPORT_SYMBOL(ctc_mpc_alloc_channel);
+
+/*
+ * ctc_mpc_establish_connectivity
+ * (exported interface)
+ */
+void ctc_mpc_establish_connectivity(int port_num,
+ void (*callback)(int, int, int))
+{
+ struct net_device *dev;
+ struct mpc_group *grp;
+ struct ctcm_priv *priv;
+ struct channel *rch, *wch;
+
+ dev = ctcmpc_get_dev(port_num);
+ if (dev == NULL)
+ return;
+ priv = dev->ml_priv;
+ grp = priv->mpcg;
+ rch = priv->channel[CTCM_READ];
+ wch = priv->channel[CTCM_WRITE];
+
+ CTCM_DBF_TEXT_(MPC_SETUP, CTC_DBF_INFO,
+ "%s(%s): state=%s",
+ CTCM_FUNTAIL, dev->name, fsm_getstate_str(grp->fsm));
+
+ grp->estconnfunc = callback;
+ grp->port_num = port_num;
+
+ switch (fsm_getstate(grp->fsm)) {
+ case MPCG_STATE_READY:
+ /* XID exchanges completed after PORT was activated */
+ /* Link station already active */
+ /* Maybe timing issue...retry callback */
+ fsm_deltimer(&grp->timer);
+ grp->estconn_callback_retries++;
+ if (grp->estconn_callback_retries < 4) {
+ if (grp->estconnfunc) {
+ grp->estconnfunc(grp->port_num, 0,
+ grp->group_max_buflen);
+ grp->estconnfunc = NULL;
+ }
+ } else {
+ /* there are problems...bail out */
+ fsm_event(grp->fsm, MPCG_EVENT_INOP, dev);
+ grp->estconn_callback_retries = 0;
+ }
+ break;
+ case MPCG_STATE_INOP:
+ case MPCG_STATE_RESET:
+ /* MPC Group is not ready to start XID - min num of */
+ /* 1 read and 1 write channel have not been acquired*/
+
+ CTCM_DBF_TEXT_(MPC_ERROR, CTC_DBF_ERROR,
+ "%s(%s): REJECTED - inactive channels",
+ CTCM_FUNTAIL, dev->name);
+ if (grp->estconnfunc) {
+ grp->estconnfunc(grp->port_num, -1, 0);
+ grp->estconnfunc = NULL;
+ }
+ break;
+ case MPCG_STATE_XID2INITW:
+ /* alloc channel was called but no XID exchange */
+ /* has occurred. initiate xside XID exchange */
+ /* make sure yside XID0 processing has not started */
+
+ if ((fsm_getstate(rch->fsm) > CH_XID0_PENDING) ||
+ (fsm_getstate(wch->fsm) > CH_XID0_PENDING)) {
+ CTCM_DBF_TEXT_(MPC_ERROR, CTC_DBF_ERROR,
+ "%s(%s): ABORT - PASSIVE XID",
+ CTCM_FUNTAIL, dev->name);
+ break;
+ }
+ grp->send_qllc_disc = 1;
+ fsm_newstate(grp->fsm, MPCG_STATE_XID0IOWAIT);
+ fsm_deltimer(&grp->timer);
+ fsm_addtimer(&grp->timer, MPC_XID_TIMEOUT_VALUE,
+ MPCG_EVENT_TIMER, dev);
+ grp->outstanding_xid7 = 0;
+ grp->outstanding_xid7_p2 = 0;
+ grp->saved_xid2 = NULL;
+ if ((rch->in_mpcgroup) &&
+ (fsm_getstate(rch->fsm) == CH_XID0_PENDING))
+ fsm_event(grp->fsm, MPCG_EVENT_XID0DO, rch);
+ else {
+ CTCM_DBF_TEXT_(MPC_ERROR, CTC_DBF_ERROR,
+ "%s(%s): RX-%s not ready for ACTIVE XID0",
+ CTCM_FUNTAIL, dev->name, rch->id);
+ if (grp->estconnfunc) {
+ grp->estconnfunc(grp->port_num, -1, 0);
+ grp->estconnfunc = NULL;
+ }
+ fsm_deltimer(&grp->timer);
+ goto done;
+ }
+ if ((wch->in_mpcgroup) &&
+ (fsm_getstate(wch->fsm) == CH_XID0_PENDING))
+ fsm_event(grp->fsm, MPCG_EVENT_XID0DO, wch);
+ else {
+ CTCM_DBF_TEXT_(MPC_ERROR, CTC_DBF_ERROR,
+ "%s(%s): WX-%s not ready for ACTIVE XID0",
+ CTCM_FUNTAIL, dev->name, wch->id);
+ if (grp->estconnfunc) {
+ grp->estconnfunc(grp->port_num, -1, 0);
+ grp->estconnfunc = NULL;
+ }
+ fsm_deltimer(&grp->timer);
+ goto done;
+ }
+ break;
+ case MPCG_STATE_XID0IOWAIT:
+ /* already in active XID negotiations */
+ default:
+ break;
+ }
+
+done:
+ CTCM_PR_DEBUG("Exit %s()\n", __func__);
+ return;
+}
+EXPORT_SYMBOL(ctc_mpc_establish_connectivity);
+
+/*
+ * ctc_mpc_dealloc_ch
+ * (exported interface)
+ */
+void ctc_mpc_dealloc_ch(int port_num)
+{
+ struct net_device *dev;
+ struct ctcm_priv *priv;
+ struct mpc_group *grp;
+
+ dev = ctcmpc_get_dev(port_num);
+ if (dev == NULL)
+ return;
+ priv = dev->ml_priv;
+ grp = priv->mpcg;
+
+ CTCM_DBF_TEXT_(MPC_SETUP, CTC_DBF_DEBUG,
+ "%s: %s: refcount = %d\n",
+ CTCM_FUNTAIL, dev->name, netdev_refcnt_read(dev));
+
+ fsm_deltimer(&priv->restart_timer);
+ grp->channels_terminating = 0;
+ fsm_deltimer(&grp->timer);
+ grp->allochanfunc = NULL;
+ grp->estconnfunc = NULL;
+ grp->port_persist = 0;
+ grp->send_qllc_disc = 0;
+ fsm_event(grp->fsm, MPCG_EVENT_INOP, dev);
+
+ ctcm_close(dev);
+ return;
+}
+EXPORT_SYMBOL(ctc_mpc_dealloc_ch);
+
+/*
+ * ctc_mpc_flow_control
+ * (exported interface)
+ */
+void ctc_mpc_flow_control(int port_num, int flowc)
+{
+ struct ctcm_priv *priv;
+ struct mpc_group *grp;
+ struct net_device *dev;
+ struct channel *rch;
+ int mpcg_state;
+
+ dev = ctcmpc_get_dev(port_num);
+ if (dev == NULL)
+ return;
+ priv = dev->ml_priv;
+ grp = priv->mpcg;
+
+ CTCM_DBF_TEXT_(MPC_TRACE, CTC_DBF_DEBUG,
+ "%s: %s: flowc = %d",
+ CTCM_FUNTAIL, dev->name, flowc);
+
+ rch = priv->channel[CTCM_READ];
+
+ mpcg_state = fsm_getstate(grp->fsm);
+ switch (flowc) {
+ case 1:
+ if (mpcg_state == MPCG_STATE_FLOWC)
+ break;
+ if (mpcg_state == MPCG_STATE_READY) {
+ if (grp->flow_off_called == 1)
+ grp->flow_off_called = 0;
+ else
+ fsm_newstate(grp->fsm, MPCG_STATE_FLOWC);
+ break;
+ }
+ break;
+ case 0:
+ if (mpcg_state == MPCG_STATE_FLOWC) {
+ fsm_newstate(grp->fsm, MPCG_STATE_READY);
+ /* ensure any data that has accumulated */
+ /* on the io_queue will now be sen t */
+ tasklet_schedule(&rch->ch_tasklet);
+ }
+ /* possible race condition */
+ if (mpcg_state == MPCG_STATE_READY) {
+ grp->flow_off_called = 1;
+ break;
+ }
+ break;
+ }
+
+}
+EXPORT_SYMBOL(ctc_mpc_flow_control);
+
+static int mpc_send_qllc_discontact(struct net_device *);
+
+/*
+ * helper function of ctcmpc_unpack_skb
+*/
+static void mpc_rcvd_sweep_resp(struct mpcg_info *mpcginfo)
+{
+ struct channel *rch = mpcginfo->ch;
+ struct net_device *dev = rch->netdev;
+ struct ctcm_priv *priv = dev->ml_priv;
+ struct mpc_group *grp = priv->mpcg;
+ struct channel *ch = priv->channel[CTCM_WRITE];
+
+ CTCM_PR_DEBUG("%s: ch=0x%p id=%s\n", __func__, ch, ch->id);
+ CTCM_D3_DUMP((char *)mpcginfo->sweep, TH_SWEEP_LENGTH);
+
+ grp->sweep_rsp_pend_num--;
+
+ if ((grp->sweep_req_pend_num == 0) &&
+ (grp->sweep_rsp_pend_num == 0)) {
+ fsm_deltimer(&ch->sweep_timer);
+ grp->in_sweep = 0;
+ rch->th_seq_num = 0x00;
+ ch->th_seq_num = 0x00;
+ ctcm_clear_busy_do(dev);
+ }
+
+ kfree(mpcginfo);
+
+ return;
+
+}
+
+/*
+ * helper function of mpc_rcvd_sweep_req
+ * which is a helper of ctcmpc_unpack_skb
+ */
+static void ctcmpc_send_sweep_resp(struct channel *rch)
+{
+ struct net_device *dev = rch->netdev;
+ struct ctcm_priv *priv = dev->ml_priv;
+ struct mpc_group *grp = priv->mpcg;
+ struct th_sweep *header;
+ struct sk_buff *sweep_skb;
+ struct channel *ch = priv->channel[CTCM_WRITE];
+
+ CTCM_PR_DEBUG("%s: ch=0x%p id=%s\n", __func__, rch, rch->id);
+
+ sweep_skb = __dev_alloc_skb(MPC_BUFSIZE_DEFAULT, GFP_ATOMIC | GFP_DMA);
+ if (sweep_skb == NULL) {
+ CTCM_DBF_TEXT_(MPC_ERROR, CTC_DBF_ERROR,
+ "%s(%s): sweep_skb allocation ERROR\n",
+ CTCM_FUNTAIL, rch->id);
+ goto done;
+ }
+
+ header = kmalloc(sizeof(struct th_sweep), gfp_type());
+
+ if (!header) {
+ dev_kfree_skb_any(sweep_skb);
+ goto done;
+ }
+
+ header->th.th_seg = 0x00 ;
+ header->th.th_ch_flag = TH_SWEEP_RESP;
+ header->th.th_blk_flag = 0x00;
+ header->th.th_is_xid = 0x00;
+ header->th.th_seq_num = 0x00;
+ header->sw.th_last_seq = ch->th_seq_num;
+
+ memcpy(skb_put(sweep_skb, TH_SWEEP_LENGTH), header, TH_SWEEP_LENGTH);
+
+ kfree(header);
+
+ dev->trans_start = jiffies;
+ skb_queue_tail(&ch->sweep_queue, sweep_skb);
+
+ fsm_addtimer(&ch->sweep_timer, 100, CTC_EVENT_RSWEEP_TIMER, ch);
+
+ return;
+
+done:
+ grp->in_sweep = 0;
+ ctcm_clear_busy_do(dev);
+ fsm_event(grp->fsm, MPCG_EVENT_INOP, dev);
+
+ return;
+}
+
+/*
+ * helper function of ctcmpc_unpack_skb
+ */
+static void mpc_rcvd_sweep_req(struct mpcg_info *mpcginfo)
+{
+ struct channel *rch = mpcginfo->ch;
+ struct net_device *dev = rch->netdev;
+ struct ctcm_priv *priv = dev->ml_priv;
+ struct mpc_group *grp = priv->mpcg;
+ struct channel *ch = priv->channel[CTCM_WRITE];
+
+ if (do_debug)
+ CTCM_DBF_TEXT_(MPC_TRACE, CTC_DBF_DEBUG,
+ " %s(): ch=0x%p id=%s\n", __func__, ch, ch->id);
+
+ if (grp->in_sweep == 0) {
+ grp->in_sweep = 1;
+ ctcm_test_and_set_busy(dev);
+ grp->sweep_req_pend_num = grp->active_channels[CTCM_READ];
+ grp->sweep_rsp_pend_num = grp->active_channels[CTCM_READ];
+ }
+
+ CTCM_D3_DUMP((char *)mpcginfo->sweep, TH_SWEEP_LENGTH);
+
+ grp->sweep_req_pend_num--;
+ ctcmpc_send_sweep_resp(ch);
+ kfree(mpcginfo);
+ return;
+}
+
+/*
+ * MPC Group Station FSM definitions
+ */
+static const char *mpcg_event_names[] = {
+ [MPCG_EVENT_INOP] = "INOP Condition",
+ [MPCG_EVENT_DISCONC] = "Discontact Received",
+ [MPCG_EVENT_XID0DO] = "Channel Active - Start XID",
+ [MPCG_EVENT_XID2] = "XID2 Received",
+ [MPCG_EVENT_XID2DONE] = "XID0 Complete",
+ [MPCG_EVENT_XID7DONE] = "XID7 Complete",
+ [MPCG_EVENT_TIMER] = "XID Setup Timer",
+ [MPCG_EVENT_DOIO] = "XID DoIO",
+};
+
+static const char *mpcg_state_names[] = {
+ [MPCG_STATE_RESET] = "Reset",
+ [MPCG_STATE_INOP] = "INOP",
+ [MPCG_STATE_XID2INITW] = "Passive XID- XID0 Pending Start",
+ [MPCG_STATE_XID2INITX] = "Passive XID- XID0 Pending Complete",
+ [MPCG_STATE_XID7INITW] = "Passive XID- XID7 Pending P1 Start",
+ [MPCG_STATE_XID7INITX] = "Passive XID- XID7 Pending P2 Complete",
+ [MPCG_STATE_XID0IOWAIT] = "Active XID- XID0 Pending Start",
+ [MPCG_STATE_XID0IOWAIX] = "Active XID- XID0 Pending Complete",
+ [MPCG_STATE_XID7INITI] = "Active XID- XID7 Pending Start",
+ [MPCG_STATE_XID7INITZ] = "Active XID- XID7 Pending Complete ",
+ [MPCG_STATE_XID7INITF] = "XID - XID7 Complete ",
+ [MPCG_STATE_FLOWC] = "FLOW CONTROL ON",
+ [MPCG_STATE_READY] = "READY",
+};
+
+/*
+ * The MPC Group Station FSM
+ * 22 events
+ */
+static const fsm_node mpcg_fsm[] = {
+ { MPCG_STATE_RESET, MPCG_EVENT_INOP, mpc_action_go_inop },
+ { MPCG_STATE_INOP, MPCG_EVENT_INOP, mpc_action_nop },
+ { MPCG_STATE_FLOWC, MPCG_EVENT_INOP, mpc_action_go_inop },
+
+ { MPCG_STATE_READY, MPCG_EVENT_DISCONC, mpc_action_discontact },
+ { MPCG_STATE_READY, MPCG_EVENT_INOP, mpc_action_go_inop },
+
+ { MPCG_STATE_XID2INITW, MPCG_EVENT_XID0DO, mpc_action_doxid0 },
+ { MPCG_STATE_XID2INITW, MPCG_EVENT_XID2, mpc_action_rcvd_xid0 },
+ { MPCG_STATE_XID2INITW, MPCG_EVENT_INOP, mpc_action_go_inop },
+ { MPCG_STATE_XID2INITW, MPCG_EVENT_TIMER, mpc_action_timeout },
+ { MPCG_STATE_XID2INITW, MPCG_EVENT_DOIO, mpc_action_yside_xid },
+
+ { MPCG_STATE_XID2INITX, MPCG_EVENT_XID0DO, mpc_action_doxid0 },
+ { MPCG_STATE_XID2INITX, MPCG_EVENT_XID2, mpc_action_rcvd_xid0 },
+ { MPCG_STATE_XID2INITX, MPCG_EVENT_INOP, mpc_action_go_inop },
+ { MPCG_STATE_XID2INITX, MPCG_EVENT_TIMER, mpc_action_timeout },
+ { MPCG_STATE_XID2INITX, MPCG_EVENT_DOIO, mpc_action_yside_xid },
+
+ { MPCG_STATE_XID7INITW, MPCG_EVENT_XID2DONE, mpc_action_doxid7 },
+ { MPCG_STATE_XID7INITW, MPCG_EVENT_DISCONC, mpc_action_discontact },
+ { MPCG_STATE_XID7INITW, MPCG_EVENT_XID2, mpc_action_rcvd_xid7 },
+ { MPCG_STATE_XID7INITW, MPCG_EVENT_INOP, mpc_action_go_inop },
+ { MPCG_STATE_XID7INITW, MPCG_EVENT_TIMER, mpc_action_timeout },
+ { MPCG_STATE_XID7INITW, MPCG_EVENT_XID7DONE, mpc_action_doxid7 },
+ { MPCG_STATE_XID7INITW, MPCG_EVENT_DOIO, mpc_action_yside_xid },
+
+ { MPCG_STATE_XID7INITX, MPCG_EVENT_DISCONC, mpc_action_discontact },
+ { MPCG_STATE_XID7INITX, MPCG_EVENT_XID2, mpc_action_rcvd_xid7 },
+ { MPCG_STATE_XID7INITX, MPCG_EVENT_INOP, mpc_action_go_inop },
+ { MPCG_STATE_XID7INITX, MPCG_EVENT_XID7DONE, mpc_action_doxid7 },
+ { MPCG_STATE_XID7INITX, MPCG_EVENT_TIMER, mpc_action_timeout },
+ { MPCG_STATE_XID7INITX, MPCG_EVENT_DOIO, mpc_action_yside_xid },
+
+ { MPCG_STATE_XID0IOWAIT, MPCG_EVENT_XID0DO, mpc_action_doxid0 },
+ { MPCG_STATE_XID0IOWAIT, MPCG_EVENT_DISCONC, mpc_action_discontact },
+ { MPCG_STATE_XID0IOWAIT, MPCG_EVENT_XID2, mpc_action_rcvd_xid0 },
+ { MPCG_STATE_XID0IOWAIT, MPCG_EVENT_INOP, mpc_action_go_inop },
+ { MPCG_STATE_XID0IOWAIT, MPCG_EVENT_TIMER, mpc_action_timeout },
+ { MPCG_STATE_XID0IOWAIT, MPCG_EVENT_DOIO, mpc_action_xside_xid },
+
+ { MPCG_STATE_XID0IOWAIX, MPCG_EVENT_XID0DO, mpc_action_doxid0 },
+ { MPCG_STATE_XID0IOWAIX, MPCG_EVENT_DISCONC, mpc_action_discontact },
+ { MPCG_STATE_XID0IOWAIX, MPCG_EVENT_XID2, mpc_action_rcvd_xid0 },
+ { MPCG_STATE_XID0IOWAIX, MPCG_EVENT_INOP, mpc_action_go_inop },
+ { MPCG_STATE_XID0IOWAIX, MPCG_EVENT_TIMER, mpc_action_timeout },
+ { MPCG_STATE_XID0IOWAIX, MPCG_EVENT_DOIO, mpc_action_xside_xid },
+
+ { MPCG_STATE_XID7INITI, MPCG_EVENT_XID2DONE, mpc_action_doxid7 },
+ { MPCG_STATE_XID7INITI, MPCG_EVENT_XID2, mpc_action_rcvd_xid7 },
+ { MPCG_STATE_XID7INITI, MPCG_EVENT_DISCONC, mpc_action_discontact },
+ { MPCG_STATE_XID7INITI, MPCG_EVENT_INOP, mpc_action_go_inop },
+ { MPCG_STATE_XID7INITI, MPCG_EVENT_TIMER, mpc_action_timeout },
+ { MPCG_STATE_XID7INITI, MPCG_EVENT_XID7DONE, mpc_action_doxid7 },
+ { MPCG_STATE_XID7INITI, MPCG_EVENT_DOIO, mpc_action_xside_xid },
+
+ { MPCG_STATE_XID7INITZ, MPCG_EVENT_XID2, mpc_action_rcvd_xid7 },
+ { MPCG_STATE_XID7INITZ, MPCG_EVENT_XID7DONE, mpc_action_doxid7 },
+ { MPCG_STATE_XID7INITZ, MPCG_EVENT_DISCONC, mpc_action_discontact },
+ { MPCG_STATE_XID7INITZ, MPCG_EVENT_INOP, mpc_action_go_inop },
+ { MPCG_STATE_XID7INITZ, MPCG_EVENT_TIMER, mpc_action_timeout },
+ { MPCG_STATE_XID7INITZ, MPCG_EVENT_DOIO, mpc_action_xside_xid },
+
+ { MPCG_STATE_XID7INITF, MPCG_EVENT_INOP, mpc_action_go_inop },
+ { MPCG_STATE_XID7INITF, MPCG_EVENT_XID7DONE, mpc_action_go_ready },
+};
+
+static int mpcg_fsm_len = ARRAY_SIZE(mpcg_fsm);
+
+/*
+ * MPC Group Station FSM action
+ * CTCM_PROTO_MPC only
+ */
+static void mpc_action_go_ready(fsm_instance *fsm, int event, void *arg)
+{
+ struct net_device *dev = arg;
+ struct ctcm_priv *priv = dev->ml_priv;
+ struct mpc_group *grp = priv->mpcg;
+
+ if (grp == NULL) {
+ CTCM_DBF_TEXT_(MPC_ERROR, CTC_DBF_ERROR,
+ "%s(%s): No MPC group",
+ CTCM_FUNTAIL, dev->name);
+ return;
+ }
+
+ fsm_deltimer(&grp->timer);
+
+ if (grp->saved_xid2->xid2_flag2 == 0x40) {
+ priv->xid->xid2_flag2 = 0x00;
+ if (grp->estconnfunc) {
+ grp->estconnfunc(grp->port_num, 1,
+ grp->group_max_buflen);
+ grp->estconnfunc = NULL;
+ } else if (grp->allochanfunc)
+ grp->send_qllc_disc = 1;
+
+ fsm_event(grp->fsm, MPCG_EVENT_INOP, dev);
+ CTCM_DBF_TEXT_(MPC_ERROR, CTC_DBF_ERROR,
+ "%s(%s): fails",
+ CTCM_FUNTAIL, dev->name);
+ return;
+ }
+
+ grp->port_persist = 1;
+ grp->out_of_sequence = 0;
+ grp->estconn_called = 0;
+
+ tasklet_hi_schedule(&grp->mpc_tasklet2);
+
+ return;
+}
+
+/*
+ * helper of ctcm_init_netdevice
+ * CTCM_PROTO_MPC only
+ */
+void mpc_group_ready(unsigned long adev)
+{
+ struct net_device *dev = (struct net_device *)adev;
+ struct ctcm_priv *priv = dev->ml_priv;
+ struct mpc_group *grp = priv->mpcg;
+ struct channel *ch = NULL;
+
+ if (grp == NULL) {
+ CTCM_DBF_TEXT_(MPC_ERROR, CTC_DBF_ERROR,
+ "%s(%s): No MPC group",
+ CTCM_FUNTAIL, dev->name);
+ return;
+ }
+
+ CTCM_DBF_TEXT_(MPC_SETUP, CTC_DBF_NOTICE,
+ "%s: %s: GROUP TRANSITIONED TO READY, maxbuf = %d\n",
+ CTCM_FUNTAIL, dev->name, grp->group_max_buflen);
+
+ fsm_newstate(grp->fsm, MPCG_STATE_READY);
+
+ /* Put up a read on the channel */
+ ch = priv->channel[CTCM_READ];
+ ch->pdu_seq = 0;
+ CTCM_PR_DBGDATA("ctcmpc: %s() ToDCM_pdu_seq= %08x\n" ,
+ __func__, ch->pdu_seq);
+
+ ctcmpc_chx_rxidle(ch->fsm, CTC_EVENT_START, ch);
+ /* Put the write channel in idle state */
+ ch = priv->channel[CTCM_WRITE];
+ if (ch->collect_len > 0) {
+ spin_lock(&ch->collect_lock);
+ ctcm_purge_skb_queue(&ch->collect_queue);
+ ch->collect_len = 0;
+ spin_unlock(&ch->collect_lock);
+ }
+ ctcm_chx_txidle(ch->fsm, CTC_EVENT_START, ch);
+ ctcm_clear_busy(dev);
+
+ if (grp->estconnfunc) {
+ grp->estconnfunc(grp->port_num, 0,
+ grp->group_max_buflen);
+ grp->estconnfunc = NULL;
+ } else if (grp->allochanfunc)
+ grp->allochanfunc(grp->port_num, grp->group_max_buflen);
+
+ grp->send_qllc_disc = 1;
+ grp->changed_side = 0;
+
+ return;
+
+}
+
+/*
+ * Increment the MPC Group Active Channel Counts
+ * helper of dev_action (called from channel fsm)
+ */
+void mpc_channel_action(struct channel *ch, int direction, int action)
+{
+ struct net_device *dev = ch->netdev;
+ struct ctcm_priv *priv = dev->ml_priv;
+ struct mpc_group *grp = priv->mpcg;
+
+ if (grp == NULL) {
+ CTCM_DBF_TEXT_(MPC_ERROR, CTC_DBF_ERROR,
+ "%s(%s): No MPC group",
+ CTCM_FUNTAIL, dev->name);
+ return;
+ }
+
+ CTCM_PR_DEBUG("enter %s: ch=0x%p id=%s\n", __func__, ch, ch->id);
+
+ CTCM_DBF_TEXT_(MPC_TRACE, CTC_DBF_NOTICE,
+ "%s: %i / Grp:%s total_channels=%i, active_channels: "
+ "read=%i, write=%i\n", __func__, action,
+ fsm_getstate_str(grp->fsm), grp->num_channel_paths,
+ grp->active_channels[CTCM_READ],
+ grp->active_channels[CTCM_WRITE]);
+
+ if ((action == MPC_CHANNEL_ADD) && (ch->in_mpcgroup == 0)) {
+ grp->num_channel_paths++;
+ grp->active_channels[direction]++;
+ grp->outstanding_xid2++;
+ ch->in_mpcgroup = 1;
+
+ if (ch->xid_skb != NULL)
+ dev_kfree_skb_any(ch->xid_skb);
+
+ ch->xid_skb = __dev_alloc_skb(MPC_BUFSIZE_DEFAULT,
+ GFP_ATOMIC | GFP_DMA);
+ if (ch->xid_skb == NULL) {
+ CTCM_DBF_TEXT_(MPC_ERROR, CTC_DBF_ERROR,
+ "%s(%s): Couldn't alloc ch xid_skb\n",
+ CTCM_FUNTAIL, dev->name);
+ fsm_event(grp->fsm, MPCG_EVENT_INOP, dev);
+ return;
+ }
+ ch->xid_skb_data = ch->xid_skb->data;
+ ch->xid_th = (struct th_header *)ch->xid_skb->data;
+ skb_put(ch->xid_skb, TH_HEADER_LENGTH);
+ ch->xid = (struct xid2 *)skb_tail_pointer(ch->xid_skb);
+ skb_put(ch->xid_skb, XID2_LENGTH);
+ ch->xid_id = skb_tail_pointer(ch->xid_skb);
+ ch->xid_skb->data = ch->xid_skb_data;
+ skb_reset_tail_pointer(ch->xid_skb);
+ ch->xid_skb->len = 0;
+
+ memcpy(skb_put(ch->xid_skb, grp->xid_skb->len),
+ grp->xid_skb->data,
+ grp->xid_skb->len);
+
+ ch->xid->xid2_dlc_type =
+ ((CHANNEL_DIRECTION(ch->flags) == CTCM_READ)
+ ? XID2_READ_SIDE : XID2_WRITE_SIDE);
+
+ if (CHANNEL_DIRECTION(ch->flags) == CTCM_WRITE)
+ ch->xid->xid2_buf_len = 0x00;
+
+ ch->xid_skb->data = ch->xid_skb_data;
+ skb_reset_tail_pointer(ch->xid_skb);
+ ch->xid_skb->len = 0;
+
+ fsm_newstate(ch->fsm, CH_XID0_PENDING);
+
+ if ((grp->active_channels[CTCM_READ] > 0) &&
+ (grp->active_channels[CTCM_WRITE] > 0) &&
+ (fsm_getstate(grp->fsm) < MPCG_STATE_XID2INITW)) {
+ fsm_newstate(grp->fsm, MPCG_STATE_XID2INITW);
+ CTCM_DBF_TEXT_(MPC_SETUP, CTC_DBF_NOTICE,
+ "%s: %s: MPC GROUP CHANNELS ACTIVE\n",
+ __func__, dev->name);
+ }
+ } else if ((action == MPC_CHANNEL_REMOVE) &&
+ (ch->in_mpcgroup == 1)) {
+ ch->in_mpcgroup = 0;
+ grp->num_channel_paths--;
+ grp->active_channels[direction]--;
+
+ if (ch->xid_skb != NULL)
+ dev_kfree_skb_any(ch->xid_skb);
+ ch->xid_skb = NULL;
+
+ if (grp->channels_terminating)
+ goto done;
+
+ if (((grp->active_channels[CTCM_READ] == 0) &&
+ (grp->active_channels[CTCM_WRITE] > 0))
+ || ((grp->active_channels[CTCM_WRITE] == 0) &&
+ (grp->active_channels[CTCM_READ] > 0)))
+ fsm_event(grp->fsm, MPCG_EVENT_INOP, dev);
+ }
+done:
+ CTCM_DBF_TEXT_(MPC_TRACE, CTC_DBF_DEBUG,
+ "exit %s: %i / Grp:%s total_channels=%i, active_channels: "
+ "read=%i, write=%i\n", __func__, action,
+ fsm_getstate_str(grp->fsm), grp->num_channel_paths,
+ grp->active_channels[CTCM_READ],
+ grp->active_channels[CTCM_WRITE]);
+
+ CTCM_PR_DEBUG("exit %s: ch=0x%p id=%s\n", __func__, ch, ch->id);
+}
+
+/**
+ * Unpack a just received skb and hand it over to
+ * upper layers.
+ * special MPC version of unpack_skb.
+ *
+ * ch The channel where this skb has been received.
+ * pskb The received skb.
+ */
+static void ctcmpc_unpack_skb(struct channel *ch, struct sk_buff *pskb)
+{
+ struct net_device *dev = ch->netdev;
+ struct ctcm_priv *priv = dev->ml_priv;
+ struct mpc_group *grp = priv->mpcg;
+ struct pdu *curr_pdu;
+ struct mpcg_info *mpcginfo;
+ struct th_header *header = NULL;
+ struct th_sweep *sweep = NULL;
+ int pdu_last_seen = 0;
+ __u32 new_len;
+ struct sk_buff *skb;
+ int skblen;
+ int sendrc = 0;
+
+ CTCM_PR_DEBUG("ctcmpc enter: %s() %s cp:%i ch:%s\n",
+ __func__, dev->name, smp_processor_id(), ch->id);
+
+ header = (struct th_header *)pskb->data;
+ if ((header->th_seg == 0) &&
+ (header->th_ch_flag == 0) &&
+ (header->th_blk_flag == 0) &&
+ (header->th_seq_num == 0))
+ /* nothing for us */ goto done;
+
+ CTCM_PR_DBGDATA("%s: th_header\n", __func__);
+ CTCM_D3_DUMP((char *)header, TH_HEADER_LENGTH);
+ CTCM_PR_DBGDATA("%s: pskb len: %04x \n", __func__, pskb->len);
+
+ pskb->dev = dev;
+ pskb->ip_summed = CHECKSUM_UNNECESSARY;
+ skb_pull(pskb, TH_HEADER_LENGTH);
+
+ if (likely(header->th_ch_flag == TH_HAS_PDU)) {
+ CTCM_PR_DBGDATA("%s: came into th_has_pdu\n", __func__);
+ if ((fsm_getstate(grp->fsm) == MPCG_STATE_FLOWC) ||
+ ((fsm_getstate(grp->fsm) == MPCG_STATE_READY) &&
+ (header->th_seq_num != ch->th_seq_num + 1) &&
+ (ch->th_seq_num != 0))) {
+ /* This is NOT the next segment *
+ * we are not the correct race winner *
+ * go away and let someone else win *
+ * BUT..this only applies if xid negot *
+ * is done *
+ */
+ grp->out_of_sequence += 1;
+ __skb_push(pskb, TH_HEADER_LENGTH);
+ skb_queue_tail(&ch->io_queue, pskb);
+ CTCM_PR_DBGDATA("%s: th_seq_num expect:%08x "
+ "got:%08x\n", __func__,
+ ch->th_seq_num + 1, header->th_seq_num);
+
+ return;
+ }
+ grp->out_of_sequence = 0;
+ ch->th_seq_num = header->th_seq_num;
+
+ CTCM_PR_DBGDATA("ctcmpc: %s() FromVTAM_th_seq=%08x\n",
+ __func__, ch->th_seq_num);
+
+ if (unlikely(fsm_getstate(grp->fsm) != MPCG_STATE_READY))
+ goto done;
+ while ((pskb->len > 0) && !pdu_last_seen) {
+ curr_pdu = (struct pdu *)pskb->data;
+
+ CTCM_PR_DBGDATA("%s: pdu_header\n", __func__);
+ CTCM_D3_DUMP((char *)pskb->data, PDU_HEADER_LENGTH);
+ CTCM_PR_DBGDATA("%s: pskb len: %04x \n",
+ __func__, pskb->len);
+
+ skb_pull(pskb, PDU_HEADER_LENGTH);
+
+ if (curr_pdu->pdu_flag & PDU_LAST)
+ pdu_last_seen = 1;
+ if (curr_pdu->pdu_flag & PDU_CNTL)
+ pskb->protocol = htons(ETH_P_SNAP);
+ else
+ pskb->protocol = htons(ETH_P_SNA_DIX);
+
+ if ((pskb->len <= 0) || (pskb->len > ch->max_bufsize)) {
+ CTCM_DBF_TEXT_(MPC_ERROR, CTC_DBF_ERROR,
+ "%s(%s): Dropping packet with "
+ "illegal siize %d",
+ CTCM_FUNTAIL, dev->name, pskb->len);
+
+ priv->stats.rx_dropped++;
+ priv->stats.rx_length_errors++;
+ goto done;
+ }
+ skb_reset_mac_header(pskb);
+ new_len = curr_pdu->pdu_offset;
+ CTCM_PR_DBGDATA("%s: new_len: %04x \n",
+ __func__, new_len);
+ if ((new_len == 0) || (new_len > pskb->len)) {
+ /* should never happen */
+ /* pskb len must be hosed...bail out */
+ CTCM_DBF_TEXT_(MPC_ERROR, CTC_DBF_ERROR,
+ "%s(%s): non valid pdu_offset: %04x",
+ /* "data may be lost", */
+ CTCM_FUNTAIL, dev->name, new_len);
+ goto done;
+ }
+ skb = __dev_alloc_skb(new_len+4, GFP_ATOMIC);
+
+ if (!skb) {
+ CTCM_DBF_TEXT_(MPC_ERROR, CTC_DBF_ERROR,
+ "%s(%s): MEMORY allocation error",
+ CTCM_FUNTAIL, dev->name);
+ priv->stats.rx_dropped++;
+ fsm_event(grp->fsm, MPCG_EVENT_INOP, dev);
+ goto done;
+ }
+ memcpy(skb_put(skb, new_len), pskb->data, new_len);
+
+ skb_reset_mac_header(skb);
+ skb->dev = pskb->dev;
+ skb->protocol = pskb->protocol;
+ skb->ip_summed = CHECKSUM_UNNECESSARY;
+ *((__u32 *) skb_push(skb, 4)) = ch->pdu_seq;
+ ch->pdu_seq++;
+
+ if (do_debug_data) {
+ ctcm_pr_debug("%s: ToDCM_pdu_seq= %08x\n",
+ __func__, ch->pdu_seq);
+ ctcm_pr_debug("%s: skb:%0lx "
+ "skb len: %d \n", __func__,
+ (unsigned long)skb, skb->len);
+ ctcm_pr_debug("%s: up to 32 bytes "
+ "of pdu_data sent\n", __func__);
+ ctcmpc_dump32((char *)skb->data, skb->len);
+ }
+
+ skblen = skb->len;
+ sendrc = netif_rx(skb);
+ priv->stats.rx_packets++;
+ priv->stats.rx_bytes += skblen;
+ skb_pull(pskb, new_len); /* point to next PDU */
+ }
+ } else {
+ mpcginfo = kmalloc(sizeof(struct mpcg_info), gfp_type());
+ if (mpcginfo == NULL)
+ goto done;
+
+ mpcginfo->ch = ch;
+ mpcginfo->th = header;
+ mpcginfo->skb = pskb;
+ CTCM_PR_DEBUG("%s: Not PDU - may be control pkt\n",
+ __func__);
+ /* it's a sweep? */
+ sweep = (struct th_sweep *)pskb->data;
+ mpcginfo->sweep = sweep;
+ if (header->th_ch_flag == TH_SWEEP_REQ)
+ mpc_rcvd_sweep_req(mpcginfo);
+ else if (header->th_ch_flag == TH_SWEEP_RESP)
+ mpc_rcvd_sweep_resp(mpcginfo);
+ else if (header->th_blk_flag == TH_DATA_IS_XID) {
+ struct xid2 *thisxid = (struct xid2 *)pskb->data;
+ skb_pull(pskb, XID2_LENGTH);
+ mpcginfo->xid = thisxid;
+ fsm_event(grp->fsm, MPCG_EVENT_XID2, mpcginfo);
+ } else if (header->th_blk_flag == TH_DISCONTACT)
+ fsm_event(grp->fsm, MPCG_EVENT_DISCONC, mpcginfo);
+ else if (header->th_seq_num != 0) {
+ CTCM_DBF_TEXT_(MPC_ERROR, CTC_DBF_ERROR,
+ "%s(%s): control pkt expected\n",
+ CTCM_FUNTAIL, dev->name);
+ priv->stats.rx_dropped++;
+ /* mpcginfo only used for non-data transfers */
+ kfree(mpcginfo);
+ if (do_debug_data)
+ ctcmpc_dump_skb(pskb, -8);
+ }
+ }
+done:
+
+ dev_kfree_skb_any(pskb);
+ if (sendrc == NET_RX_DROP) {
+ dev_warn(&dev->dev,
+ "The network backlog for %s is exceeded, "
+ "package dropped\n", __func__);
+ fsm_event(grp->fsm, MPCG_EVENT_INOP, dev);
+ }
+
+ CTCM_PR_DEBUG("exit %s: %s: ch=0x%p id=%s\n",
+ __func__, dev->name, ch, ch->id);
+}
+
+/**
+ * tasklet helper for mpc's skb unpacking.
+ *
+ * ch The channel to work on.
+ * Allow flow control back pressure to occur here.
+ * Throttling back channel can result in excessive
+ * channel inactivity and system deact of channel
+ */
+void ctcmpc_bh(unsigned long thischan)
+{
+ struct channel *ch = (struct channel *)thischan;
+ struct sk_buff *skb;
+ struct net_device *dev = ch->netdev;
+ struct ctcm_priv *priv = dev->ml_priv;
+ struct mpc_group *grp = priv->mpcg;
+
+ CTCM_PR_DEBUG("%s cp:%i enter: %s() %s\n",
+ dev->name, smp_processor_id(), __func__, ch->id);
+ /* caller has requested driver to throttle back */
+ while ((fsm_getstate(grp->fsm) != MPCG_STATE_FLOWC) &&
+ (skb = skb_dequeue(&ch->io_queue))) {
+ ctcmpc_unpack_skb(ch, skb);
+ if (grp->out_of_sequence > 20) {
+ /* assume data loss has occurred if */
+ /* missing seq_num for extended */
+ /* period of time */
+ grp->out_of_sequence = 0;
+ fsm_event(grp->fsm, MPCG_EVENT_INOP, dev);
+ break;
+ }
+ if (skb == skb_peek(&ch->io_queue))
+ break;
+ }
+ CTCM_PR_DEBUG("exit %s: %s: ch=0x%p id=%s\n",
+ __func__, dev->name, ch, ch->id);
+ return;
+}
+
+/*
+ * MPC Group Initializations
+ */
+struct mpc_group *ctcmpc_init_mpc_group(struct ctcm_priv *priv)
+{
+ struct mpc_group *grp;
+
+ CTCM_DBF_TEXT_(MPC_SETUP, CTC_DBF_INFO,
+ "Enter %s(%p)", CTCM_FUNTAIL, priv);
+
+ grp = kzalloc(sizeof(struct mpc_group), GFP_KERNEL);
+ if (grp == NULL)
+ return NULL;
+
+ grp->fsm = init_fsm("mpcg", mpcg_state_names, mpcg_event_names,
+ MPCG_NR_STATES, MPCG_NR_EVENTS, mpcg_fsm,
+ mpcg_fsm_len, GFP_KERNEL);
+ if (grp->fsm == NULL) {
+ kfree(grp);
+ return NULL;
+ }
+
+ fsm_newstate(grp->fsm, MPCG_STATE_RESET);
+ fsm_settimer(grp->fsm, &grp->timer);
+
+ grp->xid_skb =
+ __dev_alloc_skb(MPC_BUFSIZE_DEFAULT, GFP_ATOMIC | GFP_DMA);
+ if (grp->xid_skb == NULL) {
+ kfree_fsm(grp->fsm);
+ kfree(grp);
+ return NULL;
+ }
+ /* base xid for all channels in group */
+ grp->xid_skb_data = grp->xid_skb->data;
+ grp->xid_th = (struct th_header *)grp->xid_skb->data;
+ memcpy(skb_put(grp->xid_skb, TH_HEADER_LENGTH),
+ &thnorm, TH_HEADER_LENGTH);
+
+ grp->xid = (struct xid2 *)skb_tail_pointer(grp->xid_skb);
+ memcpy(skb_put(grp->xid_skb, XID2_LENGTH), &init_xid, XID2_LENGTH);
+ grp->xid->xid2_adj_id = jiffies | 0xfff00000;
+ grp->xid->xid2_sender_id = jiffies;
+
+ grp->xid_id = skb_tail_pointer(grp->xid_skb);
+ memcpy(skb_put(grp->xid_skb, 4), "VTAM", 4);
+
+ grp->rcvd_xid_skb =
+ __dev_alloc_skb(MPC_BUFSIZE_DEFAULT, GFP_ATOMIC|GFP_DMA);
+ if (grp->rcvd_xid_skb == NULL) {
+ kfree_fsm(grp->fsm);
+ dev_kfree_skb(grp->xid_skb);
+ kfree(grp);
+ return NULL;
+ }
+ grp->rcvd_xid_data = grp->rcvd_xid_skb->data;
+ grp->rcvd_xid_th = (struct th_header *)grp->rcvd_xid_skb->data;
+ memcpy(skb_put(grp->rcvd_xid_skb, TH_HEADER_LENGTH),
+ &thnorm, TH_HEADER_LENGTH);
+ grp->saved_xid2 = NULL;
+ priv->xid = grp->xid;
+ priv->mpcg = grp;
+ return grp;
+}
+
+/*
+ * The MPC Group Station FSM
+ */
+
+/*
+ * MPC Group Station FSM actions
+ * CTCM_PROTO_MPC only
+ */
+
+/**
+ * NOP action for statemachines
+ */
+static void mpc_action_nop(fsm_instance *fi, int event, void *arg)
+{
+}
+
+/*
+ * invoked when the device transitions to dev_stopped
+ * MPC will stop each individual channel if a single XID failure
+ * occurs, or will intitiate all channels be stopped if a GROUP
+ * level failure occurs.
+ */
+static void mpc_action_go_inop(fsm_instance *fi, int event, void *arg)
+{
+ struct net_device *dev = arg;
+ struct ctcm_priv *priv;
+ struct mpc_group *grp;
+ struct channel *wch;
+
+ CTCM_PR_DEBUG("Enter %s: %s\n", __func__, dev->name);
+
+ priv = dev->ml_priv;
+ grp = priv->mpcg;
+ grp->flow_off_called = 0;
+ fsm_deltimer(&grp->timer);
+ if (grp->channels_terminating)
+ return;
+
+ grp->channels_terminating = 1;
+ grp->saved_state = fsm_getstate(grp->fsm);
+ fsm_newstate(grp->fsm, MPCG_STATE_INOP);
+ if (grp->saved_state > MPCG_STATE_XID7INITF)
+ CTCM_DBF_TEXT_(MPC_TRACE, CTC_DBF_NOTICE,
+ "%s(%s): MPC GROUP INOPERATIVE",
+ CTCM_FUNTAIL, dev->name);
+ if ((grp->saved_state != MPCG_STATE_RESET) ||
+ /* dealloc_channel has been called */
+ (grp->port_persist == 0))
+ fsm_deltimer(&priv->restart_timer);
+
+ wch = priv->channel[CTCM_WRITE];
+
+ switch (grp->saved_state) {
+ case MPCG_STATE_RESET:
+ case MPCG_STATE_INOP:
+ case MPCG_STATE_XID2INITW:
+ case MPCG_STATE_XID0IOWAIT:
+ case MPCG_STATE_XID2INITX:
+ case MPCG_STATE_XID7INITW:
+ case MPCG_STATE_XID7INITX:
+ case MPCG_STATE_XID0IOWAIX:
+ case MPCG_STATE_XID7INITI:
+ case MPCG_STATE_XID7INITZ:
+ case MPCG_STATE_XID7INITF:
+ break;
+ case MPCG_STATE_FLOWC:
+ case MPCG_STATE_READY:
+ default:
+ tasklet_hi_schedule(&wch->ch_disc_tasklet);
+ }
+
+ grp->xid2_tgnum = 0;
+ grp->group_max_buflen = 0; /*min of all received */
+ grp->outstanding_xid2 = 0;
+ grp->outstanding_xid7 = 0;
+ grp->outstanding_xid7_p2 = 0;
+ grp->saved_xid2 = NULL;
+ grp->xidnogood = 0;
+ grp->changed_side = 0;
+
+ grp->rcvd_xid_skb->data = grp->rcvd_xid_data;
+ skb_reset_tail_pointer(grp->rcvd_xid_skb);
+ grp->rcvd_xid_skb->len = 0;
+ grp->rcvd_xid_th = (struct th_header *)grp->rcvd_xid_skb->data;
+ memcpy(skb_put(grp->rcvd_xid_skb, TH_HEADER_LENGTH), &thnorm,
+ TH_HEADER_LENGTH);
+
+ if (grp->send_qllc_disc == 1) {
+ grp->send_qllc_disc = 0;
+ mpc_send_qllc_discontact(dev);
+ }
+
+ /* DO NOT issue DEV_EVENT_STOP directly out of this code */
+ /* This can result in INOP of VTAM PU due to halting of */
+ /* outstanding IO which causes a sense to be returned */
+ /* Only about 3 senses are allowed and then IOS/VTAM will*/
+ /* become unreachable without manual intervention */
+ if ((grp->port_persist == 1) || (grp->alloc_called)) {
+ grp->alloc_called = 0;
+ fsm_deltimer(&priv->restart_timer);
+ fsm_addtimer(&priv->restart_timer, 500, DEV_EVENT_RESTART, dev);
+ fsm_newstate(grp->fsm, MPCG_STATE_RESET);
+ if (grp->saved_state > MPCG_STATE_XID7INITF)
+ CTCM_DBF_TEXT_(MPC_TRACE, CTC_DBF_ALWAYS,
+ "%s(%s): MPC GROUP RECOVERY SCHEDULED",
+ CTCM_FUNTAIL, dev->name);
+ } else {
+ fsm_deltimer(&priv->restart_timer);
+ fsm_addtimer(&priv->restart_timer, 500, DEV_EVENT_STOP, dev);
+ fsm_newstate(grp->fsm, MPCG_STATE_RESET);
+ CTCM_DBF_TEXT_(MPC_TRACE, CTC_DBF_ALWAYS,
+ "%s(%s): NO MPC GROUP RECOVERY ATTEMPTED",
+ CTCM_FUNTAIL, dev->name);
+ }
+}
+
+/**
+ * Handle mpc group action timeout.
+ * MPC Group Station FSM action
+ * CTCM_PROTO_MPC only
+ *
+ * fi An instance of an mpc_group fsm.
+ * event The event, just happened.
+ * arg Generic pointer, casted from net_device * upon call.
+ */
+static void mpc_action_timeout(fsm_instance *fi, int event, void *arg)
+{
+ struct net_device *dev = arg;
+ struct ctcm_priv *priv;
+ struct mpc_group *grp;
+ struct channel *wch;
+ struct channel *rch;
+
+ priv = dev->ml_priv;
+ grp = priv->mpcg;
+ wch = priv->channel[CTCM_WRITE];
+ rch = priv->channel[CTCM_READ];
+
+ switch (fsm_getstate(grp->fsm)) {
+ case MPCG_STATE_XID2INITW:
+ /* Unless there is outstanding IO on the */
+ /* channel just return and wait for ATTN */
+ /* interrupt to begin XID negotiations */
+ if ((fsm_getstate(rch->fsm) == CH_XID0_PENDING) &&
+ (fsm_getstate(wch->fsm) == CH_XID0_PENDING))
+ break;
+ default:
+ fsm_event(grp->fsm, MPCG_EVENT_INOP, dev);
+ }
+
+ CTCM_DBF_TEXT_(MPC_TRACE, CTC_DBF_DEBUG,
+ "%s: dev=%s exit",
+ CTCM_FUNTAIL, dev->name);
+ return;
+}
+
+/*
+ * MPC Group Station FSM action
+ * CTCM_PROTO_MPC only
+ */
+void mpc_action_discontact(fsm_instance *fi, int event, void *arg)
+{
+ struct mpcg_info *mpcginfo = arg;
+ struct channel *ch = mpcginfo->ch;
+ struct net_device *dev;
+ struct ctcm_priv *priv;
+ struct mpc_group *grp;
+
+ if (ch) {
+ dev = ch->netdev;
+ if (dev) {
+ priv = dev->ml_priv;
+ if (priv) {
+ CTCM_DBF_TEXT_(MPC_TRACE, CTC_DBF_NOTICE,
+ "%s: %s: %s\n",
+ CTCM_FUNTAIL, dev->name, ch->id);
+ grp = priv->mpcg;
+ grp->send_qllc_disc = 1;
+ fsm_event(grp->fsm, MPCG_EVENT_INOP, dev);
+ }
+ }
+ }
+
+ return;
+}
+
+/*
+ * MPC Group Station - not part of FSM
+ * CTCM_PROTO_MPC only
+ * called from add_channel in ctcm_main.c
+ */
+void mpc_action_send_discontact(unsigned long thischan)
+{
+ int rc;
+ struct channel *ch = (struct channel *)thischan;
+ unsigned long saveflags = 0;
+
+ spin_lock_irqsave(get_ccwdev_lock(ch->cdev), saveflags);
+ rc = ccw_device_start(ch->cdev, &ch->ccw[15],
+ (unsigned long)ch, 0xff, 0);
+ spin_unlock_irqrestore(get_ccwdev_lock(ch->cdev), saveflags);
+
+ if (rc != 0) {
+ ctcm_ccw_check_rc(ch, rc, (char *)__func__);
+ }
+
+ return;
+}
+
+
+/*
+ * helper function of mpc FSM
+ * CTCM_PROTO_MPC only
+ * mpc_action_rcvd_xid7
+*/
+static int mpc_validate_xid(struct mpcg_info *mpcginfo)
+{
+ struct channel *ch = mpcginfo->ch;
+ struct net_device *dev = ch->netdev;
+ struct ctcm_priv *priv = dev->ml_priv;
+ struct mpc_group *grp = priv->mpcg;
+ struct xid2 *xid = mpcginfo->xid;
+ int rc = 0;
+ __u64 our_id = 0;
+ __u64 their_id = 0;
+ int len = TH_HEADER_LENGTH + PDU_HEADER_LENGTH;
+
+ CTCM_PR_DEBUG("Enter %s: xid=%p\n", __func__, xid);
+
+ if (xid == NULL) {
+ rc = 1;
+ /* XID REJECTED: xid == NULL */
+ CTCM_DBF_TEXT_(MPC_ERROR, CTC_DBF_ERROR,
+ "%s(%s): xid = NULL",
+ CTCM_FUNTAIL, ch->id);
+ goto done;
+ }
+
+ CTCM_D3_DUMP((char *)xid, XID2_LENGTH);
+
+ /*the received direction should be the opposite of ours */
+ if (((CHANNEL_DIRECTION(ch->flags) == CTCM_READ) ? XID2_WRITE_SIDE :
+ XID2_READ_SIDE) != xid->xid2_dlc_type) {
+ rc = 2;
+ /* XID REJECTED: r/w channel pairing mismatch */
+ CTCM_DBF_TEXT_(MPC_ERROR, CTC_DBF_ERROR,
+ "%s(%s): r/w channel pairing mismatch",
+ CTCM_FUNTAIL, ch->id);
+ goto done;
+ }
+
+ if (xid->xid2_dlc_type == XID2_READ_SIDE) {
+ CTCM_PR_DEBUG("%s: grpmaxbuf:%d xid2buflen:%d\n", __func__,
+ grp->group_max_buflen, xid->xid2_buf_len);
+
+ if (grp->group_max_buflen == 0 || grp->group_max_buflen >
+ xid->xid2_buf_len - len)
+ grp->group_max_buflen = xid->xid2_buf_len - len;
+ }
+
+ if (grp->saved_xid2 == NULL) {
+ grp->saved_xid2 =
+ (struct xid2 *)skb_tail_pointer(grp->rcvd_xid_skb);
+
+ memcpy(skb_put(grp->rcvd_xid_skb,
+ XID2_LENGTH), xid, XID2_LENGTH);
+ grp->rcvd_xid_skb->data = grp->rcvd_xid_data;
+
+ skb_reset_tail_pointer(grp->rcvd_xid_skb);
+ grp->rcvd_xid_skb->len = 0;
+
+ /* convert two 32 bit numbers into 1 64 bit for id compare */
+ our_id = (__u64)priv->xid->xid2_adj_id;
+ our_id = our_id << 32;
+ our_id = our_id + priv->xid->xid2_sender_id;
+ their_id = (__u64)xid->xid2_adj_id;
+ their_id = their_id << 32;
+ their_id = their_id + xid->xid2_sender_id;
+ /* lower id assume the xside role */
+ if (our_id < their_id) {
+ grp->roll = XSIDE;
+ CTCM_DBF_TEXT_(MPC_TRACE, CTC_DBF_NOTICE,
+ "%s(%s): WE HAVE LOW ID - TAKE XSIDE",
+ CTCM_FUNTAIL, ch->id);
+ } else {
+ grp->roll = YSIDE;
+ CTCM_DBF_TEXT_(MPC_TRACE, CTC_DBF_NOTICE,
+ "%s(%s): WE HAVE HIGH ID - TAKE YSIDE",
+ CTCM_FUNTAIL, ch->id);
+ }
+
+ } else {
+ if (xid->xid2_flag4 != grp->saved_xid2->xid2_flag4) {
+ rc = 3;
+ /* XID REJECTED: xid flag byte4 mismatch */
+ CTCM_DBF_TEXT_(MPC_ERROR, CTC_DBF_ERROR,
+ "%s(%s): xid flag byte4 mismatch",
+ CTCM_FUNTAIL, ch->id);
+ }
+ if (xid->xid2_flag2 == 0x40) {
+ rc = 4;
+ /* XID REJECTED - xid NOGOOD */
+ CTCM_DBF_TEXT_(MPC_ERROR, CTC_DBF_ERROR,
+ "%s(%s): xid NOGOOD",
+ CTCM_FUNTAIL, ch->id);
+ }
+ if (xid->xid2_adj_id != grp->saved_xid2->xid2_adj_id) {
+ rc = 5;
+ /* XID REJECTED - Adjacent Station ID Mismatch */
+ CTCM_DBF_TEXT_(MPC_ERROR, CTC_DBF_ERROR,
+ "%s(%s): Adjacent Station ID Mismatch",
+ CTCM_FUNTAIL, ch->id);
+ }
+ if (xid->xid2_sender_id != grp->saved_xid2->xid2_sender_id) {
+ rc = 6;
+ /* XID REJECTED - Sender Address Mismatch */
+ CTCM_DBF_TEXT_(MPC_ERROR, CTC_DBF_ERROR,
+ "%s(%s): Sender Address Mismatch",
+ CTCM_FUNTAIL, ch->id);
+ }
+ }
+done:
+ if (rc) {
+ dev_warn(&dev->dev,
+ "The XID used in the MPC protocol is not valid, "
+ "rc = %d\n", rc);
+ priv->xid->xid2_flag2 = 0x40;
+ grp->saved_xid2->xid2_flag2 = 0x40;
+ }
+
+ return rc;
+}
+
+/*
+ * MPC Group Station FSM action
+ * CTCM_PROTO_MPC only
+ */
+static void mpc_action_side_xid(fsm_instance *fsm, void *arg, int side)
+{
+ struct channel *ch = arg;
+ int rc = 0;
+ int gotlock = 0;
+ unsigned long saveflags = 0; /* avoids compiler warning with
+ spin_unlock_irqrestore */
+
+ CTCM_PR_DEBUG("Enter %s: cp=%i ch=0x%p id=%s\n",
+ __func__, smp_processor_id(), ch, ch->id);
+
+ if (ctcm_checkalloc_buffer(ch))
+ goto done;
+
+ /*
+ * skb data-buffer referencing:
+ */
+ ch->trans_skb->data = ch->trans_skb_data;
+ skb_reset_tail_pointer(ch->trans_skb);
+ ch->trans_skb->len = 0;
+ /* result of the previous 3 statements is NOT always
+ * already set after ctcm_checkalloc_buffer
+ * because of possible reuse of the trans_skb
+ */
+ memset(ch->trans_skb->data, 0, 16);
+ ch->rcvd_xid_th = (struct th_header *)ch->trans_skb_data;
+ /* check is main purpose here: */
+ skb_put(ch->trans_skb, TH_HEADER_LENGTH);
+ ch->rcvd_xid = (struct xid2 *)skb_tail_pointer(ch->trans_skb);
+ /* check is main purpose here: */
+ skb_put(ch->trans_skb, XID2_LENGTH);
+ ch->rcvd_xid_id = skb_tail_pointer(ch->trans_skb);
+ /* cleanup back to startpoint */
+ ch->trans_skb->data = ch->trans_skb_data;
+ skb_reset_tail_pointer(ch->trans_skb);
+ ch->trans_skb->len = 0;
+
+ /* non-checking rewrite of above skb data-buffer referencing: */
+ /*
+ memset(ch->trans_skb->data, 0, 16);
+ ch->rcvd_xid_th = (struct th_header *)ch->trans_skb_data;
+ ch->rcvd_xid = (struct xid2 *)(ch->trans_skb_data + TH_HEADER_LENGTH);
+ ch->rcvd_xid_id = ch->trans_skb_data + TH_HEADER_LENGTH + XID2_LENGTH;
+ */
+
+ ch->ccw[8].flags = CCW_FLAG_SLI | CCW_FLAG_CC;
+ ch->ccw[8].count = 0;
+ ch->ccw[8].cda = 0x00;
+
+ if (!(ch->xid_th && ch->xid && ch->xid_id))
+ CTCM_DBF_TEXT_(MPC_TRACE, CTC_DBF_INFO,
+ "%s(%s): xid_th=%p, xid=%p, xid_id=%p",
+ CTCM_FUNTAIL, ch->id, ch->xid_th, ch->xid, ch->xid_id);
+
+ if (side == XSIDE) {
+ /* mpc_action_xside_xid */
+ if (ch->xid_th == NULL)
+ goto done;
+ ch->ccw[9].cmd_code = CCW_CMD_WRITE;
+ ch->ccw[9].flags = CCW_FLAG_SLI | CCW_FLAG_CC;
+ ch->ccw[9].count = TH_HEADER_LENGTH;
+ ch->ccw[9].cda = virt_to_phys(ch->xid_th);
+
+ if (ch->xid == NULL)
+ goto done;
+ ch->ccw[10].cmd_code = CCW_CMD_WRITE;
+ ch->ccw[10].flags = CCW_FLAG_SLI | CCW_FLAG_CC;
+ ch->ccw[10].count = XID2_LENGTH;
+ ch->ccw[10].cda = virt_to_phys(ch->xid);
+
+ ch->ccw[11].cmd_code = CCW_CMD_READ;
+ ch->ccw[11].flags = CCW_FLAG_SLI | CCW_FLAG_CC;
+ ch->ccw[11].count = TH_HEADER_LENGTH;
+ ch->ccw[11].cda = virt_to_phys(ch->rcvd_xid_th);
+
+ ch->ccw[12].cmd_code = CCW_CMD_READ;
+ ch->ccw[12].flags = CCW_FLAG_SLI | CCW_FLAG_CC;
+ ch->ccw[12].count = XID2_LENGTH;
+ ch->ccw[12].cda = virt_to_phys(ch->rcvd_xid);
+
+ ch->ccw[13].cmd_code = CCW_CMD_READ;
+ ch->ccw[13].cda = virt_to_phys(ch->rcvd_xid_id);
+
+ } else { /* side == YSIDE : mpc_action_yside_xid */
+ ch->ccw[9].cmd_code = CCW_CMD_READ;
+ ch->ccw[9].flags = CCW_FLAG_SLI | CCW_FLAG_CC;
+ ch->ccw[9].count = TH_HEADER_LENGTH;
+ ch->ccw[9].cda = virt_to_phys(ch->rcvd_xid_th);
+
+ ch->ccw[10].cmd_code = CCW_CMD_READ;
+ ch->ccw[10].flags = CCW_FLAG_SLI | CCW_FLAG_CC;
+ ch->ccw[10].count = XID2_LENGTH;
+ ch->ccw[10].cda = virt_to_phys(ch->rcvd_xid);
+
+ if (ch->xid_th == NULL)
+ goto done;
+ ch->ccw[11].cmd_code = CCW_CMD_WRITE;
+ ch->ccw[11].flags = CCW_FLAG_SLI | CCW_FLAG_CC;
+ ch->ccw[11].count = TH_HEADER_LENGTH;
+ ch->ccw[11].cda = virt_to_phys(ch->xid_th);
+
+ if (ch->xid == NULL)
+ goto done;
+ ch->ccw[12].cmd_code = CCW_CMD_WRITE;
+ ch->ccw[12].flags = CCW_FLAG_SLI | CCW_FLAG_CC;
+ ch->ccw[12].count = XID2_LENGTH;
+ ch->ccw[12].cda = virt_to_phys(ch->xid);
+
+ if (ch->xid_id == NULL)
+ goto done;
+ ch->ccw[13].cmd_code = CCW_CMD_WRITE;
+ ch->ccw[13].cda = virt_to_phys(ch->xid_id);
+
+ }
+ ch->ccw[13].flags = CCW_FLAG_SLI | CCW_FLAG_CC;
+ ch->ccw[13].count = 4;
+
+ ch->ccw[14].cmd_code = CCW_CMD_NOOP;
+ ch->ccw[14].flags = CCW_FLAG_SLI;
+ ch->ccw[14].count = 0;
+ ch->ccw[14].cda = 0;
+
+ CTCM_CCW_DUMP((char *)&ch->ccw[8], sizeof(struct ccw1) * 7);
+ CTCM_D3_DUMP((char *)ch->xid_th, TH_HEADER_LENGTH);
+ CTCM_D3_DUMP((char *)ch->xid, XID2_LENGTH);
+ CTCM_D3_DUMP((char *)ch->xid_id, 4);
+
+ if (!in_irq()) {
+ /* Such conditional locking is a known problem for
+ * sparse because its static undeterministic.
+ * Warnings should be ignored here. */
+ spin_lock_irqsave(get_ccwdev_lock(ch->cdev), saveflags);
+ gotlock = 1;
+ }
+
+ fsm_addtimer(&ch->timer, 5000 , CTC_EVENT_TIMER, ch);
+ rc = ccw_device_start(ch->cdev, &ch->ccw[8],
+ (unsigned long)ch, 0xff, 0);
+
+ if (gotlock) /* see remark above about conditional locking */
+ spin_unlock_irqrestore(get_ccwdev_lock(ch->cdev), saveflags);
+
+ if (rc != 0) {
+ ctcm_ccw_check_rc(ch, rc,
+ (side == XSIDE) ? "x-side XID" : "y-side XID");
+ }
+
+done:
+ CTCM_PR_DEBUG("Exit %s: ch=0x%p id=%s\n",
+ __func__, ch, ch->id);
+ return;
+
+}
+
+/*
+ * MPC Group Station FSM action
+ * CTCM_PROTO_MPC only
+ */
+static void mpc_action_xside_xid(fsm_instance *fsm, int event, void *arg)
+{
+ mpc_action_side_xid(fsm, arg, XSIDE);
+}
+
+/*
+ * MPC Group Station FSM action
+ * CTCM_PROTO_MPC only
+ */
+static void mpc_action_yside_xid(fsm_instance *fsm, int event, void *arg)
+{
+ mpc_action_side_xid(fsm, arg, YSIDE);
+}
+
+/*
+ * MPC Group Station FSM action
+ * CTCM_PROTO_MPC only
+ */
+static void mpc_action_doxid0(fsm_instance *fsm, int event, void *arg)
+{
+ struct channel *ch = arg;
+ struct net_device *dev = ch->netdev;
+ struct ctcm_priv *priv = dev->ml_priv;
+ struct mpc_group *grp = priv->mpcg;
+
+ CTCM_PR_DEBUG("Enter %s: cp=%i ch=0x%p id=%s\n",
+ __func__, smp_processor_id(), ch, ch->id);
+
+ if (ch->xid == NULL) {
+ CTCM_DBF_TEXT_(MPC_ERROR, CTC_DBF_ERROR,
+ "%s(%s): ch->xid == NULL",
+ CTCM_FUNTAIL, dev->name);
+ return;
+ }
+
+ fsm_newstate(ch->fsm, CH_XID0_INPROGRESS);
+
+ ch->xid->xid2_option = XID2_0;
+
+ switch (fsm_getstate(grp->fsm)) {
+ case MPCG_STATE_XID2INITW:
+ case MPCG_STATE_XID2INITX:
+ ch->ccw[8].cmd_code = CCW_CMD_SENSE_CMD;
+ break;
+ case MPCG_STATE_XID0IOWAIT:
+ case MPCG_STATE_XID0IOWAIX:
+ ch->ccw[8].cmd_code = CCW_CMD_WRITE_CTL;
+ break;
+ }
+
+ fsm_event(grp->fsm, MPCG_EVENT_DOIO, ch);
+
+ return;
+}
+
+/*
+ * MPC Group Station FSM action
+ * CTCM_PROTO_MPC only
+*/
+static void mpc_action_doxid7(fsm_instance *fsm, int event, void *arg)
+{
+ struct net_device *dev = arg;
+ struct ctcm_priv *priv = dev->ml_priv;
+ struct mpc_group *grp = NULL;
+ int direction;
+ int send = 0;
+
+ if (priv)
+ grp = priv->mpcg;
+ if (grp == NULL)
+ return;
+
+ for (direction = CTCM_READ; direction <= CTCM_WRITE; direction++) {
+ struct channel *ch = priv->channel[direction];
+ struct xid2 *thisxid = ch->xid;
+ ch->xid_skb->data = ch->xid_skb_data;
+ skb_reset_tail_pointer(ch->xid_skb);
+ ch->xid_skb->len = 0;
+ thisxid->xid2_option = XID2_7;
+ send = 0;
+
+ /* xid7 phase 1 */
+ if (grp->outstanding_xid7_p2 > 0) {
+ if (grp->roll == YSIDE) {
+ if (fsm_getstate(ch->fsm) == CH_XID7_PENDING1) {
+ fsm_newstate(ch->fsm, CH_XID7_PENDING2);
+ ch->ccw[8].cmd_code = CCW_CMD_SENSE_CMD;
+ memcpy(skb_put(ch->xid_skb,
+ TH_HEADER_LENGTH),
+ &thdummy, TH_HEADER_LENGTH);
+ send = 1;
+ }
+ } else if (fsm_getstate(ch->fsm) < CH_XID7_PENDING2) {
+ fsm_newstate(ch->fsm, CH_XID7_PENDING2);
+ ch->ccw[8].cmd_code = CCW_CMD_WRITE_CTL;
+ memcpy(skb_put(ch->xid_skb,
+ TH_HEADER_LENGTH),
+ &thnorm, TH_HEADER_LENGTH);
+ send = 1;
+ }
+ } else {
+ /* xid7 phase 2 */
+ if (grp->roll == YSIDE) {
+ if (fsm_getstate(ch->fsm) < CH_XID7_PENDING4) {
+ fsm_newstate(ch->fsm, CH_XID7_PENDING4);
+ memcpy(skb_put(ch->xid_skb,
+ TH_HEADER_LENGTH),
+ &thnorm, TH_HEADER_LENGTH);
+ ch->ccw[8].cmd_code = CCW_CMD_WRITE_CTL;
+ send = 1;
+ }
+ } else if (fsm_getstate(ch->fsm) == CH_XID7_PENDING3) {
+ fsm_newstate(ch->fsm, CH_XID7_PENDING4);
+ ch->ccw[8].cmd_code = CCW_CMD_SENSE_CMD;
+ memcpy(skb_put(ch->xid_skb, TH_HEADER_LENGTH),
+ &thdummy, TH_HEADER_LENGTH);
+ send = 1;
+ }
+ }
+
+ if (send)
+ fsm_event(grp->fsm, MPCG_EVENT_DOIO, ch);
+ }
+
+ return;
+}
+
+/*
+ * MPC Group Station FSM action
+ * CTCM_PROTO_MPC only
+ */
+static void mpc_action_rcvd_xid0(fsm_instance *fsm, int event, void *arg)
+{
+
+ struct mpcg_info *mpcginfo = arg;
+ struct channel *ch = mpcginfo->ch;
+ struct net_device *dev = ch->netdev;
+ struct ctcm_priv *priv = dev->ml_priv;
+ struct mpc_group *grp = priv->mpcg;
+
+ CTCM_PR_DEBUG("%s: ch-id:%s xid2:%i xid7:%i xidt_p2:%i \n",
+ __func__, ch->id, grp->outstanding_xid2,
+ grp->outstanding_xid7, grp->outstanding_xid7_p2);
+
+ if (fsm_getstate(ch->fsm) < CH_XID7_PENDING)
+ fsm_newstate(ch->fsm, CH_XID7_PENDING);
+
+ grp->outstanding_xid2--;
+ grp->outstanding_xid7++;
+ grp->outstanding_xid7_p2++;
+
+ /* must change state before validating xid to */
+ /* properly handle interim interrupts received*/
+ switch (fsm_getstate(grp->fsm)) {
+ case MPCG_STATE_XID2INITW:
+ fsm_newstate(grp->fsm, MPCG_STATE_XID2INITX);
+ mpc_validate_xid(mpcginfo);
+ break;
+ case MPCG_STATE_XID0IOWAIT:
+ fsm_newstate(grp->fsm, MPCG_STATE_XID0IOWAIX);
+ mpc_validate_xid(mpcginfo);
+ break;
+ case MPCG_STATE_XID2INITX:
+ if (grp->outstanding_xid2 == 0) {
+ fsm_newstate(grp->fsm, MPCG_STATE_XID7INITW);
+ mpc_validate_xid(mpcginfo);
+ fsm_event(grp->fsm, MPCG_EVENT_XID2DONE, dev);
+ }
+ break;
+ case MPCG_STATE_XID0IOWAIX:
+ if (grp->outstanding_xid2 == 0) {
+ fsm_newstate(grp->fsm, MPCG_STATE_XID7INITI);
+ mpc_validate_xid(mpcginfo);
+ fsm_event(grp->fsm, MPCG_EVENT_XID2DONE, dev);
+ }
+ break;
+ }
+ kfree(mpcginfo);
+
+ CTCM_PR_DEBUG("ctcmpc:%s() %s xid2:%i xid7:%i xidt_p2:%i \n",
+ __func__, ch->id, grp->outstanding_xid2,
+ grp->outstanding_xid7, grp->outstanding_xid7_p2);
+ CTCM_PR_DEBUG("ctcmpc:%s() %s grpstate: %s chanstate: %s \n",
+ __func__, ch->id,
+ fsm_getstate_str(grp->fsm), fsm_getstate_str(ch->fsm));
+ return;
+
+}
+
+
+/*
+ * MPC Group Station FSM action
+ * CTCM_PROTO_MPC only
+ */
+static void mpc_action_rcvd_xid7(fsm_instance *fsm, int event, void *arg)
+{
+ struct mpcg_info *mpcginfo = arg;
+ struct channel *ch = mpcginfo->ch;
+ struct net_device *dev = ch->netdev;
+ struct ctcm_priv *priv = dev->ml_priv;
+ struct mpc_group *grp = priv->mpcg;
+
+ CTCM_PR_DEBUG("Enter %s: cp=%i ch=0x%p id=%s\n",
+ __func__, smp_processor_id(), ch, ch->id);
+ CTCM_PR_DEBUG("%s: outstanding_xid7: %i, outstanding_xid7_p2: %i\n",
+ __func__, grp->outstanding_xid7, grp->outstanding_xid7_p2);
+
+ grp->outstanding_xid7--;
+ ch->xid_skb->data = ch->xid_skb_data;
+ skb_reset_tail_pointer(ch->xid_skb);
+ ch->xid_skb->len = 0;
+
+ switch (fsm_getstate(grp->fsm)) {
+ case MPCG_STATE_XID7INITI:
+ fsm_newstate(grp->fsm, MPCG_STATE_XID7INITZ);
+ mpc_validate_xid(mpcginfo);
+ break;
+ case MPCG_STATE_XID7INITW:
+ fsm_newstate(grp->fsm, MPCG_STATE_XID7INITX);
+ mpc_validate_xid(mpcginfo);
+ break;
+ case MPCG_STATE_XID7INITZ:
+ case MPCG_STATE_XID7INITX:
+ if (grp->outstanding_xid7 == 0) {
+ if (grp->outstanding_xid7_p2 > 0) {
+ grp->outstanding_xid7 =
+ grp->outstanding_xid7_p2;
+ grp->outstanding_xid7_p2 = 0;
+ } else
+ fsm_newstate(grp->fsm, MPCG_STATE_XID7INITF);
+
+ mpc_validate_xid(mpcginfo);
+ fsm_event(grp->fsm, MPCG_EVENT_XID7DONE, dev);
+ break;
+ }
+ mpc_validate_xid(mpcginfo);
+ break;
+ }
+ kfree(mpcginfo);
+ return;
+}
+
+/*
+ * mpc_action helper of an MPC Group Station FSM action
+ * CTCM_PROTO_MPC only
+ */
+static int mpc_send_qllc_discontact(struct net_device *dev)
+{
+ __u32 new_len = 0;
+ struct sk_buff *skb;
+ struct qllc *qllcptr;
+ struct ctcm_priv *priv = dev->ml_priv;
+ struct mpc_group *grp = priv->mpcg;
+
+ CTCM_PR_DEBUG("%s: GROUP STATE: %s\n",
+ __func__, mpcg_state_names[grp->saved_state]);
+
+ switch (grp->saved_state) {
+ /*
+ * establish conn callback function is
+ * preferred method to report failure
+ */
+ case MPCG_STATE_XID0IOWAIT:
+ case MPCG_STATE_XID0IOWAIX:
+ case MPCG_STATE_XID7INITI:
+ case MPCG_STATE_XID7INITZ:
+ case MPCG_STATE_XID2INITW:
+ case MPCG_STATE_XID2INITX:
+ case MPCG_STATE_XID7INITW:
+ case MPCG_STATE_XID7INITX:
+ if (grp->estconnfunc) {
+ grp->estconnfunc(grp->port_num, -1, 0);
+ grp->estconnfunc = NULL;
+ break;
+ }
+ case MPCG_STATE_FLOWC:
+ case MPCG_STATE_READY:
+ grp->send_qllc_disc = 2;
+ new_len = sizeof(struct qllc);
+ qllcptr = kzalloc(new_len, gfp_type() | GFP_DMA);
+ if (qllcptr == NULL) {
+ CTCM_DBF_TEXT_(MPC_ERROR, CTC_DBF_ERROR,
+ "%s(%s): qllcptr allocation error",
+ CTCM_FUNTAIL, dev->name);
+ return -ENOMEM;
+ }
+
+ qllcptr->qllc_address = 0xcc;
+ qllcptr->qllc_commands = 0x03;
+
+ skb = __dev_alloc_skb(new_len, GFP_ATOMIC);
+
+ if (skb == NULL) {
+ CTCM_DBF_TEXT_(MPC_ERROR, CTC_DBF_ERROR,
+ "%s(%s): skb allocation error",
+ CTCM_FUNTAIL, dev->name);
+ priv->stats.rx_dropped++;
+ kfree(qllcptr);
+ return -ENOMEM;
+ }
+
+ memcpy(skb_put(skb, new_len), qllcptr, new_len);
+ kfree(qllcptr);
+
+ if (skb_headroom(skb) < 4) {
+ CTCM_DBF_TEXT_(MPC_ERROR, CTC_DBF_ERROR,
+ "%s(%s): skb_headroom error",
+ CTCM_FUNTAIL, dev->name);
+ dev_kfree_skb_any(skb);
+ return -ENOMEM;
+ }
+
+ *((__u32 *)skb_push(skb, 4)) =
+ priv->channel[CTCM_READ]->pdu_seq;
+ priv->channel[CTCM_READ]->pdu_seq++;
+ CTCM_PR_DBGDATA("ctcmpc: %s ToDCM_pdu_seq= %08x\n",
+ __func__, priv->channel[CTCM_READ]->pdu_seq);
+
+ /* receipt of CC03 resets anticipated sequence number on
+ receiving side */
+ priv->channel[CTCM_READ]->pdu_seq = 0x00;
+ skb_reset_mac_header(skb);
+ skb->dev = dev;
+ skb->protocol = htons(ETH_P_SNAP);
+ skb->ip_summed = CHECKSUM_UNNECESSARY;
+
+ CTCM_D3_DUMP(skb->data, (sizeof(struct qllc) + 4));
+
+ netif_rx(skb);
+ break;
+ default:
+ break;
+
+ }
+
+ return 0;
+}
+/* --- This is the END my friend --- */
+
diff --git a/drivers/s390/net/ctcm_mpc.h b/drivers/s390/net/ctcm_mpc.h
new file mode 100644
index 00000000000..bd1b1cc54ff
--- /dev/null
+++ b/drivers/s390/net/ctcm_mpc.h
@@ -0,0 +1,238 @@
+/*
+ * Copyright IBM Corp. 2007
+ * Authors: Peter Tiedemann (ptiedem@de.ibm.com)
+ *
+ * MPC additions:
+ * Belinda Thompson (belindat@us.ibm.com)
+ * Andy Richter (richtera@us.ibm.com)
+ */
+
+#ifndef _CTC_MPC_H_
+#define _CTC_MPC_H_
+
+#include <linux/interrupt.h>
+#include <linux/skbuff.h>
+#include "fsm.h"
+
+/*
+ * MPC external interface
+ * Note that ctc_mpc_xyz are called with a lock on ................
+ */
+
+/* port_number is the mpc device 0, 1, 2 etc mpc2 is port_number 2 */
+
+/* passive open Just wait for XID2 exchange */
+extern int ctc_mpc_alloc_channel(int port,
+ void (*callback)(int port_num, int max_write_size));
+/* active open Alloc then send XID2 */
+extern void ctc_mpc_establish_connectivity(int port,
+ void (*callback)(int port_num, int rc, int max_write_size));
+
+extern void ctc_mpc_dealloc_ch(int port);
+extern void ctc_mpc_flow_control(int port, int flowc);
+
+/*
+ * other MPC Group prototypes and structures
+ */
+
+#define ETH_P_SNA_DIX 0x80D5
+
+/*
+ * Declaration of an XID2
+ *
+ */
+#define ALLZEROS 0x0000000000000000
+
+#define XID_FM2 0x20
+#define XID2_0 0x00
+#define XID2_7 0x07
+#define XID2_WRITE_SIDE 0x04
+#define XID2_READ_SIDE 0x05
+
+struct xid2 {
+ __u8 xid2_type_id;
+ __u8 xid2_len;
+ __u32 xid2_adj_id;
+ __u8 xid2_rlen;
+ __u8 xid2_resv1;
+ __u8 xid2_flag1;
+ __u8 xid2_fmtt;
+ __u8 xid2_flag4;
+ __u16 xid2_resv2;
+ __u8 xid2_tgnum;
+ __u32 xid2_sender_id;
+ __u8 xid2_flag2;
+ __u8 xid2_option;
+ char xid2_resv3[8];
+ __u16 xid2_resv4;
+ __u8 xid2_dlc_type;
+ __u16 xid2_resv5;
+ __u8 xid2_mpc_flag;
+ __u8 xid2_resv6;
+ __u16 xid2_buf_len;
+ char xid2_buffer[255 - (13 * sizeof(__u8) +
+ 2 * sizeof(__u32) +
+ 4 * sizeof(__u16) +
+ 8 * sizeof(char))];
+} __attribute__ ((packed));
+
+#define XID2_LENGTH (sizeof(struct xid2))
+
+struct th_header {
+ __u8 th_seg;
+ __u8 th_ch_flag;
+#define TH_HAS_PDU 0xf0
+#define TH_IS_XID 0x01
+#define TH_SWEEP_REQ 0xfe
+#define TH_SWEEP_RESP 0xff
+ __u8 th_blk_flag;
+#define TH_DATA_IS_XID 0x80
+#define TH_RETRY 0x40
+#define TH_DISCONTACT 0xc0
+#define TH_SEG_BLK 0x20
+#define TH_LAST_SEG 0x10
+#define TH_PDU_PART 0x08
+ __u8 th_is_xid; /* is 0x01 if this is XID */
+ __u32 th_seq_num;
+} __attribute__ ((packed));
+
+struct th_addon {
+ __u32 th_last_seq;
+ __u32 th_resvd;
+} __attribute__ ((packed));
+
+struct th_sweep {
+ struct th_header th;
+ struct th_addon sw;
+} __attribute__ ((packed));
+
+#define TH_HEADER_LENGTH (sizeof(struct th_header))
+#define TH_SWEEP_LENGTH (sizeof(struct th_sweep))
+
+#define PDU_LAST 0x80
+#define PDU_CNTL 0x40
+#define PDU_FIRST 0x20
+
+struct pdu {
+ __u32 pdu_offset;
+ __u8 pdu_flag;
+ __u8 pdu_proto; /* 0x01 is APPN SNA */
+ __u16 pdu_seq;
+} __attribute__ ((packed));
+
+#define PDU_HEADER_LENGTH (sizeof(struct pdu))
+
+struct qllc {
+ __u8 qllc_address;
+#define QLLC_REQ 0xFF
+#define QLLC_RESP 0x00
+ __u8 qllc_commands;
+#define QLLC_DISCONNECT 0x53
+#define QLLC_UNSEQACK 0x73
+#define QLLC_SETMODE 0x93
+#define QLLC_EXCHID 0xBF
+} __attribute__ ((packed));
+
+
+/*
+ * Definition of one MPC group
+ */
+
+#define MAX_MPCGCHAN 10
+#define MPC_XID_TIMEOUT_VALUE 10000
+#define MPC_CHANNEL_ADD 0
+#define MPC_CHANNEL_REMOVE 1
+#define MPC_CHANNEL_ATTN 2
+#define XSIDE 1
+#define YSIDE 0
+
+struct mpcg_info {
+ struct sk_buff *skb;
+ struct channel *ch;
+ struct xid2 *xid;
+ struct th_sweep *sweep;
+ struct th_header *th;
+};
+
+struct mpc_group {
+ struct tasklet_struct mpc_tasklet;
+ struct tasklet_struct mpc_tasklet2;
+ int changed_side;
+ int saved_state;
+ int channels_terminating;
+ int out_of_sequence;
+ int flow_off_called;
+ int port_num;
+ int port_persist;
+ int alloc_called;
+ __u32 xid2_adj_id;
+ __u8 xid2_tgnum;
+ __u32 xid2_sender_id;
+ int num_channel_paths;
+ int active_channels[2];
+ __u16 group_max_buflen;
+ int outstanding_xid2;
+ int outstanding_xid7;
+ int outstanding_xid7_p2;
+ int sweep_req_pend_num;
+ int sweep_rsp_pend_num;
+ struct sk_buff *xid_skb;
+ char *xid_skb_data;
+ struct th_header *xid_th;
+ struct xid2 *xid;
+ char *xid_id;
+ struct th_header *rcvd_xid_th;
+ struct sk_buff *rcvd_xid_skb;
+ char *rcvd_xid_data;
+ __u8 in_sweep;
+ __u8 roll;
+ struct xid2 *saved_xid2;
+ void (*allochanfunc)(int, int);
+ int allocchan_callback_retries;
+ void (*estconnfunc)(int, int, int);
+ int estconn_callback_retries;
+ int estconn_called;
+ int xidnogood;
+ int send_qllc_disc;
+ fsm_timer timer;
+ fsm_instance *fsm; /* group xid fsm */
+};
+
+#ifdef DEBUGDATA
+void ctcmpc_dumpit(char *buf, int len);
+#else
+static inline void ctcmpc_dumpit(char *buf, int len)
+{
+}
+#endif
+
+#ifdef DEBUGDATA
+/*
+ * Dump header and first 16 bytes of an sk_buff for debugging purposes.
+ *
+ * skb The struct sk_buff to dump.
+ * offset Offset relative to skb-data, where to start the dump.
+ */
+void ctcmpc_dump_skb(struct sk_buff *skb, int offset);
+#else
+static inline void ctcmpc_dump_skb(struct sk_buff *skb, int offset)
+{}
+#endif
+
+static inline void ctcmpc_dump32(char *buf, int len)
+{
+ if (len < 32)
+ ctcmpc_dumpit(buf, len);
+ else
+ ctcmpc_dumpit(buf, 32);
+}
+
+int ctcmpc_open(struct net_device *);
+void ctcm_ccw_check_rc(struct channel *, int, char *);
+void mpc_group_ready(unsigned long adev);
+void mpc_channel_action(struct channel *ch, int direction, int action);
+void mpc_action_send_discontact(unsigned long thischan);
+void mpc_action_discontact(fsm_instance *fi, int event, void *arg);
+void ctcmpc_bh(unsigned long thischan);
+#endif
+/* --- This is the END my friend --- */
diff --git a/drivers/s390/net/ctcm_sysfs.c b/drivers/s390/net/ctcm_sysfs.c
new file mode 100644
index 00000000000..6bcfbbb20f0
--- /dev/null
+++ b/drivers/s390/net/ctcm_sysfs.c
@@ -0,0 +1,208 @@
+/*
+ * Copyright IBM Corp. 2007, 2007
+ * Authors: Peter Tiedemann (ptiedem@de.ibm.com)
+ *
+ */
+
+#undef DEBUG
+#undef DEBUGDATA
+#undef DEBUGCCW
+
+#define KMSG_COMPONENT "ctcm"
+#define pr_fmt(fmt) KMSG_COMPONENT ": " fmt
+
+#include <linux/device.h>
+#include <linux/sysfs.h>
+#include <linux/slab.h>
+#include "ctcm_main.h"
+
+/*
+ * sysfs attributes
+ */
+
+static ssize_t ctcm_buffer_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct ctcm_priv *priv = dev_get_drvdata(dev);
+
+ if (!priv)
+ return -ENODEV;
+ return sprintf(buf, "%d\n", priv->buffer_size);
+}
+
+static ssize_t ctcm_buffer_write(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t count)
+{
+ struct net_device *ndev;
+ unsigned int bs1;
+ struct ctcm_priv *priv = dev_get_drvdata(dev);
+ int rc;
+
+ ndev = priv->channel[CTCM_READ]->netdev;
+ if (!(priv && priv->channel[CTCM_READ] && ndev)) {
+ CTCM_DBF_TEXT(SETUP, CTC_DBF_ERROR, "bfnondev");
+ return -ENODEV;
+ }
+
+ rc = sscanf(buf, "%u", &bs1);
+ if (rc != 1)
+ goto einval;
+ if (bs1 > CTCM_BUFSIZE_LIMIT)
+ goto einval;
+ if (bs1 < (576 + LL_HEADER_LENGTH + 2))
+ goto einval;
+ priv->buffer_size = bs1; /* just to overwrite the default */
+
+ if ((ndev->flags & IFF_RUNNING) &&
+ (bs1 < (ndev->mtu + LL_HEADER_LENGTH + 2)))
+ goto einval;
+
+ priv->channel[CTCM_READ]->max_bufsize = bs1;
+ priv->channel[CTCM_WRITE]->max_bufsize = bs1;
+ if (!(ndev->flags & IFF_RUNNING))
+ ndev->mtu = bs1 - LL_HEADER_LENGTH - 2;
+ priv->channel[CTCM_READ]->flags |= CHANNEL_FLAGS_BUFSIZE_CHANGED;
+ priv->channel[CTCM_WRITE]->flags |= CHANNEL_FLAGS_BUFSIZE_CHANGED;
+
+ CTCM_DBF_DEV(SETUP, ndev, buf);
+ return count;
+
+einval:
+ CTCM_DBF_DEV(SETUP, ndev, "buff_err");
+ return -EINVAL;
+}
+
+static void ctcm_print_statistics(struct ctcm_priv *priv)
+{
+ char *sbuf;
+ char *p;
+
+ if (!priv)
+ return;
+ sbuf = kmalloc(2048, GFP_KERNEL);
+ if (sbuf == NULL)
+ return;
+ p = sbuf;
+
+ p += sprintf(p, " Device FSM state: %s\n",
+ fsm_getstate_str(priv->fsm));
+ p += sprintf(p, " RX channel FSM state: %s\n",
+ fsm_getstate_str(priv->channel[CTCM_READ]->fsm));
+ p += sprintf(p, " TX channel FSM state: %s\n",
+ fsm_getstate_str(priv->channel[CTCM_WRITE]->fsm));
+ p += sprintf(p, " Max. TX buffer used: %ld\n",
+ priv->channel[WRITE]->prof.maxmulti);
+ p += sprintf(p, " Max. chained SKBs: %ld\n",
+ priv->channel[WRITE]->prof.maxcqueue);
+ p += sprintf(p, " TX single write ops: %ld\n",
+ priv->channel[WRITE]->prof.doios_single);
+ p += sprintf(p, " TX multi write ops: %ld\n",
+ priv->channel[WRITE]->prof.doios_multi);
+ p += sprintf(p, " Netto bytes written: %ld\n",
+ priv->channel[WRITE]->prof.txlen);
+ p += sprintf(p, " Max. TX IO-time: %ld\n",
+ priv->channel[WRITE]->prof.tx_time);
+
+ printk(KERN_INFO "Statistics for %s:\n%s",
+ priv->channel[CTCM_WRITE]->netdev->name, sbuf);
+ kfree(sbuf);
+ return;
+}
+
+static ssize_t stats_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct ccwgroup_device *gdev = to_ccwgroupdev(dev);
+ struct ctcm_priv *priv = dev_get_drvdata(dev);
+
+ if (!priv || gdev->state != CCWGROUP_ONLINE)
+ return -ENODEV;
+ ctcm_print_statistics(priv);
+ return sprintf(buf, "0\n");
+}
+
+static ssize_t stats_write(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct ctcm_priv *priv = dev_get_drvdata(dev);
+ if (!priv)
+ return -ENODEV;
+ /* Reset statistics */
+ memset(&priv->channel[WRITE]->prof, 0,
+ sizeof(priv->channel[CTCM_WRITE]->prof));
+ return count;
+}
+
+static ssize_t ctcm_proto_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct ctcm_priv *priv = dev_get_drvdata(dev);
+ if (!priv)
+ return -ENODEV;
+
+ return sprintf(buf, "%d\n", priv->protocol);
+}
+
+static ssize_t ctcm_proto_store(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t count)
+{
+ int value, rc;
+ struct ctcm_priv *priv = dev_get_drvdata(dev);
+
+ if (!priv)
+ return -ENODEV;
+ rc = sscanf(buf, "%d", &value);
+ if ((rc != 1) ||
+ !((value == CTCM_PROTO_S390) ||
+ (value == CTCM_PROTO_LINUX) ||
+ (value == CTCM_PROTO_MPC) ||
+ (value == CTCM_PROTO_OS390)))
+ return -EINVAL;
+ priv->protocol = value;
+ CTCM_DBF_DEV(SETUP, dev, buf);
+
+ return count;
+}
+
+static const char *ctcm_type[] = {
+ "not a channel",
+ "CTC/A",
+ "FICON channel",
+ "ESCON channel",
+ "unknown channel type",
+ "unsupported channel type",
+};
+
+static ssize_t ctcm_type_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct ccwgroup_device *cgdev;
+
+ cgdev = to_ccwgroupdev(dev);
+ if (!cgdev)
+ return -ENODEV;
+
+ return sprintf(buf, "%s\n",
+ ctcm_type[cgdev->cdev[0]->id.driver_info]);
+}
+
+static DEVICE_ATTR(buffer, 0644, ctcm_buffer_show, ctcm_buffer_write);
+static DEVICE_ATTR(protocol, 0644, ctcm_proto_show, ctcm_proto_store);
+static DEVICE_ATTR(type, 0444, ctcm_type_show, NULL);
+static DEVICE_ATTR(stats, 0644, stats_show, stats_write);
+
+static struct attribute *ctcm_attr[] = {
+ &dev_attr_protocol.attr,
+ &dev_attr_type.attr,
+ &dev_attr_buffer.attr,
+ &dev_attr_stats.attr,
+ NULL,
+};
+
+static struct attribute_group ctcm_attr_group = {
+ .attrs = ctcm_attr,
+};
+const struct attribute_group *ctcm_attr_groups[] = {
+ &ctcm_attr_group,
+ NULL,
+};
diff --git a/drivers/s390/net/ctcmain.c b/drivers/s390/net/ctcmain.c
deleted file mode 100644
index 77a503139e3..00000000000
--- a/drivers/s390/net/ctcmain.c
+++ /dev/null
@@ -1,3062 +0,0 @@
-/*
- * CTC / ESCON network driver
- *
- * Copyright (C) 2001 IBM Deutschland Entwicklung GmbH, IBM Corporation
- * Author(s): Fritz Elfert (elfert@de.ibm.com, felfert@millenux.com)
- * Fixes by : Jochen Röhrig (roehrig@de.ibm.com)
- * Arnaldo Carvalho de Melo <acme@conectiva.com.br>
- Peter Tiedemann (ptiedem@de.ibm.com)
- * Driver Model stuff by : Cornelia Huck <cornelia.huck@de.ibm.com>
- *
- * Documentation used:
- * - Principles of Operation (IBM doc#: SA22-7201-06)
- * - Common IO/-Device Commands and Self Description (IBM doc#: SA22-7204-02)
- * - Common IO/-Device Commands and Self Description (IBM doc#: SN22-5535)
- * - ESCON Channel-to-Channel Adapter (IBM doc#: SA22-7203-00)
- * - ESCON I/O Interface (IBM doc#: SA22-7202-029
- *
- * and the source of the original CTC driver by:
- * Dieter Wellerdiek (wel@de.ibm.com)
- * Martin Schwidefsky (schwidefsky@de.ibm.com)
- * Denis Joseph Barrow (djbarrow@de.ibm.com,barrow_dj@yahoo.com)
- * Jochen Röhrig (roehrig@de.ibm.com)
- *
- * 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.
- *
- */
-#undef DEBUG
-#include <linux/module.h>
-#include <linux/init.h>
-#include <linux/kernel.h>
-#include <linux/slab.h>
-#include <linux/errno.h>
-#include <linux/types.h>
-#include <linux/interrupt.h>
-#include <linux/timer.h>
-#include <linux/bitops.h>
-
-#include <linux/signal.h>
-#include <linux/string.h>
-
-#include <linux/ip.h>
-#include <linux/if_arp.h>
-#include <linux/tcp.h>
-#include <linux/skbuff.h>
-#include <linux/ctype.h>
-#include <net/dst.h>
-
-#include <asm/io.h>
-#include <asm/ccwdev.h>
-#include <asm/ccwgroup.h>
-#include <asm/uaccess.h>
-
-#include <asm/idals.h>
-
-#include "fsm.h"
-#include "cu3088.h"
-
-#include "ctcdbug.h"
-#include "ctcmain.h"
-
-MODULE_AUTHOR("(C) 2000 IBM Corp. by Fritz Elfert (felfert@millenux.com)");
-MODULE_DESCRIPTION("Linux for S/390 CTC/Escon Driver");
-MODULE_LICENSE("GPL");
-/**
- * States of the interface statemachine.
- */
-enum dev_states {
- DEV_STATE_STOPPED,
- DEV_STATE_STARTWAIT_RXTX,
- DEV_STATE_STARTWAIT_RX,
- DEV_STATE_STARTWAIT_TX,
- DEV_STATE_STOPWAIT_RXTX,
- DEV_STATE_STOPWAIT_RX,
- DEV_STATE_STOPWAIT_TX,
- DEV_STATE_RUNNING,
- /**
- * MUST be always the last element!!
- */
- CTC_NR_DEV_STATES
-};
-
-static const char *dev_state_names[] = {
- "Stopped",
- "StartWait RXTX",
- "StartWait RX",
- "StartWait TX",
- "StopWait RXTX",
- "StopWait RX",
- "StopWait TX",
- "Running",
-};
-
-/**
- * Events of the interface statemachine.
- */
-enum dev_events {
- DEV_EVENT_START,
- DEV_EVENT_STOP,
- DEV_EVENT_RXUP,
- DEV_EVENT_TXUP,
- DEV_EVENT_RXDOWN,
- DEV_EVENT_TXDOWN,
- DEV_EVENT_RESTART,
- /**
- * MUST be always the last element!!
- */
- CTC_NR_DEV_EVENTS
-};
-
-static const char *dev_event_names[] = {
- "Start",
- "Stop",
- "RX up",
- "TX up",
- "RX down",
- "TX down",
- "Restart",
-};
-
-/**
- * Events of the channel statemachine
- */
-enum ch_events {
- /**
- * Events, representing return code of
- * I/O operations (ccw_device_start, ccw_device_halt et al.)
- */
- CH_EVENT_IO_SUCCESS,
- CH_EVENT_IO_EBUSY,
- CH_EVENT_IO_ENODEV,
- CH_EVENT_IO_EIO,
- CH_EVENT_IO_UNKNOWN,
-
- CH_EVENT_ATTNBUSY,
- CH_EVENT_ATTN,
- CH_EVENT_BUSY,
-
- /**
- * Events, representing unit-check
- */
- CH_EVENT_UC_RCRESET,
- CH_EVENT_UC_RSRESET,
- CH_EVENT_UC_TXTIMEOUT,
- CH_EVENT_UC_TXPARITY,
- CH_EVENT_UC_HWFAIL,
- CH_EVENT_UC_RXPARITY,
- CH_EVENT_UC_ZERO,
- CH_EVENT_UC_UNKNOWN,
-
- /**
- * Events, representing subchannel-check
- */
- CH_EVENT_SC_UNKNOWN,
-
- /**
- * Events, representing machine checks
- */
- CH_EVENT_MC_FAIL,
- CH_EVENT_MC_GOOD,
-
- /**
- * Event, representing normal IRQ
- */
- CH_EVENT_IRQ,
- CH_EVENT_FINSTAT,
-
- /**
- * Event, representing timer expiry.
- */
- CH_EVENT_TIMER,
-
- /**
- * Events, representing commands from upper levels.
- */
- CH_EVENT_START,
- CH_EVENT_STOP,
-
- /**
- * MUST be always the last element!!
- */
- NR_CH_EVENTS,
-};
-
-/**
- * States of the channel statemachine.
- */
-enum ch_states {
- /**
- * Channel not assigned to any device,
- * initial state, direction invalid
- */
- CH_STATE_IDLE,
-
- /**
- * Channel assigned but not operating
- */
- CH_STATE_STOPPED,
- CH_STATE_STARTWAIT,
- CH_STATE_STARTRETRY,
- CH_STATE_SETUPWAIT,
- CH_STATE_RXINIT,
- CH_STATE_TXINIT,
- CH_STATE_RX,
- CH_STATE_TX,
- CH_STATE_RXIDLE,
- CH_STATE_TXIDLE,
- CH_STATE_RXERR,
- CH_STATE_TXERR,
- CH_STATE_TERM,
- CH_STATE_DTERM,
- CH_STATE_NOTOP,
-
- /**
- * MUST be always the last element!!
- */
- NR_CH_STATES,
-};
-
-static int loglevel = CTC_LOGLEVEL_DEFAULT;
-
-/**
- * Linked list of all detected channels.
- */
-static struct channel *channels = NULL;
-
-/**
- * Print Banner.
- */
-static void
-print_banner(void)
-{
- static int printed = 0;
-
- if (printed)
- return;
-
- printk(KERN_INFO "CTC driver initialized\n");
- printed = 1;
-}
-
-/**
- * Return type of a detected device.
- */
-static enum channel_types
-get_channel_type(struct ccw_device_id *id)
-{
- enum channel_types type = (enum channel_types) id->driver_info;
-
- if (type == channel_type_ficon)
- type = channel_type_escon;
-
- return type;
-}
-
-static const char *ch_event_names[] = {
- "ccw_device success",
- "ccw_device busy",
- "ccw_device enodev",
- "ccw_device ioerr",
- "ccw_device unknown",
-
- "Status ATTN & BUSY",
- "Status ATTN",
- "Status BUSY",
-
- "Unit check remote reset",
- "Unit check remote system reset",
- "Unit check TX timeout",
- "Unit check TX parity",
- "Unit check Hardware failure",
- "Unit check RX parity",
- "Unit check ZERO",
- "Unit check Unknown",
-
- "SubChannel check Unknown",
-
- "Machine check failure",
- "Machine check operational",
-
- "IRQ normal",
- "IRQ final",
-
- "Timer",
-
- "Start",
- "Stop",
-};
-
-static const char *ch_state_names[] = {
- "Idle",
- "Stopped",
- "StartWait",
- "StartRetry",
- "SetupWait",
- "RX init",
- "TX init",
- "RX",
- "TX",
- "RX idle",
- "TX idle",
- "RX error",
- "TX error",
- "Terminating",
- "Restarting",
- "Not operational",
-};
-
-#ifdef DEBUG
-/**
- * Dump header and first 16 bytes of an sk_buff for debugging purposes.
- *
- * @param skb The sk_buff to dump.
- * @param offset Offset relative to skb-data, where to start the dump.
- */
-static void
-ctc_dump_skb(struct sk_buff *skb, int offset)
-{
- unsigned char *p = skb->data;
- __u16 bl;
- struct ll_header *header;
- int i;
-
- if (!(loglevel & CTC_LOGLEVEL_DEBUG))
- return;
- p += offset;
- bl = *((__u16 *) p);
- p += 2;
- header = (struct ll_header *) p;
- p -= 2;
-
- printk(KERN_DEBUG "dump:\n");
- printk(KERN_DEBUG "blocklen=%d %04x\n", bl, bl);
-
- printk(KERN_DEBUG "h->length=%d %04x\n", header->length,
- header->length);
- printk(KERN_DEBUG "h->type=%04x\n", header->type);
- printk(KERN_DEBUG "h->unused=%04x\n", header->unused);
- if (bl > 16)
- bl = 16;
- printk(KERN_DEBUG "data: ");
- for (i = 0; i < bl; i++)
- printk("%02x%s", *p++, (i % 16) ? " " : "\n<7>");
- printk("\n");
-}
-#else
-static inline void
-ctc_dump_skb(struct sk_buff *skb, int offset)
-{
-}
-#endif
-
-/**
- * Unpack a just received skb and hand it over to
- * upper layers.
- *
- * @param ch The channel where this skb has been received.
- * @param pskb The received skb.
- */
-static void
-ctc_unpack_skb(struct channel *ch, struct sk_buff *pskb)
-{
- struct net_device *dev = ch->netdev;
- struct ctc_priv *privptr = (struct ctc_priv *) dev->priv;
- __u16 len = *((__u16 *) pskb->data);
-
- DBF_TEXT(trace, 4, __FUNCTION__);
- skb_put(pskb, 2 + LL_HEADER_LENGTH);
- skb_pull(pskb, 2);
- pskb->dev = dev;
- pskb->ip_summed = CHECKSUM_UNNECESSARY;
- while (len > 0) {
- struct sk_buff *skb;
- struct ll_header *header = (struct ll_header *) pskb->data;
-
- skb_pull(pskb, LL_HEADER_LENGTH);
- if ((ch->protocol == CTC_PROTO_S390) &&
- (header->type != ETH_P_IP)) {
-
-#ifndef DEBUG
- if (!(ch->logflags & LOG_FLAG_ILLEGALPKT)) {
-#endif
- /**
- * Check packet type only if we stick strictly
- * to S/390's protocol of OS390. This only
- * supports IP. Otherwise allow any packet
- * type.
- */
- ctc_pr_warn(
- "%s Illegal packet type 0x%04x received, dropping\n",
- dev->name, header->type);
- ch->logflags |= LOG_FLAG_ILLEGALPKT;
-#ifndef DEBUG
- }
-#endif
-#ifdef DEBUG
- ctc_dump_skb(pskb, -6);
-#endif
- privptr->stats.rx_dropped++;
- privptr->stats.rx_frame_errors++;
- return;
- }
- pskb->protocol = ntohs(header->type);
- if (header->length <= LL_HEADER_LENGTH) {
-#ifndef DEBUG
- if (!(ch->logflags & LOG_FLAG_ILLEGALSIZE)) {
-#endif
- ctc_pr_warn(
- "%s Illegal packet size %d "
- "received (MTU=%d blocklen=%d), "
- "dropping\n", dev->name, header->length,
- dev->mtu, len);
- ch->logflags |= LOG_FLAG_ILLEGALSIZE;
-#ifndef DEBUG
- }
-#endif
-#ifdef DEBUG
- ctc_dump_skb(pskb, -6);
-#endif
- privptr->stats.rx_dropped++;
- privptr->stats.rx_length_errors++;
- return;
- }
- header->length -= LL_HEADER_LENGTH;
- len -= LL_HEADER_LENGTH;
- if ((header->length > skb_tailroom(pskb)) ||
- (header->length > len)) {
-#ifndef DEBUG
- if (!(ch->logflags & LOG_FLAG_OVERRUN)) {
-#endif
- ctc_pr_warn(
- "%s Illegal packet size %d "
- "(beyond the end of received data), "
- "dropping\n", dev->name, header->length);
- ch->logflags |= LOG_FLAG_OVERRUN;
-#ifndef DEBUG
- }
-#endif
-#ifdef DEBUG
- ctc_dump_skb(pskb, -6);
-#endif
- privptr->stats.rx_dropped++;
- privptr->stats.rx_length_errors++;
- return;
- }
- skb_put(pskb, header->length);
- skb_reset_mac_header(pskb);
- len -= header->length;
- skb = dev_alloc_skb(pskb->len);
- if (!skb) {
-#ifndef DEBUG
- if (!(ch->logflags & LOG_FLAG_NOMEM)) {
-#endif
- ctc_pr_warn(
- "%s Out of memory in ctc_unpack_skb\n",
- dev->name);
- ch->logflags |= LOG_FLAG_NOMEM;
-#ifndef DEBUG
- }
-#endif
- privptr->stats.rx_dropped++;
- return;
- }
- skb_copy_from_linear_data(pskb, skb_put(skb, pskb->len),
- pskb->len);
- skb_reset_mac_header(skb);
- skb->dev = pskb->dev;
- skb->protocol = pskb->protocol;
- pskb->ip_summed = CHECKSUM_UNNECESSARY;
- /**
- * reset logflags
- */
- ch->logflags = 0;
- privptr->stats.rx_packets++;
- privptr->stats.rx_bytes += skb->len;
- netif_rx_ni(skb);
- dev->last_rx = jiffies;
- if (len > 0) {
- skb_pull(pskb, header->length);
- if (skb_tailroom(pskb) < LL_HEADER_LENGTH) {
-#ifndef DEBUG
- if (!(ch->logflags & LOG_FLAG_OVERRUN)) {
-#endif
- ctc_pr_warn(
- "%s Overrun in ctc_unpack_skb\n",
- dev->name);
- ch->logflags |= LOG_FLAG_OVERRUN;
-#ifndef DEBUG
- }
-#endif
- return;
- }
- skb_put(pskb, LL_HEADER_LENGTH);
- }
- }
-}
-
-/**
- * Check return code of a preceeding ccw_device call, halt_IO etc...
- *
- * @param ch The channel, the error belongs to.
- * @param return_code The error code to inspect.
- */
-static void
-ccw_check_return_code(struct channel *ch, int return_code, char *msg)
-{
- DBF_TEXT(trace, 5, __FUNCTION__);
- switch (return_code) {
- case 0:
- fsm_event(ch->fsm, CH_EVENT_IO_SUCCESS, ch);
- break;
- case -EBUSY:
- ctc_pr_warn("%s (%s): Busy !\n", ch->id, msg);
- fsm_event(ch->fsm, CH_EVENT_IO_EBUSY, ch);
- break;
- case -ENODEV:
- ctc_pr_emerg("%s (%s): Invalid device called for IO\n",
- ch->id, msg);
- fsm_event(ch->fsm, CH_EVENT_IO_ENODEV, ch);
- break;
- case -EIO:
- ctc_pr_emerg("%s (%s): Status pending... \n",
- ch->id, msg);
- fsm_event(ch->fsm, CH_EVENT_IO_EIO, ch);
- break;
- default:
- ctc_pr_emerg("%s (%s): Unknown error in do_IO %04x\n",
- ch->id, msg, return_code);
- fsm_event(ch->fsm, CH_EVENT_IO_UNKNOWN, ch);
- }
-}
-
-/**
- * Check sense of a unit check.
- *
- * @param ch The channel, the sense code belongs to.
- * @param sense The sense code to inspect.
- */
-static void
-ccw_unit_check(struct channel *ch, unsigned char sense)
-{
- DBF_TEXT(trace, 5, __FUNCTION__);
- if (sense & SNS0_INTERVENTION_REQ) {
- if (sense & 0x01) {
- ctc_pr_debug("%s: Interface disc. or Sel. reset "
- "(remote)\n", ch->id);
- fsm_event(ch->fsm, CH_EVENT_UC_RCRESET, ch);
- } else {
- ctc_pr_debug("%s: System reset (remote)\n", ch->id);
- fsm_event(ch->fsm, CH_EVENT_UC_RSRESET, ch);
- }
- } else if (sense & SNS0_EQUIPMENT_CHECK) {
- if (sense & SNS0_BUS_OUT_CHECK) {
- ctc_pr_warn("%s: Hardware malfunction (remote)\n",
- ch->id);
- fsm_event(ch->fsm, CH_EVENT_UC_HWFAIL, ch);
- } else {
- ctc_pr_warn("%s: Read-data parity error (remote)\n",
- ch->id);
- fsm_event(ch->fsm, CH_EVENT_UC_RXPARITY, ch);
- }
- } else if (sense & SNS0_BUS_OUT_CHECK) {
- if (sense & 0x04) {
- ctc_pr_warn("%s: Data-streaming timeout)\n", ch->id);
- fsm_event(ch->fsm, CH_EVENT_UC_TXTIMEOUT, ch);
- } else {
- ctc_pr_warn("%s: Data-transfer parity error\n", ch->id);
- fsm_event(ch->fsm, CH_EVENT_UC_TXPARITY, ch);
- }
- } else if (sense & SNS0_CMD_REJECT) {
- ctc_pr_warn("%s: Command reject\n", ch->id);
- } else if (sense == 0) {
- ctc_pr_debug("%s: Unit check ZERO\n", ch->id);
- fsm_event(ch->fsm, CH_EVENT_UC_ZERO, ch);
- } else {
- ctc_pr_warn("%s: Unit Check with sense code: %02x\n",
- ch->id, sense);
- fsm_event(ch->fsm, CH_EVENT_UC_UNKNOWN, ch);
- }
-}
-
-static void
-ctc_purge_skb_queue(struct sk_buff_head *q)
-{
- struct sk_buff *skb;
-
- DBF_TEXT(trace, 5, __FUNCTION__);
-
- while ((skb = skb_dequeue(q))) {
- atomic_dec(&skb->users);
- dev_kfree_skb_irq(skb);
- }
-}
-
-static int
-ctc_checkalloc_buffer(struct channel *ch, int warn)
-{
- DBF_TEXT(trace, 5, __FUNCTION__);
- if ((ch->trans_skb == NULL) ||
- (ch->flags & CHANNEL_FLAGS_BUFSIZE_CHANGED)) {
- if (ch->trans_skb != NULL)
- dev_kfree_skb(ch->trans_skb);
- clear_normalized_cda(&ch->ccw[1]);
- ch->trans_skb = __dev_alloc_skb(ch->max_bufsize,
- GFP_ATOMIC | GFP_DMA);
- if (ch->trans_skb == NULL) {
- if (warn)
- ctc_pr_warn(
- "%s: Couldn't alloc %s trans_skb\n",
- ch->id,
- (CHANNEL_DIRECTION(ch->flags) == READ) ?
- "RX" : "TX");
- return -ENOMEM;
- }
- ch->ccw[1].count = ch->max_bufsize;
- if (set_normalized_cda(&ch->ccw[1], ch->trans_skb->data)) {
- dev_kfree_skb(ch->trans_skb);
- ch->trans_skb = NULL;
- if (warn)
- ctc_pr_warn(
- "%s: set_normalized_cda for %s "
- "trans_skb failed, dropping packets\n",
- ch->id,
- (CHANNEL_DIRECTION(ch->flags) == READ) ?
- "RX" : "TX");
- return -ENOMEM;
- }
- ch->ccw[1].count = 0;
- ch->trans_skb_data = ch->trans_skb->data;
- ch->flags &= ~CHANNEL_FLAGS_BUFSIZE_CHANGED;
- }
- return 0;
-}
-
-/**
- * Dummy NOP action for statemachines
- */
-static void
-fsm_action_nop(fsm_instance * fi, int event, void *arg)
-{
-}
-
-/**
- * Actions for channel - statemachines.
- *****************************************************************************/
-
-/**
- * Normal data has been send. Free the corresponding
- * skb (it's in io_queue), reset dev->tbusy and
- * revert to idle state.
- *
- * @param fi An instance of a channel statemachine.
- * @param event The event, just happened.
- * @param arg Generic pointer, casted from channel * upon call.
- */
-static void
-ch_action_txdone(fsm_instance * fi, int event, void *arg)
-{
- struct channel *ch = (struct channel *) arg;
- struct net_device *dev = ch->netdev;
- struct ctc_priv *privptr = dev->priv;
- struct sk_buff *skb;
- int first = 1;
- int i;
- unsigned long duration;
- struct timespec done_stamp = current_kernel_time();
-
- DBF_TEXT(trace, 4, __FUNCTION__);
-
- duration =
- (done_stamp.tv_sec - ch->prof.send_stamp.tv_sec) * 1000000 +
- (done_stamp.tv_nsec - ch->prof.send_stamp.tv_nsec) / 1000;
- if (duration > ch->prof.tx_time)
- ch->prof.tx_time = duration;
-
- if (ch->irb->scsw.count != 0)
- ctc_pr_debug("%s: TX not complete, remaining %d bytes\n",
- dev->name, ch->irb->scsw.count);
- fsm_deltimer(&ch->timer);
- while ((skb = skb_dequeue(&ch->io_queue))) {
- privptr->stats.tx_packets++;
- privptr->stats.tx_bytes += skb->len - LL_HEADER_LENGTH;
- if (first) {
- privptr->stats.tx_bytes += 2;
- first = 0;
- }
- atomic_dec(&skb->users);
- dev_kfree_skb_irq(skb);
- }
- spin_lock(&ch->collect_lock);
- clear_normalized_cda(&ch->ccw[4]);
- if (ch->collect_len > 0) {
- int rc;
-
- if (ctc_checkalloc_buffer(ch, 1)) {
- spin_unlock(&ch->collect_lock);
- return;
- }
- ch->trans_skb->data = ch->trans_skb_data;
- skb_reset_tail_pointer(ch->trans_skb);
- ch->trans_skb->len = 0;
- if (ch->prof.maxmulti < (ch->collect_len + 2))
- ch->prof.maxmulti = ch->collect_len + 2;
- if (ch->prof.maxcqueue < skb_queue_len(&ch->collect_queue))
- ch->prof.maxcqueue = skb_queue_len(&ch->collect_queue);
- *((__u16 *) skb_put(ch->trans_skb, 2)) = ch->collect_len + 2;
- i = 0;
- while ((skb = skb_dequeue(&ch->collect_queue))) {
- skb_copy_from_linear_data(skb, skb_put(ch->trans_skb,
- skb->len),
- skb->len);
- privptr->stats.tx_packets++;
- privptr->stats.tx_bytes += skb->len - LL_HEADER_LENGTH;
- atomic_dec(&skb->users);
- dev_kfree_skb_irq(skb);
- i++;
- }
- ch->collect_len = 0;
- spin_unlock(&ch->collect_lock);
- ch->ccw[1].count = ch->trans_skb->len;
- fsm_addtimer(&ch->timer, CTC_TIMEOUT_5SEC, CH_EVENT_TIMER, ch);
- ch->prof.send_stamp = current_kernel_time();
- rc = ccw_device_start(ch->cdev, &ch->ccw[0],
- (unsigned long) ch, 0xff, 0);
- ch->prof.doios_multi++;
- if (rc != 0) {
- privptr->stats.tx_dropped += i;
- privptr->stats.tx_errors += i;
- fsm_deltimer(&ch->timer);
- ccw_check_return_code(ch, rc, "chained TX");
- }
- } else {
- spin_unlock(&ch->collect_lock);
- fsm_newstate(fi, CH_STATE_TXIDLE);
- }
- ctc_clear_busy(dev);
-}
-
-/**
- * Initial data is sent.
- * Notify device statemachine that we are up and
- * running.
- *
- * @param fi An instance of a channel statemachine.
- * @param event The event, just happened.
- * @param arg Generic pointer, casted from channel * upon call.
- */
-static void
-ch_action_txidle(fsm_instance * fi, int event, void *arg)
-{
- struct channel *ch = (struct channel *) arg;
-
- DBF_TEXT(trace, 4, __FUNCTION__);
- fsm_deltimer(&ch->timer);
- fsm_newstate(fi, CH_STATE_TXIDLE);
- fsm_event(((struct ctc_priv *) ch->netdev->priv)->fsm, DEV_EVENT_TXUP,
- ch->netdev);
-}
-
-/**
- * Got normal data, check for sanity, queue it up, allocate new buffer
- * trigger bottom half, and initiate next read.
- *
- * @param fi An instance of a channel statemachine.
- * @param event The event, just happened.
- * @param arg Generic pointer, casted from channel * upon call.
- */
-static void
-ch_action_rx(fsm_instance * fi, int event, void *arg)
-{
- struct channel *ch = (struct channel *) arg;
- struct net_device *dev = ch->netdev;
- struct ctc_priv *privptr = dev->priv;
- int len = ch->max_bufsize - ch->irb->scsw.count;
- struct sk_buff *skb = ch->trans_skb;
- __u16 block_len = *((__u16 *) skb->data);
- int check_len;
- int rc;
-
- DBF_TEXT(trace, 4, __FUNCTION__);
- fsm_deltimer(&ch->timer);
- if (len < 8) {
- ctc_pr_debug("%s: got packet with length %d < 8\n",
- dev->name, len);
- privptr->stats.rx_dropped++;
- privptr->stats.rx_length_errors++;
- goto again;
- }
- if (len > ch->max_bufsize) {
- ctc_pr_debug("%s: got packet with length %d > %d\n",
- dev->name, len, ch->max_bufsize);
- privptr->stats.rx_dropped++;
- privptr->stats.rx_length_errors++;
- goto again;
- }
-
- /**
- * VM TCP seems to have a bug sending 2 trailing bytes of garbage.
- */
- switch (ch->protocol) {
- case CTC_PROTO_S390:
- case CTC_PROTO_OS390:
- check_len = block_len + 2;
- break;
- default:
- check_len = block_len;
- break;
- }
- if ((len < block_len) || (len > check_len)) {
- ctc_pr_debug("%s: got block length %d != rx length %d\n",
- dev->name, block_len, len);
-#ifdef DEBUG
- ctc_dump_skb(skb, 0);
-#endif
- *((__u16 *) skb->data) = len;
- privptr->stats.rx_dropped++;
- privptr->stats.rx_length_errors++;
- goto again;
- }
- block_len -= 2;
- if (block_len > 0) {
- *((__u16 *) skb->data) = block_len;
- ctc_unpack_skb(ch, skb);
- }
- again:
- skb->data = ch->trans_skb_data;
- skb_reset_tail_pointer(skb);
- skb->len = 0;
- if (ctc_checkalloc_buffer(ch, 1))
- return;
- ch->ccw[1].count = ch->max_bufsize;
- rc = ccw_device_start(ch->cdev, &ch->ccw[0], (unsigned long) ch, 0xff, 0);
- if (rc != 0)
- ccw_check_return_code(ch, rc, "normal RX");
-}
-
-static void ch_action_rxidle(fsm_instance * fi, int event, void *arg);
-
-/**
- * Initialize connection by sending a __u16 of value 0.
- *
- * @param fi An instance of a channel statemachine.
- * @param event The event, just happened.
- * @param arg Generic pointer, casted from channel * upon call.
- */
-static void
-ch_action_firstio(fsm_instance * fi, int event, void *arg)
-{
- struct channel *ch = (struct channel *) arg;
- int rc;
-
- DBF_TEXT(trace, 4, __FUNCTION__);
-
- if (fsm_getstate(fi) == CH_STATE_TXIDLE)
- ctc_pr_debug("%s: remote side issued READ?, init ...\n", ch->id);
- fsm_deltimer(&ch->timer);
- if (ctc_checkalloc_buffer(ch, 1))
- return;
- if ((fsm_getstate(fi) == CH_STATE_SETUPWAIT) &&
- (ch->protocol == CTC_PROTO_OS390)) {
- /* OS/390 resp. z/OS */
- if (CHANNEL_DIRECTION(ch->flags) == READ) {
- *((__u16 *) ch->trans_skb->data) = CTC_INITIAL_BLOCKLEN;
- fsm_addtimer(&ch->timer, CTC_TIMEOUT_5SEC,
- CH_EVENT_TIMER, ch);
- ch_action_rxidle(fi, event, arg);
- } else {
- struct net_device *dev = ch->netdev;
- fsm_newstate(fi, CH_STATE_TXIDLE);
- fsm_event(((struct ctc_priv *) dev->priv)->fsm,
- DEV_EVENT_TXUP, dev);
- }
- return;
- }
-
- /**
- * Don't setup a timer for receiving the initial RX frame
- * if in compatibility mode, since VM TCP delays the initial
- * frame until it has some data to send.
- */
- if ((CHANNEL_DIRECTION(ch->flags) == WRITE) ||
- (ch->protocol != CTC_PROTO_S390))
- fsm_addtimer(&ch->timer, CTC_TIMEOUT_5SEC, CH_EVENT_TIMER, ch);
-
- *((__u16 *) ch->trans_skb->data) = CTC_INITIAL_BLOCKLEN;
- ch->ccw[1].count = 2; /* Transfer only length */
-
- fsm_newstate(fi, (CHANNEL_DIRECTION(ch->flags) == READ)
- ? CH_STATE_RXINIT : CH_STATE_TXINIT);
- rc = ccw_device_start(ch->cdev, &ch->ccw[0], (unsigned long) ch, 0xff, 0);
- if (rc != 0) {
- fsm_deltimer(&ch->timer);
- fsm_newstate(fi, CH_STATE_SETUPWAIT);
- ccw_check_return_code(ch, rc, "init IO");
- }
- /**
- * If in compatibility mode since we don't setup a timer, we
- * also signal RX channel up immediately. This enables us
- * to send packets early which in turn usually triggers some
- * reply from VM TCP which brings up the RX channel to it's
- * final state.
- */
- if ((CHANNEL_DIRECTION(ch->flags) == READ) &&
- (ch->protocol == CTC_PROTO_S390)) {
- struct net_device *dev = ch->netdev;
- fsm_event(((struct ctc_priv *) dev->priv)->fsm, DEV_EVENT_RXUP,
- dev);
- }
-}
-
-/**
- * Got initial data, check it. If OK,
- * notify device statemachine that we are up and
- * running.
- *
- * @param fi An instance of a channel statemachine.
- * @param event The event, just happened.
- * @param arg Generic pointer, casted from channel * upon call.
- */
-static void
-ch_action_rxidle(fsm_instance * fi, int event, void *arg)
-{
- struct channel *ch = (struct channel *) arg;
- struct net_device *dev = ch->netdev;
- __u16 buflen;
- int rc;
-
- DBF_TEXT(trace, 4, __FUNCTION__);
- fsm_deltimer(&ch->timer);
- buflen = *((__u16 *) ch->trans_skb->data);
-#ifdef DEBUG
- ctc_pr_debug("%s: Initial RX count %d\n", dev->name, buflen);
-#endif
- if (buflen >= CTC_INITIAL_BLOCKLEN) {
- if (ctc_checkalloc_buffer(ch, 1))
- return;
- ch->ccw[1].count = ch->max_bufsize;
- fsm_newstate(fi, CH_STATE_RXIDLE);
- rc = ccw_device_start(ch->cdev, &ch->ccw[0],
- (unsigned long) ch, 0xff, 0);
- if (rc != 0) {
- fsm_newstate(fi, CH_STATE_RXINIT);
- ccw_check_return_code(ch, rc, "initial RX");
- } else
- fsm_event(((struct ctc_priv *) dev->priv)->fsm,
- DEV_EVENT_RXUP, dev);
- } else {
- ctc_pr_debug("%s: Initial RX count %d not %d\n",
- dev->name, buflen, CTC_INITIAL_BLOCKLEN);
- ch_action_firstio(fi, event, arg);
- }
-}
-
-/**
- * Set channel into extended mode.
- *
- * @param fi An instance of a channel statemachine.
- * @param event The event, just happened.
- * @param arg Generic pointer, casted from channel * upon call.
- */
-static void
-ch_action_setmode(fsm_instance * fi, int event, void *arg)
-{
- struct channel *ch = (struct channel *) arg;
- int rc;
- unsigned long saveflags;
-
- DBF_TEXT(trace, 4, __FUNCTION__);
- fsm_deltimer(&ch->timer);
- fsm_addtimer(&ch->timer, CTC_TIMEOUT_5SEC, CH_EVENT_TIMER, ch);
- fsm_newstate(fi, CH_STATE_SETUPWAIT);
- saveflags = 0; /* avoids compiler warning with
- spin_unlock_irqrestore */
- if (event == CH_EVENT_TIMER) // only for timer not yet locked
- spin_lock_irqsave(get_ccwdev_lock(ch->cdev), saveflags);
- rc = ccw_device_start(ch->cdev, &ch->ccw[6], (unsigned long) ch, 0xff, 0);
- if (event == CH_EVENT_TIMER)
- spin_unlock_irqrestore(get_ccwdev_lock(ch->cdev), saveflags);
- if (rc != 0) {
- fsm_deltimer(&ch->timer);
- fsm_newstate(fi, CH_STATE_STARTWAIT);
- ccw_check_return_code(ch, rc, "set Mode");
- } else
- ch->retry = 0;
-}
-
-/**
- * Setup channel.
- *
- * @param fi An instance of a channel statemachine.
- * @param event The event, just happened.
- * @param arg Generic pointer, casted from channel * upon call.
- */
-static void
-ch_action_start(fsm_instance * fi, int event, void *arg)
-{
- struct channel *ch = (struct channel *) arg;
- unsigned long saveflags;
- int rc;
- struct net_device *dev;
-
- DBF_TEXT(trace, 4, __FUNCTION__);
- if (ch == NULL) {
- ctc_pr_warn("ch_action_start ch=NULL\n");
- return;
- }
- if (ch->netdev == NULL) {
- ctc_pr_warn("ch_action_start dev=NULL, id=%s\n", ch->id);
- return;
- }
- dev = ch->netdev;
-
-#ifdef DEBUG
- ctc_pr_debug("%s: %s channel start\n", dev->name,
- (CHANNEL_DIRECTION(ch->flags) == READ) ? "RX" : "TX");
-#endif
-
- if (ch->trans_skb != NULL) {
- clear_normalized_cda(&ch->ccw[1]);
- dev_kfree_skb(ch->trans_skb);
- ch->trans_skb = NULL;
- }
- if (CHANNEL_DIRECTION(ch->flags) == READ) {
- ch->ccw[1].cmd_code = CCW_CMD_READ;
- ch->ccw[1].flags = CCW_FLAG_SLI;
- ch->ccw[1].count = 0;
- } else {
- ch->ccw[1].cmd_code = CCW_CMD_WRITE;
- ch->ccw[1].flags = CCW_FLAG_SLI | CCW_FLAG_CC;
- ch->ccw[1].count = 0;
- }
- if (ctc_checkalloc_buffer(ch, 0)) {
- ctc_pr_notice(
- "%s: Could not allocate %s trans_skb, delaying "
- "allocation until first transfer\n",
- dev->name,
- (CHANNEL_DIRECTION(ch->flags) == READ) ? "RX" : "TX");
- }
-
- ch->ccw[0].cmd_code = CCW_CMD_PREPARE;
- ch->ccw[0].flags = CCW_FLAG_SLI | CCW_FLAG_CC;
- ch->ccw[0].count = 0;
- ch->ccw[0].cda = 0;
- ch->ccw[2].cmd_code = CCW_CMD_NOOP; /* jointed CE + DE */
- ch->ccw[2].flags = CCW_FLAG_SLI;
- ch->ccw[2].count = 0;
- ch->ccw[2].cda = 0;
- memcpy(&ch->ccw[3], &ch->ccw[0], sizeof (struct ccw1) * 3);
- ch->ccw[4].cda = 0;
- ch->ccw[4].flags &= ~CCW_FLAG_IDA;
-
- fsm_newstate(fi, CH_STATE_STARTWAIT);
- fsm_addtimer(&ch->timer, 1000, CH_EVENT_TIMER, ch);
- spin_lock_irqsave(get_ccwdev_lock(ch->cdev), saveflags);
- rc = ccw_device_halt(ch->cdev, (unsigned long) ch);
- spin_unlock_irqrestore(get_ccwdev_lock(ch->cdev), saveflags);
- if (rc != 0) {
- if (rc != -EBUSY)
- fsm_deltimer(&ch->timer);
- ccw_check_return_code(ch, rc, "initial HaltIO");
- }
-#ifdef DEBUG
- ctc_pr_debug("ctc: %s(): leaving\n", __func__);
-#endif
-}
-
-/**
- * Shutdown a channel.
- *
- * @param fi An instance of a channel statemachine.
- * @param event The event, just happened.
- * @param arg Generic pointer, casted from channel * upon call.
- */
-static void
-ch_action_haltio(fsm_instance * fi, int event, void *arg)
-{
- struct channel *ch = (struct channel *) arg;
- unsigned long saveflags;
- int rc;
- int oldstate;
-
- DBF_TEXT(trace, 3, __FUNCTION__);
- fsm_deltimer(&ch->timer);
- fsm_addtimer(&ch->timer, CTC_TIMEOUT_5SEC, CH_EVENT_TIMER, ch);
- saveflags = 0; /* avoids comp warning with
- spin_unlock_irqrestore */
- if (event == CH_EVENT_STOP) // only for STOP not yet locked
- spin_lock_irqsave(get_ccwdev_lock(ch->cdev), saveflags);
- oldstate = fsm_getstate(fi);
- fsm_newstate(fi, CH_STATE_TERM);
- rc = ccw_device_halt(ch->cdev, (unsigned long) ch);
- if (event == CH_EVENT_STOP)
- spin_unlock_irqrestore(get_ccwdev_lock(ch->cdev), saveflags);
- if (rc != 0) {
- if (rc != -EBUSY) {
- fsm_deltimer(&ch->timer);
- fsm_newstate(fi, oldstate);
- }
- ccw_check_return_code(ch, rc, "HaltIO in ch_action_haltio");
- }
-}
-
-/**
- * A channel has successfully been halted.
- * Cleanup it's queue and notify interface statemachine.
- *
- * @param fi An instance of a channel statemachine.
- * @param event The event, just happened.
- * @param arg Generic pointer, casted from channel * upon call.
- */
-static void
-ch_action_stopped(fsm_instance * fi, int event, void *arg)
-{
- struct channel *ch = (struct channel *) arg;
- struct net_device *dev = ch->netdev;
-
- DBF_TEXT(trace, 3, __FUNCTION__);
- fsm_deltimer(&ch->timer);
- fsm_newstate(fi, CH_STATE_STOPPED);
- if (ch->trans_skb != NULL) {
- clear_normalized_cda(&ch->ccw[1]);
- dev_kfree_skb(ch->trans_skb);
- ch->trans_skb = NULL;
- }
- if (CHANNEL_DIRECTION(ch->flags) == READ) {
- skb_queue_purge(&ch->io_queue);
- fsm_event(((struct ctc_priv *) dev->priv)->fsm,
- DEV_EVENT_RXDOWN, dev);
- } else {
- ctc_purge_skb_queue(&ch->io_queue);
- spin_lock(&ch->collect_lock);
- ctc_purge_skb_queue(&ch->collect_queue);
- ch->collect_len = 0;
- spin_unlock(&ch->collect_lock);
- fsm_event(((struct ctc_priv *) dev->priv)->fsm,
- DEV_EVENT_TXDOWN, dev);
- }
-}
-
-/**
- * A stop command from device statemachine arrived and we are in
- * not operational mode. Set state to stopped.
- *
- * @param fi An instance of a channel statemachine.
- * @param event The event, just happened.
- * @param arg Generic pointer, casted from channel * upon call.
- */
-static void
-ch_action_stop(fsm_instance * fi, int event, void *arg)
-{
- fsm_newstate(fi, CH_STATE_STOPPED);
-}
-
-/**
- * A machine check for no path, not operational status or gone device has
- * happened.
- * Cleanup queue and notify interface statemachine.
- *
- * @param fi An instance of a channel statemachine.
- * @param event The event, just happened.
- * @param arg Generic pointer, casted from channel * upon call.
- */
-static void
-ch_action_fail(fsm_instance * fi, int event, void *arg)
-{
- struct channel *ch = (struct channel *) arg;
- struct net_device *dev = ch->netdev;
-
- DBF_TEXT(trace, 3, __FUNCTION__);
- fsm_deltimer(&ch->timer);
- fsm_newstate(fi, CH_STATE_NOTOP);
- if (CHANNEL_DIRECTION(ch->flags) == READ) {
- skb_queue_purge(&ch->io_queue);
- fsm_event(((struct ctc_priv *) dev->priv)->fsm,
- DEV_EVENT_RXDOWN, dev);
- } else {
- ctc_purge_skb_queue(&ch->io_queue);
- spin_lock(&ch->collect_lock);
- ctc_purge_skb_queue(&ch->collect_queue);
- ch->collect_len = 0;
- spin_unlock(&ch->collect_lock);
- fsm_event(((struct ctc_priv *) dev->priv)->fsm,
- DEV_EVENT_TXDOWN, dev);
- }
-}
-
-/**
- * Handle error during setup of channel.
- *
- * @param fi An instance of a channel statemachine.
- * @param event The event, just happened.
- * @param arg Generic pointer, casted from channel * upon call.
- */
-static void
-ch_action_setuperr(fsm_instance * fi, int event, void *arg)
-{
- struct channel *ch = (struct channel *) arg;
- struct net_device *dev = ch->netdev;
-
- DBF_TEXT(setup, 3, __FUNCTION__);
- /**
- * Special case: Got UC_RCRESET on setmode.
- * This means that remote side isn't setup. In this case
- * simply retry after some 10 secs...
- */
- if ((fsm_getstate(fi) == CH_STATE_SETUPWAIT) &&
- ((event == CH_EVENT_UC_RCRESET) ||
- (event == CH_EVENT_UC_RSRESET))) {
- fsm_newstate(fi, CH_STATE_STARTRETRY);
- fsm_deltimer(&ch->timer);
- fsm_addtimer(&ch->timer, CTC_TIMEOUT_5SEC, CH_EVENT_TIMER, ch);
- if (CHANNEL_DIRECTION(ch->flags) == READ) {
- int rc = ccw_device_halt(ch->cdev, (unsigned long) ch);
- if (rc != 0)
- ccw_check_return_code(
- ch, rc, "HaltIO in ch_action_setuperr");
- }
- return;
- }
-
- ctc_pr_debug("%s: Error %s during %s channel setup state=%s\n",
- dev->name, ch_event_names[event],
- (CHANNEL_DIRECTION(ch->flags) == READ) ? "RX" : "TX",
- fsm_getstate_str(fi));
- if (CHANNEL_DIRECTION(ch->flags) == READ) {
- fsm_newstate(fi, CH_STATE_RXERR);
- fsm_event(((struct ctc_priv *) dev->priv)->fsm,
- DEV_EVENT_RXDOWN, dev);
- } else {
- fsm_newstate(fi, CH_STATE_TXERR);
- fsm_event(((struct ctc_priv *) dev->priv)->fsm,
- DEV_EVENT_TXDOWN, dev);
- }
-}
-
-/**
- * Restart a channel after an error.
- *
- * @param fi An instance of a channel statemachine.
- * @param event The event, just happened.
- * @param arg Generic pointer, casted from channel * upon call.
- */
-static void
-ch_action_restart(fsm_instance * fi, int event, void *arg)
-{
- unsigned long saveflags;
- int oldstate;
- int rc;
-
- struct channel *ch = (struct channel *) arg;
- struct net_device *dev = ch->netdev;
-
- DBF_TEXT(trace, 3, __FUNCTION__);
- fsm_deltimer(&ch->timer);
- ctc_pr_debug("%s: %s channel restart\n", dev->name,
- (CHANNEL_DIRECTION(ch->flags) == READ) ? "RX" : "TX");
- fsm_addtimer(&ch->timer, CTC_TIMEOUT_5SEC, CH_EVENT_TIMER, ch);
- oldstate = fsm_getstate(fi);
- fsm_newstate(fi, CH_STATE_STARTWAIT);
- saveflags = 0; /* avoids compiler warning with
- spin_unlock_irqrestore */
- if (event == CH_EVENT_TIMER) // only for timer not yet locked
- spin_lock_irqsave(get_ccwdev_lock(ch->cdev), saveflags);
- rc = ccw_device_halt(ch->cdev, (unsigned long) ch);
- if (event == CH_EVENT_TIMER)
- spin_unlock_irqrestore(get_ccwdev_lock(ch->cdev), saveflags);
- if (rc != 0) {
- if (rc != -EBUSY) {
- fsm_deltimer(&ch->timer);
- fsm_newstate(fi, oldstate);
- }
- ccw_check_return_code(ch, rc, "HaltIO in ch_action_restart");
- }
-}
-
-/**
- * Handle error during RX initial handshake (exchange of
- * 0-length block header)
- *
- * @param fi An instance of a channel statemachine.
- * @param event The event, just happened.
- * @param arg Generic pointer, casted from channel * upon call.
- */
-static void
-ch_action_rxiniterr(fsm_instance * fi, int event, void *arg)
-{
- struct channel *ch = (struct channel *) arg;
- struct net_device *dev = ch->netdev;
-
- DBF_TEXT(setup, 3, __FUNCTION__);
- if (event == CH_EVENT_TIMER) {
- fsm_deltimer(&ch->timer);
- ctc_pr_debug("%s: Timeout during RX init handshake\n", dev->name);
- if (ch->retry++ < 3)
- ch_action_restart(fi, event, arg);
- else {
- fsm_newstate(fi, CH_STATE_RXERR);
- fsm_event(((struct ctc_priv *) dev->priv)->fsm,
- DEV_EVENT_RXDOWN, dev);
- }
- } else
- ctc_pr_warn("%s: Error during RX init handshake\n", dev->name);
-}
-
-/**
- * Notify device statemachine if we gave up initialization
- * of RX channel.
- *
- * @param fi An instance of a channel statemachine.
- * @param event The event, just happened.
- * @param arg Generic pointer, casted from channel * upon call.
- */
-static void
-ch_action_rxinitfail(fsm_instance * fi, int event, void *arg)
-{
- struct channel *ch = (struct channel *) arg;
- struct net_device *dev = ch->netdev;
-
- DBF_TEXT(setup, 3, __FUNCTION__);
- fsm_newstate(fi, CH_STATE_RXERR);
- ctc_pr_warn("%s: RX initialization failed\n", dev->name);
- ctc_pr_warn("%s: RX <-> RX connection detected\n", dev->name);
- fsm_event(((struct ctc_priv *) dev->priv)->fsm, DEV_EVENT_RXDOWN, dev);
-}
-
-/**
- * Handle RX Unit check remote reset (remote disconnected)
- *
- * @param fi An instance of a channel statemachine.
- * @param event The event, just happened.
- * @param arg Generic pointer, casted from channel * upon call.
- */
-static void
-ch_action_rxdisc(fsm_instance * fi, int event, void *arg)
-{
- struct channel *ch = (struct channel *) arg;
- struct channel *ch2;
- struct net_device *dev = ch->netdev;
-
- DBF_TEXT(trace, 3, __FUNCTION__);
- fsm_deltimer(&ch->timer);
- ctc_pr_debug("%s: Got remote disconnect, re-initializing ...\n",
- dev->name);
-
- /**
- * Notify device statemachine
- */
- fsm_event(((struct ctc_priv *) dev->priv)->fsm, DEV_EVENT_RXDOWN, dev);
- fsm_event(((struct ctc_priv *) dev->priv)->fsm, DEV_EVENT_TXDOWN, dev);
-
- fsm_newstate(fi, CH_STATE_DTERM);
- ch2 = ((struct ctc_priv *) dev->priv)->channel[WRITE];
- fsm_newstate(ch2->fsm, CH_STATE_DTERM);
-
- ccw_device_halt(ch->cdev, (unsigned long) ch);
- ccw_device_halt(ch2->cdev, (unsigned long) ch2);
-}
-
-/**
- * Handle error during TX channel initialization.
- *
- * @param fi An instance of a channel statemachine.
- * @param event The event, just happened.
- * @param arg Generic pointer, casted from channel * upon call.
- */
-static void
-ch_action_txiniterr(fsm_instance * fi, int event, void *arg)
-{
- struct channel *ch = (struct channel *) arg;
- struct net_device *dev = ch->netdev;
-
- DBF_TEXT(setup, 2, __FUNCTION__);
- if (event == CH_EVENT_TIMER) {
- fsm_deltimer(&ch->timer);
- ctc_pr_debug("%s: Timeout during TX init handshake\n", dev->name);
- if (ch->retry++ < 3)
- ch_action_restart(fi, event, arg);
- else {
- fsm_newstate(fi, CH_STATE_TXERR);
- fsm_event(((struct ctc_priv *) dev->priv)->fsm,
- DEV_EVENT_TXDOWN, dev);
- }
- } else
- ctc_pr_warn("%s: Error during TX init handshake\n", dev->name);
-}
-
-/**
- * Handle TX timeout by retrying operation.
- *
- * @param fi An instance of a channel statemachine.
- * @param event The event, just happened.
- * @param arg Generic pointer, casted from channel * upon call.
- */
-static void
-ch_action_txretry(fsm_instance * fi, int event, void *arg)
-{
- struct channel *ch = (struct channel *) arg;
- struct net_device *dev = ch->netdev;
- unsigned long saveflags;
-
- DBF_TEXT(trace, 4, __FUNCTION__);
- fsm_deltimer(&ch->timer);
- if (ch->retry++ > 3) {
- ctc_pr_debug("%s: TX retry failed, restarting channel\n",
- dev->name);
- fsm_event(((struct ctc_priv *) dev->priv)->fsm,
- DEV_EVENT_TXDOWN, dev);
- ch_action_restart(fi, event, arg);
- } else {
- struct sk_buff *skb;
-
- ctc_pr_debug("%s: TX retry %d\n", dev->name, ch->retry);
- if ((skb = skb_peek(&ch->io_queue))) {
- int rc = 0;
-
- clear_normalized_cda(&ch->ccw[4]);
- ch->ccw[4].count = skb->len;
- if (set_normalized_cda(&ch->ccw[4], skb->data)) {
- ctc_pr_debug(
- "%s: IDAL alloc failed, chan restart\n",
- dev->name);
- fsm_event(((struct ctc_priv *) dev->priv)->fsm,
- DEV_EVENT_TXDOWN, dev);
- ch_action_restart(fi, event, arg);
- return;
- }
- fsm_addtimer(&ch->timer, 1000, CH_EVENT_TIMER, ch);
- saveflags = 0; /* avoids compiler warning with
- spin_unlock_irqrestore */
- if (event == CH_EVENT_TIMER) // only for TIMER not yet locked
- spin_lock_irqsave(get_ccwdev_lock(ch->cdev),
- saveflags);
- rc = ccw_device_start(ch->cdev, &ch->ccw[3],
- (unsigned long) ch, 0xff, 0);
- if (event == CH_EVENT_TIMER)
- spin_unlock_irqrestore(get_ccwdev_lock(ch->cdev),
- saveflags);
- if (rc != 0) {
- fsm_deltimer(&ch->timer);
- ccw_check_return_code(ch, rc, "TX in ch_action_txretry");
- ctc_purge_skb_queue(&ch->io_queue);
- }
- }
- }
-
-}
-
-/**
- * Handle fatal errors during an I/O command.
- *
- * @param fi An instance of a channel statemachine.
- * @param event The event, just happened.
- * @param arg Generic pointer, casted from channel * upon call.
- */
-static void
-ch_action_iofatal(fsm_instance * fi, int event, void *arg)
-{
- struct channel *ch = (struct channel *) arg;
- struct net_device *dev = ch->netdev;
-
- DBF_TEXT(trace, 3, __FUNCTION__);
- fsm_deltimer(&ch->timer);
- if (CHANNEL_DIRECTION(ch->flags) == READ) {
- ctc_pr_debug("%s: RX I/O error\n", dev->name);
- fsm_newstate(fi, CH_STATE_RXERR);
- fsm_event(((struct ctc_priv *) dev->priv)->fsm,
- DEV_EVENT_RXDOWN, dev);
- } else {
- ctc_pr_debug("%s: TX I/O error\n", dev->name);
- fsm_newstate(fi, CH_STATE_TXERR);
- fsm_event(((struct ctc_priv *) dev->priv)->fsm,
- DEV_EVENT_TXDOWN, dev);
- }
-}
-
-static void
-ch_action_reinit(fsm_instance *fi, int event, void *arg)
-{
- struct channel *ch = (struct channel *)arg;
- struct net_device *dev = ch->netdev;
- struct ctc_priv *privptr = dev->priv;
-
- DBF_TEXT(trace, 4, __FUNCTION__);
- ch_action_iofatal(fi, event, arg);
- fsm_addtimer(&privptr->restart_timer, 1000, DEV_EVENT_RESTART, dev);
-}
-
-/**
- * The statemachine for a channel.
- */
-static const fsm_node ch_fsm[] = {
- {CH_STATE_STOPPED, CH_EVENT_STOP, fsm_action_nop },
- {CH_STATE_STOPPED, CH_EVENT_START, ch_action_start },
- {CH_STATE_STOPPED, CH_EVENT_FINSTAT, fsm_action_nop },
- {CH_STATE_STOPPED, CH_EVENT_MC_FAIL, fsm_action_nop },
-
- {CH_STATE_NOTOP, CH_EVENT_STOP, ch_action_stop },
- {CH_STATE_NOTOP, CH_EVENT_START, fsm_action_nop },
- {CH_STATE_NOTOP, CH_EVENT_FINSTAT, fsm_action_nop },
- {CH_STATE_NOTOP, CH_EVENT_MC_FAIL, fsm_action_nop },
- {CH_STATE_NOTOP, CH_EVENT_MC_GOOD, ch_action_start },
-
- {CH_STATE_STARTWAIT, CH_EVENT_STOP, ch_action_haltio },
- {CH_STATE_STARTWAIT, CH_EVENT_START, fsm_action_nop },
- {CH_STATE_STARTWAIT, CH_EVENT_FINSTAT, ch_action_setmode },
- {CH_STATE_STARTWAIT, CH_EVENT_TIMER, ch_action_setuperr },
- {CH_STATE_STARTWAIT, CH_EVENT_IO_ENODEV, ch_action_iofatal },
- {CH_STATE_STARTWAIT, CH_EVENT_IO_EIO, ch_action_reinit },
- {CH_STATE_STARTWAIT, CH_EVENT_MC_FAIL, ch_action_fail },
-
- {CH_STATE_STARTRETRY, CH_EVENT_STOP, ch_action_haltio },
- {CH_STATE_STARTRETRY, CH_EVENT_TIMER, ch_action_setmode },
- {CH_STATE_STARTRETRY, CH_EVENT_FINSTAT, fsm_action_nop },
- {CH_STATE_STARTRETRY, CH_EVENT_MC_FAIL, ch_action_fail },
-
- {CH_STATE_SETUPWAIT, CH_EVENT_STOP, ch_action_haltio },
- {CH_STATE_SETUPWAIT, CH_EVENT_START, fsm_action_nop },
- {CH_STATE_SETUPWAIT, CH_EVENT_FINSTAT, ch_action_firstio },
- {CH_STATE_SETUPWAIT, CH_EVENT_UC_RCRESET, ch_action_setuperr },
- {CH_STATE_SETUPWAIT, CH_EVENT_UC_RSRESET, ch_action_setuperr },
- {CH_STATE_SETUPWAIT, CH_EVENT_TIMER, ch_action_setmode },
- {CH_STATE_SETUPWAIT, CH_EVENT_IO_ENODEV, ch_action_iofatal },
- {CH_STATE_SETUPWAIT, CH_EVENT_IO_EIO, ch_action_reinit },
- {CH_STATE_SETUPWAIT, CH_EVENT_MC_FAIL, ch_action_fail },
-
- {CH_STATE_RXINIT, CH_EVENT_STOP, ch_action_haltio },
- {CH_STATE_RXINIT, CH_EVENT_START, fsm_action_nop },
- {CH_STATE_RXINIT, CH_EVENT_FINSTAT, ch_action_rxidle },
- {CH_STATE_RXINIT, CH_EVENT_UC_RCRESET, ch_action_rxiniterr },
- {CH_STATE_RXINIT, CH_EVENT_UC_RSRESET, ch_action_rxiniterr },
- {CH_STATE_RXINIT, CH_EVENT_TIMER, ch_action_rxiniterr },
- {CH_STATE_RXINIT, CH_EVENT_ATTNBUSY, ch_action_rxinitfail },
- {CH_STATE_RXINIT, CH_EVENT_IO_ENODEV, ch_action_iofatal },
- {CH_STATE_RXINIT, CH_EVENT_IO_EIO, ch_action_reinit },
- {CH_STATE_RXINIT, CH_EVENT_UC_ZERO, ch_action_firstio },
- {CH_STATE_RXINIT, CH_EVENT_MC_FAIL, ch_action_fail },
-
- {CH_STATE_RXIDLE, CH_EVENT_STOP, ch_action_haltio },
- {CH_STATE_RXIDLE, CH_EVENT_START, fsm_action_nop },
- {CH_STATE_RXIDLE, CH_EVENT_FINSTAT, ch_action_rx },
- {CH_STATE_RXIDLE, CH_EVENT_UC_RCRESET, ch_action_rxdisc },
-// {CH_STATE_RXIDLE, CH_EVENT_UC_RSRESET, ch_action_rxretry },
- {CH_STATE_RXIDLE, CH_EVENT_IO_ENODEV, ch_action_iofatal },
- {CH_STATE_RXIDLE, CH_EVENT_IO_EIO, ch_action_reinit },
- {CH_STATE_RXIDLE, CH_EVENT_MC_FAIL, ch_action_fail },
- {CH_STATE_RXIDLE, CH_EVENT_UC_ZERO, ch_action_rx },
-
- {CH_STATE_TXINIT, CH_EVENT_STOP, ch_action_haltio },
- {CH_STATE_TXINIT, CH_EVENT_START, fsm_action_nop },
- {CH_STATE_TXINIT, CH_EVENT_FINSTAT, ch_action_txidle },
- {CH_STATE_TXINIT, CH_EVENT_UC_RCRESET, ch_action_txiniterr },
- {CH_STATE_TXINIT, CH_EVENT_UC_RSRESET, ch_action_txiniterr },
- {CH_STATE_TXINIT, CH_EVENT_TIMER, ch_action_txiniterr },
- {CH_STATE_TXINIT, CH_EVENT_IO_ENODEV, ch_action_iofatal },
- {CH_STATE_TXINIT, CH_EVENT_IO_EIO, ch_action_reinit },
- {CH_STATE_TXINIT, CH_EVENT_MC_FAIL, ch_action_fail },
-
- {CH_STATE_TXIDLE, CH_EVENT_STOP, ch_action_haltio },
- {CH_STATE_TXIDLE, CH_EVENT_START, fsm_action_nop },
- {CH_STATE_TXIDLE, CH_EVENT_FINSTAT, ch_action_firstio },
- {CH_STATE_TXIDLE, CH_EVENT_UC_RCRESET, fsm_action_nop },
- {CH_STATE_TXIDLE, CH_EVENT_UC_RSRESET, fsm_action_nop },
- {CH_STATE_TXIDLE, CH_EVENT_IO_ENODEV, ch_action_iofatal },
- {CH_STATE_TXIDLE, CH_EVENT_IO_EIO, ch_action_reinit },
- {CH_STATE_TXIDLE, CH_EVENT_MC_FAIL, ch_action_fail },
-
- {CH_STATE_TERM, CH_EVENT_STOP, fsm_action_nop },
- {CH_STATE_TERM, CH_EVENT_START, ch_action_restart },
- {CH_STATE_TERM, CH_EVENT_FINSTAT, ch_action_stopped },
- {CH_STATE_TERM, CH_EVENT_UC_RCRESET, fsm_action_nop },
- {CH_STATE_TERM, CH_EVENT_UC_RSRESET, fsm_action_nop },
- {CH_STATE_TERM, CH_EVENT_MC_FAIL, ch_action_fail },
-
- {CH_STATE_DTERM, CH_EVENT_STOP, ch_action_haltio },
- {CH_STATE_DTERM, CH_EVENT_START, ch_action_restart },
- {CH_STATE_DTERM, CH_EVENT_FINSTAT, ch_action_setmode },
- {CH_STATE_DTERM, CH_EVENT_UC_RCRESET, fsm_action_nop },
- {CH_STATE_DTERM, CH_EVENT_UC_RSRESET, fsm_action_nop },
- {CH_STATE_DTERM, CH_EVENT_MC_FAIL, ch_action_fail },
-
- {CH_STATE_TX, CH_EVENT_STOP, ch_action_haltio },
- {CH_STATE_TX, CH_EVENT_START, fsm_action_nop },
- {CH_STATE_TX, CH_EVENT_FINSTAT, ch_action_txdone },
- {CH_STATE_TX, CH_EVENT_UC_RCRESET, ch_action_txretry },
- {CH_STATE_TX, CH_EVENT_UC_RSRESET, ch_action_txretry },
- {CH_STATE_TX, CH_EVENT_TIMER, ch_action_txretry },
- {CH_STATE_TX, CH_EVENT_IO_ENODEV, ch_action_iofatal },
- {CH_STATE_TX, CH_EVENT_IO_EIO, ch_action_reinit },
- {CH_STATE_TX, CH_EVENT_MC_FAIL, ch_action_fail },
-
- {CH_STATE_RXERR, CH_EVENT_STOP, ch_action_haltio },
- {CH_STATE_TXERR, CH_EVENT_STOP, ch_action_haltio },
- {CH_STATE_TXERR, CH_EVENT_MC_FAIL, ch_action_fail },
- {CH_STATE_RXERR, CH_EVENT_MC_FAIL, ch_action_fail },
-};
-
-static const int CH_FSM_LEN = sizeof (ch_fsm) / sizeof (fsm_node);
-
-/**
- * Functions related to setup and device detection.
- *****************************************************************************/
-
-static inline int
-less_than(char *id1, char *id2)
-{
- int dev1, dev2, i;
-
- for (i = 0; i < 5; i++) {
- id1++;
- id2++;
- }
- dev1 = simple_strtoul(id1, &id1, 16);
- dev2 = simple_strtoul(id2, &id2, 16);
-
- return (dev1 < dev2);
-}
-
-/**
- * Add a new channel to the list of channels.
- * Keeps the channel list sorted.
- *
- * @param cdev The ccw_device to be added.
- * @param type The type class of the new channel.
- *
- * @return 0 on success, !0 on error.
- */
-static int
-add_channel(struct ccw_device *cdev, enum channel_types type)
-{
- struct channel **c = &channels;
- struct channel *ch;
-
- DBF_TEXT(trace, 2, __FUNCTION__);
- ch = kzalloc(sizeof(struct channel), GFP_KERNEL);
- if (!ch) {
- ctc_pr_warn("ctc: Out of memory in add_channel\n");
- return -1;
- }
- /* assure all flags and counters are reset */
- ch->ccw = kzalloc(8 * sizeof(struct ccw1), GFP_KERNEL | GFP_DMA);
- if (!ch->ccw) {
- kfree(ch);
- ctc_pr_warn("ctc: Out of memory in add_channel\n");
- return -1;
- }
-
-
- /**
- * "static" ccws are used in the following way:
- *
- * ccw[0..2] (Channel program for generic I/O):
- * 0: prepare
- * 1: read or write (depending on direction) with fixed
- * buffer (idal allocated once when buffer is allocated)
- * 2: nop
- * ccw[3..5] (Channel program for direct write of packets)
- * 3: prepare
- * 4: write (idal allocated on every write).
- * 5: nop
- * ccw[6..7] (Channel program for initial channel setup):
- * 6: set extended mode
- * 7: nop
- *
- * ch->ccw[0..5] are initialized in ch_action_start because
- * the channel's direction is yet unknown here.
- */
- ch->ccw[6].cmd_code = CCW_CMD_SET_EXTENDED;
- ch->ccw[6].flags = CCW_FLAG_SLI;
-
- ch->ccw[7].cmd_code = CCW_CMD_NOOP;
- ch->ccw[7].flags = CCW_FLAG_SLI;
-
- ch->cdev = cdev;
- snprintf(ch->id, CTC_ID_SIZE, "ch-%s", cdev->dev.bus_id);
- ch->type = type;
- ch->fsm = init_fsm(ch->id, ch_state_names,
- ch_event_names, NR_CH_STATES, NR_CH_EVENTS,
- ch_fsm, CH_FSM_LEN, GFP_KERNEL);
- if (ch->fsm == NULL) {
- ctc_pr_warn("ctc: Could not create FSM in add_channel\n");
- kfree(ch->ccw);
- kfree(ch);
- return -1;
- }
- fsm_newstate(ch->fsm, CH_STATE_IDLE);
- ch->irb = kzalloc(sizeof(struct irb), GFP_KERNEL);
- if (!ch->irb) {
- ctc_pr_warn("ctc: Out of memory in add_channel\n");
- kfree_fsm(ch->fsm);
- kfree(ch->ccw);
- kfree(ch);
- return -1;
- }
- while (*c && less_than((*c)->id, ch->id))
- c = &(*c)->next;
- if (*c && (!strncmp((*c)->id, ch->id, CTC_ID_SIZE))) {
- ctc_pr_debug(
- "ctc: add_channel: device %s already in list, "
- "using old entry\n", (*c)->id);
- kfree(ch->irb);
- kfree_fsm(ch->fsm);
- kfree(ch->ccw);
- kfree(ch);
- return 0;
- }
-
- spin_lock_init(&ch->collect_lock);
-
- fsm_settimer(ch->fsm, &ch->timer);
- skb_queue_head_init(&ch->io_queue);
- skb_queue_head_init(&ch->collect_queue);
- ch->next = *c;
- *c = ch;
- return 0;
-}
-
-/**
- * Release a specific channel in the channel list.
- *
- * @param ch Pointer to channel struct to be released.
- */
-static void
-channel_free(struct channel *ch)
-{
- ch->flags &= ~CHANNEL_FLAGS_INUSE;
- fsm_newstate(ch->fsm, CH_STATE_IDLE);
-}
-
-/**
- * Remove a specific channel in the channel list.
- *
- * @param ch Pointer to channel struct to be released.
- */
-static void
-channel_remove(struct channel *ch)
-{
- struct channel **c = &channels;
-
- DBF_TEXT(trace, 2, __FUNCTION__);
- if (ch == NULL)
- return;
-
- channel_free(ch);
- while (*c) {
- if (*c == ch) {
- *c = ch->next;
- fsm_deltimer(&ch->timer);
- kfree_fsm(ch->fsm);
- clear_normalized_cda(&ch->ccw[4]);
- if (ch->trans_skb != NULL) {
- clear_normalized_cda(&ch->ccw[1]);
- dev_kfree_skb(ch->trans_skb);
- }
- kfree(ch->ccw);
- kfree(ch->irb);
- kfree(ch);
- return;
- }
- c = &((*c)->next);
- }
-}
-
-/**
- * Get a specific channel from the channel list.
- *
- * @param type Type of channel we are interested in.
- * @param id Id of channel we are interested in.
- * @param direction Direction we want to use this channel for.
- *
- * @return Pointer to a channel or NULL if no matching channel available.
- */
-static struct channel
-*
-channel_get(enum channel_types type, char *id, int direction)
-{
- struct channel *ch = channels;
-
- DBF_TEXT(trace, 3, __FUNCTION__);
-#ifdef DEBUG
- ctc_pr_debug("ctc: %s(): searching for ch with id %s and type %d\n",
- __func__, id, type);
-#endif
-
- while (ch && ((strncmp(ch->id, id, CTC_ID_SIZE)) || (ch->type != type))) {
-#ifdef DEBUG
- ctc_pr_debug("ctc: %s(): ch=0x%p (id=%s, type=%d\n",
- __func__, ch, ch->id, ch->type);
-#endif
- ch = ch->next;
- }
-#ifdef DEBUG
- ctc_pr_debug("ctc: %s(): ch=0x%pq (id=%s, type=%d\n",
- __func__, ch, ch->id, ch->type);
-#endif
- if (!ch) {
- ctc_pr_warn("ctc: %s(): channel with id %s "
- "and type %d not found in channel list\n",
- __func__, id, type);
- } else {
- if (ch->flags & CHANNEL_FLAGS_INUSE)
- ch = NULL;
- else {
- ch->flags |= CHANNEL_FLAGS_INUSE;
- ch->flags &= ~CHANNEL_FLAGS_RWMASK;
- ch->flags |= (direction == WRITE)
- ? CHANNEL_FLAGS_WRITE : CHANNEL_FLAGS_READ;
- fsm_newstate(ch->fsm, CH_STATE_STOPPED);
- }
- }
- return ch;
-}
-
-/**
- * Return the channel type by name.
- *
- * @param name Name of network interface.
- *
- * @return Type class of channel to be used for that interface.
- */
-static enum channel_types inline
-extract_channel_media(char *name)
-{
- enum channel_types ret = channel_type_unknown;
-
- if (name != NULL) {
- if (strncmp(name, "ctc", 3) == 0)
- ret = channel_type_parallel;
- if (strncmp(name, "escon", 5) == 0)
- ret = channel_type_escon;
- }
- return ret;
-}
-
-static long
-__ctc_check_irb_error(struct ccw_device *cdev, struct irb *irb)
-{
- if (!IS_ERR(irb))
- return 0;
-
- switch (PTR_ERR(irb)) {
- case -EIO:
- ctc_pr_warn("i/o-error on device %s\n", cdev->dev.bus_id);
-// CTC_DBF_TEXT(trace, 2, "ckirberr");
-// CTC_DBF_TEXT_(trace, 2, " rc%d", -EIO);
- break;
- case -ETIMEDOUT:
- ctc_pr_warn("timeout on device %s\n", cdev->dev.bus_id);
-// CTC_DBF_TEXT(trace, 2, "ckirberr");
-// CTC_DBF_TEXT_(trace, 2, " rc%d", -ETIMEDOUT);
- break;
- default:
- ctc_pr_warn("unknown error %ld on device %s\n", PTR_ERR(irb),
- cdev->dev.bus_id);
-// CTC_DBF_TEXT(trace, 2, "ckirberr");
-// CTC_DBF_TEXT(trace, 2, " rc???");
- }
- return PTR_ERR(irb);
-}
-
-/**
- * Main IRQ handler.
- *
- * @param cdev The ccw_device the interrupt is for.
- * @param intparm interruption parameter.
- * @param irb interruption response block.
- */
-static void
-ctc_irq_handler(struct ccw_device *cdev, unsigned long intparm, struct irb *irb)
-{
- struct channel *ch;
- struct net_device *dev;
- struct ctc_priv *priv;
-
- DBF_TEXT(trace, 5, __FUNCTION__);
- if (__ctc_check_irb_error(cdev, irb))
- return;
-
- /* Check for unsolicited interrupts. */
- if (!cdev->dev.driver_data) {
- ctc_pr_warn("ctc: Got unsolicited irq: %s c-%02x d-%02x\n",
- cdev->dev.bus_id, irb->scsw.cstat,
- irb->scsw.dstat);
- return;
- }
-
- priv = ((struct ccwgroup_device *)cdev->dev.driver_data)
- ->dev.driver_data;
-
- /* Try to extract channel from driver data. */
- if (priv->channel[READ]->cdev == cdev)
- ch = priv->channel[READ];
- else if (priv->channel[WRITE]->cdev == cdev)
- ch = priv->channel[WRITE];
- else {
- ctc_pr_err("ctc: Can't determine channel for interrupt, "
- "device %s\n", cdev->dev.bus_id);
- return;
- }
-
- dev = (struct net_device *) (ch->netdev);
- if (dev == NULL) {
- ctc_pr_crit("ctc: ctc_irq_handler dev=NULL bus_id=%s, ch=0x%p\n",
- cdev->dev.bus_id, ch);
- return;
- }
-
-#ifdef DEBUG
- ctc_pr_debug("%s: interrupt for device: %s received c-%02x d-%02x\n",
- dev->name, ch->id, irb->scsw.cstat, irb->scsw.dstat);
-#endif
-
- /* Copy interruption response block. */
- memcpy(ch->irb, irb, sizeof(struct irb));
-
- /* Check for good subchannel return code, otherwise error message */
- if (ch->irb->scsw.cstat) {
- fsm_event(ch->fsm, CH_EVENT_SC_UNKNOWN, ch);
- ctc_pr_warn("%s: subchannel check for device: %s - %02x %02x\n",
- dev->name, ch->id, ch->irb->scsw.cstat,
- ch->irb->scsw.dstat);
- return;
- }
-
- /* Check the reason-code of a unit check */
- if (ch->irb->scsw.dstat & DEV_STAT_UNIT_CHECK) {
- ccw_unit_check(ch, ch->irb->ecw[0]);
- return;
- }
- if (ch->irb->scsw.dstat & DEV_STAT_BUSY) {
- if (ch->irb->scsw.dstat & DEV_STAT_ATTENTION)
- fsm_event(ch->fsm, CH_EVENT_ATTNBUSY, ch);
- else
- fsm_event(ch->fsm, CH_EVENT_BUSY, ch);
- return;
- }
- if (ch->irb->scsw.dstat & DEV_STAT_ATTENTION) {
- fsm_event(ch->fsm, CH_EVENT_ATTN, ch);
- return;
- }
- if ((ch->irb->scsw.stctl & SCSW_STCTL_SEC_STATUS) ||
- (ch->irb->scsw.stctl == SCSW_STCTL_STATUS_PEND) ||
- (ch->irb->scsw.stctl ==
- (SCSW_STCTL_ALERT_STATUS | SCSW_STCTL_STATUS_PEND)))
- fsm_event(ch->fsm, CH_EVENT_FINSTAT, ch);
- else
- fsm_event(ch->fsm, CH_EVENT_IRQ, ch);
-
-}
-
-/**
- * Actions for interface - statemachine.
- *****************************************************************************/
-
-/**
- * Startup channels by sending CH_EVENT_START to each channel.
- *
- * @param fi An instance of an interface statemachine.
- * @param event The event, just happened.
- * @param arg Generic pointer, casted from struct net_device * upon call.
- */
-static void
-dev_action_start(fsm_instance * fi, int event, void *arg)
-{
- struct net_device *dev = (struct net_device *) arg;
- struct ctc_priv *privptr = dev->priv;
- int direction;
-
- DBF_TEXT(setup, 3, __FUNCTION__);
- fsm_deltimer(&privptr->restart_timer);
- fsm_newstate(fi, DEV_STATE_STARTWAIT_RXTX);
- for (direction = READ; direction <= WRITE; direction++) {
- struct channel *ch = privptr->channel[direction];
- fsm_event(ch->fsm, CH_EVENT_START, ch);
- }
-}
-
-/**
- * Shutdown channels by sending CH_EVENT_STOP to each channel.
- *
- * @param fi An instance of an interface statemachine.
- * @param event The event, just happened.
- * @param arg Generic pointer, casted from struct net_device * upon call.
- */
-static void
-dev_action_stop(fsm_instance * fi, int event, void *arg)
-{
- struct net_device *dev = (struct net_device *) arg;
- struct ctc_priv *privptr = dev->priv;
- int direction;
-
- DBF_TEXT(trace, 3, __FUNCTION__);
- fsm_newstate(fi, DEV_STATE_STOPWAIT_RXTX);
- for (direction = READ; direction <= WRITE; direction++) {
- struct channel *ch = privptr->channel[direction];
- fsm_event(ch->fsm, CH_EVENT_STOP, ch);
- }
-}
-static void
-dev_action_restart(fsm_instance *fi, int event, void *arg)
-{
- struct net_device *dev = (struct net_device *)arg;
- struct ctc_priv *privptr = dev->priv;
-
- DBF_TEXT(trace, 3, __FUNCTION__);
- ctc_pr_debug("%s: Restarting\n", dev->name);
- dev_action_stop(fi, event, arg);
- fsm_event(privptr->fsm, DEV_EVENT_STOP, dev);
- fsm_addtimer(&privptr->restart_timer, CTC_TIMEOUT_5SEC,
- DEV_EVENT_START, dev);
-}
-
-/**
- * Called from channel statemachine
- * when a channel is up and running.
- *
- * @param fi An instance of an interface statemachine.
- * @param event The event, just happened.
- * @param arg Generic pointer, casted from struct net_device * upon call.
- */
-static void
-dev_action_chup(fsm_instance * fi, int event, void *arg)
-{
- struct net_device *dev = (struct net_device *) arg;
-
- DBF_TEXT(trace, 3, __FUNCTION__);
- switch (fsm_getstate(fi)) {
- case DEV_STATE_STARTWAIT_RXTX:
- if (event == DEV_EVENT_RXUP)
- fsm_newstate(fi, DEV_STATE_STARTWAIT_TX);
- else
- fsm_newstate(fi, DEV_STATE_STARTWAIT_RX);
- break;
- case DEV_STATE_STARTWAIT_RX:
- if (event == DEV_EVENT_RXUP) {
- fsm_newstate(fi, DEV_STATE_RUNNING);
- ctc_pr_info("%s: connected with remote side\n",
- dev->name);
- ctc_clear_busy(dev);
- }
- break;
- case DEV_STATE_STARTWAIT_TX:
- if (event == DEV_EVENT_TXUP) {
- fsm_newstate(fi, DEV_STATE_RUNNING);
- ctc_pr_info("%s: connected with remote side\n",
- dev->name);
- ctc_clear_busy(dev);
- }
- break;
- case DEV_STATE_STOPWAIT_TX:
- if (event == DEV_EVENT_RXUP)
- fsm_newstate(fi, DEV_STATE_STOPWAIT_RXTX);
- break;
- case DEV_STATE_STOPWAIT_RX:
- if (event == DEV_EVENT_TXUP)
- fsm_newstate(fi, DEV_STATE_STOPWAIT_RXTX);
- break;
- }
-}
-
-/**
- * Called from channel statemachine
- * when a channel has been shutdown.
- *
- * @param fi An instance of an interface statemachine.
- * @param event The event, just happened.
- * @param arg Generic pointer, casted from struct net_device * upon call.
- */
-static void
-dev_action_chdown(fsm_instance * fi, int event, void *arg)
-{
-
- DBF_TEXT(trace, 3, __FUNCTION__);
- switch (fsm_getstate(fi)) {
- case DEV_STATE_RUNNING:
- if (event == DEV_EVENT_TXDOWN)
- fsm_newstate(fi, DEV_STATE_STARTWAIT_TX);
- else
- fsm_newstate(fi, DEV_STATE_STARTWAIT_RX);
- break;
- case DEV_STATE_STARTWAIT_RX:
- if (event == DEV_EVENT_TXDOWN)
- fsm_newstate(fi, DEV_STATE_STARTWAIT_RXTX);
- break;
- case DEV_STATE_STARTWAIT_TX:
- if (event == DEV_EVENT_RXDOWN)
- fsm_newstate(fi, DEV_STATE_STARTWAIT_RXTX);
- break;
- case DEV_STATE_STOPWAIT_RXTX:
- if (event == DEV_EVENT_TXDOWN)
- fsm_newstate(fi, DEV_STATE_STOPWAIT_RX);
- else
- fsm_newstate(fi, DEV_STATE_STOPWAIT_TX);
- break;
- case DEV_STATE_STOPWAIT_RX:
- if (event == DEV_EVENT_RXDOWN)
- fsm_newstate(fi, DEV_STATE_STOPPED);
- break;
- case DEV_STATE_STOPWAIT_TX:
- if (event == DEV_EVENT_TXDOWN)
- fsm_newstate(fi, DEV_STATE_STOPPED);
- break;
- }
-}
-
-static const fsm_node dev_fsm[] = {
- {DEV_STATE_STOPPED, DEV_EVENT_START, dev_action_start},
-
- {DEV_STATE_STOPWAIT_RXTX, DEV_EVENT_START, dev_action_start },
- {DEV_STATE_STOPWAIT_RXTX, DEV_EVENT_RXDOWN, dev_action_chdown },
- {DEV_STATE_STOPWAIT_RXTX, DEV_EVENT_TXDOWN, dev_action_chdown },
- {DEV_STATE_STOPWAIT_RXTX, DEV_EVENT_RESTART, dev_action_restart },
-
- {DEV_STATE_STOPWAIT_RX, DEV_EVENT_START, dev_action_start },
- {DEV_STATE_STOPWAIT_RX, DEV_EVENT_RXUP, dev_action_chup },
- {DEV_STATE_STOPWAIT_RX, DEV_EVENT_TXUP, dev_action_chup },
- {DEV_STATE_STOPWAIT_RX, DEV_EVENT_RXDOWN, dev_action_chdown },
- {DEV_STATE_STOPWAIT_RX, DEV_EVENT_RESTART, dev_action_restart },
-
- {DEV_STATE_STOPWAIT_TX, DEV_EVENT_START, dev_action_start },
- {DEV_STATE_STOPWAIT_TX, DEV_EVENT_RXUP, dev_action_chup },
- {DEV_STATE_STOPWAIT_TX, DEV_EVENT_TXUP, dev_action_chup },
- {DEV_STATE_STOPWAIT_TX, DEV_EVENT_TXDOWN, dev_action_chdown },
- {DEV_STATE_STOPWAIT_TX, DEV_EVENT_RESTART, dev_action_restart },
-
- {DEV_STATE_STARTWAIT_RXTX, DEV_EVENT_STOP, dev_action_stop },
- {DEV_STATE_STARTWAIT_RXTX, DEV_EVENT_RXUP, dev_action_chup },
- {DEV_STATE_STARTWAIT_RXTX, DEV_EVENT_TXUP, dev_action_chup },
- {DEV_STATE_STARTWAIT_RXTX, DEV_EVENT_RXDOWN, dev_action_chdown },
- {DEV_STATE_STARTWAIT_RXTX, DEV_EVENT_TXDOWN, dev_action_chdown },
- {DEV_STATE_STARTWAIT_RXTX, DEV_EVENT_RESTART, dev_action_restart },
-
- {DEV_STATE_STARTWAIT_TX, DEV_EVENT_STOP, dev_action_stop },
- {DEV_STATE_STARTWAIT_TX, DEV_EVENT_RXUP, dev_action_chup },
- {DEV_STATE_STARTWAIT_TX, DEV_EVENT_TXUP, dev_action_chup },
- {DEV_STATE_STARTWAIT_TX, DEV_EVENT_RXDOWN, dev_action_chdown },
- {DEV_STATE_STARTWAIT_TX, DEV_EVENT_RESTART, dev_action_restart },
-
- {DEV_STATE_STARTWAIT_RX, DEV_EVENT_STOP, dev_action_stop },
- {DEV_STATE_STARTWAIT_RX, DEV_EVENT_RXUP, dev_action_chup },
- {DEV_STATE_STARTWAIT_RX, DEV_EVENT_TXUP, dev_action_chup },
- {DEV_STATE_STARTWAIT_RX, DEV_EVENT_TXDOWN, dev_action_chdown },
- {DEV_STATE_STARTWAIT_RX, DEV_EVENT_RESTART, dev_action_restart },
-
- {DEV_STATE_RUNNING, DEV_EVENT_STOP, dev_action_stop },
- {DEV_STATE_RUNNING, DEV_EVENT_RXDOWN, dev_action_chdown },
- {DEV_STATE_RUNNING, DEV_EVENT_TXDOWN, dev_action_chdown },
- {DEV_STATE_RUNNING, DEV_EVENT_TXUP, fsm_action_nop },
- {DEV_STATE_RUNNING, DEV_EVENT_RXUP, fsm_action_nop },
- {DEV_STATE_RUNNING, DEV_EVENT_RESTART, dev_action_restart },
-};
-
-static const int DEV_FSM_LEN = sizeof (dev_fsm) / sizeof (fsm_node);
-
-/**
- * Transmit a packet.
- * This is a helper function for ctc_tx().
- *
- * @param ch Channel to be used for sending.
- * @param skb Pointer to struct sk_buff of packet to send.
- * The linklevel header has already been set up
- * by ctc_tx().
- *
- * @return 0 on success, -ERRNO on failure. (Never fails.)
- */
-static int
-transmit_skb(struct channel *ch, struct sk_buff *skb)
-{
- unsigned long saveflags;
- struct ll_header header;
- int rc = 0;
-
- DBF_TEXT(trace, 5, __FUNCTION__);
- /* we need to acquire the lock for testing the state
- * otherwise we can have an IRQ changing the state to
- * TXIDLE after the test but before acquiring the lock.
- */
- spin_lock_irqsave(&ch->collect_lock, saveflags);
- if (fsm_getstate(ch->fsm) != CH_STATE_TXIDLE) {
- int l = skb->len + LL_HEADER_LENGTH;
-
- if (ch->collect_len + l > ch->max_bufsize - 2) {
- spin_unlock_irqrestore(&ch->collect_lock, saveflags);
- return -EBUSY;
- } else {
- atomic_inc(&skb->users);
- header.length = l;
- header.type = skb->protocol;
- header.unused = 0;
- memcpy(skb_push(skb, LL_HEADER_LENGTH), &header,
- LL_HEADER_LENGTH);
- skb_queue_tail(&ch->collect_queue, skb);
- ch->collect_len += l;
- }
- spin_unlock_irqrestore(&ch->collect_lock, saveflags);
- } else {
- __u16 block_len;
- int ccw_idx;
- struct sk_buff *nskb;
- unsigned long hi;
- spin_unlock_irqrestore(&ch->collect_lock, saveflags);
- /**
- * Protect skb against beeing free'd by upper
- * layers.
- */
- atomic_inc(&skb->users);
- ch->prof.txlen += skb->len;
- header.length = skb->len + LL_HEADER_LENGTH;
- header.type = skb->protocol;
- header.unused = 0;
- memcpy(skb_push(skb, LL_HEADER_LENGTH), &header,
- LL_HEADER_LENGTH);
- block_len = skb->len + 2;
- *((__u16 *) skb_push(skb, 2)) = block_len;
-
- /**
- * IDAL support in CTC is broken, so we have to
- * care about skb's above 2G ourselves.
- */
- hi = ((unsigned long)skb_tail_pointer(skb) +
- LL_HEADER_LENGTH) >> 31;
- if (hi) {
- nskb = alloc_skb(skb->len, GFP_ATOMIC | GFP_DMA);
- if (!nskb) {
- atomic_dec(&skb->users);
- skb_pull(skb, LL_HEADER_LENGTH + 2);
- ctc_clear_busy(ch->netdev);
- return -ENOMEM;
- } else {
- memcpy(skb_put(nskb, skb->len),
- skb->data, skb->len);
- atomic_inc(&nskb->users);
- atomic_dec(&skb->users);
- dev_kfree_skb_irq(skb);
- skb = nskb;
- }
- }
-
- ch->ccw[4].count = block_len;
- if (set_normalized_cda(&ch->ccw[4], skb->data)) {
- /**
- * idal allocation failed, try via copying to
- * trans_skb. trans_skb usually has a pre-allocated
- * idal.
- */
- if (ctc_checkalloc_buffer(ch, 1)) {
- /**
- * Remove our header. It gets added
- * again on retransmit.
- */
- atomic_dec(&skb->users);
- skb_pull(skb, LL_HEADER_LENGTH + 2);
- ctc_clear_busy(ch->netdev);
- return -EBUSY;
- }
-
- skb_reset_tail_pointer(ch->trans_skb);
- ch->trans_skb->len = 0;
- ch->ccw[1].count = skb->len;
- skb_copy_from_linear_data(skb, skb_put(ch->trans_skb,
- skb->len),
- skb->len);
- atomic_dec(&skb->users);
- dev_kfree_skb_irq(skb);
- ccw_idx = 0;
- } else {
- skb_queue_tail(&ch->io_queue, skb);
- ccw_idx = 3;
- }
- ch->retry = 0;
- fsm_newstate(ch->fsm, CH_STATE_TX);
- fsm_addtimer(&ch->timer, CTC_TIMEOUT_5SEC, CH_EVENT_TIMER, ch);
- spin_lock_irqsave(get_ccwdev_lock(ch->cdev), saveflags);
- ch->prof.send_stamp = current_kernel_time();
- rc = ccw_device_start(ch->cdev, &ch->ccw[ccw_idx],
- (unsigned long) ch, 0xff, 0);
- spin_unlock_irqrestore(get_ccwdev_lock(ch->cdev), saveflags);
- if (ccw_idx == 3)
- ch->prof.doios_single++;
- if (rc != 0) {
- fsm_deltimer(&ch->timer);
- ccw_check_return_code(ch, rc, "single skb TX");
- if (ccw_idx == 3)
- skb_dequeue_tail(&ch->io_queue);
- /**
- * Remove our header. It gets added
- * again on retransmit.
- */
- skb_pull(skb, LL_HEADER_LENGTH + 2);
- } else {
- if (ccw_idx == 0) {
- struct net_device *dev = ch->netdev;
- struct ctc_priv *privptr = dev->priv;
- privptr->stats.tx_packets++;
- privptr->stats.tx_bytes +=
- skb->len - LL_HEADER_LENGTH;
- }
- }
- }
-
- ctc_clear_busy(ch->netdev);
- return rc;
-}
-
-/**
- * Interface API for upper network layers
- *****************************************************************************/
-
-/**
- * Open an interface.
- * Called from generic network layer when ifconfig up is run.
- *
- * @param dev Pointer to interface struct.
- *
- * @return 0 on success, -ERRNO on failure. (Never fails.)
- */
-static int
-ctc_open(struct net_device * dev)
-{
- DBF_TEXT(trace, 5, __FUNCTION__);
- fsm_event(((struct ctc_priv *) dev->priv)->fsm, DEV_EVENT_START, dev);
- return 0;
-}
-
-/**
- * Close an interface.
- * Called from generic network layer when ifconfig down is run.
- *
- * @param dev Pointer to interface struct.
- *
- * @return 0 on success, -ERRNO on failure. (Never fails.)
- */
-static int
-ctc_close(struct net_device * dev)
-{
- DBF_TEXT(trace, 5, __FUNCTION__);
- fsm_event(((struct ctc_priv *) dev->priv)->fsm, DEV_EVENT_STOP, dev);
- return 0;
-}
-
-/**
- * Start transmission of a packet.
- * Called from generic network device layer.
- *
- * @param skb Pointer to buffer containing the packet.
- * @param dev Pointer to interface struct.
- *
- * @return 0 if packet consumed, !0 if packet rejected.
- * Note: If we return !0, then the packet is free'd by
- * the generic network layer.
- */
-static int
-ctc_tx(struct sk_buff *skb, struct net_device * dev)
-{
- int rc = 0;
- struct ctc_priv *privptr = (struct ctc_priv *) dev->priv;
-
- DBF_TEXT(trace, 5, __FUNCTION__);
- /**
- * Some sanity checks ...
- */
- if (skb == NULL) {
- ctc_pr_warn("%s: NULL sk_buff passed\n", dev->name);
- privptr->stats.tx_dropped++;
- return 0;
- }
- if (skb_headroom(skb) < (LL_HEADER_LENGTH + 2)) {
- ctc_pr_warn("%s: Got sk_buff with head room < %ld bytes\n",
- dev->name, LL_HEADER_LENGTH + 2);
- dev_kfree_skb(skb);
- privptr->stats.tx_dropped++;
- return 0;
- }
-
- /**
- * If channels are not running, try to restart them
- * and throw away packet.
- */
- if (fsm_getstate(privptr->fsm) != DEV_STATE_RUNNING) {
- fsm_event(privptr->fsm, DEV_EVENT_START, dev);
- dev_kfree_skb(skb);
- privptr->stats.tx_dropped++;
- privptr->stats.tx_errors++;
- privptr->stats.tx_carrier_errors++;
- return 0;
- }
-
- if (ctc_test_and_set_busy(dev))
- return -EBUSY;
-
- dev->trans_start = jiffies;
- if (transmit_skb(privptr->channel[WRITE], skb) != 0)
- rc = 1;
- return rc;
-}
-
-/**
- * Sets MTU of an interface.
- *
- * @param dev Pointer to interface struct.
- * @param new_mtu The new MTU to use for this interface.
- *
- * @return 0 on success, -EINVAL if MTU is out of valid range.
- * (valid range is 576 .. 65527). If VM is on the
- * remote side, maximum MTU is 32760, however this is
- * <em>not</em> checked here.
- */
-static int
-ctc_change_mtu(struct net_device * dev, int new_mtu)
-{
- struct ctc_priv *privptr = (struct ctc_priv *) dev->priv;
-
- DBF_TEXT(trace, 3, __FUNCTION__);
- if ((new_mtu < 576) || (new_mtu > 65527) ||
- (new_mtu > (privptr->channel[READ]->max_bufsize -
- LL_HEADER_LENGTH - 2)))
- return -EINVAL;
- dev->mtu = new_mtu;
- dev->hard_header_len = LL_HEADER_LENGTH + 2;
- return 0;
-}
-
-/**
- * Returns interface statistics of a device.
- *
- * @param dev Pointer to interface struct.
- *
- * @return Pointer to stats struct of this interface.
- */
-static struct net_device_stats *
-ctc_stats(struct net_device * dev)
-{
- return &((struct ctc_priv *) dev->priv)->stats;
-}
-
-/*
- * sysfs attributes
- */
-
-static ssize_t
-buffer_show(struct device *dev, struct device_attribute *attr, char *buf)
-{
- struct ctc_priv *priv;
-
- priv = dev->driver_data;
- if (!priv)
- return -ENODEV;
- return sprintf(buf, "%d\n",
- priv->buffer_size);
-}
-
-static ssize_t
-buffer_write(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
-{
- struct ctc_priv *priv;
- struct net_device *ndev;
- int bs1;
- char buffer[16];
-
- DBF_TEXT(trace, 3, __FUNCTION__);
- DBF_TEXT(trace, 3, buf);
- priv = dev->driver_data;
- if (!priv) {
- DBF_TEXT(trace, 3, "bfnopriv");
- return -ENODEV;
- }
-
- sscanf(buf, "%u", &bs1);
- if (bs1 > CTC_BUFSIZE_LIMIT)
- goto einval;
- if (bs1 < (576 + LL_HEADER_LENGTH + 2))
- goto einval;
- priv->buffer_size = bs1; // just to overwrite the default
-
- ndev = priv->channel[READ]->netdev;
- if (!ndev) {
- DBF_TEXT(trace, 3, "bfnondev");
- return -ENODEV;
- }
-
- if ((ndev->flags & IFF_RUNNING) &&
- (bs1 < (ndev->mtu + LL_HEADER_LENGTH + 2)))
- goto einval;
-
- priv->channel[READ]->max_bufsize = bs1;
- priv->channel[WRITE]->max_bufsize = bs1;
- if (!(ndev->flags & IFF_RUNNING))
- ndev->mtu = bs1 - LL_HEADER_LENGTH - 2;
- priv->channel[READ]->flags |= CHANNEL_FLAGS_BUFSIZE_CHANGED;
- priv->channel[WRITE]->flags |= CHANNEL_FLAGS_BUFSIZE_CHANGED;
-
- sprintf(buffer, "%d",priv->buffer_size);
- DBF_TEXT(trace, 3, buffer);
- return count;
-
-einval:
- DBF_TEXT(trace, 3, "buff_err");
- return -EINVAL;
-}
-
-static ssize_t
-loglevel_show(struct device *dev, struct device_attribute *attr, char *buf)
-{
- return sprintf(buf, "%d\n", loglevel);
-}
-
-static ssize_t
-loglevel_write(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
-{
- int ll1;
-
- DBF_TEXT(trace, 5, __FUNCTION__);
- sscanf(buf, "%i", &ll1);
-
- if ((ll1 > CTC_LOGLEVEL_MAX) || (ll1 < 0))
- return -EINVAL;
- loglevel = ll1;
- return count;
-}
-
-static void
-ctc_print_statistics(struct ctc_priv *priv)
-{
- char *sbuf;
- char *p;
-
- DBF_TEXT(trace, 4, __FUNCTION__);
- if (!priv)
- return;
- sbuf = kmalloc(2048, GFP_KERNEL);
- if (sbuf == NULL)
- return;
- p = sbuf;
-
- p += sprintf(p, " Device FSM state: %s\n",
- fsm_getstate_str(priv->fsm));
- p += sprintf(p, " RX channel FSM state: %s\n",
- fsm_getstate_str(priv->channel[READ]->fsm));
- p += sprintf(p, " TX channel FSM state: %s\n",
- fsm_getstate_str(priv->channel[WRITE]->fsm));
- p += sprintf(p, " Max. TX buffer used: %ld\n",
- priv->channel[WRITE]->prof.maxmulti);
- p += sprintf(p, " Max. chained SKBs: %ld\n",
- priv->channel[WRITE]->prof.maxcqueue);
- p += sprintf(p, " TX single write ops: %ld\n",
- priv->channel[WRITE]->prof.doios_single);
- p += sprintf(p, " TX multi write ops: %ld\n",
- priv->channel[WRITE]->prof.doios_multi);
- p += sprintf(p, " Netto bytes written: %ld\n",
- priv->channel[WRITE]->prof.txlen);
- p += sprintf(p, " Max. TX IO-time: %ld\n",
- priv->channel[WRITE]->prof.tx_time);
-
- ctc_pr_debug("Statistics for %s:\n%s",
- priv->channel[WRITE]->netdev->name, sbuf);
- kfree(sbuf);
- return;
-}
-
-static ssize_t
-stats_show(struct device *dev, struct device_attribute *attr, char *buf)
-{
- struct ctc_priv *priv = dev->driver_data;
- if (!priv)
- return -ENODEV;
- ctc_print_statistics(priv);
- return sprintf(buf, "0\n");
-}
-
-static ssize_t
-stats_write(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
-{
- struct ctc_priv *priv = dev->driver_data;
- if (!priv)
- return -ENODEV;
- /* Reset statistics */
- memset(&priv->channel[WRITE]->prof, 0,
- sizeof(priv->channel[WRITE]->prof));
- return count;
-}
-
-static void
-ctc_netdev_unregister(struct net_device * dev)
-{
- struct ctc_priv *privptr;
-
- if (!dev)
- return;
- privptr = (struct ctc_priv *) dev->priv;
- unregister_netdev(dev);
-}
-
-static int
-ctc_netdev_register(struct net_device * dev)
-{
- return register_netdev(dev);
-}
-
-static void
-ctc_free_netdevice(struct net_device * dev, int free_dev)
-{
- struct ctc_priv *privptr;
- if (!dev)
- return;
- privptr = dev->priv;
- if (privptr) {
- if (privptr->fsm)
- kfree_fsm(privptr->fsm);
- kfree(privptr);
- }
-#ifdef MODULE
- if (free_dev)
- free_netdev(dev);
-#endif
-}
-
-static ssize_t
-ctc_proto_show(struct device *dev, struct device_attribute *attr, char *buf)
-{
- struct ctc_priv *priv;
-
- priv = dev->driver_data;
- if (!priv)
- return -ENODEV;
-
- return sprintf(buf, "%d\n", priv->protocol);
-}
-
-static ssize_t
-ctc_proto_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
-{
- struct ctc_priv *priv;
- int value;
-
- DBF_TEXT(trace, 3, __FUNCTION__);
- pr_debug("%s() called\n", __FUNCTION__);
-
- priv = dev->driver_data;
- if (!priv)
- return -ENODEV;
- sscanf(buf, "%u", &value);
- if (!((value == CTC_PROTO_S390) ||
- (value == CTC_PROTO_LINUX) ||
- (value == CTC_PROTO_OS390)))
- return -EINVAL;
- priv->protocol = value;
-
- return count;
-}
-
-static ssize_t
-ctc_type_show(struct device *dev, struct device_attribute *attr, char *buf)
-{
- struct ccwgroup_device *cgdev;
-
- cgdev = to_ccwgroupdev(dev);
- if (!cgdev)
- return -ENODEV;
-
- return sprintf(buf, "%s\n", cu3088_type[cgdev->cdev[0]->id.driver_info]);
-}
-
-static DEVICE_ATTR(buffer, 0644, buffer_show, buffer_write);
-static DEVICE_ATTR(protocol, 0644, ctc_proto_show, ctc_proto_store);
-static DEVICE_ATTR(type, 0444, ctc_type_show, NULL);
-
-static DEVICE_ATTR(loglevel, 0644, loglevel_show, loglevel_write);
-static DEVICE_ATTR(stats, 0644, stats_show, stats_write);
-
-static struct attribute *ctc_attr[] = {
- &dev_attr_protocol.attr,
- &dev_attr_type.attr,
- &dev_attr_buffer.attr,
- NULL,
-};
-
-static struct attribute_group ctc_attr_group = {
- .attrs = ctc_attr,
-};
-
-static int
-ctc_add_attributes(struct device *dev)
-{
- int rc;
-
- rc = device_create_file(dev, &dev_attr_loglevel);
- if (rc)
- goto out;
- rc = device_create_file(dev, &dev_attr_stats);
- if (!rc)
- goto out;
- device_remove_file(dev, &dev_attr_loglevel);
-out:
- return rc;
-}
-
-static void
-ctc_remove_attributes(struct device *dev)
-{
- device_remove_file(dev, &dev_attr_stats);
- device_remove_file(dev, &dev_attr_loglevel);
-}
-
-static int
-ctc_add_files(struct device *dev)
-{
- pr_debug("%s() called\n", __FUNCTION__);
-
- return sysfs_create_group(&dev->kobj, &ctc_attr_group);
-}
-
-static void
-ctc_remove_files(struct device *dev)
-{
- pr_debug("%s() called\n", __FUNCTION__);
-
- sysfs_remove_group(&dev->kobj, &ctc_attr_group);
-}
-
-/**
- * Add ctc specific attributes.
- * Add ctc private data.
- *
- * @param cgdev pointer to ccwgroup_device just added
- *
- * @returns 0 on success, !0 on failure.
- */
-static int
-ctc_probe_device(struct ccwgroup_device *cgdev)
-{
- struct ctc_priv *priv;
- int rc;
- char buffer[16];
-
- pr_debug("%s() called\n", __FUNCTION__);
- DBF_TEXT(setup, 3, __FUNCTION__);
-
- if (!get_device(&cgdev->dev))
- return -ENODEV;
-
- priv = kzalloc(sizeof(struct ctc_priv), GFP_KERNEL);
- if (!priv) {
- ctc_pr_err("%s: Out of memory\n", __func__);
- put_device(&cgdev->dev);
- return -ENOMEM;
- }
-
- rc = ctc_add_files(&cgdev->dev);
- if (rc) {
- kfree(priv);
- put_device(&cgdev->dev);
- return rc;
- }
- priv->buffer_size = CTC_BUFSIZE_DEFAULT;
- cgdev->cdev[0]->handler = ctc_irq_handler;
- cgdev->cdev[1]->handler = ctc_irq_handler;
- cgdev->dev.driver_data = priv;
-
- sprintf(buffer, "%p", priv);
- DBF_TEXT(data, 3, buffer);
-
- sprintf(buffer, "%u", (unsigned int)sizeof(struct ctc_priv));
- DBF_TEXT(data, 3, buffer);
-
- sprintf(buffer, "%p", &channels);
- DBF_TEXT(data, 3, buffer);
-
- sprintf(buffer, "%u", (unsigned int)sizeof(struct channel));
- DBF_TEXT(data, 3, buffer);
-
- return 0;
-}
-
-/**
- * Device setup function called by alloc_netdev().
- *
- * @param dev Device to be setup.
- */
-void ctc_init_netdevice(struct net_device * dev)
-{
- DBF_TEXT(setup, 3, __FUNCTION__);
-
- if (dev->mtu == 0)
- dev->mtu = CTC_BUFSIZE_DEFAULT - LL_HEADER_LENGTH - 2;
- dev->hard_start_xmit = ctc_tx;
- dev->open = ctc_open;
- dev->stop = ctc_close;
- dev->get_stats = ctc_stats;
- dev->change_mtu = ctc_change_mtu;
- dev->hard_header_len = LL_HEADER_LENGTH + 2;
- dev->addr_len = 0;
- dev->type = ARPHRD_SLIP;
- dev->tx_queue_len = 100;
- dev->flags = IFF_POINTOPOINT | IFF_NOARP;
-}
-
-
-/**
- *
- * Setup an interface.
- *
- * @param cgdev Device to be setup.
- *
- * @returns 0 on success, !0 on failure.
- */
-static int
-ctc_new_device(struct ccwgroup_device *cgdev)
-{
- char read_id[CTC_ID_SIZE];
- char write_id[CTC_ID_SIZE];
- int direction;
- enum channel_types type;
- struct ctc_priv *privptr;
- struct net_device *dev;
- int ret;
- char buffer[16];
-
- pr_debug("%s() called\n", __FUNCTION__);
- DBF_TEXT(setup, 3, __FUNCTION__);
-
- privptr = cgdev->dev.driver_data;
- if (!privptr)
- return -ENODEV;
-
- sprintf(buffer, "%d", privptr->buffer_size);
- DBF_TEXT(setup, 3, buffer);
-
- type = get_channel_type(&cgdev->cdev[0]->id);
-
- snprintf(read_id, CTC_ID_SIZE, "ch-%s", cgdev->cdev[0]->dev.bus_id);
- snprintf(write_id, CTC_ID_SIZE, "ch-%s", cgdev->cdev[1]->dev.bus_id);
-
- if (add_channel(cgdev->cdev[0], type))
- return -ENOMEM;
- if (add_channel(cgdev->cdev[1], type))
- return -ENOMEM;
-
- ret = ccw_device_set_online(cgdev->cdev[0]);
- if (ret != 0) {
- printk(KERN_WARNING
- "ccw_device_set_online (cdev[0]) failed with ret = %d\n", ret);
- }
-
- ret = ccw_device_set_online(cgdev->cdev[1]);
- if (ret != 0) {
- printk(KERN_WARNING
- "ccw_device_set_online (cdev[1]) failed with ret = %d\n", ret);
- }
-
- dev = alloc_netdev(0, "ctc%d", ctc_init_netdevice);
- if (!dev) {
- ctc_pr_warn("ctc_init_netdevice failed\n");
- goto out;
- }
- dev->priv = privptr;
-
- privptr->fsm = init_fsm("ctcdev", dev_state_names,
- dev_event_names, CTC_NR_DEV_STATES, CTC_NR_DEV_EVENTS,
- dev_fsm, DEV_FSM_LEN, GFP_KERNEL);
- if (privptr->fsm == NULL) {
- free_netdev(dev);
- goto out;
- }
- fsm_newstate(privptr->fsm, DEV_STATE_STOPPED);
- fsm_settimer(privptr->fsm, &privptr->restart_timer);
-
- for (direction = READ; direction <= WRITE; direction++) {
- privptr->channel[direction] =
- channel_get(type, direction == READ ? read_id : write_id,
- direction);
- if (privptr->channel[direction] == NULL) {
- if (direction == WRITE)
- channel_free(privptr->channel[READ]);
-
- ctc_free_netdevice(dev, 1);
- goto out;
- }
- privptr->channel[direction]->netdev = dev;
- privptr->channel[direction]->protocol = privptr->protocol;
- privptr->channel[direction]->max_bufsize = privptr->buffer_size;
- }
- /* sysfs magic */
- SET_NETDEV_DEV(dev, &cgdev->dev);
-
- if (ctc_netdev_register(dev) != 0) {
- ctc_free_netdevice(dev, 1);
- goto out;
- }
-
- if (ctc_add_attributes(&cgdev->dev)) {
- ctc_netdev_unregister(dev);
- dev->priv = NULL;
- ctc_free_netdevice(dev, 1);
- goto out;
- }
-
- strlcpy(privptr->fsm->name, dev->name, sizeof (privptr->fsm->name));
-
- print_banner();
-
- ctc_pr_info("%s: read: %s, write: %s, proto: %d\n",
- dev->name, privptr->channel[READ]->id,
- privptr->channel[WRITE]->id, privptr->protocol);
-
- return 0;
-out:
- ccw_device_set_offline(cgdev->cdev[1]);
- ccw_device_set_offline(cgdev->cdev[0]);
-
- return -ENODEV;
-}
-
-/**
- * Shutdown an interface.
- *
- * @param cgdev Device to be shut down.
- *
- * @returns 0 on success, !0 on failure.
- */
-static int
-ctc_shutdown_device(struct ccwgroup_device *cgdev)
-{
- struct ctc_priv *priv;
- struct net_device *ndev;
-
- DBF_TEXT(setup, 3, __FUNCTION__);
- pr_debug("%s() called\n", __FUNCTION__);
-
-
- priv = cgdev->dev.driver_data;
- ndev = NULL;
- if (!priv)
- return -ENODEV;
-
- if (priv->channel[READ]) {
- ndev = priv->channel[READ]->netdev;
-
- /* Close the device */
- ctc_close(ndev);
- ndev->flags &=~IFF_RUNNING;
-
- ctc_remove_attributes(&cgdev->dev);
-
- channel_free(priv->channel[READ]);
- }
- if (priv->channel[WRITE])
- channel_free(priv->channel[WRITE]);
-
- if (ndev) {
- ctc_netdev_unregister(ndev);
- ndev->priv = NULL;
- ctc_free_netdevice(ndev, 1);
- }
-
- if (priv->fsm)
- kfree_fsm(priv->fsm);
-
- ccw_device_set_offline(cgdev->cdev[1]);
- ccw_device_set_offline(cgdev->cdev[0]);
-
- if (priv->channel[READ])
- channel_remove(priv->channel[READ]);
- if (priv->channel[WRITE])
- channel_remove(priv->channel[WRITE]);
- priv->channel[READ] = priv->channel[WRITE] = NULL;
-
- return 0;
-
-}
-
-static void
-ctc_remove_device(struct ccwgroup_device *cgdev)
-{
- struct ctc_priv *priv;
-
- pr_debug("%s() called\n", __FUNCTION__);
- DBF_TEXT(setup, 3, __FUNCTION__);
-
- priv = cgdev->dev.driver_data;
- if (!priv)
- return;
- if (cgdev->state == CCWGROUP_ONLINE)
- ctc_shutdown_device(cgdev);
- ctc_remove_files(&cgdev->dev);
- cgdev->dev.driver_data = NULL;
- kfree(priv);
- put_device(&cgdev->dev);
-}
-
-static struct ccwgroup_driver ctc_group_driver = {
- .owner = THIS_MODULE,
- .name = "ctc",
- .max_slaves = 2,
- .driver_id = 0xC3E3C3,
- .probe = ctc_probe_device,
- .remove = ctc_remove_device,
- .set_online = ctc_new_device,
- .set_offline = ctc_shutdown_device,
-};
-
-/**
- * Module related routines
- *****************************************************************************/
-
-/**
- * Prepare to be unloaded. Free IRQ's and release all resources.
- * This is called just before this module is unloaded. It is
- * <em>not</em> called, if the usage count is !0, so we don't need to check
- * for that.
- */
-static void __exit
-ctc_exit(void)
-{
- DBF_TEXT(setup, 3, __FUNCTION__);
- unregister_cu3088_discipline(&ctc_group_driver);
- ctc_unregister_dbf_views();
- ctc_pr_info("CTC driver unloaded\n");
-}
-
-/**
- * Initialize module.
- * This is called just after the module is loaded.
- *
- * @return 0 on success, !0 on error.
- */
-static int __init
-ctc_init(void)
-{
- int ret = 0;
-
- loglevel = CTC_LOGLEVEL_DEFAULT;
-
- DBF_TEXT(setup, 3, __FUNCTION__);
-
- print_banner();
-
- ret = ctc_register_dbf_views();
- if (ret){
- ctc_pr_crit("ctc_init failed with ctc_register_dbf_views rc = %d\n", ret);
- return ret;
- }
- ret = register_cu3088_discipline(&ctc_group_driver);
- if (ret) {
- ctc_unregister_dbf_views();
- }
- return ret;
-}
-
-module_init(ctc_init);
-module_exit(ctc_exit);
-
-/* --- This is the END my friend --- */
diff --git a/drivers/s390/net/ctcmain.h b/drivers/s390/net/ctcmain.h
deleted file mode 100644
index 7f305d119f3..00000000000
--- a/drivers/s390/net/ctcmain.h
+++ /dev/null
@@ -1,270 +0,0 @@
-/*
- * CTC / ESCON network driver
- *
- * Copyright (C) 2001 IBM Deutschland Entwicklung GmbH, IBM Corporation
- * Author(s): Fritz Elfert (elfert@de.ibm.com, felfert@millenux.com)
- Peter Tiedemann (ptiedem@de.ibm.com)
- *
- *
- * Documentation used:
- * - Principles of Operation (IBM doc#: SA22-7201-06)
- * - Common IO/-Device Commands and Self Description (IBM doc#: SA22-7204-02)
- * - Common IO/-Device Commands and Self Description (IBM doc#: SN22-5535)
- * - ESCON Channel-to-Channel Adapter (IBM doc#: SA22-7203-00)
- * - ESCON I/O Interface (IBM doc#: SA22-7202-029
- *
- * 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.
- *
- */
-
-#ifndef _CTCMAIN_H_
-#define _CTCMAIN_H_
-
-#include <asm/ccwdev.h>
-#include <asm/ccwgroup.h>
-
-#include <linux/skbuff.h>
-#include <linux/netdevice.h>
-
-#include "fsm.h"
-#include "cu3088.h"
-
-
-/**
- * CCW commands, used in this driver.
- */
-#define CCW_CMD_WRITE 0x01
-#define CCW_CMD_READ 0x02
-#define CCW_CMD_SET_EXTENDED 0xc3
-#define CCW_CMD_PREPARE 0xe3
-
-#define CTC_PROTO_S390 0
-#define CTC_PROTO_LINUX 1
-#define CTC_PROTO_OS390 3
-
-#define CTC_BUFSIZE_LIMIT 65535
-#define CTC_BUFSIZE_DEFAULT 32768
-
-#define CTC_TIMEOUT_5SEC 5000
-
-#define CTC_INITIAL_BLOCKLEN 2
-
-#define READ 0
-#define WRITE 1
-
-#define CTC_ID_SIZE BUS_ID_SIZE+3
-
-
-struct ctc_profile {
- unsigned long maxmulti;
- unsigned long maxcqueue;
- unsigned long doios_single;
- unsigned long doios_multi;
- unsigned long txlen;
- unsigned long tx_time;
- struct timespec send_stamp;
-};
-
-/**
- * Definition of one channel
- */
-struct channel {
-
- /**
- * Pointer to next channel in list.
- */
- struct channel *next;
- char id[CTC_ID_SIZE];
- struct ccw_device *cdev;
-
- /**
- * Type of this channel.
- * CTC/A or Escon for valid channels.
- */
- enum channel_types type;
-
- /**
- * Misc. flags. See CHANNEL_FLAGS_... below
- */
- __u32 flags;
-
- /**
- * The protocol of this channel
- */
- __u16 protocol;
-
- /**
- * I/O and irq related stuff
- */
- struct ccw1 *ccw;
- struct irb *irb;
-
- /**
- * RX/TX buffer size
- */
- int max_bufsize;
-
- /**
- * Transmit/Receive buffer.
- */
- struct sk_buff *trans_skb;
-
- /**
- * Universal I/O queue.
- */
- struct sk_buff_head io_queue;
-
- /**
- * TX queue for collecting skb's during busy.
- */
- struct sk_buff_head collect_queue;
-
- /**
- * Amount of data in collect_queue.
- */
- int collect_len;
-
- /**
- * spinlock for collect_queue and collect_len
- */
- spinlock_t collect_lock;
-
- /**
- * Timer for detecting unresposive
- * I/O operations.
- */
- fsm_timer timer;
-
- /**
- * Retry counter for misc. operations.
- */
- int retry;
-
- /**
- * The finite state machine of this channel
- */
- fsm_instance *fsm;
-
- /**
- * The corresponding net_device this channel
- * belongs to.
- */
- struct net_device *netdev;
-
- struct ctc_profile prof;
-
- unsigned char *trans_skb_data;
-
- __u16 logflags;
-};
-
-#define CHANNEL_FLAGS_READ 0
-#define CHANNEL_FLAGS_WRITE 1
-#define CHANNEL_FLAGS_INUSE 2
-#define CHANNEL_FLAGS_BUFSIZE_CHANGED 4
-#define CHANNEL_FLAGS_FAILED 8
-#define CHANNEL_FLAGS_WAITIRQ 16
-#define CHANNEL_FLAGS_RWMASK 1
-#define CHANNEL_DIRECTION(f) (f & CHANNEL_FLAGS_RWMASK)
-
-#define LOG_FLAG_ILLEGALPKT 1
-#define LOG_FLAG_ILLEGALSIZE 2
-#define LOG_FLAG_OVERRUN 4
-#define LOG_FLAG_NOMEM 8
-
-#define CTC_LOGLEVEL_INFO 1
-#define CTC_LOGLEVEL_NOTICE 2
-#define CTC_LOGLEVEL_WARN 4
-#define CTC_LOGLEVEL_EMERG 8
-#define CTC_LOGLEVEL_ERR 16
-#define CTC_LOGLEVEL_DEBUG 32
-#define CTC_LOGLEVEL_CRIT 64
-
-#define CTC_LOGLEVEL_DEFAULT \
-(CTC_LOGLEVEL_INFO | CTC_LOGLEVEL_NOTICE | CTC_LOGLEVEL_WARN | CTC_LOGLEVEL_CRIT)
-
-#define CTC_LOGLEVEL_MAX ((CTC_LOGLEVEL_CRIT<<1)-1)
-
-#define ctc_pr_debug(fmt, arg...) \
-do { if (loglevel & CTC_LOGLEVEL_DEBUG) printk(KERN_DEBUG fmt,##arg); } while (0)
-
-#define ctc_pr_info(fmt, arg...) \
-do { if (loglevel & CTC_LOGLEVEL_INFO) printk(KERN_INFO fmt,##arg); } while (0)
-
-#define ctc_pr_notice(fmt, arg...) \
-do { if (loglevel & CTC_LOGLEVEL_NOTICE) printk(KERN_NOTICE fmt,##arg); } while (0)
-
-#define ctc_pr_warn(fmt, arg...) \
-do { if (loglevel & CTC_LOGLEVEL_WARN) printk(KERN_WARNING fmt,##arg); } while (0)
-
-#define ctc_pr_emerg(fmt, arg...) \
-do { if (loglevel & CTC_LOGLEVEL_EMERG) printk(KERN_EMERG fmt,##arg); } while (0)
-
-#define ctc_pr_err(fmt, arg...) \
-do { if (loglevel & CTC_LOGLEVEL_ERR) printk(KERN_ERR fmt,##arg); } while (0)
-
-#define ctc_pr_crit(fmt, arg...) \
-do { if (loglevel & CTC_LOGLEVEL_CRIT) printk(KERN_CRIT fmt,##arg); } while (0)
-
-struct ctc_priv {
- struct net_device_stats stats;
- unsigned long tbusy;
- /**
- * The finite state machine of this interface.
- */
- fsm_instance *fsm;
- /**
- * The protocol of this device
- */
- __u16 protocol;
- /**
- * Timer for restarting after I/O Errors
- */
- fsm_timer restart_timer;
-
- int buffer_size;
-
- struct channel *channel[2];
-};
-
-/**
- * Definition of our link level header.
- */
-struct ll_header {
- __u16 length;
- __u16 type;
- __u16 unused;
-};
-#define LL_HEADER_LENGTH (sizeof(struct ll_header))
-
-/**
- * Compatibility macros for busy handling
- * of network devices.
- */
-static __inline__ void
-ctc_clear_busy(struct net_device * dev)
-{
- clear_bit(0, &(((struct ctc_priv *) dev->priv)->tbusy));
- netif_wake_queue(dev);
-}
-
-static __inline__ int
-ctc_test_and_set_busy(struct net_device * dev)
-{
- netif_stop_queue(dev);
- return test_and_set_bit(0, &((struct ctc_priv *) dev->priv)->tbusy);
-}
-
-#endif
diff --git a/drivers/s390/net/cu3088.c b/drivers/s390/net/cu3088.c
deleted file mode 100644
index 76728ae4b84..00000000000
--- a/drivers/s390/net/cu3088.c
+++ /dev/null
@@ -1,167 +0,0 @@
-/*
- * CTC / LCS ccw_device driver
- *
- * Copyright (C) 2002 IBM Deutschland Entwicklung GmbH, IBM Corporation
- * Author(s): Arnd Bergmann <arndb@de.ibm.com>
- * Cornelia Huck <cornelia.huck@de.ibm.com>
- *
- * 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.
- *
- */
-
-#include <linux/init.h>
-#include <linux/module.h>
-#include <linux/err.h>
-
-#include <asm/s390_rdev.h>
-#include <asm/ccwdev.h>
-#include <asm/ccwgroup.h>
-
-#include "cu3088.h"
-
-const char *cu3088_type[] = {
- "not a channel",
- "CTC/A",
- "ESCON channel",
- "FICON channel",
- "P390 LCS card",
- "OSA LCS card",
- "CLAW channel device",
- "unknown channel type",
- "unsupported channel type",
-};
-
-/* static definitions */
-
-static struct ccw_device_id cu3088_ids[] = {
- { CCW_DEVICE(0x3088, 0x08), .driver_info = channel_type_parallel },
- { CCW_DEVICE(0x3088, 0x1f), .driver_info = channel_type_escon },
- { CCW_DEVICE(0x3088, 0x1e), .driver_info = channel_type_ficon },
- { CCW_DEVICE(0x3088, 0x01), .driver_info = channel_type_p390 },
- { CCW_DEVICE(0x3088, 0x60), .driver_info = channel_type_osa2 },
- { CCW_DEVICE(0x3088, 0x61), .driver_info = channel_type_claw },
- { /* end of list */ }
-};
-
-static struct ccw_driver cu3088_driver;
-
-static struct device *cu3088_root_dev;
-
-static ssize_t
-group_write(struct device_driver *drv, const char *buf, size_t count)
-{
- const char *start, *end;
- char bus_ids[2][BUS_ID_SIZE], *argv[2];
- int i;
- int ret;
- struct ccwgroup_driver *cdrv;
-
- cdrv = to_ccwgroupdrv(drv);
- if (!cdrv)
- return -EINVAL;
- start = buf;
- for (i=0; i<2; 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 + 1);
- strlcpy (bus_ids[i], start, len);
- argv[i] = bus_ids[i];
- start = end + 1;
- }
-
- ret = ccwgroup_create(cu3088_root_dev, cdrv->driver_id,
- &cu3088_driver, 2, argv);
-
- return (ret == 0) ? count : ret;
-}
-
-static DRIVER_ATTR(group, 0200, NULL, group_write);
-
-/* Register-unregister for ctc&lcs */
-int
-register_cu3088_discipline(struct ccwgroup_driver *dcp)
-{
- int rc;
-
- if (!dcp)
- return -EINVAL;
-
- /* Register discipline.*/
- rc = ccwgroup_driver_register(dcp);
- if (rc)
- return rc;
-
- rc = driver_create_file(&dcp->driver, &driver_attr_group);
- if (rc)
- ccwgroup_driver_unregister(dcp);
-
- return rc;
-
-}
-
-void
-unregister_cu3088_discipline(struct ccwgroup_driver *dcp)
-{
- if (!dcp)
- return;
-
- driver_remove_file(&dcp->driver, &driver_attr_group);
- ccwgroup_driver_unregister(dcp);
-}
-
-static struct ccw_driver cu3088_driver = {
- .owner = THIS_MODULE,
- .ids = cu3088_ids,
- .name = "cu3088",
- .probe = ccwgroup_probe_ccwdev,
- .remove = ccwgroup_remove_ccwdev,
-};
-
-/* module setup */
-static int __init
-cu3088_init (void)
-{
- int rc;
-
- cu3088_root_dev = s390_root_dev_register("cu3088");
- if (IS_ERR(cu3088_root_dev))
- return PTR_ERR(cu3088_root_dev);
- rc = ccw_driver_register(&cu3088_driver);
- if (rc)
- s390_root_dev_unregister(cu3088_root_dev);
-
- return rc;
-}
-
-static void __exit
-cu3088_exit (void)
-{
- ccw_driver_unregister(&cu3088_driver);
- s390_root_dev_unregister(cu3088_root_dev);
-}
-
-MODULE_DEVICE_TABLE(ccw,cu3088_ids);
-MODULE_AUTHOR("Arnd Bergmann <arndb@de.ibm.com>");
-MODULE_LICENSE("GPL");
-
-module_init(cu3088_init);
-module_exit(cu3088_exit);
-
-EXPORT_SYMBOL_GPL(cu3088_type);
-EXPORT_SYMBOL_GPL(register_cu3088_discipline);
-EXPORT_SYMBOL_GPL(unregister_cu3088_discipline);
diff --git a/drivers/s390/net/cu3088.h b/drivers/s390/net/cu3088.h
deleted file mode 100644
index 1753661f702..00000000000
--- a/drivers/s390/net/cu3088.h
+++ /dev/null
@@ -1,44 +0,0 @@
-#ifndef _CU3088_H
-#define _CU3088_H
-
-/**
- * Enum for classifying detected devices.
- */
-enum channel_types {
- /* Device is not a channel */
- channel_type_none,
-
- /* Device is a CTC/A */
- channel_type_parallel,
-
- /* Device is a ESCON channel */
- channel_type_escon,
-
- /* Device is a FICON channel */
- channel_type_ficon,
-
- /* Device is a P390 LCS card */
- channel_type_p390,
-
- /* Device is a OSA2 card */
- channel_type_osa2,
-
- /* Device is a CLAW channel device */
- channel_type_claw,
-
- /* Device is a channel, but we don't know
- * anything about it */
- channel_type_unknown,
-
- /* Device is an unsupported model */
- channel_type_unsupported,
-
- /* number of type entries */
- num_channel_types
-};
-
-extern const char *cu3088_type[num_channel_types];
-extern int register_cu3088_discipline(struct ccwgroup_driver *);
-extern void unregister_cu3088_discipline(struct ccwgroup_driver *);
-
-#endif
diff --git a/drivers/s390/net/fsm.c b/drivers/s390/net/fsm.c
index 2c1db8036b7..e5dea67f902 100644
--- a/drivers/s390/net/fsm.c
+++ b/drivers/s390/net/fsm.c
@@ -5,6 +5,7 @@
#include "fsm.h"
#include <linux/module.h>
+#include <linux/slab.h>
#include <linux/timer.h>
MODULE_AUTHOR("(C) 2000 IBM Corp. by Fritz Elfert (felfert@millenux.com)");
@@ -27,6 +28,7 @@ init_fsm(char *name, const char **state_names, const char **event_names, int nr_
return NULL;
}
strlcpy(this->name, name, sizeof(this->name));
+ init_waitqueue_head(&this->wait_q);
f = kzalloc(sizeof(fsm), order);
if (f == NULL) {
diff --git a/drivers/s390/net/fsm.h b/drivers/s390/net/fsm.h
index af679c10f1b..a4510cf5903 100644
--- a/drivers/s390/net/fsm.h
+++ b/drivers/s390/net/fsm.h
@@ -8,7 +8,7 @@
#include <linux/slab.h>
#include <linux/sched.h>
#include <linux/string.h>
-#include <asm/atomic.h>
+#include <linux/atomic.h>
/**
* Define this to get debugging messages.
@@ -66,6 +66,7 @@ typedef struct fsm_instance_t {
char name[16];
void *userdata;
int userint;
+ wait_queue_head_t wait_q;
#if FSM_DEBUG_HISTORY
int history_index;
int history_size;
@@ -197,6 +198,7 @@ fsm_newstate(fsm_instance *fi, int newstate)
printk(KERN_DEBUG "fsm(%s): New state %s\n", fi->name,
fi->f->state_names[newstate]);
#endif
+ wake_up(&fi->wait_q);
}
/**
diff --git a/drivers/s390/net/lcs.c b/drivers/s390/net/lcs.c
index f51ed997258..0a7d87c372b 100644
--- a/drivers/s390/net/lcs.c
+++ b/drivers/s390/net/lcs.c
@@ -1,15 +1,12 @@
/*
- * linux/drivers/s390/net/lcs.c
- *
* Linux for S/390 Lan Channel Station Network Driver
*
- * Copyright (C) 1999-2001 IBM Deutschland Entwicklung GmbH,
- * IBM Corporation
- * Author(s): Original Code written by
- * DJ Barrow (djbarrow@de.ibm.com,barrow_dj@yahoo.com)
- * Rewritten by
- * Frank Pavlic (fpavlic@de.ibm.com) and
- * Martin Schwidefsky <schwidefsky@de.ibm.com>
+ * Copyright IBM Corp. 1999, 2009
+ * Author(s): Original Code written by
+ * DJ Barrow <djbarrow@de.ibm.com,barrow_dj@yahoo.com>
+ * Rewritten by
+ * Frank Pavlic <fpavlic@de.ibm.com> and
+ * Martin Schwidefsky <schwidefsky@de.ibm.com>
*
* 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
@@ -26,16 +23,20 @@
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
+#define KMSG_COMPONENT "lcs"
+#define pr_fmt(fmt) KMSG_COMPONENT ": " fmt
+
#include <linux/module.h>
#include <linux/if.h>
#include <linux/netdevice.h>
#include <linux/etherdevice.h>
-#include <linux/trdevice.h>
#include <linux/fddidevice.h>
#include <linux/inetdevice.h>
#include <linux/in.h>
#include <linux/igmp.h>
#include <linux/delay.h>
+#include <linux/kthread.h>
+#include <linux/slab.h>
#include <net/arp.h>
#include <net/ip.h>
@@ -46,22 +47,22 @@
#include <asm/ccwgroup.h>
#include "lcs.h"
-#include "cu3088.h"
-#if !defined(CONFIG_NET_ETHERNET) && \
- !defined(CONFIG_TR) && !defined(CONFIG_FDDI)
+#if !defined(CONFIG_ETHERNET) && !defined(CONFIG_FDDI)
#error Cannot compile lcs.c without some net devices switched on.
#endif
-#define PRINTK_HEADER " lcs: "
-
/**
* initialization string for output
*/
static char version[] __initdata = "LCS driver";
-static char debug_buffer[255];
+
+/**
+ * the root device for lcs group devices
+ */
+static struct device *lcs_root_dev;
/**
* Some prototypes.
@@ -69,12 +70,15 @@ static char debug_buffer[255];
static void lcs_tasklet(unsigned long);
static void lcs_start_kernel_thread(struct work_struct *);
static void lcs_get_frames_cb(struct lcs_channel *, struct lcs_buffer *);
+#ifdef CONFIG_IP_MULTICAST
static int lcs_send_delipm(struct lcs_card *, struct lcs_ipm_list *);
+#endif /* CONFIG_IP_MULTICAST */
static int lcs_recovery(void *ptr);
/**
* Debug Facility Stuff
*/
+static char debug_buffer[255];
static debug_info_t *lcs_dbf_setup;
static debug_info_t *lcs_dbf_trace;
@@ -96,7 +100,7 @@ lcs_register_debug_facility(void)
lcs_dbf_setup = debug_register("lcs_setup", 2, 1, 8);
lcs_dbf_trace = debug_register("lcs_trace", 4, 1, 8);
if (lcs_dbf_setup == NULL || lcs_dbf_trace == NULL) {
- PRINT_ERR("Not enough memory for debug facility.\n");
+ pr_err("Not enough memory for debug facility.\n");
lcs_unregister_debug_facility();
return -ENOMEM;
}
@@ -278,7 +282,7 @@ lcs_setup_write_ccws(struct lcs_card *card)
LCS_DBF_TEXT(3, setup, "iwritccw");
/* Setup write ccws. */
- memset(card->write.ccws, 0, sizeof(struct ccw1) * LCS_NUM_BUFFS + 1);
+ memset(card->write.ccws, 0, sizeof(struct ccw1) * (LCS_NUM_BUFFS + 1));
for (cnt = 0; cnt < LCS_NUM_BUFFS; cnt++) {
card->write.ccws[cnt].cmd_code = LCS_CCW_WRITE;
card->write.ccws[cnt].count = 0;
@@ -492,7 +496,7 @@ lcs_start_channel(struct lcs_channel *channel)
unsigned long flags;
int rc;
- LCS_DBF_TEXT_(4,trace,"ssch%s", channel->ccwdev->dev.bus_id);
+ LCS_DBF_TEXT_(4, trace,"ssch%s", dev_name(&channel->ccwdev->dev));
spin_lock_irqsave(get_ccwdev_lock(channel->ccwdev), flags);
rc = ccw_device_start(channel->ccwdev,
channel->ccws + channel->io_idx, 0, 0,
@@ -501,8 +505,11 @@ lcs_start_channel(struct lcs_channel *channel)
channel->state = LCS_CH_STATE_RUNNING;
spin_unlock_irqrestore(get_ccwdev_lock(channel->ccwdev), flags);
if (rc) {
- LCS_DBF_TEXT_(4,trace,"essh%s", channel->ccwdev->dev.bus_id);
- PRINT_ERR("Error in starting channel, rc=%d!\n", rc);
+ LCS_DBF_TEXT_(4,trace,"essh%s",
+ dev_name(&channel->ccwdev->dev));
+ dev_err(&channel->ccwdev->dev,
+ "Starting an LCS device resulted in an error,"
+ " rc=%d!\n", rc);
}
return rc;
}
@@ -514,12 +521,13 @@ lcs_clear_channel(struct lcs_channel *channel)
int rc;
LCS_DBF_TEXT(4,trace,"clearch");
- LCS_DBF_TEXT_(4,trace,"%s", channel->ccwdev->dev.bus_id);
+ LCS_DBF_TEXT_(4, trace, "%s", dev_name(&channel->ccwdev->dev));
spin_lock_irqsave(get_ccwdev_lock(channel->ccwdev), flags);
rc = ccw_device_clear(channel->ccwdev, (addr_t) channel);
spin_unlock_irqrestore(get_ccwdev_lock(channel->ccwdev), flags);
if (rc) {
- LCS_DBF_TEXT_(4,trace,"ecsc%s", channel->ccwdev->dev.bus_id);
+ LCS_DBF_TEXT_(4, trace, "ecsc%s",
+ dev_name(&channel->ccwdev->dev));
return rc;
}
wait_event(channel->wait_q, (channel->state == LCS_CH_STATE_CLEARED));
@@ -540,13 +548,14 @@ lcs_stop_channel(struct lcs_channel *channel)
if (channel->state == LCS_CH_STATE_STOPPED)
return 0;
LCS_DBF_TEXT(4,trace,"haltsch");
- LCS_DBF_TEXT_(4,trace,"%s", channel->ccwdev->dev.bus_id);
+ LCS_DBF_TEXT_(4, trace, "%s", dev_name(&channel->ccwdev->dev));
channel->state = LCS_CH_STATE_INIT;
spin_lock_irqsave(get_ccwdev_lock(channel->ccwdev), flags);
rc = ccw_device_halt(channel->ccwdev, (addr_t) channel);
spin_unlock_irqrestore(get_ccwdev_lock(channel->ccwdev), flags);
if (rc) {
- LCS_DBF_TEXT_(4,trace,"ehsc%s", channel->ccwdev->dev.bus_id);
+ LCS_DBF_TEXT_(4, trace, "ehsc%s",
+ dev_name(&channel->ccwdev->dev));
return rc;
}
/* Asynchronous halt initialted. Wait for its completion. */
@@ -632,11 +641,14 @@ __lcs_resume_channel(struct lcs_channel *channel)
return 0;
if (channel->ccws[channel->io_idx].flags & CCW_FLAG_SUSPEND)
return 0;
- LCS_DBF_TEXT_(5, trace, "rsch%s", channel->ccwdev->dev.bus_id);
+ LCS_DBF_TEXT_(5, trace, "rsch%s", dev_name(&channel->ccwdev->dev));
rc = ccw_device_resume(channel->ccwdev);
if (rc) {
- LCS_DBF_TEXT_(4, trace, "ersc%s", channel->ccwdev->dev.bus_id);
- PRINT_ERR("Error in lcs_resume_channel: rc=%d\n",rc);
+ LCS_DBF_TEXT_(4, trace, "ersc%s",
+ dev_name(&channel->ccwdev->dev));
+ dev_err(&channel->ccwdev->dev,
+ "Sending data from the LCS device to the LAN failed"
+ " with rc=%d\n",rc);
} else
channel->state = LCS_CH_STATE_RUNNING;
return rc;
@@ -826,7 +838,7 @@ lcs_notify_lancmd_waiters(struct lcs_card *card, struct lcs_cmd *cmd)
}
/**
- * Emit buffer of a lan comand.
+ * Emit buffer of a lan command.
*/
static void
lcs_lancmd_timeout(unsigned long data)
@@ -880,13 +892,14 @@ lcs_send_lancmd(struct lcs_card *card, struct lcs_buffer *buffer,
rc = lcs_ready_buffer(&card->write, buffer);
if (rc)
return rc;
- init_timer(&timer);
+ init_timer_on_stack(&timer);
timer.function = lcs_lancmd_timeout;
timer.data = (unsigned long) reply;
timer.expires = jiffies + HZ*card->lancmd_timeout;
add_timer(&timer);
wait_event(reply->wait_q, reply->received);
del_timer_sync(&timer);
+ destroy_timer_on_stack(&timer);
LCS_DBF_TEXT_(4, trace, "rc:%d",reply->rc);
rc = reply->rc;
lcs_put_reply(reply);
@@ -1082,7 +1095,7 @@ lcs_check_multicast_support(struct lcs_card *card)
cmd->cmd.lcs_qipassist.num_ip_pairs = 1;
rc = lcs_send_lancmd(card, buffer, __lcs_check_multicast_cb);
if (rc != 0) {
- PRINT_ERR("Query IPAssist failed. Assuming unsupported!\n");
+ pr_err("Query IPAssist failed. Assuming unsupported!\n");
return -EOPNOTSUPP;
}
if (card->ip_assists_supported & LCS_IPASS_MULTICAST_SUPPORT)
@@ -1108,15 +1121,15 @@ list_modified:
list_for_each_entry_safe(ipm, tmp, &card->ipm_list, list){
switch (ipm->ipm_state) {
case LCS_IPM_STATE_SET_REQUIRED:
- /* del from ipm_list so noone else can tamper with
+ /* del from ipm_list so no one else can tamper with
* this entry */
list_del_init(&ipm->list);
spin_unlock_irqrestore(&card->ipm_lock, flags);
rc = lcs_send_setipm(card, ipm);
spin_lock_irqsave(&card->ipm_lock, flags);
if (rc) {
- PRINT_INFO("Adding multicast address failed. "
- "Table possibly full!\n");
+ pr_info("Adding multicast address failed."
+ " Table possibly full!\n");
/* store ipm in failed list -> will be added
* to ipm_list again, so a retry will be done
* during the next call of this function */
@@ -1152,10 +1165,7 @@ static void
lcs_get_mac_for_ipm(__be32 ipm, char *mac, struct net_device *dev)
{
LCS_DBF_TEXT(4,trace, "getmac");
- if (dev->type == ARPHRD_IEEE802_TR)
- ip_tr_mc_map(ipm, mac);
- else
- ip_eth_mc_map(ipm, mac);
+ ip_eth_mc_map(ipm, mac);
}
/**
@@ -1174,7 +1184,8 @@ lcs_remove_mc_addresses(struct lcs_card *card, struct in_device *in4_dev)
spin_lock_irqsave(&card->ipm_lock, flags);
list_for_each(l, &card->ipm_list) {
ipm = list_entry(l, struct lcs_ipm_list, list);
- for (im4 = in4_dev->mc_list; im4 != NULL; im4 = im4->next) {
+ for (im4 = rcu_dereference(in4_dev->mc_list);
+ im4 != NULL; im4 = rcu_dereference(im4->next_rcu)) {
lcs_get_mac_for_ipm(im4->multiaddr, buf, card->dev);
if ( (ipm->ipm.ip_addr == im4->multiaddr) &&
(memcmp(buf, &ipm->ipm.mac_addr,
@@ -1219,16 +1230,16 @@ lcs_set_mc_addresses(struct lcs_card *card, struct in_device *in4_dev)
unsigned long flags;
LCS_DBF_TEXT(4, trace, "setmclst");
- for (im4 = in4_dev->mc_list; im4; im4 = im4->next) {
+ for (im4 = rcu_dereference(in4_dev->mc_list); im4 != NULL;
+ im4 = rcu_dereference(im4->next_rcu)) {
lcs_get_mac_for_ipm(im4->multiaddr, buf, card->dev);
ipm = lcs_check_addr_entry(card, im4, buf);
if (ipm != NULL)
continue; /* Address already in list. */
- ipm = (struct lcs_ipm_list *)
- kzalloc(sizeof(struct lcs_ipm_list), GFP_ATOMIC);
+ ipm = kzalloc(sizeof(struct lcs_ipm_list), GFP_ATOMIC);
if (ipm == NULL) {
- PRINT_INFO("Not enough memory to add "
- "new multicast entry!\n");
+ pr_info("Not enough memory to add"
+ " new multicast entry!\n");
break;
}
memcpy(&ipm->ipm.mac_addr, buf, LCS_MAC_LENGTH);
@@ -1248,7 +1259,6 @@ lcs_register_mc_addresses(void *data)
struct in_device *in4_dev;
card = (struct lcs_card *) data;
- daemonize("regipm");
if (!lcs_do_run_thread(card, LCS_SET_MC_THREAD))
return 0;
@@ -1257,10 +1267,10 @@ lcs_register_mc_addresses(void *data)
in4_dev = in_dev_get(card->dev);
if (in4_dev == NULL)
goto out;
- read_lock(&in4_dev->mc_list_lock);
+ rcu_read_lock();
lcs_remove_mc_addresses(card,in4_dev);
lcs_set_mc_addresses(card, in4_dev);
- read_unlock(&in4_dev->mc_list_lock);
+ rcu_read_unlock();
in_dev_put(in4_dev);
netif_carrier_off(card->dev);
@@ -1276,6 +1286,8 @@ out:
lcs_clear_thread_running_bit(card, LCS_SET_MC_THREAD);
return 0;
}
+#endif /* CONFIG_IP_MULTICAST */
+
/**
* function called by net device to
* handle multicast address relevant things
@@ -1283,16 +1295,16 @@ out:
static void
lcs_set_multicast_list(struct net_device *dev)
{
+#ifdef CONFIG_IP_MULTICAST
struct lcs_card *card;
LCS_DBF_TEXT(4, trace, "setmulti");
- card = (struct lcs_card *) dev->priv;
+ card = (struct lcs_card *) dev->ml_priv;
if (!lcs_set_thread_start_bit(card, LCS_SET_MC_THREAD))
schedule_work(&card->kernel_thread_starter);
-}
-
#endif /* CONFIG_IP_MULTICAST */
+}
static long
lcs_check_irb_error(struct ccw_device *cdev, struct irb *irb)
@@ -1302,18 +1314,21 @@ lcs_check_irb_error(struct ccw_device *cdev, struct irb *irb)
switch (PTR_ERR(irb)) {
case -EIO:
- PRINT_WARN("i/o-error on device %s\n", cdev->dev.bus_id);
+ dev_warn(&cdev->dev,
+ "An I/O-error occurred on the LCS device\n");
LCS_DBF_TEXT(2, trace, "ckirberr");
LCS_DBF_TEXT_(2, trace, " rc%d", -EIO);
break;
case -ETIMEDOUT:
- PRINT_WARN("timeout on device %s\n", cdev->dev.bus_id);
+ dev_warn(&cdev->dev,
+ "A command timed out on the LCS device\n");
LCS_DBF_TEXT(2, trace, "ckirberr");
LCS_DBF_TEXT_(2, trace, " rc%d", -ETIMEDOUT);
break;
default:
- PRINT_WARN("unknown error %ld on device %s\n", PTR_ERR(irb),
- cdev->dev.bus_id);
+ dev_warn(&cdev->dev,
+ "An error occurred on the LCS device, rc=%ld\n",
+ PTR_ERR(irb));
LCS_DBF_TEXT(2, trace, "ckirberr");
LCS_DBF_TEXT(2, trace, " rc???");
}
@@ -1327,8 +1342,8 @@ lcs_get_problem(struct ccw_device *cdev, struct irb *irb)
char *sense;
sense = (char *) irb->ecw;
- cstat = irb->scsw.cstat;
- dstat = irb->scsw.dstat;
+ cstat = irb->scsw.cmd.cstat;
+ dstat = irb->scsw.cmd.dstat;
if (cstat & (SCHN_STAT_CHN_CTRL_CHK | SCHN_STAT_INTF_CTRL_CHK |
SCHN_STAT_CHN_DATA_CHK | SCHN_STAT_CHAIN_CHECK |
@@ -1388,17 +1403,21 @@ lcs_irq(struct ccw_device *cdev, unsigned long intparm, struct irb *irb)
else
channel = &card->write;
- cstat = irb->scsw.cstat;
- dstat = irb->scsw.dstat;
- LCS_DBF_TEXT_(5, trace, "Rint%s",cdev->dev.bus_id);
- LCS_DBF_TEXT_(5, trace, "%4x%4x",irb->scsw.cstat, irb->scsw.dstat);
- LCS_DBF_TEXT_(5, trace, "%4x%4x",irb->scsw.fctl, irb->scsw.actl);
+ cstat = irb->scsw.cmd.cstat;
+ dstat = irb->scsw.cmd.dstat;
+ LCS_DBF_TEXT_(5, trace, "Rint%s", dev_name(&cdev->dev));
+ LCS_DBF_TEXT_(5, trace, "%4x%4x", irb->scsw.cmd.cstat,
+ irb->scsw.cmd.dstat);
+ LCS_DBF_TEXT_(5, trace, "%4x%4x", irb->scsw.cmd.fctl,
+ irb->scsw.cmd.actl);
/* Check for channel and device errors presented */
rc = lcs_get_problem(cdev, irb);
if (rc || (dstat & DEV_STAT_UNIT_EXCEP)) {
- PRINT_WARN("check on device %s, dstat=0x%X, cstat=0x%X \n",
- cdev->dev.bus_id, dstat, cstat);
+ dev_warn(&cdev->dev,
+ "The LCS device stopped because of an error,"
+ " dstat=0x%X, cstat=0x%X \n",
+ dstat, cstat);
if (rc) {
channel->state = LCS_CH_STATE_ERROR;
}
@@ -1410,11 +1429,12 @@ lcs_irq(struct ccw_device *cdev, unsigned long intparm, struct irb *irb)
}
/* How far in the ccw chain have we processed? */
if ((channel->state != LCS_CH_STATE_INIT) &&
- (irb->scsw.fctl & SCSW_FCTL_START_FUNC)) {
- index = (struct ccw1 *) __va((addr_t) irb->scsw.cpa)
+ (irb->scsw.cmd.fctl & SCSW_FCTL_START_FUNC) &&
+ (irb->scsw.cmd.cpa != 0)) {
+ index = (struct ccw1 *) __va((addr_t) irb->scsw.cmd.cpa)
- channel->ccws;
- if ((irb->scsw.actl & SCSW_ACTL_SUSPENDED) ||
- (irb->scsw.cstat & SCHN_STAT_PCI))
+ if ((irb->scsw.cmd.actl & SCSW_ACTL_SUSPENDED) ||
+ (irb->scsw.cmd.cstat & SCHN_STAT_PCI))
/* Bloody io subsystem tells us lies about cpa... */
index = (index - 1) & (LCS_NUM_BUFFS - 1);
while (channel->io_idx != index) {
@@ -1425,25 +1445,24 @@ lcs_irq(struct ccw_device *cdev, unsigned long intparm, struct irb *irb)
}
}
- if ((irb->scsw.dstat & DEV_STAT_DEV_END) ||
- (irb->scsw.dstat & DEV_STAT_CHN_END) ||
- (irb->scsw.dstat & DEV_STAT_UNIT_CHECK))
+ if ((irb->scsw.cmd.dstat & DEV_STAT_DEV_END) ||
+ (irb->scsw.cmd.dstat & DEV_STAT_CHN_END) ||
+ (irb->scsw.cmd.dstat & DEV_STAT_UNIT_CHECK))
/* Mark channel as stopped. */
channel->state = LCS_CH_STATE_STOPPED;
- else if (irb->scsw.actl & SCSW_ACTL_SUSPENDED)
+ else if (irb->scsw.cmd.actl & SCSW_ACTL_SUSPENDED)
/* CCW execution stopped on a suspend bit. */
channel->state = LCS_CH_STATE_SUSPENDED;
- if (irb->scsw.fctl & SCSW_FCTL_HALT_FUNC) {
- if (irb->scsw.cc != 0) {
+ if (irb->scsw.cmd.fctl & SCSW_FCTL_HALT_FUNC) {
+ if (irb->scsw.cmd.cc != 0) {
ccw_device_halt(channel->ccwdev, (addr_t) channel);
return;
}
/* The channel has been stopped by halt_IO. */
channel->state = LCS_CH_STATE_HALTED;
}
- if (irb->scsw.fctl & SCSW_FCTL_CLEAR_FUNC) {
+ if (irb->scsw.cmd.fctl & SCSW_FCTL_CLEAR_FUNC)
channel->state = LCS_CH_STATE_CLEARED;
- }
/* Do the rest in the tasklet. */
tasklet_schedule(&channel->irq_tasklet);
}
@@ -1458,10 +1477,9 @@ lcs_tasklet(unsigned long data)
struct lcs_channel *channel;
struct lcs_buffer *iob;
int buf_idx;
- int rc;
channel = (struct lcs_channel *) data;
- LCS_DBF_TEXT_(5, trace, "tlet%s",channel->ccwdev->dev.bus_id);
+ LCS_DBF_TEXT_(5, trace, "tlet%s", dev_name(&channel->ccwdev->dev));
/* Check for processed buffers. */
iob = channel->iob;
@@ -1475,14 +1493,11 @@ lcs_tasklet(unsigned long data)
channel->buf_idx = buf_idx;
if (channel->state == LCS_CH_STATE_STOPPED)
- // FIXME: what if rc != 0 ??
- rc = lcs_start_channel(channel);
+ lcs_start_channel(channel);
spin_lock_irqsave(get_ccwdev_lock(channel->ccwdev), flags);
if (channel->state == LCS_CH_STATE_SUSPENDED &&
- channel->iob[channel->io_idx].state == LCS_BUF_STATE_READY) {
- // FIXME: what if rc != 0 ??
- rc = __lcs_resume_channel(channel);
- }
+ channel->iob[channel->io_idx].state == LCS_BUF_STATE_READY)
+ __lcs_resume_channel(channel);
spin_unlock_irqrestore(get_ccwdev_lock(channel->ccwdev), flags);
/* Something happened on the channel. Wake up waiters. */
@@ -1536,24 +1551,24 @@ __lcs_start_xmit(struct lcs_card *card, struct sk_buff *skb,
struct net_device *dev)
{
struct lcs_header *header;
- int rc = 0;
+ int rc = NETDEV_TX_OK;
LCS_DBF_TEXT(5, trace, "hardxmit");
if (skb == NULL) {
card->stats.tx_dropped++;
card->stats.tx_errors++;
- return -EIO;
+ return NETDEV_TX_OK;
}
if (card->state != DEV_STATE_UP) {
dev_kfree_skb(skb);
card->stats.tx_dropped++;
card->stats.tx_errors++;
card->stats.tx_carrier_errors++;
- return 0;
+ return NETDEV_TX_OK;
}
if (skb->protocol == htons(ETH_P_IPV6)) {
dev_kfree_skb(skb);
- return 0;
+ return NETDEV_TX_OK;
}
netif_stop_queue(card->dev);
spin_lock(&card->lock);
@@ -1567,7 +1582,7 @@ __lcs_start_xmit(struct lcs_card *card, struct sk_buff *skb,
card->tx_buffer = lcs_get_buffer(&card->write);
if (card->tx_buffer == NULL) {
card->stats.tx_dropped++;
- rc = -EBUSY;
+ rc = NETDEV_TX_BUSY;
goto out;
}
card->tx_buffer->callback = lcs_txbuffer_cb;
@@ -1601,7 +1616,7 @@ lcs_start_xmit(struct sk_buff *skb, struct net_device *dev)
int rc;
LCS_DBF_TEXT(5, trace, "pktxmit");
- card = (struct lcs_card *) dev->priv;
+ card = (struct lcs_card *) dev->ml_priv;
rc = __lcs_start_xmit(card, skb, dev);
return rc;
}
@@ -1615,19 +1630,13 @@ lcs_startlan_auto(struct lcs_card *card)
int rc;
LCS_DBF_TEXT(2, trace, "strtauto");
-#ifdef CONFIG_NET_ETHERNET
+#ifdef CONFIG_ETHERNET
card->lan_type = LCS_FRAME_TYPE_ENET;
rc = lcs_send_startlan(card, LCS_INITIATOR_TCPIP);
if (rc == 0)
return 0;
#endif
-#ifdef CONFIG_TR
- card->lan_type = LCS_FRAME_TYPE_TR;
- rc = lcs_send_startlan(card, LCS_INITIATOR_TCPIP);
- if (rc == 0)
- return 0;
-#endif
#ifdef CONFIG_FDDI
card->lan_type = LCS_FRAME_TYPE_FDDI;
rc = lcs_send_startlan(card, LCS_INITIATOR_TCPIP);
@@ -1733,11 +1742,10 @@ lcs_start_kernel_thread(struct work_struct *work)
struct lcs_card *card = container_of(work, struct lcs_card, kernel_thread_starter);
LCS_DBF_TEXT(5, trace, "krnthrd");
if (lcs_do_start_thread(card, LCS_RECOVERY_THREAD))
- kernel_thread(lcs_recovery, (void *) card, SIGCHLD);
+ kthread_run(lcs_recovery, card, "lcs_recover");
#ifdef CONFIG_IP_MULTICAST
if (lcs_do_start_thread(card, LCS_SET_MC_THREAD))
- kernel_thread(lcs_register_mc_addresses,
- (void *) card, SIGCHLD);
+ kthread_run(lcs_register_mc_addresses, card, "regipm");
#endif
}
@@ -1755,13 +1763,13 @@ lcs_get_control(struct lcs_card *card, struct lcs_cmd *cmd)
lcs_schedule_recovery(card);
break;
case LCS_CMD_STOPLAN:
- PRINT_WARN("Stoplan for %s initiated by LGW.\n",
- card->dev->name);
+ pr_warning("Stoplan for %s initiated by LGW.\n",
+ card->dev->name);
if (card->dev)
netif_carrier_off(card->dev);
break;
default:
- PRINT_INFO("UNRECOGNIZED LGW COMMAND\n");
+ LCS_DBF_TEXT(5, trace, "noLGWcmd");
break;
}
} else
@@ -1784,7 +1792,8 @@ lcs_get_skb(struct lcs_card *card, char *skb_data, unsigned int skb_len)
skb = dev_alloc_skb(skb_len);
if (skb == NULL) {
- PRINT_ERR("LCS: alloc_skb failed for device=%s\n",
+ dev_err(&card->dev->dev,
+ " Allocating a socket buffer to interface %s failed\n",
card->dev->name);
card->stats.rx_dropped++;
return;
@@ -1793,7 +1802,8 @@ lcs_get_skb(struct lcs_card *card, char *skb_data, unsigned int skb_len)
skb->protocol = card->lan_type_trans(skb, card->dev);
card->stats.rx_bytes += skb_len;
card->stats.rx_packets++;
- *((__u32 *)skb->cb) = ++card->pkt_seq;
+ if (skb->protocol == htons(ETH_P_802_2))
+ *((__u32 *)skb->cb) = ++card->pkt_seq;
netif_rx(skb);
}
@@ -1856,7 +1866,7 @@ lcs_getstats(struct net_device *dev)
struct lcs_card *card;
LCS_DBF_TEXT(4, trace, "netstats");
- card = (struct lcs_card *) dev->priv;
+ card = (struct lcs_card *) dev->ml_priv;
return &card->stats;
}
@@ -1871,7 +1881,7 @@ lcs_stop_device(struct net_device *dev)
int rc;
LCS_DBF_TEXT(2, trace, "stopdev");
- card = (struct lcs_card *) dev->priv;
+ card = (struct lcs_card *) dev->ml_priv;
netif_carrier_off(dev);
netif_tx_disable(dev);
dev->flags &= ~IFF_UP;
@@ -1879,7 +1889,8 @@ lcs_stop_device(struct net_device *dev)
(card->write.state != LCS_CH_STATE_RUNNING));
rc = lcs_stopcard(card);
if (rc)
- PRINT_ERR("Try it again!\n ");
+ dev_err(&card->dev->dev,
+ " Shutting down the LCS device failed\n ");
return rc;
}
@@ -1894,11 +1905,11 @@ lcs_open_device(struct net_device *dev)
int rc;
LCS_DBF_TEXT(2, trace, "opendev");
- card = (struct lcs_card *) dev->priv;
+ card = (struct lcs_card *) dev->ml_priv;
/* initialize statistics */
rc = lcs_detect(card);
if (rc) {
- PRINT_ERR("LCS:Error in opening device!\n");
+ pr_err("Error in opening device!\n");
} else {
dev->flags |= IFF_UP;
@@ -1917,7 +1928,7 @@ lcs_portno_show (struct device *dev, struct device_attribute *attr, char *buf)
{
struct lcs_card *card;
- card = (struct lcs_card *)dev->driver_data;
+ card = dev_get_drvdata(dev);
if (!card)
return 0;
@@ -1932,14 +1943,16 @@ static ssize_t
lcs_portno_store (struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
{
struct lcs_card *card;
- int value;
+ int value, rc;
- card = (struct lcs_card *)dev->driver_data;
+ card = dev_get_drvdata(dev);
if (!card)
return 0;
- sscanf(buf, "%u", &value);
+ rc = sscanf(buf, "%d", &value);
+ if (rc != 1)
+ return -EINVAL;
/* TODO: sanity checks */
card->portno = value;
@@ -1949,6 +1962,15 @@ lcs_portno_store (struct device *dev, struct device_attribute *attr, const char
static DEVICE_ATTR(portno, 0644, lcs_portno_show, lcs_portno_store);
+static const char *lcs_type[] = {
+ "not a channel",
+ "2216 parallel",
+ "2216 channel",
+ "OSA LCS card",
+ "unknown channel type",
+ "unsupported channel type",
+};
+
static ssize_t
lcs_type_show(struct device *dev, struct device_attribute *attr, char *buf)
{
@@ -1958,7 +1980,7 @@ lcs_type_show(struct device *dev, struct device_attribute *attr, char *buf)
if (!cgdev)
return -ENODEV;
- return sprintf(buf, "%s\n", cu3088_type[cgdev->cdev[0]->id.driver_info]);
+ return sprintf(buf, "%s\n", lcs_type[cgdev->cdev[0]->id.driver_info]);
}
static DEVICE_ATTR(type, 0444, lcs_type_show, NULL);
@@ -1968,7 +1990,7 @@ lcs_timeout_show(struct device *dev, struct device_attribute *attr, char *buf)
{
struct lcs_card *card;
- card = (struct lcs_card *)dev->driver_data;
+ card = dev_get_drvdata(dev);
return card ? sprintf(buf, "%u\n", card->lancmd_timeout) : 0;
}
@@ -1977,14 +1999,17 @@ static ssize_t
lcs_timeout_store (struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
{
struct lcs_card *card;
- int value;
+ unsigned int value;
+ int rc;
- card = (struct lcs_card *)dev->driver_data;
+ card = dev_get_drvdata(dev);
if (!card)
return 0;
- sscanf(buf, "%u", &value);
+ rc = sscanf(buf, "%u", &value);
+ if (rc != 1)
+ return -EINVAL;
/* TODO: sanity checks */
card->lancmd_timeout = value;
@@ -1998,7 +2023,7 @@ static ssize_t
lcs_dev_recover_store(struct device *dev, struct device_attribute *attr,
const char *buf, size_t count)
{
- struct lcs_card *card = dev->driver_data;
+ struct lcs_card *card = dev_get_drvdata(dev);
char *tmp;
int i;
@@ -2021,10 +2046,17 @@ static struct attribute * lcs_attrs[] = {
&dev_attr_recover.attr,
NULL,
};
-
static struct attribute_group lcs_attr_group = {
.attrs = lcs_attrs,
};
+static const struct attribute_group *lcs_attr_groups[] = {
+ &lcs_attr_group,
+ NULL,
+};
+static const struct device_type lcs_devtype = {
+ .name = "lcs",
+ .groups = lcs_attr_groups,
+};
/**
* lcs_probe_device is called on establishing a new ccwgroup_device.
@@ -2033,7 +2065,6 @@ static int
lcs_probe_device(struct ccwgroup_device *ccwgdev)
{
struct lcs_card *card;
- int ret;
if (!get_device(&ccwgdev->dev))
return -ENODEV;
@@ -2041,18 +2072,11 @@ lcs_probe_device(struct ccwgroup_device *ccwgdev)
LCS_DBF_TEXT(2, setup, "add_dev");
card = lcs_alloc_card();
if (!card) {
- PRINT_ERR("Allocation of lcs card failed\n");
+ LCS_DBF_TEXT_(2, setup, " rc%d", -ENOMEM);
put_device(&ccwgdev->dev);
return -ENOMEM;
}
- ret = sysfs_create_group(&ccwgdev->dev.kobj, &lcs_attr_group);
- if (ret) {
- PRINT_ERR("Creating attributes failed");
- lcs_free_card(card);
- put_device(&ccwgdev->dev);
- return ret;
- }
- ccwgdev->dev.driver_data = card;
+ dev_set_drvdata(&ccwgdev->dev, card);
ccwgdev->cdev[0]->handler = lcs_irq;
ccwgdev->cdev[1]->handler = lcs_irq;
card->gdev = ccwgdev;
@@ -2060,7 +2084,9 @@ lcs_probe_device(struct ccwgroup_device *ccwgdev)
card->thread_start_mask = 0;
card->thread_allowed_mask = 0;
card->thread_running_mask = 0;
- return 0;
+ ccwgdev->dev.type = &lcs_devtype;
+
+ return 0;
}
static int
@@ -2069,7 +2095,7 @@ lcs_register_netdev(struct ccwgroup_device *ccwgdev)
struct lcs_card *card;
LCS_DBF_TEXT(2, setup, "regnetdv");
- card = (struct lcs_card *)ccwgdev->dev.driver_data;
+ card = dev_get_drvdata(&ccwgdev->dev);
if (card->dev->reg_state != NETREG_UNINITIALIZED)
return 0;
SET_NETDEV_DEV(card->dev, &ccwgdev->dev);
@@ -2079,6 +2105,20 @@ lcs_register_netdev(struct ccwgroup_device *ccwgdev)
/**
* lcs_new_device will be called by setting the group device online.
*/
+static const struct net_device_ops lcs_netdev_ops = {
+ .ndo_open = lcs_open_device,
+ .ndo_stop = lcs_stop_device,
+ .ndo_get_stats = lcs_getstats,
+ .ndo_start_xmit = lcs_start_xmit,
+};
+
+static const struct net_device_ops lcs_mc_netdev_ops = {
+ .ndo_open = lcs_open_device,
+ .ndo_stop = lcs_stop_device,
+ .ndo_get_stats = lcs_getstats,
+ .ndo_start_xmit = lcs_start_xmit,
+ .ndo_set_rx_mode = lcs_set_multicast_list,
+};
static int
lcs_new_device(struct ccwgroup_device *ccwgdev)
@@ -2088,7 +2128,7 @@ lcs_new_device(struct ccwgroup_device *ccwgdev)
enum lcs_dev_states recover_state;
int rc;
- card = (struct lcs_card *)ccwgdev->dev.driver_data;
+ card = dev_get_drvdata(&ccwgdev->dev);
if (!card)
return -ENODEV;
@@ -2098,8 +2138,12 @@ lcs_new_device(struct ccwgroup_device *ccwgdev)
card->write.ccwdev = ccwgdev->cdev[1];
recover_state = card->state;
- ccw_device_set_online(card->read.ccwdev);
- ccw_device_set_online(card->write.ccwdev);
+ rc = ccw_device_set_online(card->read.ccwdev);
+ if (rc)
+ goto out_err;
+ rc = ccw_device_set_online(card->write.ccwdev);
+ if (rc)
+ goto out_werr;
LCS_DBF_TEXT(3, setup, "lcsnewdv");
@@ -2107,8 +2151,9 @@ lcs_new_device(struct ccwgroup_device *ccwgdev)
rc = lcs_detect(card);
if (rc) {
LCS_DBF_TEXT(2, setup, "dtctfail");
- PRINT_WARN("Detection of LCS card failed with return code "
- "%d (0x%x)\n", rc, rc);
+ dev_err(&card->dev->dev,
+ "Detecting a network adapter for LCS devices"
+ " failed with rc=%d (0x%x)\n", rc, rc);
lcs_stopcard(card);
goto out;
}
@@ -2118,18 +2163,12 @@ lcs_new_device(struct ccwgroup_device *ccwgdev)
goto netdev_out;
}
switch (card->lan_type) {
-#ifdef CONFIG_NET_ETHERNET
+#ifdef CONFIG_ETHERNET
case LCS_FRAME_TYPE_ENET:
card->lan_type_trans = eth_type_trans;
dev = alloc_etherdev(0);
break;
#endif
-#ifdef CONFIG_TR
- case LCS_FRAME_TYPE_TR:
- card->lan_type_trans = tr_type_trans;
- dev = alloc_trdev(0);
- break;
-#endif
#ifdef CONFIG_FDDI
case LCS_FRAME_TYPE_FDDI:
card->lan_type_trans = fddi_type_trans;
@@ -2138,22 +2177,18 @@ lcs_new_device(struct ccwgroup_device *ccwgdev)
#endif
default:
LCS_DBF_TEXT(3, setup, "errinit");
- PRINT_ERR("LCS: Initialization failed\n");
- PRINT_ERR("LCS: No device found!\n");
+ pr_err(" Initialization failed\n");
goto out;
}
if (!dev)
goto out;
card->dev = dev;
- card->dev->priv = card;
- card->dev->open = lcs_open_device;
- card->dev->stop = lcs_stop_device;
- card->dev->hard_start_xmit = lcs_start_xmit;
- card->dev->get_stats = lcs_getstats;
+ card->dev->ml_priv = card;
+ card->dev->netdev_ops = &lcs_netdev_ops;
memcpy(card->dev->dev_addr, card->mac, LCS_MAC_LENGTH);
#ifdef CONFIG_IP_MULTICAST
if (!lcs_check_multicast_support(card))
- card->dev->set_multicast_list = lcs_set_multicast_list;
+ card->dev->netdev_ops = &lcs_mc_netdev_ops;
#endif
netdev_out:
lcs_set_allowed_threads(card,0xffffffff);
@@ -2171,18 +2206,20 @@ netdev_out:
goto out;
/* Print out supported assists: IPv6 */
- PRINT_INFO("LCS device %s %s IPv6 support\n", card->dev->name,
- (card->ip_assists_supported & LCS_IPASS_IPV6_SUPPORT) ?
- "with" : "without");
+ pr_info("LCS device %s %s IPv6 support\n", card->dev->name,
+ (card->ip_assists_supported & LCS_IPASS_IPV6_SUPPORT) ?
+ "with" : "without");
/* Print out supported assist: Multicast */
- PRINT_INFO("LCS device %s %s Multicast support\n", card->dev->name,
- (card->ip_assists_supported & LCS_IPASS_MULTICAST_SUPPORT) ?
- "with" : "without");
+ pr_info("LCS device %s %s Multicast support\n", card->dev->name,
+ (card->ip_assists_supported & LCS_IPASS_MULTICAST_SUPPORT) ?
+ "with" : "without");
return 0;
out:
- ccw_device_set_offline(card->read.ccwdev);
ccw_device_set_offline(card->write.ccwdev);
+out_werr:
+ ccw_device_set_offline(card->read.ccwdev);
+out_err:
return -ENODEV;
}
@@ -2194,10 +2231,10 @@ __lcs_shutdown_device(struct ccwgroup_device *ccwgdev, int recovery_mode)
{
struct lcs_card *card;
enum lcs_dev_states recover_state;
- int ret;
+ int ret = 0, ret2 = 0, ret3 = 0;
LCS_DBF_TEXT(3, setup, "shtdndev");
- card = (struct lcs_card *)ccwgdev->dev.driver_data;
+ card = dev_get_drvdata(&ccwgdev->dev);
if (!card)
return -ENODEV;
if (recovery_mode == 0) {
@@ -2209,13 +2246,15 @@ __lcs_shutdown_device(struct ccwgroup_device *ccwgdev, int recovery_mode)
recover_state = card->state;
ret = lcs_stop_device(card->dev);
- ret = ccw_device_set_offline(card->read.ccwdev);
- ret = ccw_device_set_offline(card->write.ccwdev);
+ ret2 = ccw_device_set_offline(card->read.ccwdev);
+ ret3 = ccw_device_set_offline(card->write.ccwdev);
+ if (!ret)
+ ret = (ret2) ? ret2 : ret3;
+ if (ret)
+ LCS_DBF_TEXT_(3, setup, "1err:%d", ret);
if (recover_state == DEV_STATE_UP) {
card->state = DEV_STATE_RECOVER;
}
- if (ret)
- return ret;
return 0;
}
@@ -2236,22 +2275,22 @@ lcs_recovery(void *ptr)
int rc;
card = (struct lcs_card *) ptr;
- daemonize("lcs_recover");
LCS_DBF_TEXT(4, trace, "recover1");
if (!lcs_do_run_thread(card, LCS_RECOVERY_THREAD))
return 0;
LCS_DBF_TEXT(4, trace, "recover2");
gdev = card->gdev;
- PRINT_WARN("Recovery of device %s started...\n", gdev->dev.bus_id);
+ dev_warn(&gdev->dev,
+ "A recovery process has been started for the LCS device\n");
rc = __lcs_shutdown_device(gdev, 1);
rc = lcs_new_device(gdev);
if (!rc)
- PRINT_INFO("Device %s successfully recovered!\n",
- card->dev->name);
+ pr_info("Device %s successfully recovered!\n",
+ card->dev->name);
else
- PRINT_INFO("Device %s could not be recovered!\n",
- card->dev->name);
+ pr_info("Device %s could not be recovered!\n",
+ card->dev->name);
lcs_clear_thread_running_bit(card, LCS_RECOVERY_THREAD);
return 0;
}
@@ -2264,11 +2303,10 @@ lcs_remove_device(struct ccwgroup_device *ccwgdev)
{
struct lcs_card *card;
- card = (struct lcs_card *)ccwgdev->dev.driver_data;
+ card = dev_get_drvdata(&ccwgdev->dev);
if (!card)
return;
- PRINT_INFO("Removing lcs group device ....\n");
LCS_DBF_TEXT(3, setup, "remdev");
LCS_DBF_HEX(3, setup, &card, sizeof(void*));
if (ccwgdev->state == CCWGROUP_ONLINE) {
@@ -2276,24 +2314,123 @@ lcs_remove_device(struct ccwgroup_device *ccwgdev)
}
if (card->dev)
unregister_netdev(card->dev);
- sysfs_remove_group(&ccwgdev->dev.kobj, &lcs_attr_group);
lcs_cleanup_card(card);
lcs_free_card(card);
+ dev_set_drvdata(&ccwgdev->dev, NULL);
put_device(&ccwgdev->dev);
}
+static int lcs_pm_suspend(struct lcs_card *card)
+{
+ if (card->dev)
+ netif_device_detach(card->dev);
+ lcs_set_allowed_threads(card, 0);
+ lcs_wait_for_threads(card, 0xffffffff);
+ if (card->state != DEV_STATE_DOWN)
+ __lcs_shutdown_device(card->gdev, 1);
+ return 0;
+}
+
+static int lcs_pm_resume(struct lcs_card *card)
+{
+ int rc = 0;
+
+ if (card->state == DEV_STATE_RECOVER)
+ rc = lcs_new_device(card->gdev);
+ if (card->dev)
+ netif_device_attach(card->dev);
+ if (rc) {
+ dev_warn(&card->gdev->dev, "The lcs device driver "
+ "failed to recover the device\n");
+ }
+ return rc;
+}
+
+static int lcs_prepare(struct ccwgroup_device *gdev)
+{
+ return 0;
+}
+
+static void lcs_complete(struct ccwgroup_device *gdev)
+{
+ return;
+}
+
+static int lcs_freeze(struct ccwgroup_device *gdev)
+{
+ struct lcs_card *card = dev_get_drvdata(&gdev->dev);
+ return lcs_pm_suspend(card);
+}
+
+static int lcs_thaw(struct ccwgroup_device *gdev)
+{
+ struct lcs_card *card = dev_get_drvdata(&gdev->dev);
+ return lcs_pm_resume(card);
+}
+
+static int lcs_restore(struct ccwgroup_device *gdev)
+{
+ struct lcs_card *card = dev_get_drvdata(&gdev->dev);
+ return lcs_pm_resume(card);
+}
+
+static struct ccw_device_id lcs_ids[] = {
+ {CCW_DEVICE(0x3088, 0x08), .driver_info = lcs_channel_type_parallel},
+ {CCW_DEVICE(0x3088, 0x1f), .driver_info = lcs_channel_type_2216},
+ {CCW_DEVICE(0x3088, 0x60), .driver_info = lcs_channel_type_osa2},
+ {},
+};
+MODULE_DEVICE_TABLE(ccw, lcs_ids);
+
+static struct ccw_driver lcs_ccw_driver = {
+ .driver = {
+ .owner = THIS_MODULE,
+ .name = "lcs",
+ },
+ .ids = lcs_ids,
+ .probe = ccwgroup_probe_ccwdev,
+ .remove = ccwgroup_remove_ccwdev,
+ .int_class = IRQIO_LCS,
+};
+
/**
* LCS ccwgroup driver registration
*/
static struct ccwgroup_driver lcs_group_driver = {
- .owner = THIS_MODULE,
- .name = "lcs",
- .max_slaves = 2,
- .driver_id = 0xD3C3E2,
- .probe = lcs_probe_device,
+ .driver = {
+ .owner = THIS_MODULE,
+ .name = "lcs",
+ },
+ .setup = lcs_probe_device,
.remove = lcs_remove_device,
.set_online = lcs_new_device,
.set_offline = lcs_shutdown_device,
+ .prepare = lcs_prepare,
+ .complete = lcs_complete,
+ .freeze = lcs_freeze,
+ .thaw = lcs_thaw,
+ .restore = lcs_restore,
+};
+
+static ssize_t lcs_driver_group_store(struct device_driver *ddrv,
+ const char *buf, size_t count)
+{
+ int err;
+ err = ccwgroup_create_dev(lcs_root_dev, &lcs_group_driver, 2, buf);
+ return err ? err : count;
+}
+static DRIVER_ATTR(group, 0200, NULL, lcs_driver_group_store);
+
+static struct attribute *lcs_drv_attrs[] = {
+ &driver_attr_group.attr,
+ NULL,
+};
+static struct attribute_group lcs_drv_attr_group = {
+ .attrs = lcs_drv_attrs,
+};
+static const struct attribute_group *lcs_drv_attr_groups[] = {
+ &lcs_drv_attr_group,
+ NULL,
};
/**
@@ -2304,20 +2441,33 @@ __init lcs_init_module(void)
{
int rc;
- PRINT_INFO("Loading %s\n",version);
+ pr_info("Loading %s\n", version);
rc = lcs_register_debug_facility();
LCS_DBF_TEXT(0, setup, "lcsinit");
- if (rc) {
- PRINT_ERR("Initialization failed\n");
- return rc;
- }
-
- rc = register_cu3088_discipline(&lcs_group_driver);
- if (rc) {
- PRINT_ERR("Initialization failed\n");
- return rc;
- }
+ if (rc)
+ goto out_err;
+ lcs_root_dev = root_device_register("lcs");
+ rc = PTR_ERR_OR_ZERO(lcs_root_dev);
+ if (rc)
+ goto register_err;
+ rc = ccw_driver_register(&lcs_ccw_driver);
+ if (rc)
+ goto ccw_err;
+ lcs_group_driver.driver.groups = lcs_drv_attr_groups;
+ rc = ccwgroup_driver_register(&lcs_group_driver);
+ if (rc)
+ goto ccwgroup_err;
return 0;
+
+ccwgroup_err:
+ ccw_driver_unregister(&lcs_ccw_driver);
+ccw_err:
+ root_device_unregister(lcs_root_dev);
+register_err:
+ lcs_unregister_debug_facility();
+out_err:
+ pr_err("Initializing the lcs device driver failed\n");
+ return rc;
}
@@ -2327,9 +2477,11 @@ __init lcs_init_module(void)
static void
__exit lcs_cleanup_module(void)
{
- PRINT_INFO("Terminating lcs module.\n");
+ pr_info("Terminating lcs module.\n");
LCS_DBF_TEXT(0, trace, "cleanup");
- unregister_cu3088_discipline(&lcs_group_driver);
+ ccwgroup_driver_unregister(&lcs_group_driver);
+ ccw_driver_unregister(&lcs_ccw_driver);
+ root_device_unregister(lcs_root_dev);
lcs_unregister_debug_facility();
}
diff --git a/drivers/s390/net/lcs.h b/drivers/s390/net/lcs.h
index d58fea52557..150fcb4cebc 100644
--- a/drivers/s390/net/lcs.h
+++ b/drivers/s390/net/lcs.h
@@ -16,15 +16,9 @@ do { \
debug_event(lcs_dbf_##name,level,(void*)(addr),len); \
} while (0)
-/* Allow to sort out low debug levels early to avoid wasted sprints */
-static inline int lcs_dbf_passes(debug_info_t *dbf_grp, int level)
-{
- return (level <= dbf_grp->level);
-}
-
#define LCS_DBF_TEXT_(level,name,text...) \
do { \
- if (lcs_dbf_passes(lcs_dbf_##name, level)) { \
+ if (debug_level_enabled(lcs_dbf_##name, level)) { \
sprintf(debug_buffer, text); \
debug_text_event(lcs_dbf_##name, level, debug_buffer); \
} \
@@ -34,8 +28,26 @@ static inline int lcs_dbf_passes(debug_info_t *dbf_grp, int level)
* sysfs related stuff
*/
#define CARD_FROM_DEV(cdev) \
- (struct lcs_card *) \
- ((struct ccwgroup_device *)cdev->dev.driver_data)->dev.driver_data;
+ (struct lcs_card *) dev_get_drvdata( \
+ &((struct ccwgroup_device *)dev_get_drvdata(&cdev->dev))->dev);
+
+/**
+ * Enum for classifying detected devices.
+ */
+enum lcs_channel_types {
+ /* Device is not a channel */
+ lcs_channel_type_none,
+
+ /* Device is a 2216 channel */
+ lcs_channel_type_parallel,
+
+ /* Device is a 2216 channel */
+ lcs_channel_type_2216,
+
+ /* Device is a OSA2 card */
+ lcs_channel_type_osa2
+};
+
/**
* CCW commands used in this driver
*/
diff --git a/drivers/s390/net/netiucv.c b/drivers/s390/net/netiucv.c
index 874a1999448..ce16d1bdb20 100644
--- a/drivers/s390/net/netiucv.c
+++ b/drivers/s390/net/netiucv.c
@@ -1,11 +1,15 @@
/*
* IUCV network driver
*
- * Copyright 2001 IBM Deutschland Entwicklung GmbH, IBM Corporation
- * Author(s): Fritz Elfert (elfert@de.ibm.com, felfert@millenux.com)
+ * Copyright IBM Corp. 2001, 2009
*
- * Sysfs integration and all bugs therein by Cornelia Huck
- * (cornelia.huck@de.ibm.com)
+ * Author(s):
+ * Original netiucv driver:
+ * Fritz Elfert (elfert@de.ibm.com, felfert@millenux.com)
+ * Sysfs integration and all bugs therein:
+ * Cornelia Huck (cornelia.huck@de.ibm.com)
+ * PM functions:
+ * Ursula Braun (ursula.braun@de.ibm.com)
*
* Documentation used:
* the source of the original IUCV driver by:
@@ -31,6 +35,9 @@
*
*/
+#define KMSG_COMPONENT "netiucv"
+#define pr_fmt(fmt) KMSG_COMPONENT ": " fmt
+
#undef DEBUG
#include <linux/module.h>
@@ -56,6 +63,7 @@
#include <asm/io.h>
#include <asm/uaccess.h>
+#include <asm/ebcdic.h>
#include <net/iucv/iucv.h>
#include "fsm.h"
@@ -68,7 +76,7 @@ MODULE_DESCRIPTION ("Linux for S/390 IUCV network driver");
* Debug Facility stuff
*/
#define IUCV_DBF_SETUP_NAME "iucv_setup"
-#define IUCV_DBF_SETUP_LEN 32
+#define IUCV_DBF_SETUP_LEN 64
#define IUCV_DBF_SETUP_PAGES 2
#define IUCV_DBF_SETUP_NR_AREAS 1
#define IUCV_DBF_SETUP_LEVEL 3
@@ -97,20 +105,12 @@ MODULE_DESCRIPTION ("Linux for S/390 IUCV network driver");
DECLARE_PER_CPU(char[256], iucv_dbf_txt_buf);
-/* Allow to sort out low debug levels early to avoid wasted sprints */
-static inline int iucv_dbf_passes(debug_info_t *dbf_grp, int level)
-{
- return (level <= dbf_grp->level);
-}
-
#define IUCV_DBF_TEXT_(name, level, text...) \
do { \
- if (iucv_dbf_passes(iucv_dbf_##name, level)) { \
- char* iucv_dbf_txt_buf = \
- get_cpu_var(iucv_dbf_txt_buf); \
- sprintf(iucv_dbf_txt_buf, text); \
- debug_text_event(iucv_dbf_##name, level, \
- iucv_dbf_txt_buf); \
+ if (debug_level_enabled(iucv_dbf_##name, level)) { \
+ char* __buf = get_cpu_var(iucv_dbf_txt_buf); \
+ sprintf(__buf, text); \
+ debug_text_event(iucv_dbf_##name, level, __buf); \
put_cpu_var(iucv_dbf_txt_buf); \
} \
} while (0)
@@ -124,32 +124,29 @@ static inline int iucv_dbf_passes(debug_info_t *dbf_grp, int level)
/**
* some more debug stuff
*/
-#define IUCV_HEXDUMP16(importance,header,ptr) \
-PRINT_##importance(header "%02x %02x %02x %02x %02x %02x %02x %02x " \
- "%02x %02x %02x %02x %02x %02x %02x %02x\n", \
- *(((char*)ptr)),*(((char*)ptr)+1),*(((char*)ptr)+2), \
- *(((char*)ptr)+3),*(((char*)ptr)+4),*(((char*)ptr)+5), \
- *(((char*)ptr)+6),*(((char*)ptr)+7),*(((char*)ptr)+8), \
- *(((char*)ptr)+9),*(((char*)ptr)+10),*(((char*)ptr)+11), \
- *(((char*)ptr)+12),*(((char*)ptr)+13), \
- *(((char*)ptr)+14),*(((char*)ptr)+15)); \
-PRINT_##importance(header "%02x %02x %02x %02x %02x %02x %02x %02x " \
- "%02x %02x %02x %02x %02x %02x %02x %02x\n", \
- *(((char*)ptr)+16),*(((char*)ptr)+17), \
- *(((char*)ptr)+18),*(((char*)ptr)+19), \
- *(((char*)ptr)+20),*(((char*)ptr)+21), \
- *(((char*)ptr)+22),*(((char*)ptr)+23), \
- *(((char*)ptr)+24),*(((char*)ptr)+25), \
- *(((char*)ptr)+26),*(((char*)ptr)+27), \
- *(((char*)ptr)+28),*(((char*)ptr)+29), \
- *(((char*)ptr)+30),*(((char*)ptr)+31));
-
#define PRINTK_HEADER " iucv: " /* for debugging */
+/* dummy device to make sure netiucv_pm functions are called */
+static struct device *netiucv_dev;
+
+static int netiucv_pm_prepare(struct device *);
+static void netiucv_pm_complete(struct device *);
+static int netiucv_pm_freeze(struct device *);
+static int netiucv_pm_restore_thaw(struct device *);
+
+static const struct dev_pm_ops netiucv_pm_ops = {
+ .prepare = netiucv_pm_prepare,
+ .complete = netiucv_pm_complete,
+ .freeze = netiucv_pm_freeze,
+ .thaw = netiucv_pm_restore_thaw,
+ .restore = netiucv_pm_restore_thaw,
+};
+
static struct device_driver netiucv_driver = {
.owner = THIS_MODULE,
.name = "netiucv",
.bus = &iucv_bus,
+ .pm = &netiucv_pm_ops,
};
static int netiucv_callback_connreq(struct iucv_path *,
@@ -204,6 +201,7 @@ struct iucv_connection {
struct net_device *netdev;
struct connection_profile prof;
char userid[9];
+ char userdata[17];
};
/**
@@ -230,6 +228,7 @@ struct netiucv_priv {
fsm_instance *fsm;
struct iucv_connection *conn;
struct device *dev;
+ int pm_state;
};
/**
@@ -240,7 +239,7 @@ struct ll_header {
};
#define NETIUCV_HDRLEN (sizeof(struct ll_header))
-#define NETIUCV_BUFSIZE_MAX 32768
+#define NETIUCV_BUFSIZE_MAX 65537
#define NETIUCV_BUFSIZE_DEFAULT NETIUCV_BUFSIZE_MAX
#define NETIUCV_MTU_MAX (NETIUCV_BUFSIZE_MAX - NETIUCV_HDRLEN)
#define NETIUCV_MTU_DEFAULT 9216
@@ -265,7 +264,12 @@ static inline int netiucv_test_and_set_busy(struct net_device *dev)
return test_and_set_bit(0, &priv->tbusy);
}
-static u8 iucvMagic[16] = {
+static u8 iucvMagic_ascii[16] = {
+ 0x30, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+ 0x30, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20
+};
+
+static u8 iucvMagic_ebcdic[16] = {
0xF0, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40,
0xF0, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40
};
@@ -278,18 +282,38 @@ static u8 iucvMagic[16] = {
*
* @returns The printable string (static data!!)
*/
-static char *netiucv_printname(char *name)
+static char *netiucv_printname(char *name, int len)
{
- static char tmp[9];
+ static char tmp[17];
char *p = tmp;
- memcpy(tmp, name, 8);
- tmp[8] = '\0';
- while (*p && (!isspace(*p)))
+ memcpy(tmp, name, len);
+ tmp[len] = '\0';
+ while (*p && ((p - tmp) < len) && (!isspace(*p)))
p++;
*p = '\0';
return tmp;
}
+static char *netiucv_printuser(struct iucv_connection *conn)
+{
+ static char tmp_uid[9];
+ static char tmp_udat[17];
+ static char buf[100];
+
+ if (memcmp(conn->userdata, iucvMagic_ebcdic, 16)) {
+ tmp_uid[8] = '\0';
+ tmp_udat[16] = '\0';
+ memcpy(tmp_uid, conn->userid, 8);
+ memcpy(tmp_uid, netiucv_printname(tmp_uid, 8), 8);
+ memcpy(tmp_udat, conn->userdata, 16);
+ EBCASC(tmp_udat, 16);
+ memcpy(tmp_udat, netiucv_printname(tmp_udat, 16), 16);
+ sprintf(buf, "%s.%s", tmp_uid, tmp_udat);
+ return buf;
+ } else
+ return netiucv_printname(conn->userid, 8);
+}
+
/**
* States of the interface statemachine.
*/
@@ -540,15 +564,18 @@ static int netiucv_callback_connreq(struct iucv_path *path,
{
struct iucv_connection *conn = path->private;
struct iucv_event ev;
+ static char tmp_user[9];
+ static char tmp_udat[17];
int rc;
- if (memcmp(iucvMagic, ipuser, sizeof(ipuser)))
- /* ipuser must match iucvMagic. */
- return -EINVAL;
rc = -EINVAL;
+ memcpy(tmp_user, netiucv_printname(ipvmid, 8), 8);
+ memcpy(tmp_udat, ipuser, 16);
+ EBCASC(tmp_udat, 16);
read_lock_bh(&iucv_connection_rwlock);
list_for_each_entry(conn, &iucv_connection_list, list) {
- if (strncmp(ipvmid, conn->userid, 8))
+ if (strncmp(ipvmid, conn->userid, 8) ||
+ strncmp(ipuser, conn->userdata, 16))
continue;
/* Found a matching connection for this path. */
conn->path = path;
@@ -557,6 +584,8 @@ static int netiucv_callback_connreq(struct iucv_path *path,
fsm_event(conn->fsm, CONN_EVENT_CONN_REQ, &ev);
rc = 0;
}
+ IUCV_DBF_TEXT_(setup, 2, "Connection requested for %s.%s\n",
+ tmp_user, netiucv_printname(tmp_udat, 16));
read_unlock_bh(&iucv_connection_rwlock);
return rc;
}
@@ -625,9 +654,6 @@ static void netiucv_unpack_skb(struct iucv_connection *conn,
offset += header->next;
header->next -= NETIUCV_HDRLEN;
if (skb_tailroom(pskb) < header->next) {
- PRINT_WARN("%s: Illegal next field in iucv header: "
- "%d > %d\n",
- dev->name, header->next, skb_tailroom(pskb));
IUCV_DBF_TEXT_(data, 2, "Illegal next field: %d > %d\n",
header->next, skb_tailroom(pskb));
return;
@@ -636,8 +662,6 @@ static void netiucv_unpack_skb(struct iucv_connection *conn,
skb_reset_mac_header(pskb);
skb = dev_alloc_skb(pskb->len);
if (!skb) {
- PRINT_WARN("%s Out of memory in netiucv_unpack_skb\n",
- dev->name);
IUCV_DBF_TEXT(data, 2,
"Out of memory in netiucv_unpack_skb\n");
privptr->stats.rx_dropped++;
@@ -656,7 +680,6 @@ static void netiucv_unpack_skb(struct iucv_connection *conn,
* we must use netif_rx_ni() instead of netif_rx()
*/
netif_rx_ni(skb);
- dev->last_rx = jiffies;
skb_pull(pskb, header->next);
skb_put(pskb, NETIUCV_HDRLEN);
}
@@ -670,11 +693,10 @@ static void conn_action_rx(fsm_instance *fi, int event, void *arg)
struct netiucv_priv *privptr = netdev_priv(conn->netdev);
int rc;
- IUCV_DBF_TEXT(trace, 4, __FUNCTION__);
+ IUCV_DBF_TEXT(trace, 4, __func__);
if (!conn->netdev) {
iucv_message_reject(conn->path, msg);
- PRINT_WARN("Received data for unlinked connection\n");
IUCV_DBF_TEXT(data, 2,
"Received data for unlinked connection\n");
return;
@@ -682,8 +704,6 @@ static void conn_action_rx(fsm_instance *fi, int event, void *arg)
if (msg->length > conn->max_buffsize) {
iucv_message_reject(conn->path, msg);
privptr->stats.rx_dropped++;
- PRINT_WARN("msglen %d > max_buffsize %d\n",
- msg->length, conn->max_buffsize);
IUCV_DBF_TEXT_(data, 2, "msglen %d > max_buffsize %d\n",
msg->length, conn->max_buffsize);
return;
@@ -695,7 +715,6 @@ static void conn_action_rx(fsm_instance *fi, int event, void *arg)
msg->length, NULL);
if (rc || msg->length < 5) {
privptr->stats.rx_errors++;
- PRINT_WARN("iucv_receive returned %08x\n", rc);
IUCV_DBF_TEXT_(data, 2, "rc %d from iucv_receive\n", rc);
return;
}
@@ -718,21 +737,25 @@ static void conn_action_txdone(fsm_instance *fi, int event, void *arg)
struct ll_header header;
int rc;
- IUCV_DBF_TEXT(trace, 4, __FUNCTION__);
+ IUCV_DBF_TEXT(trace, 4, __func__);
- if (conn && conn->netdev)
- privptr = netdev_priv(conn->netdev);
+ if (!conn || !conn->netdev) {
+ IUCV_DBF_TEXT(data, 2,
+ "Send confirmation for unlinked connection\n");
+ return;
+ }
+ privptr = netdev_priv(conn->netdev);
conn->prof.tx_pending--;
if (single_flag) {
if ((skb = skb_dequeue(&conn->commit_queue))) {
atomic_dec(&skb->users);
- dev_kfree_skb_any(skb);
if (privptr) {
privptr->stats.tx_packets++;
privptr->stats.tx_bytes +=
(skb->len - NETIUCV_HDRLEN
- - NETIUCV_HDRLEN);
+ - NETIUCV_HDRLEN);
}
+ dev_kfree_skb_any(skb);
}
}
conn->tx_buff->data = conn->tx_buff->head;
@@ -778,7 +801,6 @@ static void conn_action_txdone(fsm_instance *fi, int event, void *arg)
fsm_newstate(fi, CONN_STATE_IDLE);
if (privptr)
privptr->stats.tx_errors += txpackets;
- PRINT_WARN("iucv_send returned %08x\n", rc);
IUCV_DBF_TEXT_(data, 2, "rc %d from iucv_send\n", rc);
} else {
if (privptr) {
@@ -799,15 +821,13 @@ static void conn_action_connaccept(fsm_instance *fi, int event, void *arg)
struct netiucv_priv *privptr = netdev_priv(netdev);
int rc;
- IUCV_DBF_TEXT(trace, 3, __FUNCTION__);
+ IUCV_DBF_TEXT(trace, 3, __func__);
conn->path = path;
path->msglim = NETIUCV_QUEUELEN_DEFAULT;
path->flags = 0;
- rc = iucv_path_accept(path, &netiucv_handler, NULL, conn);
+ rc = iucv_path_accept(path, &netiucv_handler, conn->userdata , conn);
if (rc) {
- PRINT_WARN("%s: IUCV accept failed with error %d\n",
- netdev->name, rc);
IUCV_DBF_TEXT_(setup, 2, "rc %d from iucv_accept", rc);
return;
}
@@ -821,7 +841,7 @@ static void conn_action_connreject(fsm_instance *fi, int event, void *arg)
struct iucv_event *ev = arg;
struct iucv_path *path = ev->data;
- IUCV_DBF_TEXT(trace, 3, __FUNCTION__);
+ IUCV_DBF_TEXT(trace, 3, __func__);
iucv_path_sever(path, NULL);
}
@@ -831,7 +851,7 @@ static void conn_action_connack(fsm_instance *fi, int event, void *arg)
struct net_device *netdev = conn->netdev;
struct netiucv_priv *privptr = netdev_priv(netdev);
- IUCV_DBF_TEXT(trace, 3, __FUNCTION__);
+ IUCV_DBF_TEXT(trace, 3, __func__);
fsm_deltimer(&conn->timer);
fsm_newstate(fi, CONN_STATE_IDLE);
netdev->tx_queue_len = conn->path->msglim;
@@ -842,9 +862,9 @@ static void conn_action_conntimsev(fsm_instance *fi, int event, void *arg)
{
struct iucv_connection *conn = arg;
- IUCV_DBF_TEXT(trace, 3, __FUNCTION__);
+ IUCV_DBF_TEXT(trace, 3, __func__);
fsm_deltimer(&conn->timer);
- iucv_path_sever(conn->path, NULL);
+ iucv_path_sever(conn->path, conn->userdata);
fsm_newstate(fi, CONN_STATE_STARTWAIT);
}
@@ -854,11 +874,12 @@ static void conn_action_connsever(fsm_instance *fi, int event, void *arg)
struct net_device *netdev = conn->netdev;
struct netiucv_priv *privptr = netdev_priv(netdev);
- IUCV_DBF_TEXT(trace, 3, __FUNCTION__);
+ IUCV_DBF_TEXT(trace, 3, __func__);
fsm_deltimer(&conn->timer);
- iucv_path_sever(conn->path, NULL);
- PRINT_INFO("%s: Remote dropped connection\n", netdev->name);
+ iucv_path_sever(conn->path, conn->userdata);
+ dev_info(privptr->dev, "The peer z/VM guest %s has closed the "
+ "connection\n", netiucv_printuser(conn));
IUCV_DBF_TEXT(data, 2,
"conn_action_connsever: Remote dropped connection\n");
fsm_newstate(fi, CONN_STATE_STARTWAIT);
@@ -868,13 +889,13 @@ static void conn_action_connsever(fsm_instance *fi, int event, void *arg)
static void conn_action_start(fsm_instance *fi, int event, void *arg)
{
struct iucv_connection *conn = arg;
+ struct net_device *netdev = conn->netdev;
+ struct netiucv_priv *privptr = netdev_priv(netdev);
int rc;
- IUCV_DBF_TEXT(trace, 3, __FUNCTION__);
+ IUCV_DBF_TEXT(trace, 3, __func__);
fsm_newstate(fi, CONN_STATE_STARTWAIT);
- PRINT_DEBUG("%s('%s'): connecting ...\n",
- conn->netdev->name, conn->userid);
/*
* We must set the state before calling iucv_connect because the
@@ -884,45 +905,52 @@ static void conn_action_start(fsm_instance *fi, int event, void *arg)
fsm_newstate(fi, CONN_STATE_SETUPWAIT);
conn->path = iucv_path_alloc(NETIUCV_QUEUELEN_DEFAULT, 0, GFP_KERNEL);
+ IUCV_DBF_TEXT_(setup, 2, "%s: connecting to %s ...\n",
+ netdev->name, netiucv_printuser(conn));
+
rc = iucv_path_connect(conn->path, &netiucv_handler, conn->userid,
- NULL, iucvMagic, conn);
+ NULL, conn->userdata, conn);
switch (rc) {
case 0:
- conn->netdev->tx_queue_len = conn->path->msglim;
+ netdev->tx_queue_len = conn->path->msglim;
fsm_addtimer(&conn->timer, NETIUCV_TIMEOUT_5SEC,
CONN_EVENT_TIMER, conn);
return;
case 11:
- PRINT_INFO("%s: User %s is currently not available.\n",
- conn->netdev->name,
- netiucv_printname(conn->userid));
+ dev_warn(privptr->dev,
+ "The IUCV device failed to connect to z/VM guest %s\n",
+ netiucv_printname(conn->userid, 8));
fsm_newstate(fi, CONN_STATE_STARTWAIT);
break;
case 12:
- PRINT_INFO("%s: User %s is currently not ready.\n",
- conn->netdev->name,
- netiucv_printname(conn->userid));
+ dev_warn(privptr->dev,
+ "The IUCV device failed to connect to the peer on z/VM"
+ " guest %s\n", netiucv_printname(conn->userid, 8));
fsm_newstate(fi, CONN_STATE_STARTWAIT);
break;
case 13:
- PRINT_WARN("%s: Too many IUCV connections.\n",
- conn->netdev->name);
+ dev_err(privptr->dev,
+ "Connecting the IUCV device would exceed the maximum"
+ " number of IUCV connections\n");
fsm_newstate(fi, CONN_STATE_CONNERR);
break;
case 14:
- PRINT_WARN("%s: User %s has too many IUCV connections.\n",
- conn->netdev->name,
- netiucv_printname(conn->userid));
+ dev_err(privptr->dev,
+ "z/VM guest %s has too many IUCV connections"
+ " to connect with the IUCV device\n",
+ netiucv_printname(conn->userid, 8));
fsm_newstate(fi, CONN_STATE_CONNERR);
break;
case 15:
- PRINT_WARN("%s: No IUCV authorization in CP directory.\n",
- conn->netdev->name);
+ dev_err(privptr->dev,
+ "The IUCV device cannot connect to a z/VM guest with no"
+ " IUCV authorization\n");
fsm_newstate(fi, CONN_STATE_CONNERR);
break;
default:
- PRINT_WARN("%s: iucv_connect returned error %d\n",
- conn->netdev->name, rc);
+ dev_err(privptr->dev,
+ "Connecting the IUCV device failed with error %d\n",
+ rc);
fsm_newstate(fi, CONN_STATE_CONNERR);
break;
}
@@ -948,14 +976,14 @@ static void conn_action_stop(fsm_instance *fi, int event, void *arg)
struct net_device *netdev = conn->netdev;
struct netiucv_priv *privptr = netdev_priv(netdev);
- IUCV_DBF_TEXT(trace, 3, __FUNCTION__);
+ IUCV_DBF_TEXT(trace, 3, __func__);
fsm_deltimer(&conn->timer);
fsm_newstate(fi, CONN_STATE_STOPPED);
netiucv_purge_skb_queue(&conn->collect_queue);
if (conn->path) {
IUCV_DBF_TEXT(trace, 5, "calling iucv_path_sever\n");
- iucv_path_sever(conn->path, iucvMagic);
+ iucv_path_sever(conn->path, conn->userdata);
kfree(conn->path);
conn->path = NULL;
}
@@ -968,8 +996,8 @@ static void conn_action_inval(fsm_instance *fi, int event, void *arg)
struct iucv_connection *conn = arg;
struct net_device *netdev = conn->netdev;
- PRINT_WARN("%s: Cannot connect without username\n", netdev->name);
- IUCV_DBF_TEXT(data, 2, "conn_action_inval called\n");
+ IUCV_DBF_TEXT_(data, 2, "%s('%s'): conn_action_inval called\n",
+ netdev->name, conn->userid);
}
static const fsm_node conn_fsm[] = {
@@ -1024,7 +1052,7 @@ static void dev_action_start(fsm_instance *fi, int event, void *arg)
struct net_device *dev = arg;
struct netiucv_priv *privptr = netdev_priv(dev);
- IUCV_DBF_TEXT(trace, 3, __FUNCTION__);
+ IUCV_DBF_TEXT(trace, 3, __func__);
fsm_newstate(fi, DEV_STATE_STARTWAIT);
fsm_event(privptr->conn->fsm, CONN_EVENT_START, privptr->conn);
@@ -1044,7 +1072,7 @@ dev_action_stop(fsm_instance *fi, int event, void *arg)
struct netiucv_priv *privptr = netdev_priv(dev);
struct iucv_event ev;
- IUCV_DBF_TEXT(trace, 3, __FUNCTION__);
+ IUCV_DBF_TEXT(trace, 3, __func__);
ev.conn = privptr->conn;
@@ -1066,20 +1094,19 @@ dev_action_connup(fsm_instance *fi, int event, void *arg)
struct net_device *dev = arg;
struct netiucv_priv *privptr = netdev_priv(dev);
- IUCV_DBF_TEXT(trace, 3, __FUNCTION__);
+ IUCV_DBF_TEXT(trace, 3, __func__);
switch (fsm_getstate(fi)) {
case DEV_STATE_STARTWAIT:
fsm_newstate(fi, DEV_STATE_RUNNING);
- PRINT_INFO("%s: connected with remote side %s\n",
- dev->name, privptr->conn->userid);
+ dev_info(privptr->dev,
+ "The IUCV device has been connected"
+ " successfully to %s\n",
+ netiucv_printuser(privptr->conn));
IUCV_DBF_TEXT(setup, 3,
"connection is up and running\n");
break;
case DEV_STATE_STOPWAIT:
- PRINT_INFO(
- "%s: got connection UP event during shutdown!\n",
- dev->name);
IUCV_DBF_TEXT(data, 2,
"dev_action_connup: in DEV_STATE_STOPWAIT\n");
break;
@@ -1097,7 +1124,7 @@ dev_action_connup(fsm_instance *fi, int event, void *arg)
static void
dev_action_conndown(fsm_instance *fi, int event, void *arg)
{
- IUCV_DBF_TEXT(trace, 3, __FUNCTION__);
+ IUCV_DBF_TEXT(trace, 3, __func__);
switch (fsm_getstate(fi)) {
case DEV_STATE_RUNNING:
@@ -1174,8 +1201,6 @@ static int netiucv_transmit_skb(struct iucv_connection *conn,
nskb = alloc_skb(skb->len + NETIUCV_HDRLEN +
NETIUCV_HDRLEN, GFP_ATOMIC | GFP_DMA);
if (!nskb) {
- PRINT_WARN("%s: Could not allocate tx_skb\n",
- conn->netdev->name);
IUCV_DBF_TEXT(data, 2, "alloc_skb failed\n");
rc = -ENOMEM;
return rc;
@@ -1223,7 +1248,6 @@ static int netiucv_transmit_skb(struct iucv_connection *conn,
skb_pull(skb, NETIUCV_HDRLEN);
skb_trim(skb, skb->len - NETIUCV_HDRLEN);
}
- PRINT_WARN("iucv_send returned %08x\n", rc);
IUCV_DBF_TEXT_(data, 2, "rc %d from iucv_send\n", rc);
} else {
if (copied)
@@ -1272,6 +1296,72 @@ static int netiucv_close(struct net_device *dev)
return 0;
}
+static int netiucv_pm_prepare(struct device *dev)
+{
+ IUCV_DBF_TEXT(trace, 3, __func__);
+ return 0;
+}
+
+static void netiucv_pm_complete(struct device *dev)
+{
+ IUCV_DBF_TEXT(trace, 3, __func__);
+ return;
+}
+
+/**
+ * netiucv_pm_freeze() - Freeze PM callback
+ * @dev: netiucv device
+ *
+ * close open netiucv interfaces
+ */
+static int netiucv_pm_freeze(struct device *dev)
+{
+ struct netiucv_priv *priv = dev_get_drvdata(dev);
+ struct net_device *ndev = NULL;
+ int rc = 0;
+
+ IUCV_DBF_TEXT(trace, 3, __func__);
+ if (priv && priv->conn)
+ ndev = priv->conn->netdev;
+ if (!ndev)
+ goto out;
+ netif_device_detach(ndev);
+ priv->pm_state = fsm_getstate(priv->fsm);
+ rc = netiucv_close(ndev);
+out:
+ return rc;
+}
+
+/**
+ * netiucv_pm_restore_thaw() - Thaw and restore PM callback
+ * @dev: netiucv device
+ *
+ * re-open netiucv interfaces closed during freeze
+ */
+static int netiucv_pm_restore_thaw(struct device *dev)
+{
+ struct netiucv_priv *priv = dev_get_drvdata(dev);
+ struct net_device *ndev = NULL;
+ int rc = 0;
+
+ IUCV_DBF_TEXT(trace, 3, __func__);
+ if (priv && priv->conn)
+ ndev = priv->conn->netdev;
+ if (!ndev)
+ goto out;
+ switch (priv->pm_state) {
+ case DEV_STATE_RUNNING:
+ case DEV_STATE_STARTWAIT:
+ rc = netiucv_open(ndev);
+ break;
+ default:
+ break;
+ }
+ netif_device_attach(ndev);
+out:
+ return rc;
+}
+
/**
* Start transmission of a packet.
* Called from generic network device layer.
@@ -1288,24 +1378,21 @@ static int netiucv_tx(struct sk_buff *skb, struct net_device *dev)
struct netiucv_priv *privptr = netdev_priv(dev);
int rc;
- IUCV_DBF_TEXT(trace, 4, __FUNCTION__);
+ IUCV_DBF_TEXT(trace, 4, __func__);
/**
* Some sanity checks ...
*/
if (skb == NULL) {
- PRINT_WARN("%s: NULL sk_buff passed\n", dev->name);
IUCV_DBF_TEXT(data, 2, "netiucv_tx: skb is NULL\n");
privptr->stats.tx_dropped++;
- return 0;
+ return NETDEV_TX_OK;
}
if (skb_headroom(skb) < NETIUCV_HDRLEN) {
- PRINT_WARN("%s: Got sk_buff with head room < %ld bytes\n",
- dev->name, NETIUCV_HDRLEN);
IUCV_DBF_TEXT(data, 2,
"netiucv_tx: skb_headroom < NETIUCV_HDRLEN\n");
dev_kfree_skb(skb);
privptr->stats.tx_dropped++;
- return 0;
+ return NETDEV_TX_OK;
}
/**
@@ -1313,23 +1400,21 @@ static int netiucv_tx(struct sk_buff *skb, struct net_device *dev)
* and throw away packet.
*/
if (fsm_getstate(privptr->fsm) != DEV_STATE_RUNNING) {
- if (!in_atomic())
- fsm_event(privptr->fsm, DEV_EVENT_START, dev);
dev_kfree_skb(skb);
privptr->stats.tx_dropped++;
privptr->stats.tx_errors++;
privptr->stats.tx_carrier_errors++;
- return 0;
+ return NETDEV_TX_OK;
}
if (netiucv_test_and_set_busy(dev)) {
IUCV_DBF_TEXT(data, 2, "EBUSY from netiucv_tx\n");
- return -EBUSY;
+ return NETDEV_TX_BUSY;
}
dev->trans_start = jiffies;
- rc = netiucv_transmit_skb(privptr->conn, skb) != 0;
+ rc = netiucv_transmit_skb(privptr->conn, skb);
netiucv_clear_busy(dev);
- return rc;
+ return rc ? NETDEV_TX_BUSY : NETDEV_TX_OK;
}
/**
@@ -1344,7 +1429,7 @@ static struct net_device_stats *netiucv_stats (struct net_device * dev)
{
struct netiucv_priv *priv = netdev_priv(dev);
- IUCV_DBF_TEXT(trace, 5, __FUNCTION__);
+ IUCV_DBF_TEXT(trace, 5, __func__);
return &priv->stats;
}
@@ -1360,7 +1445,7 @@ static struct net_device_stats *netiucv_stats (struct net_device * dev)
*/
static int netiucv_change_mtu(struct net_device * dev, int new_mtu)
{
- IUCV_DBF_TEXT(trace, 3, __FUNCTION__);
+ IUCV_DBF_TEXT(trace, 3, __func__);
if (new_mtu < 576 || new_mtu > NETIUCV_MTU_MAX) {
IUCV_DBF_TEXT(setup, 2, "given MTU out of valid range\n");
return -EINVAL;
@@ -1376,70 +1461,94 @@ static int netiucv_change_mtu(struct net_device * dev, int new_mtu)
static ssize_t user_show(struct device *dev, struct device_attribute *attr,
char *buf)
{
- struct netiucv_priv *priv = dev->driver_data;
+ struct netiucv_priv *priv = dev_get_drvdata(dev);
- IUCV_DBF_TEXT(trace, 5, __FUNCTION__);
- return sprintf(buf, "%s\n", netiucv_printname(priv->conn->userid));
+ IUCV_DBF_TEXT(trace, 5, __func__);
+ return sprintf(buf, "%s\n", netiucv_printuser(priv->conn));
}
-static ssize_t user_write(struct device *dev, struct device_attribute *attr,
- const char *buf, size_t count)
+static int netiucv_check_user(const char *buf, size_t count, char *username,
+ char *userdata)
{
- struct netiucv_priv *priv = dev->driver_data;
- struct net_device *ndev = priv->conn->netdev;
- char *p;
- char *tmp;
- char username[9];
- int i;
- struct iucv_connection *cp;
+ const char *p;
+ int i;
- IUCV_DBF_TEXT(trace, 3, __FUNCTION__);
- if (count > 9) {
- PRINT_WARN("netiucv: username too long (%d)!\n", (int) count);
- IUCV_DBF_TEXT_(setup, 2,
- "%d is length of username\n", (int) count);
+ p = strchr(buf, '.');
+ if ((p && ((count > 26) ||
+ ((p - buf) > 8) ||
+ (buf + count - p > 18))) ||
+ (!p && (count > 9))) {
+ IUCV_DBF_TEXT(setup, 2, "conn_write: too long\n");
return -EINVAL;
}
- tmp = strsep((char **) &buf, "\n");
- for (i = 0, p = tmp; i < 8 && *p; i++, p++) {
- if (isalnum(*p) || (*p == '$')) {
- username[i]= toupper(*p);
+ for (i = 0, p = buf; i < 8 && *p && *p != '.'; i++, p++) {
+ if (isalnum(*p) || *p == '$') {
+ username[i] = toupper(*p);
continue;
}
- if (*p == '\n') {
+ if (*p == '\n')
/* trailing lf, grr */
break;
- }
- PRINT_WARN("netiucv: Invalid char %c in username!\n", *p);
IUCV_DBF_TEXT_(setup, 2,
- "username: invalid character %c\n", *p);
+ "conn_write: invalid character %02x\n", *p);
return -EINVAL;
}
while (i < 8)
username[i++] = ' ';
username[8] = '\0';
+ if (*p == '.') {
+ p++;
+ for (i = 0; i < 16 && *p; i++, p++) {
+ if (*p == '\n')
+ break;
+ userdata[i] = toupper(*p);
+ }
+ while (i > 0 && i < 16)
+ userdata[i++] = ' ';
+ } else
+ memcpy(userdata, iucvMagic_ascii, 16);
+ userdata[16] = '\0';
+ ASCEBC(userdata, 16);
+
+ return 0;
+}
+
+static ssize_t user_write(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct netiucv_priv *priv = dev_get_drvdata(dev);
+ struct net_device *ndev = priv->conn->netdev;
+ char username[9];
+ char userdata[17];
+ int rc;
+ struct iucv_connection *cp;
+
+ IUCV_DBF_TEXT(trace, 3, __func__);
+ rc = netiucv_check_user(buf, count, username, userdata);
+ if (rc)
+ return rc;
+
if (memcmp(username, priv->conn->userid, 9) &&
(ndev->flags & (IFF_UP | IFF_RUNNING))) {
/* username changed while the interface is active. */
- PRINT_WARN("netiucv: device %s active, connected to %s\n",
- dev->bus_id, priv->conn->userid);
- PRINT_WARN("netiucv: user cannot be updated\n");
IUCV_DBF_TEXT(setup, 2, "user_write: device active\n");
- return -EBUSY;
+ return -EPERM;
}
read_lock_bh(&iucv_connection_rwlock);
list_for_each_entry(cp, &iucv_connection_list, list) {
- if (!strncmp(username, cp->userid, 9) && cp->netdev != ndev) {
+ if (!strncmp(username, cp->userid, 9) &&
+ !strncmp(userdata, cp->userdata, 17) && cp->netdev != ndev) {
read_unlock_bh(&iucv_connection_rwlock);
- PRINT_WARN("netiucv: Connection to %s already "
- "exists\n", username);
+ IUCV_DBF_TEXT_(setup, 2, "user_write: Connection to %s "
+ "already exists\n", netiucv_printuser(cp));
return -EEXIST;
}
}
read_unlock_bh(&iucv_connection_rwlock);
memcpy(priv->conn->userid, username, 9);
+ memcpy(priv->conn->userdata, userdata, 17);
return count;
}
@@ -1447,34 +1556,33 @@ static DEVICE_ATTR(user, 0644, user_show, user_write);
static ssize_t buffer_show (struct device *dev, struct device_attribute *attr,
char *buf)
-{ struct netiucv_priv *priv = dev->driver_data;
+{
+ struct netiucv_priv *priv = dev_get_drvdata(dev);
- IUCV_DBF_TEXT(trace, 5, __FUNCTION__);
+ IUCV_DBF_TEXT(trace, 5, __func__);
return sprintf(buf, "%d\n", priv->conn->max_buffsize);
}
static ssize_t buffer_write (struct device *dev, struct device_attribute *attr,
const char *buf, size_t count)
{
- struct netiucv_priv *priv = dev->driver_data;
+ struct netiucv_priv *priv = dev_get_drvdata(dev);
struct net_device *ndev = priv->conn->netdev;
char *e;
int bs1;
- IUCV_DBF_TEXT(trace, 3, __FUNCTION__);
+ IUCV_DBF_TEXT(trace, 3, __func__);
if (count >= 39)
return -EINVAL;
bs1 = simple_strtoul(buf, &e, 0);
if (e && (!isspace(*e))) {
- PRINT_WARN("netiucv: Invalid character in buffer!\n");
- IUCV_DBF_TEXT_(setup, 2, "buffer_write: invalid char %c\n", *e);
+ IUCV_DBF_TEXT_(setup, 2, "buffer_write: invalid char %02x\n",
+ *e);
return -EINVAL;
}
if (bs1 > NETIUCV_BUFSIZE_MAX) {
- PRINT_WARN("netiucv: Given buffer size %d too large.\n",
- bs1);
IUCV_DBF_TEXT_(setup, 2,
"buffer_write: buffer size %d too large\n",
bs1);
@@ -1482,16 +1590,12 @@ static ssize_t buffer_write (struct device *dev, struct device_attribute *attr,
}
if ((ndev->flags & IFF_RUNNING) &&
(bs1 < (ndev->mtu + NETIUCV_HDRLEN + 2))) {
- PRINT_WARN("netiucv: Given buffer size %d too small.\n",
- bs1);
IUCV_DBF_TEXT_(setup, 2,
"buffer_write: buffer size %d too small\n",
bs1);
return -EINVAL;
}
if (bs1 < (576 + NETIUCV_HDRLEN + NETIUCV_HDRLEN)) {
- PRINT_WARN("netiucv: Given buffer size %d too small.\n",
- bs1);
IUCV_DBF_TEXT_(setup, 2,
"buffer_write: buffer size %d too small\n",
bs1);
@@ -1511,9 +1615,9 @@ static DEVICE_ATTR(buffer, 0644, buffer_show, buffer_write);
static ssize_t dev_fsm_show (struct device *dev, struct device_attribute *attr,
char *buf)
{
- struct netiucv_priv *priv = dev->driver_data;
+ struct netiucv_priv *priv = dev_get_drvdata(dev);
- IUCV_DBF_TEXT(trace, 5, __FUNCTION__);
+ IUCV_DBF_TEXT(trace, 5, __func__);
return sprintf(buf, "%s\n", fsm_getstate_str(priv->fsm));
}
@@ -1522,9 +1626,9 @@ static DEVICE_ATTR(device_fsm_state, 0444, dev_fsm_show, NULL);
static ssize_t conn_fsm_show (struct device *dev,
struct device_attribute *attr, char *buf)
{
- struct netiucv_priv *priv = dev->driver_data;
+ struct netiucv_priv *priv = dev_get_drvdata(dev);
- IUCV_DBF_TEXT(trace, 5, __FUNCTION__);
+ IUCV_DBF_TEXT(trace, 5, __func__);
return sprintf(buf, "%s\n", fsm_getstate_str(priv->conn->fsm));
}
@@ -1533,9 +1637,9 @@ static DEVICE_ATTR(connection_fsm_state, 0444, conn_fsm_show, NULL);
static ssize_t maxmulti_show (struct device *dev,
struct device_attribute *attr, char *buf)
{
- struct netiucv_priv *priv = dev->driver_data;
+ struct netiucv_priv *priv = dev_get_drvdata(dev);
- IUCV_DBF_TEXT(trace, 5, __FUNCTION__);
+ IUCV_DBF_TEXT(trace, 5, __func__);
return sprintf(buf, "%ld\n", priv->conn->prof.maxmulti);
}
@@ -1543,9 +1647,9 @@ static ssize_t maxmulti_write (struct device *dev,
struct device_attribute *attr,
const char *buf, size_t count)
{
- struct netiucv_priv *priv = dev->driver_data;
+ struct netiucv_priv *priv = dev_get_drvdata(dev);
- IUCV_DBF_TEXT(trace, 4, __FUNCTION__);
+ IUCV_DBF_TEXT(trace, 4, __func__);
priv->conn->prof.maxmulti = 0;
return count;
}
@@ -1555,18 +1659,18 @@ static DEVICE_ATTR(max_tx_buffer_used, 0644, maxmulti_show, maxmulti_write);
static ssize_t maxcq_show (struct device *dev, struct device_attribute *attr,
char *buf)
{
- struct netiucv_priv *priv = dev->driver_data;
+ struct netiucv_priv *priv = dev_get_drvdata(dev);
- IUCV_DBF_TEXT(trace, 5, __FUNCTION__);
+ IUCV_DBF_TEXT(trace, 5, __func__);
return sprintf(buf, "%ld\n", priv->conn->prof.maxcqueue);
}
static ssize_t maxcq_write (struct device *dev, struct device_attribute *attr,
const char *buf, size_t count)
{
- struct netiucv_priv *priv = dev->driver_data;
+ struct netiucv_priv *priv = dev_get_drvdata(dev);
- IUCV_DBF_TEXT(trace, 4, __FUNCTION__);
+ IUCV_DBF_TEXT(trace, 4, __func__);
priv->conn->prof.maxcqueue = 0;
return count;
}
@@ -1576,18 +1680,18 @@ static DEVICE_ATTR(max_chained_skbs, 0644, maxcq_show, maxcq_write);
static ssize_t sdoio_show (struct device *dev, struct device_attribute *attr,
char *buf)
{
- struct netiucv_priv *priv = dev->driver_data;
+ struct netiucv_priv *priv = dev_get_drvdata(dev);
- IUCV_DBF_TEXT(trace, 5, __FUNCTION__);
+ IUCV_DBF_TEXT(trace, 5, __func__);
return sprintf(buf, "%ld\n", priv->conn->prof.doios_single);
}
static ssize_t sdoio_write (struct device *dev, struct device_attribute *attr,
const char *buf, size_t count)
{
- struct netiucv_priv *priv = dev->driver_data;
+ struct netiucv_priv *priv = dev_get_drvdata(dev);
- IUCV_DBF_TEXT(trace, 4, __FUNCTION__);
+ IUCV_DBF_TEXT(trace, 4, __func__);
priv->conn->prof.doios_single = 0;
return count;
}
@@ -1597,18 +1701,18 @@ static DEVICE_ATTR(tx_single_write_ops, 0644, sdoio_show, sdoio_write);
static ssize_t mdoio_show (struct device *dev, struct device_attribute *attr,
char *buf)
{
- struct netiucv_priv *priv = dev->driver_data;
+ struct netiucv_priv *priv = dev_get_drvdata(dev);
- IUCV_DBF_TEXT(trace, 5, __FUNCTION__);
+ IUCV_DBF_TEXT(trace, 5, __func__);
return sprintf(buf, "%ld\n", priv->conn->prof.doios_multi);
}
static ssize_t mdoio_write (struct device *dev, struct device_attribute *attr,
const char *buf, size_t count)
{
- struct netiucv_priv *priv = dev->driver_data;
+ struct netiucv_priv *priv = dev_get_drvdata(dev);
- IUCV_DBF_TEXT(trace, 5, __FUNCTION__);
+ IUCV_DBF_TEXT(trace, 5, __func__);
priv->conn->prof.doios_multi = 0;
return count;
}
@@ -1618,18 +1722,18 @@ static DEVICE_ATTR(tx_multi_write_ops, 0644, mdoio_show, mdoio_write);
static ssize_t txlen_show (struct device *dev, struct device_attribute *attr,
char *buf)
{
- struct netiucv_priv *priv = dev->driver_data;
+ struct netiucv_priv *priv = dev_get_drvdata(dev);
- IUCV_DBF_TEXT(trace, 5, __FUNCTION__);
+ IUCV_DBF_TEXT(trace, 5, __func__);
return sprintf(buf, "%ld\n", priv->conn->prof.txlen);
}
static ssize_t txlen_write (struct device *dev, struct device_attribute *attr,
const char *buf, size_t count)
{
- struct netiucv_priv *priv = dev->driver_data;
+ struct netiucv_priv *priv = dev_get_drvdata(dev);
- IUCV_DBF_TEXT(trace, 4, __FUNCTION__);
+ IUCV_DBF_TEXT(trace, 4, __func__);
priv->conn->prof.txlen = 0;
return count;
}
@@ -1639,18 +1743,18 @@ static DEVICE_ATTR(netto_bytes, 0644, txlen_show, txlen_write);
static ssize_t txtime_show (struct device *dev, struct device_attribute *attr,
char *buf)
{
- struct netiucv_priv *priv = dev->driver_data;
+ struct netiucv_priv *priv = dev_get_drvdata(dev);
- IUCV_DBF_TEXT(trace, 5, __FUNCTION__);
+ IUCV_DBF_TEXT(trace, 5, __func__);
return sprintf(buf, "%ld\n", priv->conn->prof.tx_time);
}
static ssize_t txtime_write (struct device *dev, struct device_attribute *attr,
const char *buf, size_t count)
{
- struct netiucv_priv *priv = dev->driver_data;
+ struct netiucv_priv *priv = dev_get_drvdata(dev);
- IUCV_DBF_TEXT(trace, 4, __FUNCTION__);
+ IUCV_DBF_TEXT(trace, 4, __func__);
priv->conn->prof.tx_time = 0;
return count;
}
@@ -1660,18 +1764,18 @@ static DEVICE_ATTR(max_tx_io_time, 0644, txtime_show, txtime_write);
static ssize_t txpend_show (struct device *dev, struct device_attribute *attr,
char *buf)
{
- struct netiucv_priv *priv = dev->driver_data;
+ struct netiucv_priv *priv = dev_get_drvdata(dev);
- IUCV_DBF_TEXT(trace, 5, __FUNCTION__);
+ IUCV_DBF_TEXT(trace, 5, __func__);
return sprintf(buf, "%ld\n", priv->conn->prof.tx_pending);
}
static ssize_t txpend_write (struct device *dev, struct device_attribute *attr,
const char *buf, size_t count)
{
- struct netiucv_priv *priv = dev->driver_data;
+ struct netiucv_priv *priv = dev_get_drvdata(dev);
- IUCV_DBF_TEXT(trace, 4, __FUNCTION__);
+ IUCV_DBF_TEXT(trace, 4, __func__);
priv->conn->prof.tx_pending = 0;
return count;
}
@@ -1681,18 +1785,18 @@ static DEVICE_ATTR(tx_pending, 0644, txpend_show, txpend_write);
static ssize_t txmpnd_show (struct device *dev, struct device_attribute *attr,
char *buf)
{
- struct netiucv_priv *priv = dev->driver_data;
+ struct netiucv_priv *priv = dev_get_drvdata(dev);
- IUCV_DBF_TEXT(trace, 5, __FUNCTION__);
+ IUCV_DBF_TEXT(trace, 5, __func__);
return sprintf(buf, "%ld\n", priv->conn->prof.tx_max_pending);
}
static ssize_t txmpnd_write (struct device *dev, struct device_attribute *attr,
const char *buf, size_t count)
{
- struct netiucv_priv *priv = dev->driver_data;
+ struct netiucv_priv *priv = dev_get_drvdata(dev);
- IUCV_DBF_TEXT(trace, 4, __FUNCTION__);
+ IUCV_DBF_TEXT(trace, 4, __func__);
priv->conn->prof.tx_max_pending = 0;
return count;
}
@@ -1728,26 +1832,11 @@ static struct attribute_group netiucv_stat_attr_group = {
.attrs = netiucv_stat_attrs,
};
-static int netiucv_add_files(struct device *dev)
-{
- int ret;
-
- IUCV_DBF_TEXT(trace, 3, __FUNCTION__);
- ret = sysfs_create_group(&dev->kobj, &netiucv_attr_group);
- if (ret)
- return ret;
- ret = sysfs_create_group(&dev->kobj, &netiucv_stat_attr_group);
- if (ret)
- sysfs_remove_group(&dev->kobj, &netiucv_attr_group);
- return ret;
-}
-
-static void netiucv_remove_files(struct device *dev)
-{
- IUCV_DBF_TEXT(trace, 3, __FUNCTION__);
- sysfs_remove_group(&dev->kobj, &netiucv_stat_attr_group);
- sysfs_remove_group(&dev->kobj, &netiucv_attr_group);
-}
+static const struct attribute_group *netiucv_attr_groups[] = {
+ &netiucv_stat_attr_group,
+ &netiucv_attr_group,
+ NULL,
+};
static int netiucv_register_device(struct net_device *ndev)
{
@@ -1755,13 +1844,13 @@ static int netiucv_register_device(struct net_device *ndev)
struct device *dev = kzalloc(sizeof(struct device), GFP_KERNEL);
int ret;
-
- IUCV_DBF_TEXT(trace, 3, __FUNCTION__);
+ IUCV_DBF_TEXT(trace, 3, __func__);
if (dev) {
- snprintf(dev->bus_id, BUS_ID_SIZE, "net%s", ndev->name);
+ dev_set_name(dev, "net%s", ndev->name);
dev->bus = &iucv_bus;
dev->parent = iucv_root;
+ dev->groups = netiucv_attr_groups;
/*
* The release function could be called after the
* module has been unloaded. It's _only_ task is to
@@ -1775,25 +1864,18 @@ static int netiucv_register_device(struct net_device *ndev)
return -ENOMEM;
ret = device_register(dev);
-
- if (ret)
+ if (ret) {
+ put_device(dev);
return ret;
- ret = netiucv_add_files(dev);
- if (ret)
- goto out_unreg;
+ }
priv->dev = dev;
- dev->driver_data = priv;
+ dev_set_drvdata(dev, priv);
return 0;
-
-out_unreg:
- device_unregister(dev);
- return ret;
}
static void netiucv_unregister_device(struct device *dev)
{
- IUCV_DBF_TEXT(trace, 3, __FUNCTION__);
- netiucv_remove_files(dev);
+ IUCV_DBF_TEXT(trace, 3, __func__);
device_unregister(dev);
}
@@ -1802,7 +1884,8 @@ static void netiucv_unregister_device(struct device *dev)
* Add it to the list of netiucv connections;
*/
static struct iucv_connection *netiucv_new_connection(struct net_device *dev,
- char *username)
+ char *username,
+ char *userdata)
{
struct iucv_connection *conn;
@@ -1831,6 +1914,8 @@ static struct iucv_connection *netiucv_new_connection(struct net_device *dev,
fsm_settimer(conn->fsm, &conn->timer);
fsm_newstate(conn->fsm, CONN_STATE_INVALID);
+ if (userdata)
+ memcpy(conn->userdata, userdata, 17);
if (username) {
memcpy(conn->userid, username, 9);
fsm_newstate(conn->fsm, CONN_STATE_STOPPED);
@@ -1857,14 +1942,15 @@ out:
*/
static void netiucv_remove_connection(struct iucv_connection *conn)
{
- IUCV_DBF_TEXT(trace, 3, __FUNCTION__);
+
+ IUCV_DBF_TEXT(trace, 3, __func__);
write_lock_bh(&iucv_connection_rwlock);
list_del_init(&conn->list);
write_unlock_bh(&iucv_connection_rwlock);
fsm_deltimer(&conn->timer);
netiucv_purge_skb_queue(&conn->collect_queue);
if (conn->path) {
- iucv_path_sever(conn->path, iucvMagic);
+ iucv_path_sever(conn->path, conn->userdata);
kfree(conn->path);
conn->path = NULL;
}
@@ -1881,7 +1967,7 @@ static void netiucv_free_netdevice(struct net_device *dev)
{
struct netiucv_priv *privptr = netdev_priv(dev);
- IUCV_DBF_TEXT(trace, 3, __FUNCTION__);
+ IUCV_DBF_TEXT(trace, 3, __func__);
if (!dev)
return;
@@ -1900,26 +1986,30 @@ static void netiucv_free_netdevice(struct net_device *dev)
/**
* Initialize a net device. (Called from kernel in alloc_netdev())
*/
+static const struct net_device_ops netiucv_netdev_ops = {
+ .ndo_open = netiucv_open,
+ .ndo_stop = netiucv_close,
+ .ndo_get_stats = netiucv_stats,
+ .ndo_start_xmit = netiucv_tx,
+ .ndo_change_mtu = netiucv_change_mtu,
+};
+
static void netiucv_setup_netdevice(struct net_device *dev)
{
dev->mtu = NETIUCV_MTU_DEFAULT;
- dev->hard_start_xmit = netiucv_tx;
- dev->open = netiucv_open;
- dev->stop = netiucv_close;
- dev->get_stats = netiucv_stats;
- dev->change_mtu = netiucv_change_mtu;
dev->destructor = netiucv_free_netdevice;
dev->hard_header_len = NETIUCV_HDRLEN;
dev->addr_len = 0;
dev->type = ARPHRD_SLIP;
dev->tx_queue_len = NETIUCV_QUEUELEN_DEFAULT;
dev->flags = IFF_POINTOPOINT | IFF_NOARP;
+ dev->netdev_ops = &netiucv_netdev_ops;
}
/**
* Allocate and initialize everything of a net device.
*/
-static struct net_device *netiucv_init_netdevice(char *username)
+static struct net_device *netiucv_init_netdevice(char *username, char *userdata)
{
struct netiucv_priv *privptr;
struct net_device *dev;
@@ -1928,6 +2018,7 @@ static struct net_device *netiucv_init_netdevice(char *username)
netiucv_setup_netdevice);
if (!dev)
return NULL;
+ rtnl_lock();
if (dev_alloc_name(dev, dev->name) < 0)
goto out_netdev;
@@ -1938,7 +2029,7 @@ static struct net_device *netiucv_init_netdevice(char *username)
if (!privptr->fsm)
goto out_netdev;
- privptr->conn = netiucv_new_connection(dev, username);
+ privptr->conn = netiucv_new_connection(dev, username, userdata);
if (!privptr->conn) {
IUCV_DBF_TEXT(setup, 2, "NULL from netiucv_new_connection\n");
goto out_fsm;
@@ -1949,6 +2040,7 @@ static struct net_device *netiucv_init_netdevice(char *username)
out_fsm:
kfree_fsm(privptr->fsm);
out_netdev:
+ rtnl_unlock();
free_netdev(dev);
return NULL;
}
@@ -1956,59 +2048,39 @@ out_netdev:
static ssize_t conn_write(struct device_driver *drv,
const char *buf, size_t count)
{
- const char *p;
char username[9];
- int i, rc;
+ char userdata[17];
+ int rc;
struct net_device *dev;
struct netiucv_priv *priv;
struct iucv_connection *cp;
- IUCV_DBF_TEXT(trace, 3, __FUNCTION__);
- if (count>9) {
- PRINT_WARN("netiucv: username too long (%d)!\n", (int)count);
- IUCV_DBF_TEXT(setup, 2, "conn_write: too long\n");
- return -EINVAL;
- }
-
- for (i = 0, p = buf; i < 8 && *p; i++, p++) {
- if (isalnum(*p) || *p == '$') {
- username[i] = toupper(*p);
- continue;
- }
- if (*p == '\n')
- /* trailing lf, grr */
- break;
- PRINT_WARN("netiucv: Invalid character in username!\n");
- IUCV_DBF_TEXT_(setup, 2,
- "conn_write: invalid character %c\n", *p);
- return -EINVAL;
- }
- while (i < 8)
- username[i++] = ' ';
- username[8] = '\0';
+ IUCV_DBF_TEXT(trace, 3, __func__);
+ rc = netiucv_check_user(buf, count, username, userdata);
+ if (rc)
+ return rc;
read_lock_bh(&iucv_connection_rwlock);
list_for_each_entry(cp, &iucv_connection_list, list) {
- if (!strncmp(username, cp->userid, 9)) {
+ if (!strncmp(username, cp->userid, 9) &&
+ !strncmp(userdata, cp->userdata, 17)) {
read_unlock_bh(&iucv_connection_rwlock);
- PRINT_WARN("netiucv: Connection to %s already "
- "exists\n", username);
+ IUCV_DBF_TEXT_(setup, 2, "conn_write: Connection to %s "
+ "already exists\n", netiucv_printuser(cp));
return -EEXIST;
}
}
read_unlock_bh(&iucv_connection_rwlock);
- dev = netiucv_init_netdevice(username);
+ dev = netiucv_init_netdevice(username, userdata);
if (!dev) {
- PRINT_WARN("netiucv: Could not allocate network device "
- "structure for user '%s'\n",
- netiucv_printname(username));
IUCV_DBF_TEXT(setup, 2, "NULL from netiucv_init_netdevice\n");
return -ENODEV;
}
rc = netiucv_register_device(dev);
if (rc) {
+ rtnl_unlock();
IUCV_DBF_TEXT_(setup, 2,
"ret %d from netiucv_register_device\n", rc);
goto out_free_ndev;
@@ -2018,19 +2090,20 @@ static ssize_t conn_write(struct device_driver *drv,
priv = netdev_priv(dev);
SET_NETDEV_DEV(dev, priv->dev);
- rc = register_netdev(dev);
+ rc = register_netdevice(dev);
+ rtnl_unlock();
if (rc)
goto out_unreg;
- PRINT_INFO("%s: '%s'\n", dev->name, netiucv_printname(username));
+ dev_info(priv->dev, "The IUCV interface to %s has been established "
+ "successfully\n",
+ netiucv_printuser(priv->conn));
return count;
out_unreg:
netiucv_unregister_device(priv->dev);
out_free_ndev:
- PRINT_WARN("netiucv: Could not register '%s'\n", dev->name);
- IUCV_DBF_TEXT(setup, 2, "conn_write: could not register\n");
netiucv_free_netdevice(dev);
return rc;
}
@@ -2048,10 +2121,10 @@ static ssize_t remove_write (struct device_driver *drv,
const char *p;
int i;
- IUCV_DBF_TEXT(trace, 3, __FUNCTION__);
+ IUCV_DBF_TEXT(trace, 3, __func__);
if (count >= IFNAMSIZ)
- count = IFNAMSIZ - 1;;
+ count = IFNAMSIZ - 1;
for (i = 0, p = buf; i < count && *p; i++, p++) {
if (*p == '\n' || *p == ' ')
@@ -2070,19 +2143,17 @@ static ssize_t remove_write (struct device_driver *drv,
continue;
read_unlock_bh(&iucv_connection_rwlock);
if (ndev->flags & (IFF_UP | IFF_RUNNING)) {
- PRINT_WARN("netiucv: net device %s active with peer "
- "%s\n", ndev->name, priv->conn->userid);
- PRINT_WARN("netiucv: %s cannot be removed\n",
- ndev->name);
+ dev_warn(dev, "The IUCV device is connected"
+ " to %s and cannot be removed\n",
+ priv->conn->userid);
IUCV_DBF_TEXT(data, 2, "remove_write: still active\n");
- return -EBUSY;
+ return -EPERM;
}
unregister_netdev(ndev);
netiucv_unregister_device(dev);
return count;
}
read_unlock_bh(&iucv_connection_rwlock);
- PRINT_WARN("netiucv: net device %s unknown\n", name);
IUCV_DBF_TEXT(data, 2, "remove_write: unknown device\n");
return -EINVAL;
}
@@ -2099,14 +2170,14 @@ static struct attribute_group netiucv_drv_attr_group = {
.attrs = netiucv_drv_attrs,
};
-static struct attribute_group *netiucv_drv_attr_groups[] = {
+static const struct attribute_group *netiucv_drv_attr_groups[] = {
&netiucv_drv_attr_group,
NULL,
};
static void netiucv_banner(void)
{
- PRINT_INFO("NETIUCV driver initialized\n");
+ pr_info("driver initialized\n");
}
static void __exit netiucv_exit(void)
@@ -2116,7 +2187,7 @@ static void __exit netiucv_exit(void)
struct netiucv_priv *priv;
struct device *dev;
- IUCV_DBF_TEXT(trace, 3, __FUNCTION__);
+ IUCV_DBF_TEXT(trace, 3, __func__);
while (!list_empty(&iucv_connection_list)) {
cp = list_entry(iucv_connection_list.next,
struct iucv_connection, list);
@@ -2128,11 +2199,12 @@ static void __exit netiucv_exit(void)
netiucv_unregister_device(dev);
}
+ device_unregister(netiucv_dev);
driver_unregister(&netiucv_driver);
iucv_unregister(&netiucv_handler, 1);
iucv_unregister_dbf_views();
- PRINT_INFO("NETIUCV driver unloaded\n");
+ pr_info("driver unloaded\n");
return;
}
@@ -2146,18 +2218,34 @@ static int __init netiucv_init(void)
rc = iucv_register(&netiucv_handler, 1);
if (rc)
goto out_dbf;
- IUCV_DBF_TEXT(trace, 3, __FUNCTION__);
+ IUCV_DBF_TEXT(trace, 3, __func__);
netiucv_driver.groups = netiucv_drv_attr_groups;
rc = driver_register(&netiucv_driver);
if (rc) {
- PRINT_ERR("NETIUCV: failed to register driver.\n");
IUCV_DBF_TEXT_(setup, 2, "ret %d from driver_register\n", rc);
goto out_iucv;
}
-
+ /* establish dummy device */
+ netiucv_dev = kzalloc(sizeof(struct device), GFP_KERNEL);
+ if (!netiucv_dev) {
+ rc = -ENOMEM;
+ goto out_driver;
+ }
+ dev_set_name(netiucv_dev, "netiucv");
+ netiucv_dev->bus = &iucv_bus;
+ netiucv_dev->parent = iucv_root;
+ netiucv_dev->release = (void (*)(struct device *))kfree;
+ netiucv_dev->driver = &netiucv_driver;
+ rc = device_register(netiucv_dev);
+ if (rc) {
+ put_device(netiucv_dev);
+ goto out_driver;
+ }
netiucv_banner();
return rc;
+out_driver:
+ driver_unregister(&netiucv_driver);
out_iucv:
iucv_unregister(&netiucv_handler, 1);
out_dbf:
diff --git a/drivers/s390/net/qeth.h b/drivers/s390/net/qeth.h
deleted file mode 100644
index 8c6b72d05b1..00000000000
--- a/drivers/s390/net/qeth.h
+++ /dev/null
@@ -1,1253 +0,0 @@
-#ifndef __QETH_H__
-#define __QETH_H__
-
-#include <linux/if.h>
-#include <linux/if_arp.h>
-
-#include <linux/if_tr.h>
-#include <linux/trdevice.h>
-#include <linux/etherdevice.h>
-#include <linux/if_vlan.h>
-#include <linux/ctype.h>
-
-#include <net/ipv6.h>
-#include <linux/in6.h>
-#include <net/if_inet6.h>
-#include <net/addrconf.h>
-
-
-#include <linux/bitops.h>
-
-#include <asm/debug.h>
-#include <asm/qdio.h>
-#include <asm/ccwdev.h>
-#include <asm/ccwgroup.h>
-
-#include "qeth_mpc.h"
-
-#ifdef CONFIG_QETH_IPV6
-#define QETH_VERSION_IPV6 ":IPv6"
-#else
-#define QETH_VERSION_IPV6 ""
-#endif
-#ifdef CONFIG_QETH_VLAN
-#define QETH_VERSION_VLAN ":VLAN"
-#else
-#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_PAGES 8
-#define QETH_DBF_SETUP_NR_AREAS 1
-#define QETH_DBF_SETUP_LEVEL 5
-
-#define QETH_DBF_MISC_NAME "qeth_misc"
-#define QETH_DBF_MISC_LEN 128
-#define QETH_DBF_MISC_PAGES 2
-#define QETH_DBF_MISC_NR_AREAS 1
-#define QETH_DBF_MISC_LEVEL 2
-
-#define QETH_DBF_DATA_NAME "qeth_data"
-#define QETH_DBF_DATA_LEN 96
-#define QETH_DBF_DATA_PAGES 8
-#define QETH_DBF_DATA_NR_AREAS 1
-#define QETH_DBF_DATA_LEVEL 2
-
-#define QETH_DBF_CONTROL_NAME "qeth_control"
-#define QETH_DBF_CONTROL_LEN 256
-#define QETH_DBF_CONTROL_PAGES 8
-#define QETH_DBF_CONTROL_NR_AREAS 2
-#define QETH_DBF_CONTROL_LEVEL 5
-
-#define QETH_DBF_TRACE_NAME "qeth_trace"
-#define QETH_DBF_TRACE_LEN 8
-#define QETH_DBF_TRACE_PAGES 4
-#define QETH_DBF_TRACE_NR_AREAS 2
-#define QETH_DBF_TRACE_LEVEL 3
-extern debug_info_t *qeth_dbf_trace;
-
-#define QETH_DBF_SENSE_NAME "qeth_sense"
-#define QETH_DBF_SENSE_LEN 64
-#define QETH_DBF_SENSE_PAGES 2
-#define QETH_DBF_SENSE_NR_AREAS 1
-#define QETH_DBF_SENSE_LEVEL 2
-
-#define QETH_DBF_QERR_NAME "qeth_qerr"
-#define QETH_DBF_QERR_LEN 8
-#define QETH_DBF_QERR_PAGES 2
-#define QETH_DBF_QERR_NR_AREAS 2
-#define QETH_DBF_QERR_LEVEL 2
-
-#define QETH_DBF_TEXT(name,level,text) \
- do { \
- debug_text_event(qeth_dbf_##name,level,text); \
- } while (0)
-
-#define QETH_DBF_HEX(name,level,addr,len) \
- do { \
- debug_event(qeth_dbf_##name,level,(void*)(addr),len); \
- } while (0)
-
-DECLARE_PER_CPU(char[256], qeth_dbf_txt_buf);
-
-#define QETH_DBF_TEXT_(name,level,text...) \
- do { \
- char* dbf_txt_buf = get_cpu_var(qeth_dbf_txt_buf); \
- sprintf(dbf_txt_buf, text); \
- debug_text_event(qeth_dbf_##name,level,dbf_txt_buf); \
- put_cpu_var(qeth_dbf_txt_buf); \
- } while (0)
-
-#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)
-
-/**
- * some more debug stuff
- */
-#define PRINTK_HEADER "qeth: "
-
-#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));
-
-static inline void
-qeth_hex_dump(unsigned char *buf, size_t len)
-{
- size_t i;
-
- for (i = 0; i < len; i++) {
- if (i && !(i % 16))
- printk("\n");
- printk("%02x ", *(buf + i));
- }
- printk("\n");
-}
-
-#define SENSE_COMMAND_REJECT_BYTE 0
-#define SENSE_COMMAND_REJECT_FLAG 0x80
-#define SENSE_RESETTING_EVENT_BYTE 1
-#define SENSE_RESETTING_EVENT_FLAG 0x80
-
-/*
- * 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
-
-#define CARD_FROM_CDEV(cdev) (struct qeth_card *) \
- ((struct ccwgroup_device *)cdev->dev.driver_data)\
- ->dev.driver_data;
-
-/**
- * card stuff
- */
-struct qeth_perf_stats {
- unsigned int bufs_rec;
- unsigned int bufs_sent;
-
- unsigned int skbs_sent_pack;
- unsigned int bufs_sent_pack;
-
- unsigned int sc_dp_p;
- unsigned int sc_p_dp;
- /* qdio_input_handler: number of times called, time spent in */
- __u64 inbound_start_time;
- unsigned int inbound_cnt;
- unsigned int inbound_time;
- /* qeth_send_packet: number of times called, time spent in */
- __u64 outbound_start_time;
- unsigned int outbound_cnt;
- unsigned int outbound_time;
- /* qdio_output_handler: number of times called, time spent in */
- __u64 outbound_handler_start_time;
- unsigned int outbound_handler_cnt;
- unsigned int outbound_handler_time;
- /* number of calls to and time spent in do_QDIO for inbound queue */
- __u64 inbound_do_qdio_start_time;
- unsigned int inbound_do_qdio_cnt;
- unsigned int inbound_do_qdio_time;
- /* number of calls to and time spent in do_QDIO for outbound queues */
- __u64 outbound_do_qdio_start_time;
- unsigned int outbound_do_qdio_cnt;
- unsigned int outbound_do_qdio_time;
- /* eddp data */
- unsigned int large_send_bytes;
- unsigned int large_send_cnt;
- unsigned int sg_skbs_sent;
- unsigned int sg_frags_sent;
- /* initial values when measuring starts */
- unsigned long initial_rx_packets;
- unsigned long initial_tx_packets;
- /* inbound scatter gather data */
- unsigned int sg_skbs_rx;
- unsigned int sg_frags_rx;
- unsigned int sg_alloc_page_rx;
-};
-
-/* Routing stuff */
-struct qeth_routing_info {
- enum qeth_routing_types type;
-};
-
-/* IPA stuff */
-struct qeth_ipa_info {
- __u32 supported_funcs;
- __u32 enabled_funcs;
-};
-
-static inline int
-qeth_is_ipa_supported(struct qeth_ipa_info *ipa, enum qeth_ipa_funcs func)
-{
- return (ipa->supported_funcs & func);
-}
-
-static inline int
-qeth_is_ipa_enabled(struct qeth_ipa_info *ipa, enum qeth_ipa_funcs func)
-{
- return (ipa->supported_funcs & ipa->enabled_funcs & func);
-}
-
-#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)
-
-
-#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_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}, \
- {0x1731,0x06,0x1732,0x06,QETH_CARD_TYPE_OSN,0, \
- QETH_IDX_FUNC_LEVEL_OSAE_ENA_IPAT, \
- QETH_IDX_FUNC_LEVEL_OSAE_DIS_IPAT, \
- QETH_MAX_QUEUES,0}, \
- {0,0,0,0,0,0,0,0,0}}
-
-#define QETH_REAL_CARD 1
-#define QETH_VLAN_CARD 2
-#define QETH_BUFSIZE 4096
-
-/**
- * some more defs
- */
-#define IF_NAME_LEN 16
-#define QETH_TX_TIMEOUT 100 * HZ
-#define QETH_RCD_TIMEOUT 60 * HZ
-#define QETH_HEADER_SIZE 32
-#define MAX_PORTNO 15
-#define QETH_FAKE_LL_LEN_ETH ETH_HLEN
-#define QETH_FAKE_LL_LEN_TR (sizeof(struct trh_hdr)-TR_MAXRIFLEN+sizeof(struct trllc))
-#define QETH_FAKE_LL_V6_ADDR_POS 24
-
-/*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 / 2)
-
-/* 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
-
-/* Packing */
-#define QETH_LOW_WATERMARK_PACK 2
-#define QETH_HIGH_WATERMARK_PACK 5
-#define QETH_WATERMARK_PACK_FUZZ 1
-
-#define QETH_IP_HEADER_SIZE 40
-
-/* large receive scatter gather copy break */
-#define QETH_RX_SG_CB (PAGE_SIZE >> 1)
-
-struct qeth_hdr_layer3 {
- __u8 id;
- __u8 flags;
- __u16 inbound_checksum; /*TSO:__u16 seqno */
- __u32 token; /*TSO: __u32 reserved */
- __u16 length;
- __u8 vlan_prio;
- __u8 ext_flags;
- __u16 vlan_id;
- __u16 frame_offset;
- __u8 dest_addr[16];
-} __attribute__ ((packed));
-
-struct qeth_hdr_layer2 {
- __u8 id;
- __u8 flags[3];
- __u8 port_no;
- __u8 hdr_length;
- __u16 pkt_length;
- __u16 seq_no;
- __u16 vlan_id;
- __u32 reserved;
- __u8 reserved2[16];
-} __attribute__ ((packed));
-
-struct qeth_hdr_osn {
- __u8 id;
- __u8 reserved;
- __u16 seq_no;
- __u16 reserved2;
- __u16 control_flags;
- __u16 pdu_length;
- __u8 reserved3[18];
- __u32 ccid;
-} __attribute__ ((packed));
-
-struct qeth_hdr {
- union {
- struct qeth_hdr_layer2 l2;
- struct qeth_hdr_layer3 l3;
- struct qeth_hdr_osn osn;
- } hdr;
-} __attribute__ ((packed));
-
-/*TCP Segmentation Offload header*/
-struct qeth_hdr_ext_tso {
- __u16 hdr_tot_len;
- __u8 imb_hdr_no;
- __u8 reserved;
- __u8 hdr_type;
- __u8 hdr_version;
- __u16 hdr_len;
- __u32 payload_len;
- __u16 mss;
- __u16 dg_hdr_len;
- __u8 padding[16];
-} __attribute__ ((packed));
-
-struct qeth_hdr_tso {
- struct qeth_hdr hdr; /*hdr->hdr.l3.xxx*/
- struct qeth_hdr_ext_tso ext;
-} __attribute__ ((packed));
-
-
-/* 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,
-};
-
-enum qeth_layer2_frame_flags {
- QETH_LAYER2_FLAG_MULTICAST = 0x01,
- QETH_LAYER2_FLAG_BROADCAST = 0x02,
- QETH_LAYER2_FLAG_UNICAST = 0x04,
- QETH_LAYER2_FLAG_VLAN = 0x10,
-};
-
-enum qeth_header_ids {
- QETH_HEADER_TYPE_LAYER3 = 0x01,
- QETH_HEADER_TYPE_LAYER2 = 0x02,
- QETH_HEADER_TYPE_TSO = 0x03,
- QETH_HEADER_TYPE_OSN = 0x04,
-};
-/* flags for qeth_hdr.ext_flags */
-#define QETH_HDR_EXT_VLAN_FRAME 0x01
-#define QETH_HDR_EXT_TOKEN_ID 0x02
-#define QETH_HDR_EXT_INCLUDE_VLAN_TAG 0x04
-#define QETH_HDR_EXT_SRC_MAC_ADDR 0x08
-#define QETH_HDR_EXT_CSUM_HDR_REQ 0x10
-#define QETH_HDR_EXT_CSUM_TRANSP_REQ 0x20
-#define QETH_HDR_EXT_UDP_TSO 0x40 /*bit off for TCP*/
-
-static inline int
-qeth_is_last_sbale(struct qdio_buffer_element *sbale)
-{
- return (sbale->flags & SBAL_FLAGS_LAST_ENTRY);
-}
-
-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,
-};
-
-enum qeth_qdio_info_states {
- QETH_QDIO_UNINITIALIZED,
- QETH_QDIO_ALLOCATED,
- QETH_QDIO_ESTABLISHED,
- QETH_QDIO_CLEANING
-};
-
-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_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)));
-
-/* possible types of qeth large_send support */
-enum qeth_large_send_types {
- QETH_LARGE_SEND_NO,
- QETH_LARGE_SEND_EDDP,
- QETH_LARGE_SEND_TSO,
-};
-
-struct qeth_qdio_out_buffer {
- struct qdio_buffer *buffer;
- atomic_t state;
- volatile int next_element_to_fill;
- struct sk_buff_head skb_list;
- struct list_head ctx_list;
-};
-
-struct qeth_card;
-
-enum qeth_out_q_states {
- QETH_OUT_Q_UNLOCKED,
- QETH_OUT_Q_LOCKED,
- QETH_OUT_Q_LOCKED_FLUSH,
-};
-
-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;
- atomic_t state;
- volatile int do_pack;
- /*
- * index of buffer to be filled by driver; state EMPTY or PACKING
- */
- volatile int next_buf_to_fill;
- /*
- * 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 {
- atomic_t 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;
-
- /* output */
- int no_out_queues;
- struct qeth_qdio_out_q **out_qs;
-
- /* priority queueing */
- int do_prio_queueing;
- int default_out_queue;
-};
-
-enum qeth_send_errors {
- QETH_SEND_ERROR_NONE,
- QETH_SEND_ERROR_LINK_FAILURE,
- QETH_SEND_ERROR_RETRY,
- QETH_SEND_ERROR_KICK_IT,
-};
-
-#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 DEFAULT_ADD_HHLEN 0
-#define MAX_ADD_HHLEN 1024
-
-/**
- * buffer stuff for read channel
- */
-#define QETH_CMD_BUFFER_NO 8
-
-/**
- * channel state machine
- */
-enum qeth_channel_states {
- CH_STATE_UP,
- CH_STATE_DOWN,
- CH_STATE_ACTIVATING,
- CH_STATE_HALTED,
- CH_STATE_STOPPED,
- CH_STATE_RCD,
- CH_STATE_RCD_DONE,
-};
-/**
- * card state machine
- */
-enum qeth_card_states {
- CARD_STATE_DOWN,
- CARD_STATE_HARDSETUP,
- CARD_STATE_SOFTSETUP,
- CARD_STATE_UP,
- CARD_STATE_RECOVER,
-};
-
-/**
- * Protocol versions
- */
-enum qeth_prot_versions {
- QETH_PROT_IPV4 = 0x0004,
- QETH_PROT_IPV6 = 0x0006,
-};
-
-enum qeth_ip_types {
- QETH_IP_TYPE_NORMAL,
- QETH_IP_TYPE_VIPA,
- QETH_IP_TYPE_RXIP,
- QETH_IP_TYPE_DEL_ALL_MC,
-};
-
-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;
-};
-
-struct qeth_ipato {
- int enabled;
- int invert4;
- int invert6;
- struct list_head entries;
-};
-
-struct qeth_channel;
-
-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 *);
-};
-
-
-/**
- * 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;
-};
-
-/**
- * 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;
- __u16 ipa;
- __u32 pkt_seqno;
-};
-
-struct qeth_reply {
- struct list_head list;
- wait_queue_head_t wait_q;
- int (*callback)(struct qeth_card *,struct qeth_reply *,unsigned long);
- u32 seqno;
- unsigned long offset;
- atomic_t received;
- int rc;
- void *param;
- struct qeth_card *card;
- atomic_t refcnt;
-};
-
-
-struct qeth_card_blkt {
- int time_total;
- int inter_packet;
- int inter_packet_jumbo;
-};
-
-#define QETH_BROADCAST_WITH_ECHO 0x01
-#define QETH_BROADCAST_WITHOUT_ECHO 0x02
-#define QETH_LAYER2_MAC_READ 0x01
-#define QETH_LAYER2_MAC_REGISTERED 0x02
-struct qeth_card_info {
- unsigned short unit_addr2;
- unsigned short cula;
- unsigned short chpid;
- __u16 func_level;
- char mcl_level[QETH_MCL_LENGTH + 1];
- int guestlan;
- int mac_bits;
- 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 broadcast_capable;
- int unique_id;
- struct qeth_card_blkt blkt;
- __u32 csum_mask;
- enum qeth_ipa_promisc_modes promisc_mode;
-};
-
-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 fake_broadcast;
- int add_hhlen;
- int fake_ll;
- int layer2;
- enum qeth_large_send_types large_send;
- int performance_stats;
- int rx_sg_cb;
-};
-
-/*
- * thread bits for qeth_card thread masks
- */
-enum qeth_threads {
- QETH_SET_IP_THREAD = 1,
- QETH_RECOVER_THREAD = 2,
- QETH_SET_PROMISC_MODE_THREAD = 4,
-};
-
-struct qeth_osn_info {
- int (*assist_cb)(struct net_device *dev, void *data);
- int (*data_cb)(struct sk_buff *skb);
-};
-
-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;
-
- struct net_device *dev;
- struct net_device_stats stats;
-
- struct qeth_card_info info;
- struct qeth_token token;
- struct qeth_seqno seqno;
- struct qeth_card_options options;
-
- 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;
- struct qeth_perf_stats perf_stats;
- int use_hard_stop;
- const struct header_ops *orig_header_ops;
- struct qeth_osn_info osn_info;
- atomic_t force_alloc_skb;
-};
-
-struct qeth_card_list_struct {
- struct list_head list;
- rwlock_t rwlock;
-};
-
-extern struct qeth_card_list_struct qeth_card_list;
-
-/*notifier list */
-struct qeth_notify_list_struct {
- struct list_head list;
- struct task_struct *task;
- int signum;
-};
-extern spinlock_t qeth_notify_lock;
-extern struct list_head qeth_notify_list;
-
-/*some helper functions*/
-
-#define QETH_CARD_IFNAME(card) (((card)->dev)? (card)->dev->name : "")
-
-static inline __u8
-qeth_get_ipa_adp_type(enum qeth_link_types link_type)
-{
- switch (link_type) {
- case QETH_LINK_TYPE_HSTR:
- return 2;
- default:
- return 1;
- }
-}
-
-static inline struct sk_buff *
-qeth_realloc_headroom(struct qeth_card *card, struct sk_buff *skb, int size)
-{
- struct sk_buff *new_skb = skb;
-
- if (skb_headroom(skb) >= size)
- return skb;
- new_skb = skb_realloc_headroom(skb, size);
- if (!new_skb)
- PRINT_ERR("Could not realloc headroom for qeth_hdr "
- "on interface %s", QETH_CARD_IFNAME(card));
- return new_skb;
-}
-
-static inline struct sk_buff *
-qeth_pskb_unshare(struct sk_buff *skb, gfp_t pri)
-{
- struct sk_buff *nskb;
- if (!skb_cloned(skb))
- return skb;
- nskb = skb_copy(skb, pri);
- return nskb;
-}
-
-static inline void *
-qeth_push_skb(struct qeth_card *card, struct sk_buff *skb, int size)
-{
- void *hdr;
-
- hdr = (void *) skb_push(skb, size);
- /*
- * sanity check, the Linux memory allocation scheme should
- * never present us cases like this one (the qdio header size plus
- * the first 40 bytes of the paket cross a 4k boundary)
- */
- if ((((unsigned long) hdr) & (~(PAGE_SIZE - 1))) !=
- (((unsigned long) hdr + size +
- QETH_IP_HEADER_SIZE) & (~(PAGE_SIZE - 1)))) {
- PRINT_ERR("Misaligned packet on interface %s. Discarded.",
- QETH_CARD_IFNAME(card));
- return NULL;
- }
- return hdr;
-}
-
-
-static inline int
-qeth_get_hlen(__u8 link_type)
-{
-#ifdef CONFIG_QETH_IPV6
- switch (link_type) {
- case QETH_LINK_TYPE_HSTR:
- case QETH_LINK_TYPE_LANE_TR:
- return sizeof(struct qeth_hdr_tso) + TR_HLEN;
- default:
-#ifdef CONFIG_QETH_VLAN
- return sizeof(struct qeth_hdr_tso) + VLAN_ETH_HLEN;
-#else
- return sizeof(struct qeth_hdr_tso) + ETH_HLEN;
-#endif
- }
-#else /* CONFIG_QETH_IPV6 */
-#ifdef CONFIG_QETH_VLAN
- return sizeof(struct qeth_hdr_tso) + VLAN_HLEN;
-#else
- return sizeof(struct qeth_hdr_tso);
-#endif
-#endif /* CONFIG_QETH_IPV6 */
-}
-
-static inline unsigned short
-qeth_get_netdev_flags(struct qeth_card *card)
-{
- if (card->options.layer2 &&
- (card->info.type == QETH_CARD_TYPE_OSAE))
- return 0;
- switch (card->info.type) {
- case QETH_CARD_TYPE_IQD:
- case QETH_CARD_TYPE_OSN:
- return IFF_NOARP;
-#ifdef CONFIG_QETH_IPV6
- default:
- return 0;
-#else
- default:
- return IFF_NOARP;
-#endif
- }
-}
-
-static inline int
-qeth_get_initial_mtu_for_card(struct qeth_card * card)
-{
- switch (card->info.type) {
- case QETH_CARD_TYPE_UNKNOWN:
- return 1500;
- case QETH_CARD_TYPE_IQD:
- return card->info.max_mtu;
- case QETH_CARD_TYPE_OSAE:
- switch (card->info.link_type) {
- case QETH_LINK_TYPE_HSTR:
- case QETH_LINK_TYPE_LANE_TR:
- return 2000;
- default:
- return 1492;
- }
- default:
- return 1500;
- }
-}
-
-static inline int
-qeth_get_max_mtu_for_card(int cardtype)
-{
- switch (cardtype) {
-
- case QETH_CARD_TYPE_UNKNOWN:
- case QETH_CARD_TYPE_OSAE:
- case QETH_CARD_TYPE_OSN:
- return 61440;
- case QETH_CARD_TYPE_IQD:
- return 57344;
- default:
- return 1500;
- }
-}
-
-static inline int
-qeth_get_mtu_out_of_mpc(int cardtype)
-{
- switch (cardtype) {
- case QETH_CARD_TYPE_IQD:
- return 1;
- default:
- return 0;
- }
-}
-
-static inline int
-qeth_get_mtu_outof_framesize(int framesize)
-{
- switch (framesize) {
- case 0x4000:
- return 8192;
- case 0x6000:
- return 16384;
- case 0xa000:
- return 32768;
- case 0xffff:
- return 57344;
- default:
- return 0;
- }
-}
-
-static inline int
-qeth_mtu_is_valid(struct qeth_card * card, int mtu)
-{
- switch (card->info.type) {
- case QETH_CARD_TYPE_OSAE:
- return ((mtu >= 576) && (mtu <= 61440));
- case QETH_CARD_TYPE_IQD:
- return ((mtu >= 576) &&
- (mtu <= card->info.max_mtu + 4096 - 32));
- case QETH_CARD_TYPE_OSN:
- case QETH_CARD_TYPE_UNKNOWN:
- default:
- return 1;
- }
-}
-
-static inline int
-qeth_get_arphdr_type(int cardtype, int linktype)
-{
- switch (cardtype) {
- case QETH_CARD_TYPE_OSAE:
- case QETH_CARD_TYPE_OSN:
- 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:
- default:
- return ARPHRD_ETHER;
- }
-}
-
-static inline int
-qeth_get_micros(void)
-{
- return (int) (get_clock() >> 12);
-}
-
-static inline int
-qeth_get_qdio_q_format(struct qeth_card *card)
-{
- switch (card->info.type) {
- case QETH_CARD_TYPE_IQD:
- return 2;
- default:
- return 0;
- }
-}
-
-static inline int
-qeth_isxdigit(char * buf)
-{
- while (*buf) {
- if (!isxdigit(*buf++))
- return 0;
- }
- return 1;
-}
-
-static inline void
-qeth_ipaddr4_to_string(const __u8 *addr, char *buf)
-{
- sprintf(buf, "%i.%i.%i.%i", addr[0], addr[1], addr[2], addr[3]);
-}
-
-static inline int
-qeth_string_to_ipaddr4(const char *buf, __u8 *addr)
-{
- int count = 0, rc = 0;
- int in[4];
- char c;
-
- rc = sscanf(buf, "%u.%u.%u.%u%c",
- &in[0], &in[1], &in[2], &in[3], &c);
- if (rc != 4 && (rc != 5 || c != '\n'))
- return -EINVAL;
- for (count = 0; count < 4; count++) {
- if (in[count] > 255)
- return -EINVAL;
- addr[count] = in[count];
- }
- 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]);
-}
-
-static inline int
-qeth_string_to_ipaddr6(const char *buf, __u8 *addr)
-{
- const char *end, *end_tmp, *start;
- __u16 *in;
- char num[5];
- int num2, cnt, out, found, save_cnt;
- unsigned short in_tmp[8] = {0, };
-
- cnt = out = found = save_cnt = num2 = 0;
- end = start = buf;
- in = (__u16 *) addr;
- memset(in, 0, 16);
- while (*end) {
- end = strchr(start,':');
- if (end == NULL) {
- end = buf + strlen(buf);
- if ((end_tmp = strchr(start, '\n')) != NULL)
- end = end_tmp;
- out = 1;
- }
- if ((end - start)) {
- memset(num, 0, 5);
- if ((end - start) > 4)
- return -EINVAL;
- memcpy(num, start, end - start);
- if (!qeth_isxdigit(num))
- return -EINVAL;
- sscanf(start, "%x", &num2);
- if (found)
- in_tmp[save_cnt++] = num2;
- else
- in[cnt++] = num2;
- if (out)
- break;
- } else {
- if (found)
- return -EINVAL;
- found = 1;
- }
- start = ++end;
- }
- if (cnt + save_cnt > 8)
- return -EINVAL;
- cnt = 7;
- while (save_cnt)
- in[cnt--] = in_tmp[--save_cnt];
- return 0;
-}
-
-static inline void
-qeth_ipaddr_to_string(enum qeth_prot_versions proto, const __u8 *addr,
- char *buf)
-{
- if (proto == QETH_PROT_IPV4)
- qeth_ipaddr4_to_string(addr, buf);
- else if (proto == QETH_PROT_IPV6)
- qeth_ipaddr6_to_string(addr, buf);
-}
-
-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;
-}
-
-extern int
-qeth_setrouting_v4(struct qeth_card *);
-extern int
-qeth_setrouting_v6(struct qeth_card *);
-
-extern int
-qeth_add_ipato_entry(struct qeth_card *, struct qeth_ipato_entry *);
-
-extern void
-qeth_del_ipato_entry(struct qeth_card *, enum qeth_prot_versions, u8 *, int);
-
-extern int
-qeth_add_vipa(struct qeth_card *, enum qeth_prot_versions, const u8 *);
-
-extern void
-qeth_del_vipa(struct qeth_card *, enum qeth_prot_versions, const u8 *);
-
-extern int
-qeth_add_rxip(struct qeth_card *, enum qeth_prot_versions, const u8 *);
-
-extern void
-qeth_del_rxip(struct qeth_card *, enum qeth_prot_versions, const u8 *);
-
-extern int
-qeth_notifier_register(struct task_struct *, int );
-
-extern int
-qeth_notifier_unregister(struct task_struct * );
-
-extern void
-qeth_schedule_recovery(struct qeth_card *);
-
-extern int
-qeth_realloc_buffer_pool(struct qeth_card *, int);
-
-extern int
-qeth_set_large_send(struct qeth_card *, enum qeth_large_send_types);
-
-extern void
-qeth_fill_header(struct qeth_card *, struct qeth_hdr *,
- struct sk_buff *, int, int);
-extern void
-qeth_flush_buffers(struct qeth_qdio_out_q *, int, int, int);
-
-extern int
-qeth_osn_assist(struct net_device *, void *, int);
-
-extern int
-qeth_osn_register(unsigned char *read_dev_no,
- struct net_device **,
- int (*assist_cb)(struct net_device *, void *),
- int (*data_cb)(struct sk_buff *));
-
-extern void
-qeth_osn_deregister(struct net_device *);
-
-#endif /* __QETH_H__ */
diff --git a/drivers/s390/net/qeth_core.h b/drivers/s390/net/qeth_core.h
new file mode 100644
index 00000000000..a2088af51cc
--- /dev/null
+++ b/drivers/s390/net/qeth_core.h
@@ -0,0 +1,987 @@
+/*
+ * Copyright IBM Corp. 2007
+ * Author(s): Utz Bacher <utz.bacher@de.ibm.com>,
+ * Frank Pavlic <fpavlic@de.ibm.com>,
+ * Thomas Spatzier <tspat@de.ibm.com>,
+ * Frank Blaschka <frank.blaschka@de.ibm.com>
+ */
+
+#ifndef __QETH_CORE_H__
+#define __QETH_CORE_H__
+
+#include <linux/if.h>
+#include <linux/if_arp.h>
+#include <linux/etherdevice.h>
+#include <linux/if_vlan.h>
+#include <linux/ctype.h>
+#include <linux/in6.h>
+#include <linux/bitops.h>
+#include <linux/seq_file.h>
+#include <linux/ethtool.h>
+
+#include <net/ipv6.h>
+#include <net/if_inet6.h>
+#include <net/addrconf.h>
+
+#include <asm/debug.h>
+#include <asm/qdio.h>
+#include <asm/ccwdev.h>
+#include <asm/ccwgroup.h>
+#include <asm/sysinfo.h>
+
+#include "qeth_core_mpc.h"
+
+/**
+ * Debug Facility stuff
+ */
+enum qeth_dbf_names {
+ QETH_DBF_SETUP,
+ QETH_DBF_MSG,
+ QETH_DBF_CTRL,
+ QETH_DBF_INFOS /* must be last element */
+};
+
+struct qeth_dbf_info {
+ char name[DEBUG_MAX_NAME_LEN];
+ int pages;
+ int areas;
+ int len;
+ int level;
+ struct debug_view *view;
+ debug_info_t *id;
+};
+
+#define QETH_DBF_CTRL_LEN 256
+
+#define QETH_DBF_TEXT(name, level, text) \
+ debug_text_event(qeth_dbf[QETH_DBF_##name].id, level, text)
+
+#define QETH_DBF_HEX(name, level, addr, len) \
+ debug_event(qeth_dbf[QETH_DBF_##name].id, level, (void *)(addr), len)
+
+#define QETH_DBF_MESSAGE(level, text...) \
+ debug_sprintf_event(qeth_dbf[QETH_DBF_MSG].id, level, text)
+
+#define QETH_DBF_TEXT_(name, level, text...) \
+ qeth_dbf_longtext(qeth_dbf[QETH_DBF_##name].id, level, text)
+
+#define QETH_CARD_TEXT(card, level, text) \
+ debug_text_event(card->debug, level, text)
+
+#define QETH_CARD_HEX(card, level, addr, len) \
+ debug_event(card->debug, level, (void *)(addr), len)
+
+#define QETH_CARD_MESSAGE(card, text...) \
+ debug_sprintf_event(card->debug, level, text)
+
+#define QETH_CARD_TEXT_(card, level, text...) \
+ qeth_dbf_longtext(card->debug, level, text)
+
+#define SENSE_COMMAND_REJECT_BYTE 0
+#define SENSE_COMMAND_REJECT_FLAG 0x80
+#define SENSE_RESETTING_EVENT_BYTE 1
+#define SENSE_RESETTING_EVENT_FLAG 0x80
+
+/*
+ * Common IO related definitions
+ */
+#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) dev_name(&card->gdev->dev)
+#define CARD_RDEV_ID(card) dev_name(&card->read.ccwdev->dev)
+#define CARD_WDEV_ID(card) dev_name(&card->write.ccwdev->dev)
+#define CARD_DDEV_ID(card) dev_name(&card->data.ccwdev->dev)
+#define CHANNEL_ID(channel) dev_name(&channel->ccwdev->dev)
+
+/**
+ * card stuff
+ */
+struct qeth_perf_stats {
+ unsigned int bufs_rec;
+ unsigned int bufs_sent;
+
+ unsigned int skbs_sent_pack;
+ unsigned int bufs_sent_pack;
+
+ unsigned int sc_dp_p;
+ unsigned int sc_p_dp;
+ /* qdio_cq_handler: number of times called, time spent in */
+ __u64 cq_start_time;
+ unsigned int cq_cnt;
+ unsigned int cq_time;
+ /* qdio_input_handler: number of times called, time spent in */
+ __u64 inbound_start_time;
+ unsigned int inbound_cnt;
+ unsigned int inbound_time;
+ /* qeth_send_packet: number of times called, time spent in */
+ __u64 outbound_start_time;
+ unsigned int outbound_cnt;
+ unsigned int outbound_time;
+ /* qdio_output_handler: number of times called, time spent in */
+ __u64 outbound_handler_start_time;
+ unsigned int outbound_handler_cnt;
+ unsigned int outbound_handler_time;
+ /* number of calls to and time spent in do_QDIO for inbound queue */
+ __u64 inbound_do_qdio_start_time;
+ unsigned int inbound_do_qdio_cnt;
+ unsigned int inbound_do_qdio_time;
+ /* number of calls to and time spent in do_QDIO for outbound queues */
+ __u64 outbound_do_qdio_start_time;
+ unsigned int outbound_do_qdio_cnt;
+ unsigned int outbound_do_qdio_time;
+ unsigned int large_send_bytes;
+ unsigned int large_send_cnt;
+ unsigned int sg_skbs_sent;
+ unsigned int sg_frags_sent;
+ /* initial values when measuring starts */
+ unsigned long initial_rx_packets;
+ unsigned long initial_tx_packets;
+ /* inbound scatter gather data */
+ unsigned int sg_skbs_rx;
+ unsigned int sg_frags_rx;
+ unsigned int sg_alloc_page_rx;
+ unsigned int tx_csum;
+ unsigned int tx_lin;
+};
+
+/* Routing stuff */
+struct qeth_routing_info {
+ enum qeth_routing_types type;
+};
+
+/* IPA stuff */
+struct qeth_ipa_info {
+ __u32 supported_funcs;
+ __u32 enabled_funcs;
+};
+
+/* SETBRIDGEPORT stuff */
+enum qeth_sbp_roles {
+ QETH_SBP_ROLE_NONE = 0,
+ QETH_SBP_ROLE_PRIMARY = 1,
+ QETH_SBP_ROLE_SECONDARY = 2,
+};
+
+enum qeth_sbp_states {
+ QETH_SBP_STATE_INACTIVE = 0,
+ QETH_SBP_STATE_STANDBY = 1,
+ QETH_SBP_STATE_ACTIVE = 2,
+};
+
+#define QETH_SBP_HOST_NOTIFICATION 1
+
+struct qeth_sbp_info {
+ __u32 supported_funcs;
+ enum qeth_sbp_roles role;
+ __u32 hostnotification:1;
+};
+
+static inline int qeth_is_ipa_supported(struct qeth_ipa_info *ipa,
+ enum qeth_ipa_funcs func)
+{
+ return (ipa->supported_funcs & func);
+}
+
+static inline int qeth_is_ipa_enabled(struct qeth_ipa_info *ipa,
+ enum qeth_ipa_funcs func)
+{
+ return (ipa->supported_funcs & ipa->enabled_funcs & func);
+}
+
+#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)
+#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)
+#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))
+
+#define QETH_IDX_FUNC_LEVEL_OSD 0x0101
+#define QETH_IDX_FUNC_LEVEL_IQD 0x4108
+
+#define QETH_MODELLIST_ARRAY \
+ {{0x1731, 0x01, 0x1732, QETH_CARD_TYPE_OSD, QETH_MAX_QUEUES, 0}, \
+ {0x1731, 0x05, 0x1732, QETH_CARD_TYPE_IQD, QETH_MAX_QUEUES, 0x103}, \
+ {0x1731, 0x06, 0x1732, QETH_CARD_TYPE_OSN, QETH_MAX_QUEUES, 0}, \
+ {0x1731, 0x02, 0x1732, QETH_CARD_TYPE_OSM, QETH_MAX_QUEUES, 0}, \
+ {0x1731, 0x02, 0x1732, QETH_CARD_TYPE_OSX, QETH_MAX_QUEUES, 0}, \
+ {0, 0, 0, 0, 0, 0} }
+#define QETH_CU_TYPE_IND 0
+#define QETH_CU_MODEL_IND 1
+#define QETH_DEV_TYPE_IND 2
+#define QETH_DEV_MODEL_IND 3
+#define QETH_QUEUE_NO_IND 4
+#define QETH_MULTICAST_IND 5
+
+#define QETH_REAL_CARD 1
+#define QETH_VLAN_CARD 2
+#define QETH_BUFSIZE 4096
+
+/**
+ * some more defs
+ */
+#define QETH_TX_TIMEOUT 100 * HZ
+#define QETH_RCD_TIMEOUT 60 * HZ
+#define QETH_RECLAIM_WORK_TIME HZ
+#define QETH_HEADER_SIZE 32
+#define QETH_MAX_PORTNO 15
+
+/*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 64
+#define QETH_IN_BUF_COUNT_HSDEFAULT 128
+#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 / 2)
+
+/* 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
+
+/* 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 QETH_PRIO_Q_ING_SKB 3
+#define QETH_PRIO_Q_ING_VLAN 4
+
+/* Packing */
+#define QETH_LOW_WATERMARK_PACK 2
+#define QETH_HIGH_WATERMARK_PACK 5
+#define QETH_WATERMARK_PACK_FUZZ 1
+
+#define QETH_IP_HEADER_SIZE 40
+
+/* large receive scatter gather copy break */
+#define QETH_RX_SG_CB (PAGE_SIZE >> 1)
+#define QETH_RX_PULL_LEN 256
+
+struct qeth_hdr_layer3 {
+ __u8 id;
+ __u8 flags;
+ __u16 inbound_checksum; /*TSO:__u16 seqno */
+ __u32 token; /*TSO: __u32 reserved */
+ __u16 length;
+ __u8 vlan_prio;
+ __u8 ext_flags;
+ __u16 vlan_id;
+ __u16 frame_offset;
+ __u8 dest_addr[16];
+} __attribute__ ((packed));
+
+struct qeth_hdr_layer2 {
+ __u8 id;
+ __u8 flags[3];
+ __u8 port_no;
+ __u8 hdr_length;
+ __u16 pkt_length;
+ __u16 seq_no;
+ __u16 vlan_id;
+ __u32 reserved;
+ __u8 reserved2[16];
+} __attribute__ ((packed));
+
+struct qeth_hdr_osn {
+ __u8 id;
+ __u8 reserved;
+ __u16 seq_no;
+ __u16 reserved2;
+ __u16 control_flags;
+ __u16 pdu_length;
+ __u8 reserved3[18];
+ __u32 ccid;
+} __attribute__ ((packed));
+
+struct qeth_hdr {
+ union {
+ struct qeth_hdr_layer2 l2;
+ struct qeth_hdr_layer3 l3;
+ struct qeth_hdr_osn osn;
+ } hdr;
+} __attribute__ ((packed));
+
+/*TCP Segmentation Offload header*/
+struct qeth_hdr_ext_tso {
+ __u16 hdr_tot_len;
+ __u8 imb_hdr_no;
+ __u8 reserved;
+ __u8 hdr_type;
+ __u8 hdr_version;
+ __u16 hdr_len;
+ __u32 payload_len;
+ __u16 mss;
+ __u16 dg_hdr_len;
+ __u8 padding[16];
+} __attribute__ ((packed));
+
+struct qeth_hdr_tso {
+ struct qeth_hdr hdr; /*hdr->hdr.l3.xxx*/
+ struct qeth_hdr_ext_tso ext;
+} __attribute__ ((packed));
+
+
+/* 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,
+};
+
+enum qeth_layer2_frame_flags {
+ QETH_LAYER2_FLAG_MULTICAST = 0x01,
+ QETH_LAYER2_FLAG_BROADCAST = 0x02,
+ QETH_LAYER2_FLAG_UNICAST = 0x04,
+ QETH_LAYER2_FLAG_VLAN = 0x10,
+};
+
+enum qeth_header_ids {
+ QETH_HEADER_TYPE_LAYER3 = 0x01,
+ QETH_HEADER_TYPE_LAYER2 = 0x02,
+ QETH_HEADER_TYPE_TSO = 0x03,
+ QETH_HEADER_TYPE_OSN = 0x04,
+};
+/* flags for qeth_hdr.ext_flags */
+#define QETH_HDR_EXT_VLAN_FRAME 0x01
+#define QETH_HDR_EXT_TOKEN_ID 0x02
+#define QETH_HDR_EXT_INCLUDE_VLAN_TAG 0x04
+#define QETH_HDR_EXT_SRC_MAC_ADDR 0x08
+#define QETH_HDR_EXT_CSUM_HDR_REQ 0x10
+#define QETH_HDR_EXT_CSUM_TRANSP_REQ 0x20
+#define QETH_HDR_EXT_UDP 0x40 /*bit off for TCP*/
+
+static inline int qeth_is_last_sbale(struct qdio_buffer_element *sbale)
+{
+ return (sbale->eflags & SBAL_EFLAGS_LAST_ENTRY);
+}
+
+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: not applicable
+ * outbound: identified to be pending in TPQ
+ */
+ QETH_QDIO_BUF_PENDING,
+ /*
+ * inbound: not applicable
+ * outbound: found in completion queue
+ */
+ QETH_QDIO_BUF_IN_CQ,
+ /*
+ * inbound: not applicable
+ * outbound: handled via transfer pending / completion queue
+ */
+ QETH_QDIO_BUF_HANDLED_DELAYED,
+};
+
+enum qeth_qdio_info_states {
+ QETH_QDIO_UNINITIALIZED,
+ QETH_QDIO_ALLOCATED,
+ QETH_QDIO_ESTABLISHED,
+ QETH_QDIO_CLEANING
+};
+
+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;
+ /* the buffer pool entry currently associated to this buffer */
+ struct qeth_buffer_pool_entry *pool_entry;
+ struct sk_buff *rx_skb;
+};
+
+struct qeth_qdio_q {
+ struct qdio_buffer qdio_bufs[QDIO_MAX_BUFFERS_PER_Q];
+ struct qeth_qdio_buffer bufs[QDIO_MAX_BUFFERS_PER_Q];
+ int next_buf_to_init;
+} __attribute__ ((aligned(256)));
+
+struct qeth_qdio_out_buffer {
+ struct qdio_buffer *buffer;
+ atomic_t state;
+ int next_element_to_fill;
+ struct sk_buff_head skb_list;
+ int is_header[16];
+
+ struct qaob *aob;
+ struct qeth_qdio_out_q *q;
+ struct qeth_qdio_out_buffer *next_pending;
+};
+
+struct qeth_card;
+
+enum qeth_out_q_states {
+ QETH_OUT_Q_UNLOCKED,
+ QETH_OUT_Q_LOCKED,
+ QETH_OUT_Q_LOCKED_FLUSH,
+};
+
+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];
+ struct qdio_outbuf_state *bufstates; /* convenience pointer */
+ int queue_no;
+ struct qeth_card *card;
+ atomic_t state;
+ int do_pack;
+ /*
+ * index of buffer to be filled by driver; state EMPTY or PACKING
+ */
+ int next_buf_to_fill;
+ /*
+ * 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 {
+ atomic_t state;
+ /* input */
+ int no_in_queues;
+ struct qeth_qdio_q *in_q;
+ struct qeth_qdio_q *c_q;
+ struct qeth_qdio_buffer_pool in_buf_pool;
+ struct qeth_qdio_buffer_pool init_pool;
+ int in_buf_size;
+
+ /* output */
+ int no_out_queues;
+ struct qeth_qdio_out_q **out_qs;
+ struct qdio_outbuf_state *out_bufstates;
+
+ /* priority queueing */
+ int do_prio_queueing;
+ int default_out_queue;
+};
+
+enum qeth_send_errors {
+ QETH_SEND_ERROR_NONE,
+ QETH_SEND_ERROR_LINK_FAILURE,
+ QETH_SEND_ERROR_RETRY,
+ QETH_SEND_ERROR_KICK_IT,
+};
+
+#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 DEFAULT_ADD_HHLEN 0
+#define MAX_ADD_HHLEN 1024
+
+/**
+ * buffer stuff for read channel
+ */
+#define QETH_CMD_BUFFER_NO 8
+
+/**
+ * channel state machine
+ */
+enum qeth_channel_states {
+ CH_STATE_UP,
+ CH_STATE_DOWN,
+ CH_STATE_ACTIVATING,
+ CH_STATE_HALTED,
+ CH_STATE_STOPPED,
+ CH_STATE_RCD,
+ CH_STATE_RCD_DONE,
+};
+/**
+ * card state machine
+ */
+enum qeth_card_states {
+ CARD_STATE_DOWN,
+ CARD_STATE_HARDSETUP,
+ CARD_STATE_SOFTSETUP,
+ CARD_STATE_UP,
+ CARD_STATE_RECOVER,
+};
+
+/**
+ * Protocol versions
+ */
+enum qeth_prot_versions {
+ QETH_PROT_IPV4 = 0x0004,
+ QETH_PROT_IPV6 = 0x0006,
+};
+
+enum qeth_ip_types {
+ QETH_IP_TYPE_NORMAL,
+ QETH_IP_TYPE_VIPA,
+ QETH_IP_TYPE_RXIP,
+ QETH_IP_TYPE_DEL_ALL_MC,
+};
+
+enum qeth_cmd_buffer_state {
+ BUF_STATE_FREE,
+ BUF_STATE_LOCKED,
+ BUF_STATE_PROCESSED,
+};
+
+enum qeth_cq {
+ QETH_CQ_DISABLED = 0,
+ QETH_CQ_ENABLED = 1,
+ QETH_CQ_NOTAVAILABLE = 2,
+};
+
+struct qeth_ipato {
+ int enabled;
+ int invert4;
+ int invert6;
+ struct list_head entries;
+};
+
+struct qeth_channel;
+
+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 *);
+};
+
+/**
+ * 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;
+ int io_buf_no;
+ int buf_no;
+};
+
+/**
+ * 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;
+ __u16 ipa;
+ __u32 pkt_seqno;
+};
+
+struct qeth_reply {
+ struct list_head list;
+ wait_queue_head_t wait_q;
+ int (*callback)(struct qeth_card *, struct qeth_reply *,
+ unsigned long);
+ u32 seqno;
+ unsigned long offset;
+ atomic_t received;
+ int rc;
+ void *param;
+ struct qeth_card *card;
+ atomic_t refcnt;
+};
+
+
+struct qeth_card_blkt {
+ int time_total;
+ int inter_packet;
+ int inter_packet_jumbo;
+};
+
+#define QETH_BROADCAST_WITH_ECHO 0x01
+#define QETH_BROADCAST_WITHOUT_ECHO 0x02
+#define QETH_LAYER2_MAC_READ 0x01
+#define QETH_LAYER2_MAC_REGISTERED 0x02
+struct qeth_card_info {
+ unsigned short unit_addr2;
+ unsigned short cula;
+ unsigned short chpid;
+ __u16 func_level;
+ char mcl_level[QETH_MCL_LENGTH + 1];
+ int guestlan;
+ int mac_bits;
+ 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 broadcast_capable;
+ int unique_id;
+ struct qeth_card_blkt blkt;
+ __u32 csum_mask;
+ __u32 tx_csum_mask;
+ enum qeth_ipa_promisc_modes promisc_mode;
+ __u32 diagass_support;
+ __u32 hwtrap;
+};
+
+struct qeth_card_options {
+ struct qeth_routing_info route4;
+ struct qeth_ipa_info ipa4;
+ struct qeth_ipa_info adp; /*Adapter parameters*/
+ struct qeth_routing_info route6;
+ struct qeth_ipa_info ipa6;
+ struct qeth_sbp_info sbp; /* SETBRIDGEPORT options */
+ int fake_broadcast;
+ int add_hhlen;
+ int layer2;
+ int performance_stats;
+ int rx_sg_cb;
+ enum qeth_ipa_isolation_modes isolation;
+ enum qeth_ipa_isolation_modes prev_isolation;
+ int sniffer;
+ enum qeth_cq cq;
+ char hsuid[9];
+};
+
+/*
+ * thread bits for qeth_card thread masks
+ */
+enum qeth_threads {
+ QETH_RECOVER_THREAD = 1,
+};
+
+struct qeth_osn_info {
+ int (*assist_cb)(struct net_device *dev, void *data);
+ int (*data_cb)(struct sk_buff *skb);
+};
+
+enum qeth_discipline_id {
+ QETH_DISCIPLINE_LAYER3 = 0,
+ QETH_DISCIPLINE_LAYER2 = 1,
+};
+
+struct qeth_discipline {
+ void (*start_poll)(struct ccw_device *, int, unsigned long);
+ qdio_handler_t *input_handler;
+ qdio_handler_t *output_handler;
+ int (*recover)(void *ptr);
+ int (*setup) (struct ccwgroup_device *);
+ void (*remove) (struct ccwgroup_device *);
+ int (*set_online) (struct ccwgroup_device *);
+ int (*set_offline) (struct ccwgroup_device *);
+ void (*shutdown)(struct ccwgroup_device *);
+ int (*prepare) (struct ccwgroup_device *);
+ void (*complete) (struct ccwgroup_device *);
+ int (*freeze)(struct ccwgroup_device *);
+ int (*thaw) (struct ccwgroup_device *);
+ int (*restore)(struct ccwgroup_device *);
+ int (*control_event_handler)(struct qeth_card *card,
+ struct qeth_ipa_cmd *cmd);
+};
+
+struct qeth_vlan_vid {
+ struct list_head list;
+ unsigned short vid;
+};
+
+struct qeth_mc_mac {
+ struct list_head list;
+ __u8 mc_addr[MAX_ADDR_LEN];
+ unsigned char mc_addrlen;
+ int is_vmac;
+};
+
+struct qeth_rx {
+ int b_count;
+ int b_index;
+ struct qdio_buffer_element *b_element;
+ int e_offset;
+ int qdio_err;
+};
+
+struct carrier_info {
+ __u8 card_type;
+ __u16 port_mode;
+ __u32 port_speed;
+};
+
+#define QETH_NAPI_WEIGHT NAPI_POLL_WEIGHT
+
+struct qeth_card {
+ struct list_head list;
+ enum qeth_card_states state;
+ int lan_online;
+ spinlock_t lock;
+ struct ccwgroup_device *gdev;
+ struct qeth_channel read;
+ struct qeth_channel write;
+ struct qeth_channel data;
+
+ struct net_device *dev;
+ struct net_device_stats stats;
+
+ struct qeth_card_info info;
+ struct qeth_token token;
+ struct qeth_seqno seqno;
+ struct qeth_card_options options;
+
+ wait_queue_head_t wait_q;
+ spinlock_t vlanlock;
+ spinlock_t mclock;
+ unsigned long active_vlans[BITS_TO_LONGS(VLAN_N_VID)];
+ struct list_head vid_list;
+ struct list_head mc_list;
+ struct work_struct kernel_thread_starter;
+ spinlock_t thread_mask_lock;
+ unsigned long thread_start_mask;
+ unsigned long thread_allowed_mask;
+ unsigned long thread_running_mask;
+ struct task_struct *recovery_task;
+ 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;
+ struct qeth_perf_stats perf_stats;
+ int read_or_write_problem;
+ struct qeth_osn_info osn_info;
+ struct qeth_discipline *discipline;
+ atomic_t force_alloc_skb;
+ struct service_level qeth_service_level;
+ struct qdio_ssqd_desc ssqd;
+ debug_info_t *debug;
+ struct mutex conf_mutex;
+ struct mutex discipline_mutex;
+ struct napi_struct napi;
+ struct qeth_rx rx;
+ struct delayed_work buffer_reclaim_work;
+ int reclaim_index;
+ struct work_struct close_dev_work;
+};
+
+struct qeth_card_list_struct {
+ struct list_head list;
+ rwlock_t rwlock;
+};
+
+struct qeth_trap_id {
+ __u16 lparnr;
+ char vmname[8];
+ __u8 chpid;
+ __u8 ssid;
+ __u16 devno;
+} __packed;
+
+/*some helper functions*/
+#define QETH_CARD_IFNAME(card) (((card)->dev)? (card)->dev->name : "")
+
+static inline struct qeth_card *CARD_FROM_CDEV(struct ccw_device *cdev)
+{
+ struct qeth_card *card = dev_get_drvdata(&((struct ccwgroup_device *)
+ dev_get_drvdata(&cdev->dev))->dev);
+ return card;
+}
+
+static inline int qeth_get_micros(void)
+{
+ return (int) (get_tod_clock() >> 12);
+}
+
+static inline int qeth_get_ip_version(struct sk_buff *skb)
+{
+ __be16 *p = &((struct ethhdr *)skb->data)->h_proto;
+
+ if (*p == ETH_P_8021Q)
+ p += 2;
+ switch (*p) {
+ case ETH_P_IPV6:
+ return 6;
+ case ETH_P_IP:
+ return 4;
+ default:
+ return 0;
+ }
+}
+
+static inline void qeth_put_buffer_pool_entry(struct qeth_card *card,
+ struct qeth_buffer_pool_entry *entry)
+{
+ list_add_tail(&entry->list, &card->qdio.in_buf_pool.entry_list);
+}
+
+static inline int qeth_is_diagass_supported(struct qeth_card *card,
+ enum qeth_diags_cmds cmd)
+{
+ return card->info.diagass_support & (__u32)cmd;
+}
+
+extern struct qeth_discipline qeth_l2_discipline;
+extern struct qeth_discipline qeth_l3_discipline;
+extern const struct attribute_group *qeth_generic_attr_groups[];
+extern const struct attribute_group *qeth_osn_attr_groups[];
+extern struct workqueue_struct *qeth_wq;
+
+const char *qeth_get_cardname_short(struct qeth_card *);
+int qeth_realloc_buffer_pool(struct qeth_card *, int);
+int qeth_core_load_discipline(struct qeth_card *, enum qeth_discipline_id);
+void qeth_core_free_discipline(struct qeth_card *);
+void qeth_buffer_reclaim_work(struct work_struct *);
+
+/* exports for qeth discipline device drivers */
+extern struct qeth_card_list_struct qeth_core_card_list;
+extern struct kmem_cache *qeth_core_header_cache;
+extern struct qeth_dbf_info qeth_dbf[QETH_DBF_INFOS];
+
+void qeth_set_recovery_task(struct qeth_card *);
+void qeth_clear_recovery_task(struct qeth_card *);
+void qeth_set_allowed_threads(struct qeth_card *, unsigned long , int);
+int qeth_threads_running(struct qeth_card *, unsigned long);
+int qeth_wait_for_threads(struct qeth_card *, unsigned long);
+int qeth_do_run_thread(struct qeth_card *, unsigned long);
+void qeth_clear_thread_start_bit(struct qeth_card *, unsigned long);
+void qeth_clear_thread_running_bit(struct qeth_card *, unsigned long);
+int qeth_core_hardsetup_card(struct qeth_card *);
+void qeth_print_status_message(struct qeth_card *);
+int qeth_init_qdio_queues(struct qeth_card *);
+int qeth_send_startlan(struct qeth_card *);
+int qeth_send_stoplan(struct qeth_card *);
+int qeth_send_ipa_cmd(struct qeth_card *, struct qeth_cmd_buffer *,
+ int (*reply_cb)
+ (struct qeth_card *, struct qeth_reply *, unsigned long),
+ void *);
+struct qeth_cmd_buffer *qeth_get_ipacmd_buffer(struct qeth_card *,
+ enum qeth_ipa_cmds, enum qeth_prot_versions);
+int qeth_query_setadapterparms(struct qeth_card *);
+int qeth_check_qdio_errors(struct qeth_card *, struct qdio_buffer *,
+ unsigned int, const char *);
+void qeth_queue_input_buffer(struct qeth_card *, int);
+struct sk_buff *qeth_core_get_next_skb(struct qeth_card *,
+ struct qeth_qdio_buffer *, struct qdio_buffer_element **, int *,
+ struct qeth_hdr **);
+void qeth_schedule_recovery(struct qeth_card *);
+void qeth_qdio_start_poll(struct ccw_device *, int, unsigned long);
+void qeth_qdio_input_handler(struct ccw_device *,
+ unsigned int, unsigned int, int,
+ int, unsigned long);
+void qeth_qdio_output_handler(struct ccw_device *, unsigned int,
+ int, int, int, unsigned long);
+void qeth_clear_ipacmd_list(struct qeth_card *);
+int qeth_qdio_clear_card(struct qeth_card *, int);
+void qeth_clear_working_pool_list(struct qeth_card *);
+void qeth_clear_cmd_buffers(struct qeth_channel *);
+void qeth_clear_qdio_buffers(struct qeth_card *);
+void qeth_setadp_promisc_mode(struct qeth_card *);
+struct net_device_stats *qeth_get_stats(struct net_device *);
+int qeth_change_mtu(struct net_device *, int);
+int qeth_setadpparms_change_macaddr(struct qeth_card *);
+void qeth_tx_timeout(struct net_device *);
+void qeth_prepare_control_data(struct qeth_card *, int,
+ struct qeth_cmd_buffer *);
+void qeth_release_buffer(struct qeth_channel *, struct qeth_cmd_buffer *);
+void qeth_prepare_ipa_cmd(struct qeth_card *, struct qeth_cmd_buffer *, char);
+struct qeth_cmd_buffer *qeth_wait_for_buffer(struct qeth_channel *);
+int qeth_mdio_read(struct net_device *, int, int);
+int qeth_snmp_command(struct qeth_card *, char __user *);
+int qeth_query_oat_command(struct qeth_card *, char __user *);
+int qeth_query_card_info(struct qeth_card *card,
+ struct carrier_info *carrier_info);
+int qeth_send_control_data(struct qeth_card *, int, struct qeth_cmd_buffer *,
+ int (*reply_cb)(struct qeth_card *, struct qeth_reply*, unsigned long),
+ void *reply_param);
+int qeth_bridgeport_query_ports(struct qeth_card *card,
+ enum qeth_sbp_roles *role, enum qeth_sbp_states *state);
+int qeth_bridgeport_setrole(struct qeth_card *card, enum qeth_sbp_roles role);
+int qeth_bridgeport_an_set(struct qeth_card *card, int enable);
+int qeth_get_priority_queue(struct qeth_card *, struct sk_buff *, int, int);
+int qeth_get_elements_no(struct qeth_card *, struct sk_buff *, int);
+int qeth_get_elements_for_frags(struct sk_buff *);
+int qeth_do_send_packet_fast(struct qeth_card *, struct qeth_qdio_out_q *,
+ struct sk_buff *, struct qeth_hdr *, int, int, int);
+int qeth_do_send_packet(struct qeth_card *, struct qeth_qdio_out_q *,
+ struct sk_buff *, struct qeth_hdr *, int);
+int qeth_core_get_sset_count(struct net_device *, int);
+void qeth_core_get_ethtool_stats(struct net_device *,
+ struct ethtool_stats *, u64 *);
+void qeth_core_get_strings(struct net_device *, u32, u8 *);
+void qeth_core_get_drvinfo(struct net_device *, struct ethtool_drvinfo *);
+void qeth_dbf_longtext(debug_info_t *id, int level, char *text, ...);
+int qeth_core_ethtool_get_settings(struct net_device *, struct ethtool_cmd *);
+int qeth_set_access_ctrl_online(struct qeth_card *card, int fallback);
+int qeth_hdr_chk_and_bounce(struct sk_buff *, struct qeth_hdr **, int);
+int qeth_configure_cq(struct qeth_card *, enum qeth_cq);
+int qeth_hw_trap(struct qeth_card *, enum qeth_diags_trap_action);
+int qeth_query_ipassists(struct qeth_card *, enum qeth_prot_versions prot);
+void qeth_trace_features(struct qeth_card *);
+void qeth_close_dev(struct qeth_card *);
+
+/* exports for OSN */
+int qeth_osn_assist(struct net_device *, void *, int);
+int qeth_osn_register(unsigned char *read_dev_no, struct net_device **,
+ int (*assist_cb)(struct net_device *, void *),
+ int (*data_cb)(struct sk_buff *));
+void qeth_osn_deregister(struct net_device *);
+
+#endif /* __QETH_CORE_H__ */
diff --git a/drivers/s390/net/qeth_core_main.c b/drivers/s390/net/qeth_core_main.c
new file mode 100644
index 00000000000..f54bec54d67
--- /dev/null
+++ b/drivers/s390/net/qeth_core_main.c
@@ -0,0 +1,5894 @@
+/*
+ * Copyright IBM Corp. 2007, 2009
+ * Author(s): Utz Bacher <utz.bacher@de.ibm.com>,
+ * Frank Pavlic <fpavlic@de.ibm.com>,
+ * Thomas Spatzier <tspat@de.ibm.com>,
+ * Frank Blaschka <frank.blaschka@de.ibm.com>
+ */
+
+#define KMSG_COMPONENT "qeth"
+#define pr_fmt(fmt) KMSG_COMPONENT ": " fmt
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/string.h>
+#include <linux/errno.h>
+#include <linux/kernel.h>
+#include <linux/ip.h>
+#include <linux/tcp.h>
+#include <linux/mii.h>
+#include <linux/kthread.h>
+#include <linux/slab.h>
+#include <net/iucv/af_iucv.h>
+#include <net/dsfield.h>
+
+#include <asm/ebcdic.h>
+#include <asm/chpid.h>
+#include <asm/io.h>
+#include <asm/sysinfo.h>
+#include <asm/compat.h>
+
+#include "qeth_core.h"
+
+struct qeth_dbf_info qeth_dbf[QETH_DBF_INFOS] = {
+ /* define dbf - Name, Pages, Areas, Maxlen, Level, View, Handle */
+ /* N P A M L V H */
+ [QETH_DBF_SETUP] = {"qeth_setup",
+ 8, 1, 8, 5, &debug_hex_ascii_view, NULL},
+ [QETH_DBF_MSG] = {"qeth_msg", 8, 1, 11 * sizeof(long), 3,
+ &debug_sprintf_view, NULL},
+ [QETH_DBF_CTRL] = {"qeth_control",
+ 8, 1, QETH_DBF_CTRL_LEN, 5, &debug_hex_ascii_view, NULL},
+};
+EXPORT_SYMBOL_GPL(qeth_dbf);
+
+struct qeth_card_list_struct qeth_core_card_list;
+EXPORT_SYMBOL_GPL(qeth_core_card_list);
+struct kmem_cache *qeth_core_header_cache;
+EXPORT_SYMBOL_GPL(qeth_core_header_cache);
+static struct kmem_cache *qeth_qdio_outbuf_cache;
+
+static struct device *qeth_core_root_dev;
+static unsigned int known_devices[][6] = QETH_MODELLIST_ARRAY;
+static struct lock_class_key qdio_out_skb_queue_key;
+static struct mutex qeth_mod_mutex;
+
+static void qeth_send_control_data_cb(struct qeth_channel *,
+ struct qeth_cmd_buffer *);
+static int qeth_issue_next_read(struct qeth_card *);
+static struct qeth_cmd_buffer *qeth_get_buffer(struct qeth_channel *);
+static void qeth_setup_ccw(struct qeth_channel *, unsigned char *, __u32);
+static void qeth_free_buffer_pool(struct qeth_card *);
+static int qeth_qdio_establish(struct qeth_card *);
+static void qeth_free_qdio_buffers(struct qeth_card *);
+static void qeth_notify_skbs(struct qeth_qdio_out_q *queue,
+ struct qeth_qdio_out_buffer *buf,
+ enum iucv_tx_notify notification);
+static void qeth_release_skbs(struct qeth_qdio_out_buffer *buf);
+static void qeth_clear_output_buffer(struct qeth_qdio_out_q *queue,
+ struct qeth_qdio_out_buffer *buf,
+ enum qeth_qdio_buffer_states newbufstate);
+static int qeth_init_qdio_out_buf(struct qeth_qdio_out_q *, int);
+
+struct workqueue_struct *qeth_wq;
+EXPORT_SYMBOL_GPL(qeth_wq);
+
+static void qeth_close_dev_handler(struct work_struct *work)
+{
+ struct qeth_card *card;
+
+ card = container_of(work, struct qeth_card, close_dev_work);
+ QETH_CARD_TEXT(card, 2, "cldevhdl");
+ rtnl_lock();
+ dev_close(card->dev);
+ rtnl_unlock();
+ ccwgroup_set_offline(card->gdev);
+}
+
+void qeth_close_dev(struct qeth_card *card)
+{
+ QETH_CARD_TEXT(card, 2, "cldevsubm");
+ queue_work(qeth_wq, &card->close_dev_work);
+}
+EXPORT_SYMBOL_GPL(qeth_close_dev);
+
+static inline const char *qeth_get_cardname(struct qeth_card *card)
+{
+ if (card->info.guestlan) {
+ switch (card->info.type) {
+ case QETH_CARD_TYPE_OSD:
+ return " Virtual NIC QDIO";
+ case QETH_CARD_TYPE_IQD:
+ return " Virtual NIC Hiper";
+ case QETH_CARD_TYPE_OSM:
+ return " Virtual NIC QDIO - OSM";
+ case QETH_CARD_TYPE_OSX:
+ return " Virtual NIC QDIO - OSX";
+ default:
+ return " unknown";
+ }
+ } else {
+ switch (card->info.type) {
+ case QETH_CARD_TYPE_OSD:
+ return " OSD Express";
+ case QETH_CARD_TYPE_IQD:
+ return " HiperSockets";
+ case QETH_CARD_TYPE_OSN:
+ return " OSN QDIO";
+ case QETH_CARD_TYPE_OSM:
+ return " OSM QDIO";
+ case QETH_CARD_TYPE_OSX:
+ return " OSX QDIO";
+ default:
+ return " unknown";
+ }
+ }
+ return " n/a";
+}
+
+/* max length to be returned: 14 */
+const char *qeth_get_cardname_short(struct qeth_card *card)
+{
+ if (card->info.guestlan) {
+ switch (card->info.type) {
+ case QETH_CARD_TYPE_OSD:
+ return "Virt.NIC QDIO";
+ case QETH_CARD_TYPE_IQD:
+ return "Virt.NIC Hiper";
+ case QETH_CARD_TYPE_OSM:
+ return "Virt.NIC OSM";
+ case QETH_CARD_TYPE_OSX:
+ return "Virt.NIC OSX";
+ default:
+ return "unknown";
+ }
+ } else {
+ switch (card->info.type) {
+ case QETH_CARD_TYPE_OSD:
+ 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_10GBIT_ETH:
+ return "OSD_10GIG";
+ 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";
+ case QETH_CARD_TYPE_OSN:
+ return "OSN";
+ case QETH_CARD_TYPE_OSM:
+ return "OSM_1000";
+ case QETH_CARD_TYPE_OSX:
+ return "OSX_10GIG";
+ default:
+ return "unknown";
+ }
+ }
+ return "n/a";
+}
+
+void qeth_set_recovery_task(struct qeth_card *card)
+{
+ card->recovery_task = current;
+}
+EXPORT_SYMBOL_GPL(qeth_set_recovery_task);
+
+void qeth_clear_recovery_task(struct qeth_card *card)
+{
+ card->recovery_task = NULL;
+}
+EXPORT_SYMBOL_GPL(qeth_clear_recovery_task);
+
+static bool qeth_is_recovery_task(const struct qeth_card *card)
+{
+ return card->recovery_task == current;
+}
+
+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);
+}
+EXPORT_SYMBOL_GPL(qeth_set_allowed_threads);
+
+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;
+}
+EXPORT_SYMBOL_GPL(qeth_threads_running);
+
+int qeth_wait_for_threads(struct qeth_card *card, unsigned long threads)
+{
+ if (qeth_is_recovery_task(card))
+ return 0;
+ return wait_event_interruptible(card->wait_q,
+ qeth_threads_running(card, threads) == 0);
+}
+EXPORT_SYMBOL_GPL(qeth_wait_for_threads);
+
+void qeth_clear_working_pool_list(struct qeth_card *card)
+{
+ struct qeth_buffer_pool_entry *pool_entry, *tmp;
+
+ QETH_CARD_TEXT(card, 5, "clwrklst");
+ list_for_each_entry_safe(pool_entry, tmp,
+ &card->qdio.in_buf_pool.entry_list, list){
+ list_del(&pool_entry->list);
+ }
+}
+EXPORT_SYMBOL_GPL(qeth_clear_working_pool_list);
+
+static int qeth_alloc_buffer_pool(struct qeth_card *card)
+{
+ struct qeth_buffer_pool_entry *pool_entry;
+ void *ptr;
+ int i, j;
+
+ QETH_CARD_TEXT(card, 5, "alocpool");
+ for (i = 0; i < card->qdio.init_pool.buf_count; ++i) {
+ pool_entry = kzalloc(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);
+ }
+ return 0;
+}
+
+int qeth_realloc_buffer_pool(struct qeth_card *card, int bufcnt)
+{
+ QETH_CARD_TEXT(card, 2, "realcbp");
+
+ if ((card->state != CARD_STATE_DOWN) &&
+ (card->state != CARD_STATE_RECOVER))
+ return -EPERM;
+
+ /* TODO: steel/add buffers from/to a running card's buffer pool (?) */
+ qeth_clear_working_pool_list(card);
+ qeth_free_buffer_pool(card);
+ card->qdio.in_buf_pool.buf_count = bufcnt;
+ card->qdio.init_pool.buf_count = bufcnt;
+ return qeth_alloc_buffer_pool(card);
+}
+EXPORT_SYMBOL_GPL(qeth_realloc_buffer_pool);
+
+static inline int qeth_cq_init(struct qeth_card *card)
+{
+ int rc;
+
+ if (card->options.cq == QETH_CQ_ENABLED) {
+ QETH_DBF_TEXT(SETUP, 2, "cqinit");
+ memset(card->qdio.c_q->qdio_bufs, 0,
+ QDIO_MAX_BUFFERS_PER_Q * sizeof(struct qdio_buffer));
+ card->qdio.c_q->next_buf_to_init = 127;
+ rc = do_QDIO(CARD_DDEV(card), QDIO_FLAG_SYNC_INPUT,
+ card->qdio.no_in_queues - 1, 0,
+ 127);
+ if (rc) {
+ QETH_DBF_TEXT_(SETUP, 2, "1err%d", rc);
+ goto out;
+ }
+ }
+ rc = 0;
+out:
+ return rc;
+}
+
+static inline int qeth_alloc_cq(struct qeth_card *card)
+{
+ int rc;
+
+ if (card->options.cq == QETH_CQ_ENABLED) {
+ int i;
+ struct qdio_outbuf_state *outbuf_states;
+
+ QETH_DBF_TEXT(SETUP, 2, "cqon");
+ card->qdio.c_q = kzalloc(sizeof(struct qeth_qdio_q),
+ GFP_KERNEL);
+ if (!card->qdio.c_q) {
+ rc = -1;
+ goto kmsg_out;
+ }
+ QETH_DBF_HEX(SETUP, 2, &card->qdio.c_q, sizeof(void *));
+
+ for (i = 0; i < QDIO_MAX_BUFFERS_PER_Q; ++i) {
+ card->qdio.c_q->bufs[i].buffer =
+ &card->qdio.c_q->qdio_bufs[i];
+ }
+
+ card->qdio.no_in_queues = 2;
+
+ card->qdio.out_bufstates =
+ kzalloc(card->qdio.no_out_queues *
+ QDIO_MAX_BUFFERS_PER_Q *
+ sizeof(struct qdio_outbuf_state), GFP_KERNEL);
+ outbuf_states = card->qdio.out_bufstates;
+ if (outbuf_states == NULL) {
+ rc = -1;
+ goto free_cq_out;
+ }
+ for (i = 0; i < card->qdio.no_out_queues; ++i) {
+ card->qdio.out_qs[i]->bufstates = outbuf_states;
+ outbuf_states += QDIO_MAX_BUFFERS_PER_Q;
+ }
+ } else {
+ QETH_DBF_TEXT(SETUP, 2, "nocq");
+ card->qdio.c_q = NULL;
+ card->qdio.no_in_queues = 1;
+ }
+ QETH_DBF_TEXT_(SETUP, 2, "iqc%d", card->qdio.no_in_queues);
+ rc = 0;
+out:
+ return rc;
+free_cq_out:
+ kfree(card->qdio.c_q);
+ card->qdio.c_q = NULL;
+kmsg_out:
+ dev_err(&card->gdev->dev, "Failed to create completion queue\n");
+ goto out;
+}
+
+static inline void qeth_free_cq(struct qeth_card *card)
+{
+ if (card->qdio.c_q) {
+ --card->qdio.no_in_queues;
+ kfree(card->qdio.c_q);
+ card->qdio.c_q = NULL;
+ }
+ kfree(card->qdio.out_bufstates);
+ card->qdio.out_bufstates = NULL;
+}
+
+static inline enum iucv_tx_notify qeth_compute_cq_notification(int sbalf15,
+ int delayed) {
+ enum iucv_tx_notify n;
+
+ switch (sbalf15) {
+ case 0:
+ n = delayed ? TX_NOTIFY_DELAYED_OK : TX_NOTIFY_OK;
+ break;
+ case 4:
+ case 16:
+ case 17:
+ case 18:
+ n = delayed ? TX_NOTIFY_DELAYED_UNREACHABLE :
+ TX_NOTIFY_UNREACHABLE;
+ break;
+ default:
+ n = delayed ? TX_NOTIFY_DELAYED_GENERALERROR :
+ TX_NOTIFY_GENERALERROR;
+ break;
+ }
+
+ return n;
+}
+
+static inline void qeth_cleanup_handled_pending(struct qeth_qdio_out_q *q,
+ int bidx, int forced_cleanup)
+{
+ if (q->card->options.cq != QETH_CQ_ENABLED)
+ return;
+
+ if (q->bufs[bidx]->next_pending != NULL) {
+ struct qeth_qdio_out_buffer *head = q->bufs[bidx];
+ struct qeth_qdio_out_buffer *c = q->bufs[bidx]->next_pending;
+
+ while (c) {
+ if (forced_cleanup ||
+ atomic_read(&c->state) ==
+ QETH_QDIO_BUF_HANDLED_DELAYED) {
+ struct qeth_qdio_out_buffer *f = c;
+ QETH_CARD_TEXT(f->q->card, 5, "fp");
+ QETH_CARD_TEXT_(f->q->card, 5, "%lx", (long) f);
+ /* release here to avoid interleaving between
+ outbound tasklet and inbound tasklet
+ regarding notifications and lifecycle */
+ qeth_release_skbs(c);
+
+ c = f->next_pending;
+ WARN_ON_ONCE(head->next_pending != f);
+ head->next_pending = c;
+ kmem_cache_free(qeth_qdio_outbuf_cache, f);
+ } else {
+ head = c;
+ c = c->next_pending;
+ }
+
+ }
+ }
+ if (forced_cleanup && (atomic_read(&(q->bufs[bidx]->state)) ==
+ QETH_QDIO_BUF_HANDLED_DELAYED)) {
+ /* for recovery situations */
+ q->bufs[bidx]->aob = q->bufstates[bidx].aob;
+ qeth_init_qdio_out_buf(q, bidx);
+ QETH_CARD_TEXT(q->card, 2, "clprecov");
+ }
+}
+
+
+static inline void qeth_qdio_handle_aob(struct qeth_card *card,
+ unsigned long phys_aob_addr) {
+ struct qaob *aob;
+ struct qeth_qdio_out_buffer *buffer;
+ enum iucv_tx_notify notification;
+
+ aob = (struct qaob *) phys_to_virt(phys_aob_addr);
+ QETH_CARD_TEXT(card, 5, "haob");
+ QETH_CARD_TEXT_(card, 5, "%lx", phys_aob_addr);
+ buffer = (struct qeth_qdio_out_buffer *) aob->user1;
+ QETH_CARD_TEXT_(card, 5, "%lx", aob->user1);
+
+ if (atomic_cmpxchg(&buffer->state, QETH_QDIO_BUF_PRIMED,
+ QETH_QDIO_BUF_IN_CQ) == QETH_QDIO_BUF_PRIMED) {
+ notification = TX_NOTIFY_OK;
+ } else {
+ WARN_ON_ONCE(atomic_read(&buffer->state) !=
+ QETH_QDIO_BUF_PENDING);
+ atomic_set(&buffer->state, QETH_QDIO_BUF_IN_CQ);
+ notification = TX_NOTIFY_DELAYED_OK;
+ }
+
+ if (aob->aorc != 0) {
+ QETH_CARD_TEXT_(card, 2, "aorc%02X", aob->aorc);
+ notification = qeth_compute_cq_notification(aob->aorc, 1);
+ }
+ qeth_notify_skbs(buffer->q, buffer, notification);
+
+ buffer->aob = NULL;
+ qeth_clear_output_buffer(buffer->q, buffer,
+ QETH_QDIO_BUF_HANDLED_DELAYED);
+
+ /* from here on: do not touch buffer anymore */
+ qdio_release_aob(aob);
+}
+
+static inline int qeth_is_cq(struct qeth_card *card, unsigned int queue)
+{
+ return card->options.cq == QETH_CQ_ENABLED &&
+ card->qdio.c_q != NULL &&
+ queue != 0 &&
+ queue == card->qdio.no_in_queues - 1;
+}
+
+
+static int qeth_issue_next_read(struct qeth_card *card)
+{
+ int rc;
+ struct qeth_cmd_buffer *iob;
+
+ QETH_CARD_TEXT(card, 5, "issnxrd");
+ if (card->read.state != CH_STATE_UP)
+ return -EIO;
+ iob = qeth_get_buffer(&card->read);
+ if (!iob) {
+ dev_warn(&card->gdev->dev, "The qeth device driver "
+ "failed to recover an error on the device\n");
+ QETH_DBF_MESSAGE(2, "%s issue_next_read failed: no iob "
+ "available\n", dev_name(&card->gdev->dev));
+ return -ENOMEM;
+ }
+ qeth_setup_ccw(&card->read, iob->data, QETH_BUFSIZE);
+ QETH_CARD_TEXT(card, 6, "noirqpnd");
+ rc = ccw_device_start(card->read.ccwdev, &card->read.ccw,
+ (addr_t) iob, 0, 0);
+ if (rc) {
+ QETH_DBF_MESSAGE(2, "%s error in starting next read ccw! "
+ "rc=%i\n", dev_name(&card->gdev->dev), rc);
+ atomic_set(&card->read.irq_pending, 0);
+ card->read_or_write_problem = 1;
+ 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 = kzalloc(sizeof(struct qeth_reply), GFP_ATOMIC);
+ if (reply) {
+ atomic_set(&reply->refcnt, 1);
+ atomic_set(&reply->received, 0);
+ 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_issue_ipa_msg(struct qeth_ipa_cmd *cmd, int rc,
+ struct qeth_card *card)
+{
+ char *ipa_name;
+ int com = cmd->hdr.command;
+ ipa_name = qeth_get_ipa_cmd_name(com);
+ if (rc)
+ QETH_DBF_MESSAGE(2, "IPA: %s(x%X) for %s/%s returned "
+ "x%X \"%s\"\n",
+ ipa_name, com, dev_name(&card->gdev->dev),
+ QETH_CARD_IFNAME(card), rc,
+ qeth_get_ipa_msg(rc));
+ else
+ QETH_DBF_MESSAGE(5, "IPA: %s(x%X) for %s/%s succeeded\n",
+ ipa_name, com, dev_name(&card->gdev->dev),
+ QETH_CARD_IFNAME(card));
+}
+
+static struct qeth_ipa_cmd *qeth_check_ipa_data(struct qeth_card *card,
+ struct qeth_cmd_buffer *iob)
+{
+ struct qeth_ipa_cmd *cmd = NULL;
+
+ QETH_CARD_TEXT(card, 5, "chkipad");
+ if (IS_IPA(iob->data)) {
+ cmd = (struct qeth_ipa_cmd *) PDU_ENCAPSULATION(iob->data);
+ if (IS_IPA_REPLY(cmd)) {
+ if (cmd->hdr.command != IPA_CMD_SETCCID &&
+ cmd->hdr.command != IPA_CMD_DELCCID &&
+ cmd->hdr.command != IPA_CMD_MODCCID &&
+ cmd->hdr.command != IPA_CMD_SET_DIAG_ASS)
+ qeth_issue_ipa_msg(cmd,
+ cmd->hdr.return_code, card);
+ return cmd;
+ } else {
+ switch (cmd->hdr.command) {
+ case IPA_CMD_STOPLAN:
+ if (cmd->hdr.return_code ==
+ IPA_RC_VEPA_TO_VEB_TRANSITION) {
+ dev_err(&card->gdev->dev,
+ "Interface %s is down because the "
+ "adjacent port is no longer in "
+ "reflective relay mode\n",
+ QETH_CARD_IFNAME(card));
+ qeth_close_dev(card);
+ } else {
+ dev_warn(&card->gdev->dev,
+ "The link for interface %s on CHPID"
+ " 0x%X failed\n",
+ QETH_CARD_IFNAME(card),
+ card->info.chpid);
+ qeth_issue_ipa_msg(cmd,
+ cmd->hdr.return_code, card);
+ }
+ card->lan_online = 0;
+ if (card->dev && netif_carrier_ok(card->dev))
+ netif_carrier_off(card->dev);
+ return NULL;
+ case IPA_CMD_STARTLAN:
+ dev_info(&card->gdev->dev,
+ "The link for %s on CHPID 0x%X has"
+ " been restored\n",
+ QETH_CARD_IFNAME(card),
+ card->info.chpid);
+ netif_carrier_on(card->dev);
+ card->lan_online = 1;
+ if (card->info.hwtrap)
+ card->info.hwtrap = 2;
+ qeth_schedule_recovery(card);
+ return NULL;
+ case IPA_CMD_SETBRIDGEPORT:
+ case IPA_CMD_ADDRESS_CHANGE_NOTIF:
+ if (card->discipline->control_event_handler
+ (card, cmd))
+ return cmd;
+ else
+ return NULL;
+ case IPA_CMD_MODCCID:
+ return cmd;
+ case IPA_CMD_REGISTER_LOCAL_ADDR:
+ QETH_CARD_TEXT(card, 3, "irla");
+ break;
+ case IPA_CMD_UNREGISTER_LOCAL_ADDR:
+ QETH_CARD_TEXT(card, 3, "urla");
+ break;
+ default:
+ QETH_DBF_MESSAGE(2, "Received data is IPA "
+ "but not a reply!\n");
+ break;
+ }
+ }
+ }
+ return cmd;
+}
+
+void qeth_clear_ipacmd_list(struct qeth_card *card)
+{
+ struct qeth_reply *reply, *r;
+ unsigned long flags;
+
+ QETH_CARD_TEXT(card, 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;
+ atomic_inc(&reply->received);
+ list_del_init(&reply->list);
+ wake_up(&reply->wait_q);
+ qeth_put_reply(reply);
+ }
+ spin_unlock_irqrestore(&card->lock, flags);
+ atomic_set(&card->write.irq_pending, 0);
+}
+EXPORT_SYMBOL_GPL(qeth_clear_ipacmd_list);
+
+static int qeth_check_idx_response(struct qeth_card *card,
+ unsigned char *buffer)
+{
+ if (!buffer)
+ return 0;
+
+ QETH_DBF_HEX(CTRL, 2, buffer, QETH_DBF_CTRL_LEN);
+ if ((buffer[2] & 0xc0) == 0xc0) {
+ QETH_DBF_MESSAGE(2, "received an IDX TERMINATE "
+ "with cause code 0x%02x%s\n",
+ buffer[4],
+ ((buffer[4] == 0x22) ?
+ " -- try another portname" : ""));
+ QETH_CARD_TEXT(card, 2, "ckidxres");
+ QETH_CARD_TEXT(card, 2, " idxterm");
+ QETH_CARD_TEXT_(card, 2, " rc%d", -EIO);
+ if (buffer[4] == 0xf6) {
+ dev_err(&card->gdev->dev,
+ "The qeth device is not configured "
+ "for the OSI layer required by z/VM\n");
+ return -EPERM;
+ }
+ return -EIO;
+ }
+ return 0;
+}
+
+static void qeth_setup_ccw(struct qeth_channel *channel, unsigned char *iob,
+ __u32 len)
+{
+ struct qeth_card *card;
+
+ card = CARD_FROM_CDEV(channel->ccwdev);
+ QETH_CARD_TEXT(card, 4, "setupccw");
+ 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);
+}
+
+static struct qeth_cmd_buffer *__qeth_get_buffer(struct qeth_channel *channel)
+{
+ __u8 index;
+
+ QETH_CARD_TEXT(CARD_FROM_CDEV(channel->ccwdev), 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;
+}
+
+void qeth_release_buffer(struct qeth_channel *channel,
+ struct qeth_cmd_buffer *iob)
+{
+ unsigned long flags;
+
+ QETH_CARD_TEXT(CARD_FROM_CDEV(channel->ccwdev), 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);
+ wake_up(&channel->wait_q);
+}
+EXPORT_SYMBOL_GPL(qeth_release_buffer);
+
+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;
+}
+
+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;
+}
+EXPORT_SYMBOL_GPL(qeth_wait_for_buffer);
+
+void qeth_clear_cmd_buffers(struct qeth_channel *channel)
+{
+ int cnt;
+
+ 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;
+}
+EXPORT_SYMBOL_GPL(qeth_clear_cmd_buffers);
+
+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;
+ int rc = 0;
+
+ card = CARD_FROM_CDEV(channel->ccwdev);
+ QETH_CARD_TEXT(card, 4, "sndctlcb");
+ rc = qeth_check_idx_response(card, iob->data);
+ switch (rc) {
+ case 0:
+ break;
+ case -EIO:
+ qeth_clear_ipacmd_list(card);
+ qeth_schedule_recovery(card);
+ /* fall through */
+ default:
+ goto out;
+ }
+
+ cmd = qeth_check_ipa_data(card, iob);
+ if ((cmd == NULL) && (card->state != CARD_STATE_DOWN))
+ goto out;
+ /*in case of OSN : check if cmd is set */
+ if (card->info.type == QETH_CARD_TYPE_OSN &&
+ cmd &&
+ cmd->hdr.command != IPA_CMD_STARTLAN &&
+ card->osn_info.assist_cb != NULL) {
+ card->osn_info.assist_cb(card->dev, cmd);
+ 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) {
+ reply->offset = (__u16)((char *)cmd -
+ (char *)iob->data);
+ keep_reply = reply->callback(card,
+ reply,
+ (unsigned long)cmd);
+ } else
+ keep_reply = reply->callback(card,
+ reply,
+ (unsigned long)iob);
+ }
+ if (cmd)
+ reply->rc = (u16) 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 {
+ atomic_inc(&reply->received);
+ 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_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 =
+ kzalloc(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);
+ return 0;
+}
+
+static int qeth_set_thread_start_bit(struct qeth_card *card,
+ unsigned long thread)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&card->thread_mask_lock, flags);
+ if (!(card->thread_allowed_mask & thread) ||
+ (card->thread_start_mask & thread)) {
+ spin_unlock_irqrestore(&card->thread_mask_lock, flags);
+ return -EPERM;
+ }
+ card->thread_start_mask |= thread;
+ spin_unlock_irqrestore(&card->thread_mask_lock, flags);
+ return 0;
+}
+
+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);
+}
+EXPORT_SYMBOL_GPL(qeth_clear_thread_start_bit);
+
+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);
+}
+EXPORT_SYMBOL_GPL(qeth_clear_thread_running_bit);
+
+static 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;
+}
+
+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;
+}
+EXPORT_SYMBOL_GPL(qeth_do_run_thread);
+
+void qeth_schedule_recovery(struct qeth_card *card)
+{
+ QETH_CARD_TEXT(card, 2, "startrec");
+ if (qeth_set_thread_start_bit(card, QETH_RECOVER_THREAD) == 0)
+ schedule_work(&card->kernel_thread_starter);
+}
+EXPORT_SYMBOL_GPL(qeth_schedule_recovery);
+
+static int qeth_get_problem(struct ccw_device *cdev, struct irb *irb)
+{
+ int dstat, cstat;
+ char *sense;
+ struct qeth_card *card;
+
+ sense = (char *) irb->ecw;
+ cstat = irb->scsw.cmd.cstat;
+ dstat = irb->scsw.cmd.dstat;
+ card = CARD_FROM_CDEV(cdev);
+
+ 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_CARD_TEXT(card, 2, "CGENCHK");
+ dev_warn(&cdev->dev, "The qeth device driver "
+ "failed to recover an error on the device\n");
+ QETH_DBF_MESSAGE(2, "%s check on device dstat=x%x, cstat=x%x\n",
+ dev_name(&cdev->dev), dstat, cstat);
+ print_hex_dump(KERN_WARNING, "qeth: irb ", DUMP_PREFIX_OFFSET,
+ 16, 1, irb, 64, 1);
+ return 1;
+ }
+
+ if (dstat & DEV_STAT_UNIT_CHECK) {
+ if (sense[SENSE_RESETTING_EVENT_BYTE] &
+ SENSE_RESETTING_EVENT_FLAG) {
+ QETH_CARD_TEXT(card, 2, "REVIND");
+ return 1;
+ }
+ if (sense[SENSE_COMMAND_REJECT_BYTE] &
+ SENSE_COMMAND_REJECT_FLAG) {
+ QETH_CARD_TEXT(card, 2, "CMDREJi");
+ return 1;
+ }
+ if ((sense[2] == 0xaf) && (sense[3] == 0xfe)) {
+ QETH_CARD_TEXT(card, 2, "AFFE");
+ return 1;
+ }
+ if ((!sense[0]) && (!sense[1]) && (!sense[2]) && (!sense[3])) {
+ QETH_CARD_TEXT(card, 2, "ZEROSEN");
+ return 0;
+ }
+ QETH_CARD_TEXT(card, 2, "DGENCHK");
+ return 1;
+ }
+ return 0;
+}
+
+static long __qeth_check_irb_error(struct ccw_device *cdev,
+ unsigned long intparm, struct irb *irb)
+{
+ struct qeth_card *card;
+
+ card = CARD_FROM_CDEV(cdev);
+
+ if (!card || !IS_ERR(irb))
+ return 0;
+
+ switch (PTR_ERR(irb)) {
+ case -EIO:
+ QETH_DBF_MESSAGE(2, "%s i/o-error on device\n",
+ dev_name(&cdev->dev));
+ QETH_CARD_TEXT(card, 2, "ckirberr");
+ QETH_CARD_TEXT_(card, 2, " rc%d", -EIO);
+ break;
+ case -ETIMEDOUT:
+ dev_warn(&cdev->dev, "A hardware operation timed out"
+ " on the device\n");
+ QETH_CARD_TEXT(card, 2, "ckirberr");
+ QETH_CARD_TEXT_(card, 2, " rc%d", -ETIMEDOUT);
+ if (intparm == QETH_RCD_PARM) {
+ if (card->data.ccwdev == cdev) {
+ card->data.state = CH_STATE_DOWN;
+ wake_up(&card->wait_q);
+ }
+ }
+ break;
+ default:
+ QETH_DBF_MESSAGE(2, "%s unknown error %ld on device\n",
+ dev_name(&cdev->dev), PTR_ERR(irb));
+ QETH_CARD_TEXT(card, 2, "ckirberr");
+ QETH_CARD_TEXT(card, 2, " rc???");
+ }
+ return PTR_ERR(irb);
+}
+
+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;
+ struct qeth_cmd_buffer *iob;
+ __u8 index;
+
+ if (__qeth_check_irb_error(cdev, intparm, irb))
+ return;
+ cstat = irb->scsw.cmd.cstat;
+ dstat = irb->scsw.cmd.dstat;
+
+ card = CARD_FROM_CDEV(cdev);
+ if (!card)
+ return;
+
+ QETH_CARD_TEXT(card, 5, "irq");
+
+ if (card->read.ccwdev == cdev) {
+ channel = &card->read;
+ QETH_CARD_TEXT(card, 5, "read");
+ } else if (card->write.ccwdev == cdev) {
+ channel = &card->write;
+ QETH_CARD_TEXT(card, 5, "write");
+ } else {
+ channel = &card->data;
+ QETH_CARD_TEXT(card, 5, "data");
+ }
+ atomic_set(&channel->irq_pending, 0);
+
+ if (irb->scsw.cmd.fctl & (SCSW_FCTL_CLEAR_FUNC))
+ channel->state = CH_STATE_STOPPED;
+
+ if (irb->scsw.cmd.fctl & (SCSW_FCTL_HALT_FUNC))
+ channel->state = CH_STATE_HALTED;
+
+ /*let's wake up immediately on data channel*/
+ if ((channel == &card->data) && (intparm != 0) &&
+ (intparm != QETH_RCD_PARM))
+ goto out;
+
+ if (intparm == QETH_CLEAR_CHANNEL_PARM) {
+ QETH_CARD_TEXT(card, 6, "clrchpar");
+ /* we don't have to handle this further */
+ intparm = 0;
+ }
+ if (intparm == QETH_HALT_CHANNEL_PARM) {
+ QETH_CARD_TEXT(card, 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) {
+ dev_warn(&channel->ccwdev->dev,
+ "The qeth device driver failed to recover "
+ "an error on the device\n");
+ QETH_DBF_MESSAGE(2, "%s sense data available. cstat "
+ "0x%X dstat 0x%X\n",
+ dev_name(&channel->ccwdev->dev), cstat, dstat);
+ print_hex_dump(KERN_WARNING, "qeth: irb ",
+ DUMP_PREFIX_OFFSET, 16, 1, irb, 32, 1);
+ print_hex_dump(KERN_WARNING, "qeth: sense data ",
+ DUMP_PREFIX_OFFSET, 16, 1, irb->ecw, 32, 1);
+ }
+ if (intparm == QETH_RCD_PARM) {
+ channel->state = CH_STATE_DOWN;
+ goto out;
+ }
+ rc = qeth_get_problem(cdev, irb);
+ if (rc) {
+ qeth_clear_ipacmd_list(card);
+ qeth_schedule_recovery(card);
+ goto out;
+ }
+ }
+
+ if (intparm == QETH_RCD_PARM) {
+ channel->state = CH_STATE_RCD_DONE;
+ 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);
+
+ iob = channel->iob;
+ index = channel->buf_no;
+ 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;
+out:
+ wake_up(&card->wait_q);
+ return;
+}
+
+static void qeth_notify_skbs(struct qeth_qdio_out_q *q,
+ struct qeth_qdio_out_buffer *buf,
+ enum iucv_tx_notify notification)
+{
+ struct sk_buff *skb;
+
+ if (skb_queue_empty(&buf->skb_list))
+ goto out;
+ skb = skb_peek(&buf->skb_list);
+ while (skb) {
+ QETH_CARD_TEXT_(q->card, 5, "skbn%d", notification);
+ QETH_CARD_TEXT_(q->card, 5, "%lx", (long) skb);
+ if (skb->protocol == ETH_P_AF_IUCV) {
+ if (skb->sk) {
+ struct iucv_sock *iucv = iucv_sk(skb->sk);
+ iucv->sk_txnotify(skb, notification);
+ }
+ }
+ if (skb_queue_is_last(&buf->skb_list, skb))
+ skb = NULL;
+ else
+ skb = skb_queue_next(&buf->skb_list, skb);
+ }
+out:
+ return;
+}
+
+static void qeth_release_skbs(struct qeth_qdio_out_buffer *buf)
+{
+ struct sk_buff *skb;
+ struct iucv_sock *iucv;
+ int notify_general_error = 0;
+
+ if (atomic_read(&buf->state) == QETH_QDIO_BUF_PENDING)
+ notify_general_error = 1;
+
+ /* release may never happen from within CQ tasklet scope */
+ WARN_ON_ONCE(atomic_read(&buf->state) == QETH_QDIO_BUF_IN_CQ);
+
+ skb = skb_dequeue(&buf->skb_list);
+ while (skb) {
+ QETH_CARD_TEXT(buf->q->card, 5, "skbr");
+ QETH_CARD_TEXT_(buf->q->card, 5, "%lx", (long) skb);
+ if (notify_general_error && skb->protocol == ETH_P_AF_IUCV) {
+ if (skb->sk) {
+ iucv = iucv_sk(skb->sk);
+ iucv->sk_txnotify(skb, TX_NOTIFY_GENERALERROR);
+ }
+ }
+ atomic_dec(&skb->users);
+ dev_kfree_skb_any(skb);
+ skb = skb_dequeue(&buf->skb_list);
+ }
+}
+
+static void qeth_clear_output_buffer(struct qeth_qdio_out_q *queue,
+ struct qeth_qdio_out_buffer *buf,
+ enum qeth_qdio_buffer_states newbufstate)
+{
+ int i;
+
+ /* is PCI flag set on buffer? */
+ if (buf->buffer->element[0].sflags & SBAL_SFLAGS0_PCI_REQ)
+ atomic_dec(&queue->set_pci_flags_count);
+
+ if (newbufstate == QETH_QDIO_BUF_EMPTY) {
+ qeth_release_skbs(buf);
+ }
+ for (i = 0; i < QETH_MAX_BUFFER_ELEMENTS(queue->card); ++i) {
+ if (buf->buffer->element[i].addr && buf->is_header[i])
+ kmem_cache_free(qeth_core_header_cache,
+ buf->buffer->element[i].addr);
+ buf->is_header[i] = 0;
+ buf->buffer->element[i].length = 0;
+ buf->buffer->element[i].addr = NULL;
+ buf->buffer->element[i].eflags = 0;
+ buf->buffer->element[i].sflags = 0;
+ }
+ buf->buffer->element[15].eflags = 0;
+ buf->buffer->element[15].sflags = 0;
+ buf->next_element_to_fill = 0;
+ atomic_set(&buf->state, newbufstate);
+}
+
+static void qeth_clear_outq_buffers(struct qeth_qdio_out_q *q, int free)
+{
+ int j;
+
+ for (j = 0; j < QDIO_MAX_BUFFERS_PER_Q; ++j) {
+ if (!q->bufs[j])
+ continue;
+ qeth_cleanup_handled_pending(q, j, 1);
+ qeth_clear_output_buffer(q, q->bufs[j], QETH_QDIO_BUF_EMPTY);
+ if (free) {
+ kmem_cache_free(qeth_qdio_outbuf_cache, q->bufs[j]);
+ q->bufs[j] = NULL;
+ }
+ }
+}
+
+void qeth_clear_qdio_buffers(struct qeth_card *card)
+{
+ int i;
+
+ QETH_CARD_TEXT(card, 2, "clearqdbf");
+ /* clear outbound buffers to free skbs */
+ for (i = 0; i < card->qdio.no_out_queues; ++i) {
+ if (card->qdio.out_qs[i]) {
+ qeth_clear_outq_buffers(card->qdio.out_qs[i], 0);
+ }
+ }
+}
+EXPORT_SYMBOL_GPL(qeth_clear_qdio_buffers);
+
+static void qeth_free_buffer_pool(struct qeth_card *card)
+{
+ struct qeth_buffer_pool_entry *pool_entry, *tmp;
+ int i = 0;
+ 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 void qeth_free_qdio_buffers(struct qeth_card *card)
+{
+ int i, j;
+
+ if (atomic_xchg(&card->qdio.state, QETH_QDIO_UNINITIALIZED) ==
+ QETH_QDIO_UNINITIALIZED)
+ return;
+
+ qeth_free_cq(card);
+ cancel_delayed_work_sync(&card->buffer_reclaim_work);
+ for (j = 0; j < QDIO_MAX_BUFFERS_PER_Q; ++j) {
+ if (card->qdio.in_q->bufs[j].rx_skb)
+ dev_kfree_skb_any(card->qdio.in_q->bufs[j].rx_skb);
+ }
+ kfree(card->qdio.in_q);
+ card->qdio.in_q = NULL;
+ /* inbound buffer pool */
+ qeth_free_buffer_pool(card);
+ /* free outbound qdio_qs */
+ if (card->qdio.out_qs) {
+ for (i = 0; i < card->qdio.no_out_queues; ++i) {
+ qeth_clear_outq_buffers(card->qdio.out_qs[i], 1);
+ kfree(card->qdio.out_qs[i]);
+ }
+ kfree(card->qdio.out_qs);
+ card->qdio.out_qs = NULL;
+ }
+}
+
+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);
+}
+
+static void qeth_set_single_write_queues(struct qeth_card *card)
+{
+ if ((atomic_read(&card->qdio.state) != QETH_QDIO_UNINITIALIZED) &&
+ (card->qdio.no_out_queues == 4))
+ qeth_free_qdio_buffers(card);
+
+ card->qdio.no_out_queues = 1;
+ if (card->qdio.default_out_queue != 0)
+ dev_info(&card->gdev->dev, "Priority Queueing not supported\n");
+
+ card->qdio.default_out_queue = 0;
+}
+
+static void qeth_set_multiple_write_queues(struct qeth_card *card)
+{
+ if ((atomic_read(&card->qdio.state) != QETH_QDIO_UNINITIALIZED) &&
+ (card->qdio.no_out_queues == 1)) {
+ qeth_free_qdio_buffers(card);
+ card->qdio.default_out_queue = 2;
+ }
+ card->qdio.no_out_queues = 4;
+}
+
+static void qeth_update_from_chp_desc(struct qeth_card *card)
+{
+ struct ccw_device *ccwdev;
+ struct channel_path_desc *chp_dsc;
+
+ QETH_DBF_TEXT(SETUP, 2, "chp_desc");
+
+ ccwdev = card->data.ccwdev;
+ chp_dsc = ccw_device_get_chp_desc(ccwdev, 0);
+ if (!chp_dsc)
+ goto out;
+
+ card->info.func_level = 0x4100 + chp_dsc->desc;
+ if (card->info.type == QETH_CARD_TYPE_IQD)
+ goto out;
+
+ /* CHPP field bit 6 == 1 -> single queue */
+ if ((chp_dsc->chpp & 0x02) == 0x02)
+ qeth_set_single_write_queues(card);
+ else
+ qeth_set_multiple_write_queues(card);
+out:
+ kfree(chp_dsc);
+ QETH_DBF_TEXT_(SETUP, 2, "nr:%x", card->qdio.no_out_queues);
+ QETH_DBF_TEXT_(SETUP, 2, "lvl:%02x", card->info.func_level);
+}
+
+static void qeth_init_qdio_info(struct qeth_card *card)
+{
+ QETH_DBF_TEXT(SETUP, 4, "intqdinf");
+ atomic_set(&card->qdio.state, QETH_QDIO_UNINITIALIZED);
+ /* inbound */
+ card->qdio.in_buf_size = QETH_IN_BUF_SIZE_DEFAULT;
+ if (card->info.type == QETH_CARD_TYPE_IQD)
+ card->qdio.init_pool.buf_count = QETH_IN_BUF_COUNT_HSDEFAULT;
+ else
+ 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);
+}
+
+static void qeth_set_intial_options(struct qeth_card *card)
+{
+ card->options.route4.type = NO_ROUTER;
+ card->options.route6.type = NO_ROUTER;
+ card->options.fake_broadcast = 0;
+ card->options.add_hhlen = DEFAULT_ADD_HHLEN;
+ card->options.performance_stats = 0;
+ card->options.rx_sg_cb = QETH_RX_SG_CB;
+ card->options.isolation = ISOLATION_MODE_NONE;
+ card->options.cq = QETH_CQ_DISABLED;
+}
+
+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_CARD_TEXT_(card, 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 work_struct *work)
+{
+ struct task_struct *ts;
+ struct qeth_card *card = container_of(work, struct qeth_card,
+ kernel_thread_starter);
+ QETH_CARD_TEXT(card , 2, "strthrd");
+
+ if (card->read.state != CH_STATE_UP &&
+ card->write.state != CH_STATE_UP)
+ return;
+ if (qeth_do_start_thread(card, QETH_RECOVER_THREAD)) {
+ ts = kthread_run(card->discipline->recover, (void *)card,
+ "qeth_recover");
+ if (IS_ERR(ts)) {
+ qeth_clear_thread_start_bit(card, QETH_RECOVER_THREAD);
+ qeth_clear_thread_running_bit(card,
+ QETH_RECOVER_THREAD);
+ }
+ }
+}
+
+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->read_or_write_problem = 0;
+ card->dev = NULL;
+ spin_lock_init(&card->vlanlock);
+ spin_lock_init(&card->mclock);
+ spin_lock_init(&card->lock);
+ spin_lock_init(&card->ip_lock);
+ spin_lock_init(&card->thread_mask_lock);
+ mutex_init(&card->conf_mutex);
+ mutex_init(&card->discipline_mutex);
+ card->thread_start_mask = 0;
+ card->thread_allowed_mask = 0;
+ card->thread_running_mask = 0;
+ INIT_WORK(&card->kernel_thread_starter, qeth_start_kernel_thread);
+ 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);
+ /* initial 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);
+ INIT_DELAYED_WORK(&card->buffer_reclaim_work, qeth_buffer_reclaim_work);
+ INIT_WORK(&card->close_dev_work, qeth_close_dev_handler);
+ return 0;
+}
+
+static void qeth_core_sl_print(struct seq_file *m, struct service_level *slr)
+{
+ struct qeth_card *card = container_of(slr, struct qeth_card,
+ qeth_service_level);
+ if (card->info.mcl_level[0])
+ seq_printf(m, "qeth: %s firmware level %s\n",
+ CARD_BUS_ID(card), card->info.mcl_level);
+}
+
+static struct qeth_card *qeth_alloc_card(void)
+{
+ struct qeth_card *card;
+
+ QETH_DBF_TEXT(SETUP, 2, "alloccrd");
+ card = kzalloc(sizeof(struct qeth_card), GFP_DMA|GFP_KERNEL);
+ if (!card)
+ goto out;
+ QETH_DBF_HEX(SETUP, 2, &card, sizeof(void *));
+ card->ip_tbd_list = kzalloc(sizeof(struct list_head), GFP_KERNEL);
+ if (!card->ip_tbd_list) {
+ QETH_DBF_TEXT(SETUP, 0, "iptbdnom");
+ goto out_card;
+ }
+ if (qeth_setup_channel(&card->read))
+ goto out_ip;
+ if (qeth_setup_channel(&card->write))
+ goto out_channel;
+ card->options.layer2 = -1;
+ card->qeth_service_level.seq_print = qeth_core_sl_print;
+ register_service_level(&card->qeth_service_level);
+ return card;
+
+out_channel:
+ qeth_clean_channel(&card->read);
+out_ip:
+ kfree(card->ip_tbd_list);
+out_card:
+ kfree(card);
+out:
+ return NULL;
+}
+
+static int qeth_determine_card_type(struct qeth_card *card)
+{
+ int i = 0;
+
+ QETH_DBF_TEXT(SETUP, 2, "detcdtyp");
+
+ card->qdio.do_prio_queueing = QETH_PRIOQ_DEFAULT;
+ card->qdio.default_out_queue = QETH_DEFAULT_QUEUE;
+ while (known_devices[i][QETH_DEV_MODEL_IND]) {
+ if ((CARD_RDEV(card)->id.dev_type ==
+ known_devices[i][QETH_DEV_TYPE_IND]) &&
+ (CARD_RDEV(card)->id.dev_model ==
+ known_devices[i][QETH_DEV_MODEL_IND])) {
+ card->info.type = known_devices[i][QETH_DEV_MODEL_IND];
+ card->qdio.no_out_queues =
+ known_devices[i][QETH_QUEUE_NO_IND];
+ card->qdio.no_in_queues = 1;
+ card->info.is_multicast_different =
+ known_devices[i][QETH_MULTICAST_IND];
+ qeth_update_from_chp_desc(card);
+ return 0;
+ }
+ i++;
+ }
+ card->info.type = QETH_CARD_TYPE_UNKNOWN;
+ dev_err(&card->gdev->dev, "The adapter hardware is of an "
+ "unknown type\n");
+ return -ENOENT;
+}
+
+static int qeth_clear_channel(struct qeth_channel *channel)
+{
+ unsigned long flags;
+ struct qeth_card *card;
+ int rc;
+
+ card = CARD_FROM_CDEV(channel->ccwdev);
+ QETH_CARD_TEXT(card, 3, "clearch");
+ 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;
+
+ card = CARD_FROM_CDEV(channel->ccwdev);
+ QETH_CARD_TEXT(card, 3, "haltch");
+ 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 rc1 = 0, rc2 = 0, rc3 = 0;
+
+ QETH_CARD_TEXT(card, 3, "haltchs");
+ rc1 = qeth_halt_channel(&card->read);
+ rc2 = qeth_halt_channel(&card->write);
+ rc3 = qeth_halt_channel(&card->data);
+ if (rc1)
+ return rc1;
+ if (rc2)
+ return rc2;
+ return rc3;
+}
+
+static int qeth_clear_channels(struct qeth_card *card)
+{
+ int rc1 = 0, rc2 = 0, rc3 = 0;
+
+ QETH_CARD_TEXT(card, 3, "clearchs");
+ rc1 = qeth_clear_channel(&card->read);
+ rc2 = qeth_clear_channel(&card->write);
+ rc3 = qeth_clear_channel(&card->data);
+ if (rc1)
+ return rc1;
+ if (rc2)
+ return rc2;
+ return rc3;
+}
+
+static int qeth_clear_halt_card(struct qeth_card *card, int halt)
+{
+ int rc = 0;
+
+ QETH_CARD_TEXT(card, 3, "clhacrd");
+
+ if (halt)
+ rc = qeth_halt_channels(card);
+ if (rc)
+ return rc;
+ return qeth_clear_channels(card);
+}
+
+int qeth_qdio_clear_card(struct qeth_card *card, int use_halt)
+{
+ int rc = 0;
+
+ QETH_CARD_TEXT(card, 3, "qdioclr");
+ switch (atomic_cmpxchg(&card->qdio.state, QETH_QDIO_ESTABLISHED,
+ QETH_QDIO_CLEANING)) {
+ case QETH_QDIO_ESTABLISHED:
+ if (card->info.type == QETH_CARD_TYPE_IQD)
+ rc = qdio_shutdown(CARD_DDEV(card),
+ QDIO_FLAG_CLEANUP_USING_HALT);
+ else
+ rc = qdio_shutdown(CARD_DDEV(card),
+ QDIO_FLAG_CLEANUP_USING_CLEAR);
+ if (rc)
+ QETH_CARD_TEXT_(card, 3, "1err%d", rc);
+ atomic_set(&card->qdio.state, QETH_QDIO_ALLOCATED);
+ break;
+ case QETH_QDIO_CLEANING:
+ return rc;
+ default:
+ break;
+ }
+ rc = qeth_clear_halt_card(card, use_halt);
+ if (rc)
+ QETH_CARD_TEXT_(card, 3, "2err%d", rc);
+ card->state = CARD_STATE_DOWN;
+ return rc;
+}
+EXPORT_SYMBOL_GPL(qeth_qdio_clear_card);
+
+static int qeth_read_conf_data(struct qeth_card *card, void **buffer,
+ int *length)
+{
+ struct ciw *ciw;
+ char *rcd_buf;
+ int ret;
+ struct qeth_channel *channel = &card->data;
+ unsigned long flags;
+
+ /*
+ * scan for RCD command in extended SenseID data
+ */
+ ciw = ccw_device_get_ciw(channel->ccwdev, CIW_TYPE_RCD);
+ if (!ciw || ciw->cmd == 0)
+ return -EOPNOTSUPP;
+ rcd_buf = kzalloc(ciw->count, GFP_KERNEL | GFP_DMA);
+ if (!rcd_buf)
+ return -ENOMEM;
+
+ channel->ccw.cmd_code = ciw->cmd;
+ channel->ccw.cda = (__u32) __pa(rcd_buf);
+ channel->ccw.count = ciw->count;
+ channel->ccw.flags = CCW_FLAG_SLI;
+ channel->state = CH_STATE_RCD;
+ spin_lock_irqsave(get_ccwdev_lock(channel->ccwdev), flags);
+ ret = ccw_device_start_timeout(channel->ccwdev, &channel->ccw,
+ QETH_RCD_PARM, LPM_ANYPATH, 0,
+ QETH_RCD_TIMEOUT);
+ spin_unlock_irqrestore(get_ccwdev_lock(channel->ccwdev), flags);
+ if (!ret)
+ wait_event(card->wait_q,
+ (channel->state == CH_STATE_RCD_DONE ||
+ channel->state == CH_STATE_DOWN));
+ if (channel->state == CH_STATE_DOWN)
+ ret = -EIO;
+ else
+ channel->state = CH_STATE_DOWN;
+ if (ret) {
+ kfree(rcd_buf);
+ *buffer = NULL;
+ *length = 0;
+ } else {
+ *length = ciw->count;
+ *buffer = rcd_buf;
+ }
+ return ret;
+}
+
+static void qeth_configure_unitaddr(struct qeth_card *card, char *prcd)
+{
+ QETH_DBF_TEXT(SETUP, 2, "cfgunit");
+ 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']));
+}
+
+static void qeth_configure_blkt_default(struct qeth_card *card, char *prcd)
+{
+ QETH_DBF_TEXT(SETUP, 2, "cfgblkt");
+
+ if (prcd[74] == 0xF0 && prcd[75] == 0xF0 &&
+ prcd[76] >= 0xF1 && prcd[76] <= 0xF4) {
+ card->info.blkt.time_total = 0;
+ card->info.blkt.inter_packet = 0;
+ card->info.blkt.inter_packet_jumbo = 0;
+ } else {
+ card->info.blkt.time_total = 250;
+ card->info.blkt.inter_packet = 5;
+ card->info.blkt.inter_packet_jumbo = 15;
+ }
+}
+
+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 void qeth_init_func_level(struct qeth_card *card)
+{
+ switch (card->info.type) {
+ case QETH_CARD_TYPE_IQD:
+ card->info.func_level = QETH_IDX_FUNC_LEVEL_IQD;
+ break;
+ case QETH_CARD_TYPE_OSD:
+ case QETH_CARD_TYPE_OSN:
+ card->info.func_level = QETH_IDX_FUNC_LEVEL_OSD;
+ break;
+ default:
+ break;
+ }
+}
+
+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_cmpxchg(&channel->irq_pending, 0, 1) == 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) {
+ QETH_DBF_MESSAGE(2, "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);
+ qeth_clear_cmd_buffers(channel);
+ } 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;
+ __u8 tmp;
+ int rc;
+ struct ccw_dev_id temp_devid;
+
+ 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);
+ }
+ tmp = ((__u8)card->info.portno) | 0x80;
+ memcpy(QETH_IDX_ACT_PNO(iob->data), &tmp, 1);
+ 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));
+ ccw_device_get_id(CARD_DDEV(card), &temp_devid);
+ memcpy(QETH_IDX_ACT_QDIO_DEV_CUA(iob->data), &temp_devid.devno, 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_cmpxchg(&channel->irq_pending, 0, 1) == 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) {
+ QETH_DBF_MESSAGE(2, "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) {
+ dev_warn(&channel->ccwdev->dev, "The qeth device driver"
+ " failed to recover an error on the device\n");
+ QETH_DBF_MESSAGE(2, "%s IDX activate timed out\n",
+ dev_name(&channel->ccwdev->dev));
+ QETH_DBF_TEXT_(SETUP, 2, "2err%d", -ETIME);
+ qeth_clear_cmd_buffers(channel);
+ return -ETIME;
+ }
+ return qeth_idx_activate_get_answer(channel, idx_reply_cb);
+}
+
+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))) {
+ if (QETH_IDX_ACT_CAUSE_CODE(iob->data) == QETH_IDX_ACT_ERR_EXCL)
+ dev_err(&card->write.ccwdev->dev,
+ "The adapter is used exclusively by another "
+ "host\n");
+ else
+ QETH_DBF_MESSAGE(2, "%s IDX_ACTIVATE on write channel:"
+ " negative reply\n",
+ dev_name(&card->write.ccwdev->dev));
+ goto out;
+ }
+ memcpy(&temp, QETH_IDX_ACT_FUNC_LEVEL(iob->data), 2);
+ if ((temp & ~0x0100) != qeth_peer_func_level(card->info.func_level)) {
+ QETH_DBF_MESSAGE(2, "%s IDX_ACTIVATE on write channel: "
+ "function level mismatch (sent: 0x%x, received: "
+ "0x%x)\n", dev_name(&card->write.ccwdev->dev),
+ card->info.func_level, temp);
+ goto out;
+ }
+ channel->state = CH_STATE_UP;
+out:
+ qeth_release_buffer(channel, iob);
+}
+
+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(card, iob->data))
+ goto out;
+
+ if (!(QETH_IS_IDX_ACT_POS_REPLY(iob->data))) {
+ switch (QETH_IDX_ACT_CAUSE_CODE(iob->data)) {
+ case QETH_IDX_ACT_ERR_EXCL:
+ dev_err(&card->write.ccwdev->dev,
+ "The adapter is used exclusively by another "
+ "host\n");
+ break;
+ case QETH_IDX_ACT_ERR_AUTH:
+ case QETH_IDX_ACT_ERR_AUTH_USER:
+ dev_err(&card->read.ccwdev->dev,
+ "Setting the device online failed because of "
+ "insufficient authorization\n");
+ break;
+ default:
+ QETH_DBF_MESSAGE(2, "%s IDX_ACTIVATE on read channel:"
+ " negative reply\n",
+ dev_name(&card->read.ccwdev->dev));
+ }
+ QETH_CARD_TEXT_(card, 2, "idxread%c",
+ QETH_IDX_ACT_CAUSE_CODE(iob->data));
+ 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_OSD))
+ 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)) {
+ QETH_DBF_MESSAGE(2, "%s IDX_ACTIVATE on read channel: function "
+ "level mismatch (sent: 0x%x, received: 0x%x)\n",
+ dev_name(&card->read.ccwdev->dev),
+ 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);
+}
+
+void qeth_prepare_control_data(struct qeth_card *card, int len,
+ struct qeth_cmd_buffer *iob)
+{
+ qeth_setup_ccw(&card->write, iob->data, len);
+ iob->callback = qeth_release_buffer;
+
+ 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);
+ QETH_DBF_HEX(CTRL, 2, iob->data, QETH_DBF_CTRL_LEN);
+}
+EXPORT_SYMBOL_GPL(qeth_prepare_control_data);
+
+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 = NULL;
+ unsigned long timeout, event_timeout;
+ struct qeth_ipa_cmd *cmd;
+
+ QETH_CARD_TEXT(card, 2, "sendctl");
+
+ if (card->read_or_write_problem) {
+ qeth_release_buffer(iob->channel, iob);
+ return -EIO;
+ }
+ reply = qeth_alloc_reply(card);
+ if (!reply) {
+ 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_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(CTRL, 2, iob->data, QETH_DBF_CTRL_LEN);
+
+ while (atomic_cmpxchg(&card->write.irq_pending, 0, 1)) ;
+ qeth_prepare_control_data(card, len, iob);
+
+ if (IS_IPA(iob->data))
+ event_timeout = QETH_IPA_TIMEOUT;
+ else
+ event_timeout = QETH_TIMEOUT;
+ timeout = jiffies + event_timeout;
+
+ QETH_CARD_TEXT(card, 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) {
+ QETH_DBF_MESSAGE(2, "%s qeth_send_control_data: "
+ "ccw_device_start rc = %i\n",
+ dev_name(&card->write.ccwdev->dev), rc);
+ QETH_CARD_TEXT_(card, 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;
+ }
+
+ /* we have only one long running ipassist, since we can ensure
+ process context of this command we can sleep */
+ cmd = (struct qeth_ipa_cmd *)(iob->data+IPA_PDU_HEADER_SIZE);
+ if ((cmd->hdr.command == IPA_CMD_SETIP) &&
+ (cmd->hdr.prot_version == QETH_PROT_IPV4)) {
+ if (!wait_event_timeout(reply->wait_q,
+ atomic_read(&reply->received), event_timeout))
+ goto time_err;
+ } else {
+ while (!atomic_read(&reply->received)) {
+ if (time_after(jiffies, timeout))
+ goto time_err;
+ cpu_relax();
+ }
+ }
+
+ if (reply->rc == -EIO)
+ goto error;
+ rc = reply->rc;
+ qeth_put_reply(reply);
+ return rc;
+
+time_err:
+ reply->rc = -ETIME;
+ spin_lock_irqsave(&reply->card->lock, flags);
+ list_del_init(&reply->list);
+ spin_unlock_irqrestore(&reply->card->lock, flags);
+ atomic_inc(&reply->received);
+error:
+ atomic_set(&card->write.irq_pending, 0);
+ qeth_release_buffer(iob->channel, iob);
+ card->write.buf_no = (card->write.buf_no + 1) % QETH_CMD_BUFFER_NO;
+ rc = reply->rc;
+ qeth_put_reply(reply);
+ return rc;
+}
+EXPORT_SYMBOL_GPL(qeth_send_control_data);
+
+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 inline int qeth_get_initial_mtu_for_card(struct qeth_card *card)
+{
+ switch (card->info.type) {
+ case QETH_CARD_TYPE_UNKNOWN:
+ return 1500;
+ case QETH_CARD_TYPE_IQD:
+ return card->info.max_mtu;
+ case QETH_CARD_TYPE_OSD:
+ switch (card->info.link_type) {
+ case QETH_LINK_TYPE_HSTR:
+ case QETH_LINK_TYPE_LANE_TR:
+ return 2000;
+ default:
+ return card->options.layer2 ? 1500 : 1492;
+ }
+ case QETH_CARD_TYPE_OSM:
+ case QETH_CARD_TYPE_OSX:
+ return card->options.layer2 ? 1500 : 1492;
+ default:
+ return 1500;
+ }
+}
+
+static inline int qeth_get_mtu_outof_framesize(int framesize)
+{
+ switch (framesize) {
+ case 0x4000:
+ return 8192;
+ case 0x6000:
+ return 16384;
+ case 0xa000:
+ return 32768;
+ case 0xffff:
+ return 57344;
+ default:
+ return 0;
+ }
+}
+
+static inline int qeth_mtu_is_valid(struct qeth_card *card, int mtu)
+{
+ switch (card->info.type) {
+ case QETH_CARD_TYPE_OSD:
+ case QETH_CARD_TYPE_OSM:
+ case QETH_CARD_TYPE_OSX:
+ case QETH_CARD_TYPE_IQD:
+ return ((mtu >= 576) &&
+ (mtu <= card->info.max_mtu));
+ case QETH_CARD_TYPE_OSN:
+ case QETH_CARD_TYPE_UNKNOWN:
+ default:
+ return 1;
+ }
+}
+
+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 (card->info.type == QETH_CARD_TYPE_IQD) {
+ 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;
+ }
+ if (card->info.initial_mtu && (card->info.initial_mtu != mtu)) {
+ /* frame size has changed */
+ if (card->dev &&
+ ((card->dev->mtu == card->info.initial_mtu) ||
+ (card->dev->mtu > mtu)))
+ card->dev->mtu = mtu;
+ qeth_free_qdio_buffers(card);
+ }
+ card->info.initial_mtu = mtu;
+ card->info.max_mtu = mtu;
+ card->qdio.in_buf_size = mtu + 2 * PAGE_SIZE;
+ } else {
+ card->info.max_mtu = *(__u16 *)QETH_ULP_ENABLE_RESP_MAX_MTU(
+ iob->data);
+ card->info.initial_mtu = min(card->info.max_mtu,
+ qeth_get_initial_mtu_for_card(card));
+ 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, "link%d", card->info.link_type);
+ QETH_DBF_TEXT_(SETUP, 2, " rc%d", iob->rc);
+ return 0;
+}
+
+static int qeth_ulp_enable(struct qeth_card *card)
+{
+ int rc;
+ char prot_type;
+ 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;
+ if (card->options.layer2)
+ if (card->info.type == QETH_CARD_TYPE_OSN)
+ prot_type = QETH_PROT_OSN2;
+ else
+ prot_type = QETH_PROT_LAYER2;
+ else
+ prot_type = QETH_PROT_TCPIP;
+
+ memcpy(QETH_ULP_ENABLE_PROT_TYPE(iob->data), &prot_type, 1);
+ memcpy(QETH_ULP_ENABLE_DEST_ADDR(iob->data),
+ &card->token.cm_connection_r, QETH_MPC_TOKEN_LENGTH);
+ memcpy(QETH_ULP_ENABLE_FILTER_TOKEN(iob->data),
+ &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 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);
+ if (!strncmp("00S", QETH_ULP_SETUP_RESP_CONNECTION_TOKEN(iob->data),
+ 3)) {
+ QETH_DBF_TEXT(SETUP, 2, "olmlimit");
+ dev_err(&card->gdev->dev, "A connection could not be "
+ "established because of an OLM limit\n");
+ iob->rc = -EMLINK;
+ }
+ 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;
+ struct ccw_dev_id dev_id;
+
+ 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);
+
+ ccw_device_get_id(CARD_DDEV(card), &dev_id);
+ memcpy(QETH_ULP_SETUP_CUA(iob->data), &dev_id.devno, 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 int qeth_init_qdio_out_buf(struct qeth_qdio_out_q *q, int bidx)
+{
+ int rc;
+ struct qeth_qdio_out_buffer *newbuf;
+
+ rc = 0;
+ newbuf = kmem_cache_zalloc(qeth_qdio_outbuf_cache, GFP_ATOMIC);
+ if (!newbuf) {
+ rc = -ENOMEM;
+ goto out;
+ }
+ newbuf->buffer = &q->qdio_bufs[bidx];
+ skb_queue_head_init(&newbuf->skb_list);
+ lockdep_set_class(&newbuf->skb_list.lock, &qdio_out_skb_queue_key);
+ newbuf->q = q;
+ newbuf->aob = NULL;
+ newbuf->next_pending = q->bufs[bidx];
+ atomic_set(&newbuf->state, QETH_QDIO_BUF_EMPTY);
+ q->bufs[bidx] = newbuf;
+ if (q->bufstates) {
+ q->bufstates[bidx].user = newbuf;
+ QETH_CARD_TEXT_(q->card, 2, "nbs%d", bidx);
+ QETH_CARD_TEXT_(q->card, 2, "%lx", (long) newbuf);
+ QETH_CARD_TEXT_(q->card, 2, "%lx",
+ (long) newbuf->next_pending);
+ }
+out:
+ return rc;
+}
+
+
+static int qeth_alloc_qdio_buffers(struct qeth_card *card)
+{
+ int i, j;
+
+ QETH_DBF_TEXT(SETUP, 2, "allcqdbf");
+
+ if (atomic_cmpxchg(&card->qdio.state, QETH_QDIO_UNINITIALIZED,
+ QETH_QDIO_ALLOCATED) != QETH_QDIO_UNINITIALIZED)
+ return 0;
+
+ card->qdio.in_q = kzalloc(sizeof(struct qeth_qdio_q),
+ GFP_KERNEL);
+ if (!card->qdio.in_q)
+ goto out_nomem;
+ 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];
+ card->qdio.in_q->bufs[i].rx_skb = NULL;
+ }
+ /* inbound buffer pool */
+ if (qeth_alloc_buffer_pool(card))
+ goto out_freeinq;
+
+ /* outbound */
+ card->qdio.out_qs =
+ kzalloc(card->qdio.no_out_queues *
+ sizeof(struct qeth_qdio_out_q *), GFP_KERNEL);
+ if (!card->qdio.out_qs)
+ goto out_freepool;
+ for (i = 0; i < card->qdio.no_out_queues; ++i) {
+ card->qdio.out_qs[i] = kzalloc(sizeof(struct qeth_qdio_out_q),
+ GFP_KERNEL);
+ if (!card->qdio.out_qs[i])
+ goto out_freeoutq;
+ QETH_DBF_TEXT_(SETUP, 2, "outq %i", i);
+ QETH_DBF_HEX(SETUP, 2, &card->qdio.out_qs[i], sizeof(void *));
+ card->qdio.out_qs[i]->queue_no = i;
+ /* give outbound qeth_qdio_buffers their qdio_buffers */
+ for (j = 0; j < QDIO_MAX_BUFFERS_PER_Q; ++j) {
+ WARN_ON(card->qdio.out_qs[i]->bufs[j] != NULL);
+ if (qeth_init_qdio_out_buf(card->qdio.out_qs[i], j))
+ goto out_freeoutqbufs;
+ }
+ }
+
+ /* completion */
+ if (qeth_alloc_cq(card))
+ goto out_freeoutq;
+
+ return 0;
+
+out_freeoutqbufs:
+ while (j > 0) {
+ --j;
+ kmem_cache_free(qeth_qdio_outbuf_cache,
+ card->qdio.out_qs[i]->bufs[j]);
+ card->qdio.out_qs[i]->bufs[j] = NULL;
+ }
+out_freeoutq:
+ while (i > 0) {
+ kfree(card->qdio.out_qs[--i]);
+ qeth_clear_outq_buffers(card->qdio.out_qs[i], 1);
+ }
+ kfree(card->qdio.out_qs);
+ card->qdio.out_qs = NULL;
+out_freepool:
+ qeth_free_buffer_pool(card);
+out_freeinq:
+ kfree(card->qdio.in_q);
+ card->qdio.in_q = NULL;
+out_nomem:
+ atomic_set(&card->qdio.state, QETH_QDIO_UNINITIALIZED);
+ return -ENOMEM;
+}
+
+static void qeth_create_qib_param_field(struct qeth_card *card,
+ char *param_field)
+{
+
+ param_field[0] = _ascebc['P'];
+ param_field[1] = _ascebc['C'];
+ param_field[2] = _ascebc['I'];
+ param_field[3] = _ascebc['T'];
+ *((unsigned int *) (&param_field[4])) = QETH_PCI_THRESHOLD_A(card);
+ *((unsigned int *) (&param_field[8])) = QETH_PCI_THRESHOLD_B(card);
+ *((unsigned int *) (&param_field[12])) = QETH_PCI_TIMER_VALUE(card);
+}
+
+static void qeth_create_qib_param_field_blkt(struct qeth_card *card,
+ char *param_field)
+{
+ param_field[16] = _ascebc['B'];
+ param_field[17] = _ascebc['L'];
+ param_field[18] = _ascebc['K'];
+ param_field[19] = _ascebc['T'];
+ *((unsigned int *) (&param_field[20])) = card->info.blkt.time_total;
+ *((unsigned int *) (&param_field[24])) = card->info.blkt.inter_packet;
+ *((unsigned int *) (&param_field[28])) =
+ card->info.blkt.inter_packet_jumbo;
+}
+
+static int qeth_qdio_activate(struct qeth_card *card)
+{
+ QETH_DBF_TEXT(SETUP, 3, "qdioact");
+ return qdio_activate(CARD_DDEV(card));
+}
+
+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");
+
+ rc = qeth_issue_next_read(card);
+ if (rc) {
+ QETH_DBF_TEXT_(SETUP, 2, "1err%d", rc);
+ return rc;
+ }
+ rc = qeth_cm_enable(card);
+ if (rc) {
+ QETH_DBF_TEXT_(SETUP, 2, "2err%d", rc);
+ goto out_qdio;
+ }
+ rc = qeth_cm_setup(card);
+ if (rc) {
+ QETH_DBF_TEXT_(SETUP, 2, "3err%d", rc);
+ goto out_qdio;
+ }
+ rc = qeth_ulp_enable(card);
+ if (rc) {
+ QETH_DBF_TEXT_(SETUP, 2, "4err%d", rc);
+ goto out_qdio;
+ }
+ rc = qeth_ulp_setup(card);
+ if (rc) {
+ QETH_DBF_TEXT_(SETUP, 2, "5err%d", rc);
+ goto out_qdio;
+ }
+ rc = qeth_alloc_qdio_buffers(card);
+ if (rc) {
+ QETH_DBF_TEXT_(SETUP, 2, "5err%d", rc);
+ goto out_qdio;
+ }
+ rc = qeth_qdio_establish(card);
+ if (rc) {
+ QETH_DBF_TEXT_(SETUP, 2, "6err%d", rc);
+ qeth_free_qdio_buffers(card);
+ goto out_qdio;
+ }
+ rc = qeth_qdio_activate(card);
+ if (rc) {
+ QETH_DBF_TEXT_(SETUP, 2, "7err%d", rc);
+ goto out_qdio;
+ }
+ rc = qeth_dm_act(card);
+ if (rc) {
+ 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_IQD);
+ qdio_free(CARD_DDEV(card));
+ 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;
+ dev_info(&card->gdev->dev, "Device is a%s card%s%s%s\n"
+ "with link type %s (portname: %s)\n",
+ 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])
+ dev_info(&card->gdev->dev, "Device is a%s "
+ "card%s%s%s\nwith link type %s "
+ "(no portname needed by interface).\n",
+ 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
+ dev_info(&card->gdev->dev, "Device is a%s "
+ "card%s%s%s\nwith link type %s.\n",
+ 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));
+}
+
+void qeth_print_status_message(struct qeth_card *card)
+{
+ switch (card->info.type) {
+ case QETH_CARD_TYPE_OSD:
+ case QETH_CARD_TYPE_OSM:
+ case QETH_CARD_TYPE_OSX:
+ /* 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:
+ if ((card->info.guestlan) ||
+ (card->info.mcl_level[0] & 0x80)) {
+ 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);
+}
+EXPORT_SYMBOL_GPL(qeth_print_status_message);
+
+static void qeth_initialize_working_pool_list(struct qeth_card *card)
+{
+ struct qeth_buffer_pool_entry *entry;
+
+ QETH_CARD_TEXT(card, 5, "inwrklst");
+
+ list_for_each_entry(entry,
+ &card->qdio.init_pool.entry_list, init_list) {
+ qeth_put_buffer_pool_entry(card, entry);
+ }
+}
+
+static inline struct qeth_buffer_pool_entry *qeth_find_free_buffer_pool_entry(
+ struct qeth_card *card)
+{
+ struct list_head *plh;
+ struct qeth_buffer_pool_entry *entry;
+ int i, free;
+ struct page *page;
+
+ if (list_empty(&card->qdio.in_buf_pool.entry_list))
+ return NULL;
+
+ list_for_each(plh, &card->qdio.in_buf_pool.entry_list) {
+ entry = list_entry(plh, struct qeth_buffer_pool_entry, list);
+ free = 1;
+ for (i = 0; i < QETH_MAX_BUFFER_ELEMENTS(card); ++i) {
+ if (page_count(virt_to_page(entry->elements[i])) > 1) {
+ free = 0;
+ break;
+ }
+ }
+ if (free) {
+ list_del_init(&entry->list);
+ return entry;
+ }
+ }
+
+ /* no free buffer in pool so take first one and swap pages */
+ entry = list_entry(card->qdio.in_buf_pool.entry_list.next,
+ struct qeth_buffer_pool_entry, list);
+ for (i = 0; i < QETH_MAX_BUFFER_ELEMENTS(card); ++i) {
+ if (page_count(virt_to_page(entry->elements[i])) > 1) {
+ page = alloc_page(GFP_ATOMIC);
+ if (!page) {
+ return NULL;
+ } else {
+ free_page((unsigned long)entry->elements[i]);
+ entry->elements[i] = page_address(page);
+ if (card->options.performance_stats)
+ card->perf_stats.sg_alloc_page_rx++;
+ }
+ }
+ }
+ list_del_init(&entry->list);
+ return entry;
+}
+
+static int qeth_init_input_buffer(struct qeth_card *card,
+ struct qeth_qdio_buffer *buf)
+{
+ struct qeth_buffer_pool_entry *pool_entry;
+ int i;
+
+ if ((card->options.cq == QETH_CQ_ENABLED) && (!buf->rx_skb)) {
+ buf->rx_skb = dev_alloc_skb(QETH_RX_PULL_LEN + ETH_HLEN);
+ if (!buf->rx_skb)
+ return 1;
+ }
+
+ pool_entry = qeth_find_free_buffer_pool_entry(card);
+ if (!pool_entry)
+ return 1;
+
+ /*
+ * 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
+ */
+
+ 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].eflags = SBAL_EFLAGS_LAST_ENTRY;
+ else
+ buf->buffer->element[i].eflags = 0;
+ buf->buffer->element[i].sflags = 0;
+ }
+ return 0;
+}
+
+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));
+ qeth_initialize_working_pool_list(card);
+ /*give only as many buffers to hardware as we have buffer pool entries*/
+ for (i = 0; i < card->qdio.in_buf_pool.buf_count - 1; ++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 - 1;
+ rc = do_QDIO(CARD_DDEV(card), QDIO_FLAG_SYNC_INPUT, 0, 0,
+ card->qdio.in_buf_pool.buf_count - 1);
+ if (rc) {
+ QETH_DBF_TEXT_(SETUP, 2, "1err%d", rc);
+ return rc;
+ }
+
+ /* completion */
+ rc = qeth_cq_init(card);
+ if (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->qdio.out_qs[i],
+ card->qdio.out_qs[i]->bufs[j],
+ QETH_QDIO_BUF_EMPTY);
+ }
+ card->qdio.out_qs[i]->card = card;
+ card->qdio.out_qs[i]->next_buf_to_fill = 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);
+ atomic_set(&card->qdio.out_qs[i]->state,
+ QETH_OUT_Q_UNLOCKED);
+ }
+ return 0;
+}
+EXPORT_SYMBOL_GPL(qeth_init_qdio_queues);
+
+static inline __u8 qeth_get_ipa_adp_type(enum qeth_link_types link_type)
+{
+ switch (link_type) {
+ case QETH_LINK_TYPE_HSTR:
+ return 2;
+ default:
+ return 1;
+ }
+}
+
+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;
+ if (card->options.layer2)
+ cmd->hdr.prim_version_no = 2;
+ else
+ cmd->hdr.prim_version_no = 1;
+ cmd->hdr.param_count = 1;
+ cmd->hdr.prot_version = prot;
+ cmd->hdr.ipa_supported = 0;
+ cmd->hdr.ipa_enabled = 0;
+}
+
+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;
+}
+EXPORT_SYMBOL_GPL(qeth_get_ipacmd_buffer);
+
+void qeth_prepare_ipa_cmd(struct qeth_card *card, struct qeth_cmd_buffer *iob,
+ char prot_type)
+{
+ memcpy(iob->data, IPA_PDU_HEADER, IPA_PDU_HEADER_SIZE);
+ memcpy(QETH_IPA_CMD_PROT_TYPE(iob->data), &prot_type, 1);
+ memcpy(QETH_IPA_CMD_DEST_ADDR(iob->data),
+ &card->token.ulp_connection_r, QETH_MPC_TOKEN_LENGTH);
+}
+EXPORT_SYMBOL_GPL(qeth_prepare_ipa_cmd);
+
+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)
+{
+ int rc;
+ char prot_type;
+
+ QETH_CARD_TEXT(card, 4, "sendipa");
+
+ if (card->options.layer2)
+ if (card->info.type == QETH_CARD_TYPE_OSN)
+ prot_type = QETH_PROT_OSN2;
+ else
+ prot_type = QETH_PROT_LAYER2;
+ else
+ prot_type = QETH_PROT_TCPIP;
+ qeth_prepare_ipa_cmd(card, iob, prot_type);
+ rc = qeth_send_control_data(card, IPA_CMD_LENGTH,
+ iob, reply_cb, reply_param);
+ if (rc == -ETIME) {
+ qeth_clear_ipacmd_list(card);
+ qeth_schedule_recovery(card);
+ }
+ return rc;
+}
+EXPORT_SYMBOL_GPL(qeth_send_ipa_cmd);
+
+int qeth_send_startlan(struct qeth_card *card)
+{
+ int rc;
+ struct qeth_cmd_buffer *iob;
+
+ QETH_DBF_TEXT(SETUP, 2, "strtlan");
+
+ iob = qeth_get_ipacmd_buffer(card, IPA_CMD_STARTLAN, 0);
+ rc = qeth_send_ipa_cmd(card, iob, NULL, NULL);
+ return rc;
+}
+EXPORT_SYMBOL_GPL(qeth_send_startlan);
+
+static int qeth_default_setadapterparms_cb(struct qeth_card *card,
+ struct qeth_reply *reply, unsigned long data)
+{
+ struct qeth_ipa_cmd *cmd;
+
+ QETH_CARD_TEXT(card, 4, "defadpcb");
+
+ cmd = (struct qeth_ipa_cmd *) data;
+ if (cmd->hdr.return_code == 0)
+ cmd->hdr.return_code =
+ cmd->data.setadapterparms.hdr.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_CARD_TEXT(card, 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;
+ QETH_DBF_TEXT_(SETUP, 2, "lnk %d", card->info.link_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 struct qeth_cmd_buffer *qeth_get_adapter_cmd(struct qeth_card *card,
+ __u32 command, __u32 cmdlen)
+{
+ 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.hdr.cmdlength = cmdlen;
+ cmd->data.setadapterparms.hdr.command_code = command;
+ cmd->data.setadapterparms.hdr.used_total = 1;
+ cmd->data.setadapterparms.hdr.seq_no = 1;
+
+ return iob;
+}
+
+int qeth_query_setadapterparms(struct qeth_card *card)
+{
+ int rc;
+ struct qeth_cmd_buffer *iob;
+
+ QETH_CARD_TEXT(card, 3, "queryadp");
+ iob = qeth_get_adapter_cmd(card, IPA_SETADP_QUERY_COMMANDS_SUPPORTED,
+ sizeof(struct qeth_ipacmd_setadpparms));
+ rc = qeth_send_ipa_cmd(card, iob, qeth_query_setadapterparms_cb, NULL);
+ return rc;
+}
+EXPORT_SYMBOL_GPL(qeth_query_setadapterparms);
+
+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;
+
+ switch (cmd->hdr.return_code) {
+ case IPA_RC_NOTSUPP:
+ case IPA_RC_L2_UNSUPPORTED_CMD:
+ QETH_DBF_TEXT(SETUP, 2, "ipaunsup");
+ card->options.ipa4.supported_funcs |= IPA_SETADAPTERPARMS;
+ card->options.ipa6.supported_funcs |= IPA_SETADAPTERPARMS;
+ return -0;
+ default:
+ if (cmd->hdr.return_code) {
+ QETH_DBF_MESSAGE(1, "%s IPA_CMD_QIPASSIST: Unhandled "
+ "rc=%d\n",
+ dev_name(&card->gdev->dev),
+ cmd->hdr.return_code);
+ return 0;
+ }
+ }
+
+ 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 if (cmd->hdr.prot_version == QETH_PROT_IPV6) {
+ card->options.ipa6.supported_funcs = cmd->hdr.ipa_supported;
+ card->options.ipa6.enabled_funcs = cmd->hdr.ipa_enabled;
+ } else
+ QETH_DBF_MESSAGE(1, "%s IPA_CMD_QIPASSIST: Flawed LIC detected"
+ "\n", dev_name(&card->gdev->dev));
+ return 0;
+}
+
+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;
+}
+EXPORT_SYMBOL_GPL(qeth_query_ipassists);
+
+static int qeth_query_setdiagass_cb(struct qeth_card *card,
+ struct qeth_reply *reply, unsigned long data)
+{
+ struct qeth_ipa_cmd *cmd;
+ __u16 rc;
+
+ cmd = (struct qeth_ipa_cmd *)data;
+ rc = cmd->hdr.return_code;
+ if (rc)
+ QETH_CARD_TEXT_(card, 2, "diagq:%x", rc);
+ else
+ card->info.diagass_support = cmd->data.diagass.ext;
+ return 0;
+}
+
+static int qeth_query_setdiagass(struct qeth_card *card)
+{
+ struct qeth_cmd_buffer *iob;
+ struct qeth_ipa_cmd *cmd;
+
+ QETH_DBF_TEXT(SETUP, 2, "qdiagass");
+ iob = qeth_get_ipacmd_buffer(card, IPA_CMD_SET_DIAG_ASS, 0);
+ cmd = (struct qeth_ipa_cmd *)(iob->data+IPA_PDU_HEADER_SIZE);
+ cmd->data.diagass.subcmd_len = 16;
+ cmd->data.diagass.subcmd = QETH_DIAGS_CMD_QUERY;
+ return qeth_send_ipa_cmd(card, iob, qeth_query_setdiagass_cb, NULL);
+}
+
+static void qeth_get_trap_id(struct qeth_card *card, struct qeth_trap_id *tid)
+{
+ unsigned long info = get_zeroed_page(GFP_KERNEL);
+ struct sysinfo_2_2_2 *info222 = (struct sysinfo_2_2_2 *)info;
+ struct sysinfo_3_2_2 *info322 = (struct sysinfo_3_2_2 *)info;
+ struct ccw_dev_id ccwid;
+ int level;
+
+ tid->chpid = card->info.chpid;
+ ccw_device_get_id(CARD_RDEV(card), &ccwid);
+ tid->ssid = ccwid.ssid;
+ tid->devno = ccwid.devno;
+ if (!info)
+ return;
+ level = stsi(NULL, 0, 0, 0);
+ if ((level >= 2) && (stsi(info222, 2, 2, 2) == 0))
+ tid->lparnr = info222->lpar_number;
+ if ((level >= 3) && (stsi(info322, 3, 2, 2) == 0)) {
+ EBCASC(info322->vm[0].name, sizeof(info322->vm[0].name));
+ memcpy(tid->vmname, info322->vm[0].name, sizeof(tid->vmname));
+ }
+ free_page(info);
+ return;
+}
+
+static int qeth_hw_trap_cb(struct qeth_card *card,
+ struct qeth_reply *reply, unsigned long data)
+{
+ struct qeth_ipa_cmd *cmd;
+ __u16 rc;
+
+ cmd = (struct qeth_ipa_cmd *)data;
+ rc = cmd->hdr.return_code;
+ if (rc)
+ QETH_CARD_TEXT_(card, 2, "trapc:%x", rc);
+ return 0;
+}
+
+int qeth_hw_trap(struct qeth_card *card, enum qeth_diags_trap_action action)
+{
+ struct qeth_cmd_buffer *iob;
+ struct qeth_ipa_cmd *cmd;
+
+ QETH_DBF_TEXT(SETUP, 2, "diagtrap");
+ iob = qeth_get_ipacmd_buffer(card, IPA_CMD_SET_DIAG_ASS, 0);
+ cmd = (struct qeth_ipa_cmd *)(iob->data+IPA_PDU_HEADER_SIZE);
+ cmd->data.diagass.subcmd_len = 80;
+ cmd->data.diagass.subcmd = QETH_DIAGS_CMD_TRAP;
+ cmd->data.diagass.type = 1;
+ cmd->data.diagass.action = action;
+ switch (action) {
+ case QETH_DIAGS_TRAP_ARM:
+ cmd->data.diagass.options = 0x0003;
+ cmd->data.diagass.ext = 0x00010000 +
+ sizeof(struct qeth_trap_id);
+ qeth_get_trap_id(card,
+ (struct qeth_trap_id *)cmd->data.diagass.cdata);
+ break;
+ case QETH_DIAGS_TRAP_DISARM:
+ cmd->data.diagass.options = 0x0001;
+ break;
+ case QETH_DIAGS_TRAP_CAPTURE:
+ break;
+ }
+ return qeth_send_ipa_cmd(card, iob, qeth_hw_trap_cb, NULL);
+}
+EXPORT_SYMBOL_GPL(qeth_hw_trap);
+
+int qeth_check_qdio_errors(struct qeth_card *card, struct qdio_buffer *buf,
+ unsigned int qdio_error, const char *dbftext)
+{
+ if (qdio_error) {
+ QETH_CARD_TEXT(card, 2, dbftext);
+ QETH_CARD_TEXT_(card, 2, " F15=%02X",
+ buf->element[15].sflags);
+ QETH_CARD_TEXT_(card, 2, " F14=%02X",
+ buf->element[14].sflags);
+ QETH_CARD_TEXT_(card, 2, " qerr=%X", qdio_error);
+ if ((buf->element[15].sflags) == 0x12) {
+ card->stats.rx_dropped++;
+ return 0;
+ } else
+ return 1;
+ }
+ return 0;
+}
+EXPORT_SYMBOL_GPL(qeth_check_qdio_errors);
+
+void qeth_buffer_reclaim_work(struct work_struct *work)
+{
+ struct qeth_card *card = container_of(work, struct qeth_card,
+ buffer_reclaim_work.work);
+
+ QETH_CARD_TEXT_(card, 2, "brw:%x", card->reclaim_index);
+ qeth_queue_input_buffer(card, card->reclaim_index);
+}
+
+void qeth_queue_input_buffer(struct qeth_card *card, int index)
+{
+ struct qeth_qdio_q *queue = card->qdio.in_q;
+ struct list_head *lh;
+ int count;
+ int i;
+ int rc;
+ int newcount = 0;
+
+ 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) {
+ if (qeth_init_input_buffer(card,
+ &queue->bufs[i % QDIO_MAX_BUFFERS_PER_Q])) {
+ break;
+ } else {
+ newcount++;
+ }
+ }
+
+ if (newcount < count) {
+ /* we are in memory shortage so we switch back to
+ traditional skb allocation and drop packages */
+ atomic_set(&card->force_alloc_skb, 3);
+ count = newcount;
+ } else {
+ atomic_add_unless(&card->force_alloc_skb, -1, 0);
+ }
+
+ if (!count) {
+ i = 0;
+ list_for_each(lh, &card->qdio.in_buf_pool.entry_list)
+ i++;
+ if (i == card->qdio.in_buf_pool.buf_count) {
+ QETH_CARD_TEXT(card, 2, "qsarbw");
+ card->reclaim_index = index;
+ schedule_delayed_work(
+ &card->buffer_reclaim_work,
+ QETH_RECLAIM_WORK_TIME);
+ }
+ return;
+ }
+
+ /*
+ * 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
+ */
+ if (card->options.performance_stats) {
+ card->perf_stats.inbound_do_qdio_cnt++;
+ card->perf_stats.inbound_do_qdio_start_time =
+ qeth_get_micros();
+ }
+ rc = do_QDIO(CARD_DDEV(card), QDIO_FLAG_SYNC_INPUT, 0,
+ queue->next_buf_to_init, count);
+ if (card->options.performance_stats)
+ card->perf_stats.inbound_do_qdio_time +=
+ qeth_get_micros() -
+ card->perf_stats.inbound_do_qdio_start_time;
+ if (rc) {
+ QETH_CARD_TEXT(card, 2, "qinberr");
+ }
+ queue->next_buf_to_init = (queue->next_buf_to_init + count) %
+ QDIO_MAX_BUFFERS_PER_Q;
+ }
+}
+EXPORT_SYMBOL_GPL(qeth_queue_input_buffer);
+
+static int qeth_handle_send_error(struct qeth_card *card,
+ struct qeth_qdio_out_buffer *buffer, unsigned int qdio_err)
+{
+ int sbalf15 = buffer->buffer->element[15].sflags;
+
+ QETH_CARD_TEXT(card, 6, "hdsnderr");
+ if (card->info.type == QETH_CARD_TYPE_IQD) {
+ if (sbalf15 == 0) {
+ qdio_err = 0;
+ } else {
+ qdio_err = 1;
+ }
+ }
+ qeth_check_qdio_errors(card, buffer->buffer, qdio_err, "qouterr");
+
+ if (!qdio_err)
+ return QETH_SEND_ERROR_NONE;
+
+ if ((sbalf15 >= 15) && (sbalf15 <= 31))
+ return QETH_SEND_ERROR_RETRY;
+
+ QETH_CARD_TEXT(card, 1, "lnkfail");
+ QETH_CARD_TEXT_(card, 1, "%04x %02x",
+ (u16)qdio_err, (u8)sbalf15);
+ return QETH_SEND_ERROR_LINK_FAILURE;
+}
+
+/*
+ * Switched to packing state if the number of used buffers on a queue
+ * reaches a certain limit.
+ */
+static void qeth_switch_to_packing_if_needed(struct qeth_qdio_out_q *queue)
+{
+ if (!queue->do_pack) {
+ if (atomic_read(&queue->used_buffers)
+ >= QETH_HIGH_WATERMARK_PACK){
+ /* switch non-PACKING -> PACKING */
+ QETH_CARD_TEXT(queue->card, 6, "np->pack");
+ if (queue->card->options.performance_stats)
+ queue->card->perf_stats.sc_dp_p++;
+ queue->do_pack = 1;
+ }
+ }
+}
+
+/*
+ * Switches from packing to non-packing mode. If there is a packing
+ * buffer on the queue this buffer will be prepared to be flushed.
+ * In that case 1 is returned to inform the caller. If no buffer
+ * has to be flushed, zero is returned.
+ */
+static int qeth_switch_to_nonpacking_if_needed(struct qeth_qdio_out_q *queue)
+{
+ struct qeth_qdio_out_buffer *buffer;
+ int flush_count = 0;
+
+ if (queue->do_pack) {
+ if (atomic_read(&queue->used_buffers)
+ <= QETH_LOW_WATERMARK_PACK) {
+ /* switch PACKING -> non-PACKING */
+ QETH_CARD_TEXT(queue->card, 6, "pack->np");
+ if (queue->card->options.performance_stats)
+ queue->card->perf_stats.sc_p_dp++;
+ queue->do_pack = 0;
+ /* flush packing buffers */
+ buffer = queue->bufs[queue->next_buf_to_fill];
+ if ((atomic_read(&buffer->state) ==
+ QETH_QDIO_BUF_EMPTY) &&
+ (buffer->next_element_to_fill > 0)) {
+ atomic_set(&buffer->state,
+ QETH_QDIO_BUF_PRIMED);
+ flush_count++;
+ queue->next_buf_to_fill =
+ (queue->next_buf_to_fill + 1) %
+ QDIO_MAX_BUFFERS_PER_Q;
+ }
+ }
+ }
+ return flush_count;
+}
+
+
+/*
+ * Called to flush a packing buffer if no more pci flags are on the queue.
+ * Checks if there is a packing buffer and prepares it to be flushed.
+ * In that case returns 1, otherwise zero.
+ */
+static int qeth_flush_buffers_on_no_pci(struct qeth_qdio_out_q *queue)
+{
+ struct qeth_qdio_out_buffer *buffer;
+
+ buffer = queue->bufs[queue->next_buf_to_fill];
+ if ((atomic_read(&buffer->state) == QETH_QDIO_BUF_EMPTY) &&
+ (buffer->next_element_to_fill > 0)) {
+ /* it's a packing buffer */
+ atomic_set(&buffer->state, QETH_QDIO_BUF_PRIMED);
+ queue->next_buf_to_fill =
+ (queue->next_buf_to_fill + 1) % QDIO_MAX_BUFFERS_PER_Q;
+ return 1;
+ }
+ return 0;
+}
+
+static void qeth_flush_buffers(struct qeth_qdio_out_q *queue, int index,
+ int count)
+{
+ struct qeth_qdio_out_buffer *buf;
+ int rc;
+ int i;
+ unsigned int qdio_flags;
+
+ for (i = index; i < index + count; ++i) {
+ int bidx = i % QDIO_MAX_BUFFERS_PER_Q;
+ buf = queue->bufs[bidx];
+ buf->buffer->element[buf->next_element_to_fill - 1].eflags |=
+ SBAL_EFLAGS_LAST_ENTRY;
+
+ if (queue->bufstates)
+ queue->bufstates[bidx].user = buf;
+
+ if (queue->card->info.type == QETH_CARD_TYPE_IQD)
+ continue;
+
+ 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].sflags |= SBAL_SFLAGS0_PCI_REQ;
+ }
+ } 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].sflags |= SBAL_SFLAGS0_PCI_REQ;
+ }
+ }
+ }
+
+ queue->card->dev->trans_start = jiffies;
+ if (queue->card->options.performance_stats) {
+ queue->card->perf_stats.outbound_do_qdio_cnt++;
+ queue->card->perf_stats.outbound_do_qdio_start_time =
+ qeth_get_micros();
+ }
+ qdio_flags = QDIO_FLAG_SYNC_OUTPUT;
+ if (atomic_read(&queue->set_pci_flags_count))
+ qdio_flags |= QDIO_FLAG_PCI_OUT;
+ rc = do_QDIO(CARD_DDEV(queue->card), qdio_flags,
+ queue->queue_no, index, count);
+ if (queue->card->options.performance_stats)
+ queue->card->perf_stats.outbound_do_qdio_time +=
+ qeth_get_micros() -
+ queue->card->perf_stats.outbound_do_qdio_start_time;
+ atomic_add(count, &queue->used_buffers);
+ if (rc) {
+ queue->card->stats.tx_errors += count;
+ /* ignore temporary SIGA errors without busy condition */
+ if (rc == -ENOBUFS)
+ return;
+ QETH_CARD_TEXT(queue->card, 2, "flushbuf");
+ QETH_CARD_TEXT_(queue->card, 2, " q%d", queue->queue_no);
+ QETH_CARD_TEXT_(queue->card, 2, " idx%d", index);
+ QETH_CARD_TEXT_(queue->card, 2, " c%d", count);
+ QETH_CARD_TEXT_(queue->card, 2, " err%d", rc);
+
+ /* this must not happen under normal circumstances. if it
+ * happens something is really wrong -> recover */
+ qeth_schedule_recovery(queue->card);
+ return;
+ }
+ if (queue->card->options.performance_stats)
+ queue->card->perf_stats.bufs_sent += count;
+}
+
+static void qeth_check_outbound_queue(struct qeth_qdio_out_q *queue)
+{
+ int index;
+ int flush_cnt = 0;
+ int q_was_packing = 0;
+
+ /*
+ * check if weed have to switch to non-packing mode or if
+ * we have to get a pci flag out on the queue
+ */
+ if ((atomic_read(&queue->used_buffers) <= QETH_LOW_WATERMARK_PACK) ||
+ !atomic_read(&queue->set_pci_flags_count)) {
+ if (atomic_xchg(&queue->state, QETH_OUT_Q_LOCKED_FLUSH) ==
+ QETH_OUT_Q_UNLOCKED) {
+ /*
+ * If we get in here, there was no action in
+ * do_send_packet. So, we check if there is a
+ * packing buffer to be flushed here.
+ */
+ netif_stop_queue(queue->card->dev);
+ index = queue->next_buf_to_fill;
+ q_was_packing = queue->do_pack;
+ /* queue->do_pack may change */
+ barrier();
+ flush_cnt += qeth_switch_to_nonpacking_if_needed(queue);
+ if (!flush_cnt &&
+ !atomic_read(&queue->set_pci_flags_count))
+ flush_cnt +=
+ qeth_flush_buffers_on_no_pci(queue);
+ if (queue->card->options.performance_stats &&
+ q_was_packing)
+ queue->card->perf_stats.bufs_sent_pack +=
+ flush_cnt;
+ if (flush_cnt)
+ qeth_flush_buffers(queue, index, flush_cnt);
+ atomic_set(&queue->state, QETH_OUT_Q_UNLOCKED);
+ }
+ }
+}
+
+void qeth_qdio_start_poll(struct ccw_device *ccwdev, int queue,
+ unsigned long card_ptr)
+{
+ struct qeth_card *card = (struct qeth_card *)card_ptr;
+
+ if (card->dev && (card->dev->flags & IFF_UP))
+ napi_schedule(&card->napi);
+}
+EXPORT_SYMBOL_GPL(qeth_qdio_start_poll);
+
+int qeth_configure_cq(struct qeth_card *card, enum qeth_cq cq)
+{
+ int rc;
+
+ if (card->options.cq == QETH_CQ_NOTAVAILABLE) {
+ rc = -1;
+ goto out;
+ } else {
+ if (card->options.cq == cq) {
+ rc = 0;
+ goto out;
+ }
+
+ if (card->state != CARD_STATE_DOWN &&
+ card->state != CARD_STATE_RECOVER) {
+ rc = -1;
+ goto out;
+ }
+
+ qeth_free_qdio_buffers(card);
+ card->options.cq = cq;
+ rc = 0;
+ }
+out:
+ return rc;
+
+}
+EXPORT_SYMBOL_GPL(qeth_configure_cq);
+
+
+static void qeth_qdio_cq_handler(struct qeth_card *card,
+ unsigned int qdio_err,
+ unsigned int queue, int first_element, int count) {
+ struct qeth_qdio_q *cq = card->qdio.c_q;
+ int i;
+ int rc;
+
+ if (!qeth_is_cq(card, queue))
+ goto out;
+
+ QETH_CARD_TEXT_(card, 5, "qcqhe%d", first_element);
+ QETH_CARD_TEXT_(card, 5, "qcqhc%d", count);
+ QETH_CARD_TEXT_(card, 5, "qcqherr%d", qdio_err);
+
+ if (qdio_err) {
+ netif_stop_queue(card->dev);
+ qeth_schedule_recovery(card);
+ goto out;
+ }
+
+ if (card->options.performance_stats) {
+ card->perf_stats.cq_cnt++;
+ card->perf_stats.cq_start_time = qeth_get_micros();
+ }
+
+ for (i = first_element; i < first_element + count; ++i) {
+ int bidx = i % QDIO_MAX_BUFFERS_PER_Q;
+ struct qdio_buffer *buffer = &cq->qdio_bufs[bidx];
+ int e;
+
+ e = 0;
+ while (buffer->element[e].addr) {
+ unsigned long phys_aob_addr;
+
+ phys_aob_addr = (unsigned long) buffer->element[e].addr;
+ qeth_qdio_handle_aob(card, phys_aob_addr);
+ buffer->element[e].addr = NULL;
+ buffer->element[e].eflags = 0;
+ buffer->element[e].sflags = 0;
+ buffer->element[e].length = 0;
+
+ ++e;
+ }
+
+ buffer->element[15].eflags = 0;
+ buffer->element[15].sflags = 0;
+ }
+ rc = do_QDIO(CARD_DDEV(card), QDIO_FLAG_SYNC_INPUT, queue,
+ card->qdio.c_q->next_buf_to_init,
+ count);
+ if (rc) {
+ dev_warn(&card->gdev->dev,
+ "QDIO reported an error, rc=%i\n", rc);
+ QETH_CARD_TEXT(card, 2, "qcqherr");
+ }
+ card->qdio.c_q->next_buf_to_init = (card->qdio.c_q->next_buf_to_init
+ + count) % QDIO_MAX_BUFFERS_PER_Q;
+
+ netif_wake_queue(card->dev);
+
+ if (card->options.performance_stats) {
+ int delta_t = qeth_get_micros();
+ delta_t -= card->perf_stats.cq_start_time;
+ card->perf_stats.cq_time += delta_t;
+ }
+out:
+ return;
+}
+
+void qeth_qdio_input_handler(struct ccw_device *ccwdev, unsigned int qdio_err,
+ unsigned int queue, int first_elem, int count,
+ unsigned long card_ptr)
+{
+ struct qeth_card *card = (struct qeth_card *)card_ptr;
+
+ QETH_CARD_TEXT_(card, 2, "qihq%d", queue);
+ QETH_CARD_TEXT_(card, 2, "qiec%d", qdio_err);
+
+ if (qeth_is_cq(card, queue))
+ qeth_qdio_cq_handler(card, qdio_err, queue, first_elem, count);
+ else if (qdio_err)
+ qeth_schedule_recovery(card);
+
+
+}
+EXPORT_SYMBOL_GPL(qeth_qdio_input_handler);
+
+void qeth_qdio_output_handler(struct ccw_device *ccwdev,
+ unsigned int qdio_error, 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_CARD_TEXT(card, 6, "qdouhdl");
+ if (qdio_error & QDIO_ERROR_FATAL) {
+ QETH_CARD_TEXT(card, 2, "achkcond");
+ netif_stop_queue(card->dev);
+ qeth_schedule_recovery(card);
+ return;
+ }
+ if (card->options.performance_stats) {
+ card->perf_stats.outbound_handler_cnt++;
+ card->perf_stats.outbound_handler_start_time =
+ qeth_get_micros();
+ }
+ for (i = first_element; i < (first_element + count); ++i) {
+ int bidx = i % QDIO_MAX_BUFFERS_PER_Q;
+ buffer = queue->bufs[bidx];
+ qeth_handle_send_error(card, buffer, qdio_error);
+
+ if (queue->bufstates &&
+ (queue->bufstates[bidx].flags &
+ QDIO_OUTBUF_STATE_FLAG_PENDING) != 0) {
+ WARN_ON_ONCE(card->options.cq != QETH_CQ_ENABLED);
+
+ if (atomic_cmpxchg(&buffer->state,
+ QETH_QDIO_BUF_PRIMED,
+ QETH_QDIO_BUF_PENDING) ==
+ QETH_QDIO_BUF_PRIMED) {
+ qeth_notify_skbs(queue, buffer,
+ TX_NOTIFY_PENDING);
+ }
+ buffer->aob = queue->bufstates[bidx].aob;
+ QETH_CARD_TEXT_(queue->card, 5, "pel%d", bidx);
+ QETH_CARD_TEXT(queue->card, 5, "aob");
+ QETH_CARD_TEXT_(queue->card, 5, "%lx",
+ virt_to_phys(buffer->aob));
+ if (qeth_init_qdio_out_buf(queue, bidx)) {
+ QETH_CARD_TEXT(card, 2, "outofbuf");
+ qeth_schedule_recovery(card);
+ }
+ } else {
+ if (card->options.cq == QETH_CQ_ENABLED) {
+ enum iucv_tx_notify n;
+
+ n = qeth_compute_cq_notification(
+ buffer->buffer->element[15].sflags, 0);
+ qeth_notify_skbs(queue, buffer, n);
+ }
+
+ qeth_clear_output_buffer(queue, buffer,
+ QETH_QDIO_BUF_EMPTY);
+ }
+ qeth_cleanup_handled_pending(queue, bidx, 0);
+ }
+ atomic_sub(count, &queue->used_buffers);
+ /* check if we need to do something on this outbound queue */
+ if (card->info.type != QETH_CARD_TYPE_IQD)
+ qeth_check_outbound_queue(queue);
+
+ netif_wake_queue(queue->card->dev);
+ if (card->options.performance_stats)
+ card->perf_stats.outbound_handler_time += qeth_get_micros() -
+ card->perf_stats.outbound_handler_start_time;
+}
+EXPORT_SYMBOL_GPL(qeth_qdio_output_handler);
+
+/**
+ * Note: Function assumes that we have 4 outbound queues.
+ */
+int qeth_get_priority_queue(struct qeth_card *card, struct sk_buff *skb,
+ int ipv, int cast_type)
+{
+ __be16 *tci;
+ u8 tos;
+
+ if (cast_type && card->info.is_multicast_different)
+ return card->info.is_multicast_different &
+ (card->qdio.no_out_queues - 1);
+
+ switch (card->qdio.do_prio_queueing) {
+ case QETH_PRIO_Q_ING_TOS:
+ case QETH_PRIO_Q_ING_PREC:
+ switch (ipv) {
+ case 4:
+ tos = ipv4_get_dsfield(ip_hdr(skb));
+ break;
+ case 6:
+ tos = ipv6_get_dsfield(ipv6_hdr(skb));
+ break;
+ default:
+ return card->qdio.default_out_queue;
+ }
+ if (card->qdio.do_prio_queueing == QETH_PRIO_Q_ING_PREC)
+ return ~tos >> 6 & 3;
+ if (tos & IPTOS_MINCOST)
+ return 3;
+ if (tos & IPTOS_RELIABILITY)
+ return 2;
+ if (tos & IPTOS_THROUGHPUT)
+ return 1;
+ if (tos & IPTOS_LOWDELAY)
+ return 0;
+ break;
+ case QETH_PRIO_Q_ING_SKB:
+ if (skb->priority > 5)
+ return 0;
+ return ~skb->priority >> 1 & 3;
+ case QETH_PRIO_Q_ING_VLAN:
+ tci = &((struct ethhdr *)skb->data)->h_proto;
+ if (*tci == ETH_P_8021Q)
+ return ~*(tci + 1) >> (VLAN_PRIO_SHIFT + 1) & 3;
+ break;
+ default:
+ break;
+ }
+ return card->qdio.default_out_queue;
+}
+EXPORT_SYMBOL_GPL(qeth_get_priority_queue);
+
+int qeth_get_elements_for_frags(struct sk_buff *skb)
+{
+ int cnt, length, e, elements = 0;
+ struct skb_frag_struct *frag;
+ char *data;
+
+ for (cnt = 0; cnt < skb_shinfo(skb)->nr_frags; cnt++) {
+ frag = &skb_shinfo(skb)->frags[cnt];
+ data = (char *)page_to_phys(skb_frag_page(frag)) +
+ frag->page_offset;
+ length = frag->size;
+ e = PFN_UP((unsigned long)data + length - 1) -
+ PFN_DOWN((unsigned long)data);
+ elements += e;
+ }
+ return elements;
+}
+EXPORT_SYMBOL_GPL(qeth_get_elements_for_frags);
+
+int qeth_get_elements_no(struct qeth_card *card,
+ struct sk_buff *skb, int elems)
+{
+ int dlen = skb->len - skb->data_len;
+ int elements_needed = PFN_UP((unsigned long)skb->data + dlen - 1) -
+ PFN_DOWN((unsigned long)skb->data);
+
+ elements_needed += qeth_get_elements_for_frags(skb);
+
+ if ((elements_needed + elems) > QETH_MAX_BUFFER_ELEMENTS(card)) {
+ QETH_DBF_MESSAGE(2, "Invalid size of IP packet "
+ "(Number=%d / Length=%d). Discarded.\n",
+ (elements_needed+elems), skb->len);
+ return 0;
+ }
+ return elements_needed;
+}
+EXPORT_SYMBOL_GPL(qeth_get_elements_no);
+
+int qeth_hdr_chk_and_bounce(struct sk_buff *skb, struct qeth_hdr **hdr, int len)
+{
+ int hroom, inpage, rest;
+
+ if (((unsigned long)skb->data & PAGE_MASK) !=
+ (((unsigned long)skb->data + len - 1) & PAGE_MASK)) {
+ hroom = skb_headroom(skb);
+ inpage = PAGE_SIZE - ((unsigned long) skb->data % PAGE_SIZE);
+ rest = len - inpage;
+ if (rest > hroom)
+ return 1;
+ memmove(skb->data - rest, skb->data, skb->len - skb->data_len);
+ skb->data -= rest;
+ skb->tail -= rest;
+ *hdr = (struct qeth_hdr *)skb->data;
+ QETH_DBF_MESSAGE(2, "skb bounce len: %d rest: %d\n", len, rest);
+ }
+ return 0;
+}
+EXPORT_SYMBOL_GPL(qeth_hdr_chk_and_bounce);
+
+static inline void __qeth_fill_buffer(struct sk_buff *skb,
+ struct qdio_buffer *buffer, int is_tso, int *next_element_to_fill,
+ int offset)
+{
+ int length = skb->len - skb->data_len;
+ int length_here;
+ int element;
+ char *data;
+ int first_lap, cnt;
+ struct skb_frag_struct *frag;
+
+ element = *next_element_to_fill;
+ data = skb->data;
+ first_lap = (is_tso == 0 ? 1 : 0);
+
+ if (offset >= 0) {
+ data = skb->data + offset;
+ length -= offset;
+ first_lap = 0;
+ }
+
+ 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)
+ if (skb_shinfo(skb)->nr_frags)
+ buffer->element[element].eflags =
+ SBAL_EFLAGS_FIRST_FRAG;
+ else
+ buffer->element[element].eflags = 0;
+ else
+ buffer->element[element].eflags =
+ SBAL_EFLAGS_MIDDLE_FRAG;
+ } else {
+ if (first_lap)
+ buffer->element[element].eflags =
+ SBAL_EFLAGS_FIRST_FRAG;
+ else
+ buffer->element[element].eflags =
+ SBAL_EFLAGS_MIDDLE_FRAG;
+ }
+ data += length_here;
+ element++;
+ first_lap = 0;
+ }
+
+ for (cnt = 0; cnt < skb_shinfo(skb)->nr_frags; cnt++) {
+ frag = &skb_shinfo(skb)->frags[cnt];
+ data = (char *)page_to_phys(skb_frag_page(frag)) +
+ frag->page_offset;
+ length = frag->size;
+ while (length > 0) {
+ 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;
+ buffer->element[element].eflags =
+ SBAL_EFLAGS_MIDDLE_FRAG;
+ length -= length_here;
+ data += length_here;
+ element++;
+ }
+ }
+
+ if (buffer->element[element - 1].eflags)
+ buffer->element[element - 1].eflags = SBAL_EFLAGS_LAST_FRAG;
+ *next_element_to_fill = element;
+}
+
+static inline int qeth_fill_buffer(struct qeth_qdio_out_q *queue,
+ struct qeth_qdio_out_buffer *buf, struct sk_buff *skb,
+ struct qeth_hdr *hdr, int offset, int hd_len)
+{
+ struct qdio_buffer *buffer;
+ int flush_cnt = 0, hdr_len, large_send = 0;
+
+ buffer = buf->buffer;
+ atomic_inc(&skb->users);
+ skb_queue_tail(&buf->skb_list, skb);
+
+ /*check first on TSO ....*/
+ if (hdr->hdr.l3.id == QETH_HEADER_TYPE_TSO) {
+ int element = buf->next_element_to_fill;
+
+ hdr_len = sizeof(struct qeth_hdr_tso) +
+ ((struct qeth_hdr_tso *)hdr)->ext.dg_hdr_len;
+ /*fill first buffer entry only with header information */
+ buffer->element[element].addr = skb->data;
+ buffer->element[element].length = hdr_len;
+ buffer->element[element].eflags = SBAL_EFLAGS_FIRST_FRAG;
+ buf->next_element_to_fill++;
+ skb->data += hdr_len;
+ skb->len -= hdr_len;
+ large_send = 1;
+ }
+
+ if (offset >= 0) {
+ int element = buf->next_element_to_fill;
+ buffer->element[element].addr = hdr;
+ buffer->element[element].length = sizeof(struct qeth_hdr) +
+ hd_len;
+ buffer->element[element].eflags = SBAL_EFLAGS_FIRST_FRAG;
+ buf->is_header[element] = 1;
+ buf->next_element_to_fill++;
+ }
+
+ __qeth_fill_buffer(skb, buffer, large_send,
+ (int *)&buf->next_element_to_fill, offset);
+
+ if (!queue->do_pack) {
+ QETH_CARD_TEXT(queue->card, 6, "fillbfnp");
+ /* set state to PRIMED -> will be flushed */
+ atomic_set(&buf->state, QETH_QDIO_BUF_PRIMED);
+ flush_cnt = 1;
+ } else {
+ QETH_CARD_TEXT(queue->card, 6, "fillbfpa");
+ if (queue->card->options.performance_stats)
+ queue->card->perf_stats.skbs_sent_pack++;
+ if (buf->next_element_to_fill >=
+ QETH_MAX_BUFFER_ELEMENTS(queue->card)) {
+ /*
+ * packed buffer if full -> set state PRIMED
+ * -> will be flushed
+ */
+ atomic_set(&buf->state, QETH_QDIO_BUF_PRIMED);
+ flush_cnt = 1;
+ }
+ }
+ return flush_cnt;
+}
+
+int qeth_do_send_packet_fast(struct qeth_card *card,
+ struct qeth_qdio_out_q *queue, struct sk_buff *skb,
+ struct qeth_hdr *hdr, int elements_needed,
+ int offset, int hd_len)
+{
+ struct qeth_qdio_out_buffer *buffer;
+ int index;
+
+ /* spin until we get the queue ... */
+ while (atomic_cmpxchg(&queue->state, QETH_OUT_Q_UNLOCKED,
+ QETH_OUT_Q_LOCKED) != QETH_OUT_Q_UNLOCKED);
+ /* ... now we've got the queue */
+ index = queue->next_buf_to_fill;
+ buffer = queue->bufs[queue->next_buf_to_fill];
+ /*
+ * check if buffer is empty to make sure that we do not 'overtake'
+ * ourselves and try to fill a buffer that is already primed
+ */
+ if (atomic_read(&buffer->state) != QETH_QDIO_BUF_EMPTY)
+ goto out;
+ queue->next_buf_to_fill = (queue->next_buf_to_fill + 1) %
+ QDIO_MAX_BUFFERS_PER_Q;
+ atomic_set(&queue->state, QETH_OUT_Q_UNLOCKED);
+ qeth_fill_buffer(queue, buffer, skb, hdr, offset, hd_len);
+ qeth_flush_buffers(queue, index, 1);
+ return 0;
+out:
+ atomic_set(&queue->state, QETH_OUT_Q_UNLOCKED);
+ return -EBUSY;
+}
+EXPORT_SYMBOL_GPL(qeth_do_send_packet_fast);
+
+int qeth_do_send_packet(struct qeth_card *card, struct qeth_qdio_out_q *queue,
+ struct sk_buff *skb, struct qeth_hdr *hdr,
+ int elements_needed)
+{
+ struct qeth_qdio_out_buffer *buffer;
+ int start_index;
+ int flush_count = 0;
+ int do_pack = 0;
+ int tmp;
+ int rc = 0;
+
+ /* spin until we get the queue ... */
+ while (atomic_cmpxchg(&queue->state, QETH_OUT_Q_UNLOCKED,
+ QETH_OUT_Q_LOCKED) != QETH_OUT_Q_UNLOCKED);
+ start_index = queue->next_buf_to_fill;
+ buffer = queue->bufs[queue->next_buf_to_fill];
+ /*
+ * check if buffer is empty to make sure that we do not 'overtake'
+ * ourselves and try to fill a buffer that is already primed
+ */
+ if (atomic_read(&buffer->state) != QETH_QDIO_BUF_EMPTY) {
+ atomic_set(&queue->state, QETH_OUT_Q_UNLOCKED);
+ return -EBUSY;
+ }
+ /* check if we need to switch packing state of this queue */
+ qeth_switch_to_packing_if_needed(queue);
+ if (queue->do_pack) {
+ do_pack = 1;
+ /* does packet fit in current buffer? */
+ if ((QETH_MAX_BUFFER_ELEMENTS(card) -
+ buffer->next_element_to_fill) < elements_needed) {
+ /* ... no -> set state PRIMED */
+ atomic_set(&buffer->state, QETH_QDIO_BUF_PRIMED);
+ flush_count++;
+ queue->next_buf_to_fill =
+ (queue->next_buf_to_fill + 1) %
+ QDIO_MAX_BUFFERS_PER_Q;
+ buffer = queue->bufs[queue->next_buf_to_fill];
+ /* we did a step forward, so check buffer state
+ * again */
+ if (atomic_read(&buffer->state) !=
+ QETH_QDIO_BUF_EMPTY) {
+ qeth_flush_buffers(queue, start_index,
+ flush_count);
+ atomic_set(&queue->state,
+ QETH_OUT_Q_UNLOCKED);
+ return -EBUSY;
+ }
+ }
+ }
+ tmp = qeth_fill_buffer(queue, buffer, skb, hdr, -1, 0);
+ queue->next_buf_to_fill = (queue->next_buf_to_fill + tmp) %
+ QDIO_MAX_BUFFERS_PER_Q;
+ flush_count += tmp;
+ if (flush_count)
+ qeth_flush_buffers(queue, start_index, flush_count);
+ else if (!atomic_read(&queue->set_pci_flags_count))
+ atomic_xchg(&queue->state, QETH_OUT_Q_LOCKED_FLUSH);
+ /*
+ * queue->state will go from LOCKED -> UNLOCKED or from
+ * LOCKED_FLUSH -> LOCKED if output_handler wanted to 'notify' us
+ * (switch packing state or flush buffer to get another pci flag out).
+ * In that case we will enter this loop
+ */
+ while (atomic_dec_return(&queue->state)) {
+ flush_count = 0;
+ start_index = queue->next_buf_to_fill;
+ /* check if we can go back to non-packing state */
+ flush_count += qeth_switch_to_nonpacking_if_needed(queue);
+ /*
+ * check if we need to flush a packing buffer to get a pci
+ * flag out on the queue
+ */
+ if (!flush_count && !atomic_read(&queue->set_pci_flags_count))
+ flush_count += qeth_flush_buffers_on_no_pci(queue);
+ if (flush_count)
+ qeth_flush_buffers(queue, start_index, flush_count);
+ }
+ /* at this point the queue is UNLOCKED again */
+ if (queue->card->options.performance_stats && do_pack)
+ queue->card->perf_stats.bufs_sent_pack += flush_count;
+
+ return rc;
+}
+EXPORT_SYMBOL_GPL(qeth_do_send_packet);
+
+static int qeth_setadp_promisc_mode_cb(struct qeth_card *card,
+ struct qeth_reply *reply, unsigned long data)
+{
+ struct qeth_ipa_cmd *cmd;
+ struct qeth_ipacmd_setadpparms *setparms;
+
+ QETH_CARD_TEXT(card, 4, "prmadpcb");
+
+ cmd = (struct qeth_ipa_cmd *) data;
+ setparms = &(cmd->data.setadapterparms);
+
+ qeth_default_setadapterparms_cb(card, reply, (unsigned long)cmd);
+ if (cmd->hdr.return_code) {
+ QETH_CARD_TEXT_(card, 4, "prmrc%2.2x", cmd->hdr.return_code);
+ setparms->data.mode = SET_PROMISC_MODE_OFF;
+ }
+ card->info.promisc_mode = setparms->data.mode;
+ return 0;
+}
+
+void qeth_setadp_promisc_mode(struct qeth_card *card)
+{
+ enum qeth_ipa_promisc_modes mode;
+ struct net_device *dev = card->dev;
+ struct qeth_cmd_buffer *iob;
+ struct qeth_ipa_cmd *cmd;
+
+ QETH_CARD_TEXT(card, 4, "setprom");
+
+ if (((dev->flags & IFF_PROMISC) &&
+ (card->info.promisc_mode == SET_PROMISC_MODE_ON)) ||
+ (!(dev->flags & IFF_PROMISC) &&
+ (card->info.promisc_mode == SET_PROMISC_MODE_OFF)))
+ return;
+ mode = SET_PROMISC_MODE_OFF;
+ if (dev->flags & IFF_PROMISC)
+ mode = SET_PROMISC_MODE_ON;
+ QETH_CARD_TEXT_(card, 4, "mode:%x", mode);
+
+ iob = qeth_get_adapter_cmd(card, IPA_SETADP_SET_PROMISC_MODE,
+ sizeof(struct qeth_ipacmd_setadpparms));
+ cmd = (struct qeth_ipa_cmd *)(iob->data + IPA_PDU_HEADER_SIZE);
+ cmd->data.setadapterparms.data.mode = mode;
+ qeth_send_ipa_cmd(card, iob, qeth_setadp_promisc_mode_cb, NULL);
+}
+EXPORT_SYMBOL_GPL(qeth_setadp_promisc_mode);
+
+int qeth_change_mtu(struct net_device *dev, int new_mtu)
+{
+ struct qeth_card *card;
+ char dbf_text[15];
+
+ card = dev->ml_priv;
+
+ QETH_CARD_TEXT(card, 4, "chgmtu");
+ sprintf(dbf_text, "%8x", new_mtu);
+ QETH_CARD_TEXT(card, 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;
+}
+EXPORT_SYMBOL_GPL(qeth_change_mtu);
+
+struct net_device_stats *qeth_get_stats(struct net_device *dev)
+{
+ struct qeth_card *card;
+
+ card = dev->ml_priv;
+
+ QETH_CARD_TEXT(card, 5, "getstat");
+
+ return &card->stats;
+}
+EXPORT_SYMBOL_GPL(qeth_get_stats);
+
+static int qeth_setadpparms_change_macaddr_cb(struct qeth_card *card,
+ struct qeth_reply *reply, unsigned long data)
+{
+ struct qeth_ipa_cmd *cmd;
+
+ QETH_CARD_TEXT(card, 4, "chgmaccb");
+
+ cmd = (struct qeth_ipa_cmd *) data;
+ if (!card->options.layer2 ||
+ !(card->info.mac_bits & QETH_LAYER2_MAC_READ)) {
+ memcpy(card->dev->dev_addr,
+ &cmd->data.setadapterparms.data.change_addr.addr,
+ OSA_ADDR_LEN);
+ card->info.mac_bits |= QETH_LAYER2_MAC_READ;
+ }
+ qeth_default_setadapterparms_cb(card, reply, (unsigned long) cmd);
+ return 0;
+}
+
+int qeth_setadpparms_change_macaddr(struct qeth_card *card)
+{
+ int rc;
+ struct qeth_cmd_buffer *iob;
+ struct qeth_ipa_cmd *cmd;
+
+ QETH_CARD_TEXT(card, 4, "chgmac");
+
+ iob = qeth_get_adapter_cmd(card, IPA_SETADP_ALTER_MAC_ADDRESS,
+ sizeof(struct qeth_ipacmd_setadpparms));
+ 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;
+}
+EXPORT_SYMBOL_GPL(qeth_setadpparms_change_macaddr);
+
+static int qeth_setadpparms_set_access_ctrl_cb(struct qeth_card *card,
+ struct qeth_reply *reply, unsigned long data)
+{
+ struct qeth_ipa_cmd *cmd;
+ struct qeth_set_access_ctrl *access_ctrl_req;
+ int fallback = *(int *)reply->param;
+
+ QETH_CARD_TEXT(card, 4, "setaccb");
+
+ cmd = (struct qeth_ipa_cmd *) data;
+ access_ctrl_req = &cmd->data.setadapterparms.data.set_access_ctrl;
+ QETH_DBF_TEXT_(SETUP, 2, "setaccb");
+ QETH_DBF_TEXT_(SETUP, 2, "%s", card->gdev->dev.kobj.name);
+ QETH_DBF_TEXT_(SETUP, 2, "rc=%d",
+ cmd->data.setadapterparms.hdr.return_code);
+ if (cmd->data.setadapterparms.hdr.return_code !=
+ SET_ACCESS_CTRL_RC_SUCCESS)
+ QETH_DBF_MESSAGE(3, "ERR:SET_ACCESS_CTRL(%s,%d)==%d\n",
+ card->gdev->dev.kobj.name,
+ access_ctrl_req->subcmd_code,
+ cmd->data.setadapterparms.hdr.return_code);
+ switch (cmd->data.setadapterparms.hdr.return_code) {
+ case SET_ACCESS_CTRL_RC_SUCCESS:
+ if (card->options.isolation == ISOLATION_MODE_NONE) {
+ dev_info(&card->gdev->dev,
+ "QDIO data connection isolation is deactivated\n");
+ } else {
+ dev_info(&card->gdev->dev,
+ "QDIO data connection isolation is activated\n");
+ }
+ break;
+ case SET_ACCESS_CTRL_RC_ALREADY_NOT_ISOLATED:
+ QETH_DBF_MESSAGE(2, "%s QDIO data connection isolation already "
+ "deactivated\n", dev_name(&card->gdev->dev));
+ if (fallback)
+ card->options.isolation = card->options.prev_isolation;
+ break;
+ case SET_ACCESS_CTRL_RC_ALREADY_ISOLATED:
+ QETH_DBF_MESSAGE(2, "%s QDIO data connection isolation already"
+ " activated\n", dev_name(&card->gdev->dev));
+ if (fallback)
+ card->options.isolation = card->options.prev_isolation;
+ break;
+ case SET_ACCESS_CTRL_RC_NOT_SUPPORTED:
+ dev_err(&card->gdev->dev, "Adapter does not "
+ "support QDIO data connection isolation\n");
+ break;
+ case SET_ACCESS_CTRL_RC_NONE_SHARED_ADAPTER:
+ dev_err(&card->gdev->dev,
+ "Adapter is dedicated. "
+ "QDIO data connection isolation not supported\n");
+ if (fallback)
+ card->options.isolation = card->options.prev_isolation;
+ break;
+ case SET_ACCESS_CTRL_RC_ACTIVE_CHECKSUM_OFF:
+ dev_err(&card->gdev->dev,
+ "TSO does not permit QDIO data connection isolation\n");
+ if (fallback)
+ card->options.isolation = card->options.prev_isolation;
+ break;
+ case SET_ACCESS_CTRL_RC_REFLREL_UNSUPPORTED:
+ dev_err(&card->gdev->dev, "The adjacent switch port does not "
+ "support reflective relay mode\n");
+ if (fallback)
+ card->options.isolation = card->options.prev_isolation;
+ break;
+ case SET_ACCESS_CTRL_RC_REFLREL_FAILED:
+ dev_err(&card->gdev->dev, "The reflective relay mode cannot be "
+ "enabled at the adjacent switch port");
+ if (fallback)
+ card->options.isolation = card->options.prev_isolation;
+ break;
+ case SET_ACCESS_CTRL_RC_REFLREL_DEACT_FAILED:
+ dev_warn(&card->gdev->dev, "Turning off reflective relay mode "
+ "at the adjacent switch failed\n");
+ break;
+ default:
+ /* this should never happen */
+ if (fallback)
+ card->options.isolation = card->options.prev_isolation;
+ break;
+ }
+ qeth_default_setadapterparms_cb(card, reply, (unsigned long) cmd);
+ return 0;
+}
+
+static int qeth_setadpparms_set_access_ctrl(struct qeth_card *card,
+ enum qeth_ipa_isolation_modes isolation, int fallback)
+{
+ int rc;
+ struct qeth_cmd_buffer *iob;
+ struct qeth_ipa_cmd *cmd;
+ struct qeth_set_access_ctrl *access_ctrl_req;
+
+ QETH_CARD_TEXT(card, 4, "setacctl");
+
+ QETH_DBF_TEXT_(SETUP, 2, "setacctl");
+ QETH_DBF_TEXT_(SETUP, 2, "%s", card->gdev->dev.kobj.name);
+
+ iob = qeth_get_adapter_cmd(card, IPA_SETADP_SET_ACCESS_CONTROL,
+ sizeof(struct qeth_ipacmd_setadpparms_hdr) +
+ sizeof(struct qeth_set_access_ctrl));
+ cmd = (struct qeth_ipa_cmd *)(iob->data+IPA_PDU_HEADER_SIZE);
+ access_ctrl_req = &cmd->data.setadapterparms.data.set_access_ctrl;
+ access_ctrl_req->subcmd_code = isolation;
+
+ rc = qeth_send_ipa_cmd(card, iob, qeth_setadpparms_set_access_ctrl_cb,
+ &fallback);
+ QETH_DBF_TEXT_(SETUP, 2, "rc=%d", rc);
+ return rc;
+}
+
+int qeth_set_access_ctrl_online(struct qeth_card *card, int fallback)
+{
+ int rc = 0;
+
+ QETH_CARD_TEXT(card, 4, "setactlo");
+
+ if ((card->info.type == QETH_CARD_TYPE_OSD ||
+ card->info.type == QETH_CARD_TYPE_OSX) &&
+ qeth_adp_supported(card, IPA_SETADP_SET_ACCESS_CONTROL)) {
+ rc = qeth_setadpparms_set_access_ctrl(card,
+ card->options.isolation, fallback);
+ if (rc) {
+ QETH_DBF_MESSAGE(3,
+ "IPA(SET_ACCESS_CTRL,%s,%d) sent failed\n",
+ card->gdev->dev.kobj.name,
+ rc);
+ rc = -EOPNOTSUPP;
+ }
+ } else if (card->options.isolation != ISOLATION_MODE_NONE) {
+ card->options.isolation = ISOLATION_MODE_NONE;
+
+ dev_err(&card->gdev->dev, "Adapter does not "
+ "support QDIO data connection isolation\n");
+ rc = -EOPNOTSUPP;
+ }
+ return rc;
+}
+EXPORT_SYMBOL_GPL(qeth_set_access_ctrl_online);
+
+void qeth_tx_timeout(struct net_device *dev)
+{
+ struct qeth_card *card;
+
+ card = dev->ml_priv;
+ QETH_CARD_TEXT(card, 4, "txtimeo");
+ card->stats.tx_errors++;
+ qeth_schedule_recovery(card);
+}
+EXPORT_SYMBOL_GPL(qeth_tx_timeout);
+
+int qeth_mdio_read(struct net_device *dev, int phy_id, int regnum)
+{
+ struct qeth_card *card = dev->ml_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) &&
+ (card->info.link_type != QETH_LINK_TYPE_OSN) &&
+ (card->info.link_type != QETH_LINK_TYPE_10GBIT_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:
+ break;
+ }
+ return rc;
+}
+EXPORT_SYMBOL_GPL(qeth_mdio_read);
+
+static int qeth_send_ipa_snmp_cmd(struct qeth_card *card,
+ struct qeth_cmd_buffer *iob, int len,
+ int (*reply_cb)(struct qeth_card *, struct qeth_reply *,
+ unsigned long),
+ void *reply_param)
+{
+ u16 s1, s2;
+
+ QETH_CARD_TEXT(card, 4, "sendsnmp");
+
+ 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);
+ /* adjust PDU length fields in IPA_PDU_HEADER */
+ s1 = (u32) IPA_PDU_HEADER_SIZE + len;
+ s2 = (u32) 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);
+ return qeth_send_control_data(card, IPA_PDU_HEADER_SIZE + len, iob,
+ reply_cb, reply_param);
+}
+
+static int qeth_snmp_command_cb(struct qeth_card *card,
+ struct qeth_reply *reply, unsigned long sdata)
+{
+ struct qeth_ipa_cmd *cmd;
+ struct qeth_arp_query_info *qinfo;
+ struct qeth_snmp_cmd *snmp;
+ unsigned char *data;
+ __u16 data_len;
+
+ QETH_CARD_TEXT(card, 3, "snpcmdcb");
+
+ cmd = (struct qeth_ipa_cmd *) sdata;
+ data = (unsigned char *)((char *)cmd - reply->offset);
+ qinfo = (struct qeth_arp_query_info *) reply->param;
+ snmp = &cmd->data.setadapterparms.data.snmp;
+
+ if (cmd->hdr.return_code) {
+ QETH_CARD_TEXT_(card, 4, "scer1%i", cmd->hdr.return_code);
+ return 0;
+ }
+ if (cmd->data.setadapterparms.hdr.return_code) {
+ cmd->hdr.return_code =
+ cmd->data.setadapterparms.hdr.return_code;
+ QETH_CARD_TEXT_(card, 4, "scer2%i", cmd->hdr.return_code);
+ return 0;
+ }
+ data_len = *((__u16 *)QETH_IPA_PDU_LEN_PDU1(data));
+ if (cmd->data.setadapterparms.hdr.seq_no == 1)
+ data_len -= (__u16)((char *)&snmp->data - (char *)cmd);
+ else
+ data_len -= (__u16)((char *)&snmp->request - (char *)cmd);
+
+ /* check if there is enough room in userspace */
+ if ((qinfo->udata_len - qinfo->udata_offset) < data_len) {
+ QETH_CARD_TEXT_(card, 4, "scer3%i", -ENOMEM);
+ cmd->hdr.return_code = IPA_RC_ENOMEM;
+ return 0;
+ }
+ QETH_CARD_TEXT_(card, 4, "snore%i",
+ cmd->data.setadapterparms.hdr.used_total);
+ QETH_CARD_TEXT_(card, 4, "sseqn%i",
+ cmd->data.setadapterparms.hdr.seq_no);
+ /*copy entries to user buffer*/
+ if (cmd->data.setadapterparms.hdr.seq_no == 1) {
+ memcpy(qinfo->udata + qinfo->udata_offset,
+ (char *)snmp,
+ data_len + offsetof(struct qeth_snmp_cmd, data));
+ qinfo->udata_offset += offsetof(struct qeth_snmp_cmd, data);
+ } else {
+ memcpy(qinfo->udata + qinfo->udata_offset,
+ (char *)&snmp->request, data_len);
+ }
+ qinfo->udata_offset += data_len;
+ /* check if all replies received ... */
+ QETH_CARD_TEXT_(card, 4, "srtot%i",
+ cmd->data.setadapterparms.hdr.used_total);
+ QETH_CARD_TEXT_(card, 4, "srseq%i",
+ cmd->data.setadapterparms.hdr.seq_no);
+ if (cmd->data.setadapterparms.hdr.seq_no <
+ cmd->data.setadapterparms.hdr.used_total)
+ return 1;
+ return 0;
+}
+
+int qeth_snmp_command(struct qeth_card *card, char __user *udata)
+{
+ struct qeth_cmd_buffer *iob;
+ struct qeth_ipa_cmd *cmd;
+ struct qeth_snmp_ureq *ureq;
+ unsigned int req_len;
+ struct qeth_arp_query_info qinfo = {0, };
+ int rc = 0;
+
+ QETH_CARD_TEXT(card, 3, "snmpcmd");
+
+ if (card->info.guestlan)
+ return -EOPNOTSUPP;
+
+ if ((!qeth_adp_supported(card, IPA_SETADP_SET_SNMP_CONTROL)) &&
+ (!card->options.layer2)) {
+ return -EOPNOTSUPP;
+ }
+ /* skip 4 bytes (data_len struct member) to get req_len */
+ if (copy_from_user(&req_len, udata + sizeof(int), sizeof(int)))
+ return -EFAULT;
+ if (req_len > (QETH_BUFSIZE - IPA_PDU_HEADER_SIZE -
+ sizeof(struct qeth_ipacmd_hdr) -
+ sizeof(struct qeth_ipacmd_setadpparms_hdr)))
+ return -EINVAL;
+ ureq = memdup_user(udata, req_len + sizeof(struct qeth_snmp_ureq_hdr));
+ if (IS_ERR(ureq)) {
+ QETH_CARD_TEXT(card, 2, "snmpnome");
+ return PTR_ERR(ureq);
+ }
+ qinfo.udata_len = ureq->hdr.data_len;
+ qinfo.udata = kzalloc(qinfo.udata_len, GFP_KERNEL);
+ if (!qinfo.udata) {
+ kfree(ureq);
+ return -ENOMEM;
+ }
+ qinfo.udata_offset = sizeof(struct qeth_snmp_ureq_hdr);
+
+ iob = qeth_get_adapter_cmd(card, IPA_SETADP_SET_SNMP_CONTROL,
+ QETH_SNMP_SETADP_CMDLENGTH + req_len);
+ cmd = (struct qeth_ipa_cmd *)(iob->data+IPA_PDU_HEADER_SIZE);
+ memcpy(&cmd->data.setadapterparms.data.snmp, &ureq->cmd, req_len);
+ rc = qeth_send_ipa_snmp_cmd(card, iob, QETH_SETADP_BASE_LEN + req_len,
+ qeth_snmp_command_cb, (void *)&qinfo);
+ if (rc)
+ QETH_DBF_MESSAGE(2, "SNMP command failed on %s: (0x%x)\n",
+ QETH_CARD_IFNAME(card), rc);
+ else {
+ if (copy_to_user(udata, qinfo.udata, qinfo.udata_len))
+ rc = -EFAULT;
+ }
+
+ kfree(ureq);
+ kfree(qinfo.udata);
+ return rc;
+}
+EXPORT_SYMBOL_GPL(qeth_snmp_command);
+
+static int qeth_setadpparms_query_oat_cb(struct qeth_card *card,
+ struct qeth_reply *reply, unsigned long data)
+{
+ struct qeth_ipa_cmd *cmd;
+ struct qeth_qoat_priv *priv;
+ char *resdata;
+ int resdatalen;
+
+ QETH_CARD_TEXT(card, 3, "qoatcb");
+
+ cmd = (struct qeth_ipa_cmd *)data;
+ priv = (struct qeth_qoat_priv *)reply->param;
+ resdatalen = cmd->data.setadapterparms.hdr.cmdlength;
+ resdata = (char *)data + 28;
+
+ if (resdatalen > (priv->buffer_len - priv->response_len)) {
+ cmd->hdr.return_code = IPA_RC_FFFF;
+ return 0;
+ }
+
+ memcpy((priv->buffer + priv->response_len), resdata,
+ resdatalen);
+ priv->response_len += resdatalen;
+
+ if (cmd->data.setadapterparms.hdr.seq_no <
+ cmd->data.setadapterparms.hdr.used_total)
+ return 1;
+ return 0;
+}
+
+int qeth_query_oat_command(struct qeth_card *card, char __user *udata)
+{
+ int rc = 0;
+ struct qeth_cmd_buffer *iob;
+ struct qeth_ipa_cmd *cmd;
+ struct qeth_query_oat *oat_req;
+ struct qeth_query_oat_data oat_data;
+ struct qeth_qoat_priv priv;
+ void __user *tmp;
+
+ QETH_CARD_TEXT(card, 3, "qoatcmd");
+
+ if (!qeth_adp_supported(card, IPA_SETADP_QUERY_OAT)) {
+ rc = -EOPNOTSUPP;
+ goto out;
+ }
+
+ if (copy_from_user(&oat_data, udata,
+ sizeof(struct qeth_query_oat_data))) {
+ rc = -EFAULT;
+ goto out;
+ }
+
+ priv.buffer_len = oat_data.buffer_len;
+ priv.response_len = 0;
+ priv.buffer = kzalloc(oat_data.buffer_len, GFP_KERNEL);
+ if (!priv.buffer) {
+ rc = -ENOMEM;
+ goto out;
+ }
+
+ iob = qeth_get_adapter_cmd(card, IPA_SETADP_QUERY_OAT,
+ sizeof(struct qeth_ipacmd_setadpparms_hdr) +
+ sizeof(struct qeth_query_oat));
+ cmd = (struct qeth_ipa_cmd *)(iob->data+IPA_PDU_HEADER_SIZE);
+ oat_req = &cmd->data.setadapterparms.data.query_oat;
+ oat_req->subcmd_code = oat_data.command;
+
+ rc = qeth_send_ipa_cmd(card, iob, qeth_setadpparms_query_oat_cb,
+ &priv);
+ if (!rc) {
+ if (is_compat_task())
+ tmp = compat_ptr(oat_data.ptr);
+ else
+ tmp = (void __user *)(unsigned long)oat_data.ptr;
+
+ if (copy_to_user(tmp, priv.buffer,
+ priv.response_len)) {
+ rc = -EFAULT;
+ goto out_free;
+ }
+
+ oat_data.response_len = priv.response_len;
+
+ if (copy_to_user(udata, &oat_data,
+ sizeof(struct qeth_query_oat_data)))
+ rc = -EFAULT;
+ } else
+ if (rc == IPA_RC_FFFF)
+ rc = -EFAULT;
+
+out_free:
+ kfree(priv.buffer);
+out:
+ return rc;
+}
+EXPORT_SYMBOL_GPL(qeth_query_oat_command);
+
+static int qeth_query_card_info_cb(struct qeth_card *card,
+ struct qeth_reply *reply, unsigned long data)
+{
+ struct qeth_ipa_cmd *cmd;
+ struct qeth_query_card_info *card_info;
+ struct carrier_info *carrier_info;
+
+ QETH_CARD_TEXT(card, 2, "qcrdincb");
+ carrier_info = (struct carrier_info *)reply->param;
+ cmd = (struct qeth_ipa_cmd *)data;
+ card_info = &cmd->data.setadapterparms.data.card_info;
+ if (cmd->data.setadapterparms.hdr.return_code == 0) {
+ carrier_info->card_type = card_info->card_type;
+ carrier_info->port_mode = card_info->port_mode;
+ carrier_info->port_speed = card_info->port_speed;
+ }
+
+ qeth_default_setadapterparms_cb(card, reply, (unsigned long) cmd);
+ return 0;
+}
+
+int qeth_query_card_info(struct qeth_card *card,
+ struct carrier_info *carrier_info)
+{
+ struct qeth_cmd_buffer *iob;
+
+ QETH_CARD_TEXT(card, 2, "qcrdinfo");
+ if (!qeth_adp_supported(card, IPA_SETADP_QUERY_CARD_INFO))
+ return -EOPNOTSUPP;
+ iob = qeth_get_adapter_cmd(card, IPA_SETADP_QUERY_CARD_INFO,
+ sizeof(struct qeth_ipacmd_setadpparms_hdr));
+ return qeth_send_ipa_cmd(card, iob, qeth_query_card_info_cb,
+ (void *)carrier_info);
+}
+EXPORT_SYMBOL_GPL(qeth_query_card_info);
+
+static inline int qeth_get_qdio_q_format(struct qeth_card *card)
+{
+ switch (card->info.type) {
+ case QETH_CARD_TYPE_IQD:
+ return 2;
+ default:
+ return 0;
+ }
+}
+
+static void qeth_determine_capabilities(struct qeth_card *card)
+{
+ int rc;
+ int length;
+ char *prcd;
+ struct ccw_device *ddev;
+ int ddev_offline = 0;
+
+ QETH_DBF_TEXT(SETUP, 2, "detcapab");
+ ddev = CARD_DDEV(card);
+ if (!ddev->online) {
+ ddev_offline = 1;
+ rc = ccw_device_set_online(ddev);
+ if (rc) {
+ QETH_DBF_TEXT_(SETUP, 2, "3err%d", rc);
+ goto out;
+ }
+ }
+
+ rc = qeth_read_conf_data(card, (void **) &prcd, &length);
+ if (rc) {
+ QETH_DBF_MESSAGE(2, "%s qeth_read_conf_data returned %i\n",
+ dev_name(&card->gdev->dev), rc);
+ QETH_DBF_TEXT_(SETUP, 2, "5err%d", rc);
+ goto out_offline;
+ }
+ qeth_configure_unitaddr(card, prcd);
+ if (ddev_offline)
+ qeth_configure_blkt_default(card, prcd);
+ kfree(prcd);
+
+ rc = qdio_get_ssqd_desc(ddev, &card->ssqd);
+ if (rc)
+ QETH_DBF_TEXT_(SETUP, 2, "6err%d", rc);
+
+ QETH_DBF_TEXT_(SETUP, 2, "qfmt%d", card->ssqd.qfmt);
+ QETH_DBF_TEXT_(SETUP, 2, "%d", card->ssqd.qdioac1);
+ QETH_DBF_TEXT_(SETUP, 2, "%d", card->ssqd.qdioac3);
+ QETH_DBF_TEXT_(SETUP, 2, "icnt%d", card->ssqd.icnt);
+ if (!((card->ssqd.qfmt != QDIO_IQDIO_QFMT) ||
+ ((card->ssqd.qdioac1 & CHSC_AC1_INITIATE_INPUTQ) == 0) ||
+ ((card->ssqd.qdioac3 & CHSC_AC3_FORMAT2_CQ_AVAILABLE) == 0))) {
+ dev_info(&card->gdev->dev,
+ "Completion Queueing supported\n");
+ } else {
+ card->options.cq = QETH_CQ_NOTAVAILABLE;
+ }
+
+
+out_offline:
+ if (ddev_offline == 1)
+ ccw_device_set_offline(ddev);
+out:
+ return;
+}
+
+static inline void qeth_qdio_establish_cq(struct qeth_card *card,
+ struct qdio_buffer **in_sbal_ptrs,
+ void (**queue_start_poll) (struct ccw_device *, int, unsigned long)) {
+ int i;
+
+ if (card->options.cq == QETH_CQ_ENABLED) {
+ int offset = QDIO_MAX_BUFFERS_PER_Q *
+ (card->qdio.no_in_queues - 1);
+ i = QDIO_MAX_BUFFERS_PER_Q * (card->qdio.no_in_queues - 1);
+ for (i = 0; i < QDIO_MAX_BUFFERS_PER_Q; ++i) {
+ in_sbal_ptrs[offset + i] = (struct qdio_buffer *)
+ virt_to_phys(card->qdio.c_q->bufs[i].buffer);
+ }
+
+ queue_start_poll[card->qdio.no_in_queues - 1] = NULL;
+ }
+}
+
+static int qeth_qdio_establish(struct qeth_card *card)
+{
+ struct qdio_initialize init_data;
+ char *qib_param_field;
+ struct qdio_buffer **in_sbal_ptrs;
+ void (**queue_start_poll) (struct ccw_device *, int, unsigned long);
+ struct qdio_buffer **out_sbal_ptrs;
+ int i, j, k;
+ int rc = 0;
+
+ QETH_DBF_TEXT(SETUP, 2, "qdioest");
+
+ qib_param_field = kzalloc(QDIO_MAX_BUFFERS_PER_Q * sizeof(char),
+ GFP_KERNEL);
+ if (!qib_param_field) {
+ rc = -ENOMEM;
+ goto out_free_nothing;
+ }
+
+ qeth_create_qib_param_field(card, qib_param_field);
+ qeth_create_qib_param_field_blkt(card, qib_param_field);
+
+ in_sbal_ptrs = kzalloc(card->qdio.no_in_queues *
+ QDIO_MAX_BUFFERS_PER_Q * sizeof(void *),
+ GFP_KERNEL);
+ if (!in_sbal_ptrs) {
+ rc = -ENOMEM;
+ goto out_free_qib_param;
+ }
+ 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);
+ }
+
+ queue_start_poll = kzalloc(sizeof(void *) * card->qdio.no_in_queues,
+ GFP_KERNEL);
+ if (!queue_start_poll) {
+ rc = -ENOMEM;
+ goto out_free_in_sbals;
+ }
+ for (i = 0; i < card->qdio.no_in_queues; ++i)
+ queue_start_poll[i] = card->discipline->start_poll;
+
+ qeth_qdio_establish_cq(card, in_sbal_ptrs, queue_start_poll);
+
+ out_sbal_ptrs =
+ kzalloc(card->qdio.no_out_queues * QDIO_MAX_BUFFERS_PER_Q *
+ sizeof(void *), GFP_KERNEL);
+ if (!out_sbal_ptrs) {
+ rc = -ENOMEM;
+ goto out_free_queue_start_poll;
+ }
+ 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.no_input_qs = card->qdio.no_in_queues;
+ init_data.no_output_qs = card->qdio.no_out_queues;
+ init_data.input_handler = card->discipline->input_handler;
+ init_data.output_handler = card->discipline->output_handler;
+ init_data.queue_start_poll_array = queue_start_poll;
+ init_data.int_parm = (unsigned long) card;
+ init_data.input_sbal_addr_array = (void **) in_sbal_ptrs;
+ init_data.output_sbal_addr_array = (void **) out_sbal_ptrs;
+ init_data.output_sbal_state_array = card->qdio.out_bufstates;
+ init_data.scan_threshold =
+ (card->info.type == QETH_CARD_TYPE_IQD) ? 1 : 32;
+
+ if (atomic_cmpxchg(&card->qdio.state, QETH_QDIO_ALLOCATED,
+ QETH_QDIO_ESTABLISHED) == QETH_QDIO_ALLOCATED) {
+ rc = qdio_allocate(&init_data);
+ if (rc) {
+ atomic_set(&card->qdio.state, QETH_QDIO_ALLOCATED);
+ goto out;
+ }
+ rc = qdio_establish(&init_data);
+ if (rc) {
+ atomic_set(&card->qdio.state, QETH_QDIO_ALLOCATED);
+ qdio_free(CARD_DDEV(card));
+ }
+ }
+
+ switch (card->options.cq) {
+ case QETH_CQ_ENABLED:
+ dev_info(&card->gdev->dev, "Completion Queue support enabled");
+ break;
+ case QETH_CQ_DISABLED:
+ dev_info(&card->gdev->dev, "Completion Queue support disabled");
+ break;
+ default:
+ break;
+ }
+out:
+ kfree(out_sbal_ptrs);
+out_free_queue_start_poll:
+ kfree(queue_start_poll);
+out_free_in_sbals:
+ kfree(in_sbal_ptrs);
+out_free_qib_param:
+ kfree(qib_param_field);
+out_free_nothing:
+ return rc;
+}
+
+static void qeth_core_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);
+ kfree(card->ip_tbd_list);
+ qeth_free_qdio_buffers(card);
+ unregister_service_level(&card->qeth_service_level);
+ kfree(card);
+}
+
+void qeth_trace_features(struct qeth_card *card)
+{
+ QETH_CARD_TEXT(card, 2, "features");
+ QETH_CARD_TEXT_(card, 2, "%x", card->options.ipa4.supported_funcs);
+ QETH_CARD_TEXT_(card, 2, "%x", card->options.ipa4.enabled_funcs);
+ QETH_CARD_TEXT_(card, 2, "%x", card->options.ipa6.supported_funcs);
+ QETH_CARD_TEXT_(card, 2, "%x", card->options.ipa6.enabled_funcs);
+ QETH_CARD_TEXT_(card, 2, "%x", card->options.adp.supported_funcs);
+ QETH_CARD_TEXT_(card, 2, "%x", card->options.adp.enabled_funcs);
+ QETH_CARD_TEXT_(card, 2, "%x", card->info.diagass_support);
+}
+EXPORT_SYMBOL_GPL(qeth_trace_features);
+
+static struct ccw_device_id qeth_ids[] = {
+ {CCW_DEVICE_DEVTYPE(0x1731, 0x01, 0x1732, 0x01),
+ .driver_info = QETH_CARD_TYPE_OSD},
+ {CCW_DEVICE_DEVTYPE(0x1731, 0x05, 0x1732, 0x05),
+ .driver_info = QETH_CARD_TYPE_IQD},
+ {CCW_DEVICE_DEVTYPE(0x1731, 0x06, 0x1732, 0x06),
+ .driver_info = QETH_CARD_TYPE_OSN},
+ {CCW_DEVICE_DEVTYPE(0x1731, 0x02, 0x1732, 0x03),
+ .driver_info = QETH_CARD_TYPE_OSM},
+ {CCW_DEVICE_DEVTYPE(0x1731, 0x02, 0x1732, 0x02),
+ .driver_info = QETH_CARD_TYPE_OSX},
+ {},
+};
+MODULE_DEVICE_TABLE(ccw, qeth_ids);
+
+static struct ccw_driver qeth_ccw_driver = {
+ .driver = {
+ .owner = THIS_MODULE,
+ .name = "qeth",
+ },
+ .ids = qeth_ids,
+ .probe = ccwgroup_probe_ccwdev,
+ .remove = ccwgroup_remove_ccwdev,
+};
+
+int qeth_core_hardsetup_card(struct qeth_card *card)
+{
+ int retries = 3;
+ int rc;
+
+ QETH_DBF_TEXT(SETUP, 2, "hrdsetup");
+ atomic_set(&card->force_alloc_skb, 0);
+ qeth_update_from_chp_desc(card);
+retry:
+ if (retries < 3)
+ QETH_DBF_MESSAGE(2, "%s Retrying to do IDX activates.\n",
+ dev_name(&card->gdev->dev));
+ rc = qeth_qdio_clear_card(card, card->info.type != QETH_CARD_TYPE_IQD);
+ ccw_device_set_offline(CARD_DDEV(card));
+ ccw_device_set_offline(CARD_WDEV(card));
+ ccw_device_set_offline(CARD_RDEV(card));
+ qdio_free(CARD_DDEV(card));
+ rc = ccw_device_set_online(CARD_RDEV(card));
+ if (rc)
+ goto retriable;
+ rc = ccw_device_set_online(CARD_WDEV(card));
+ if (rc)
+ goto retriable;
+ rc = ccw_device_set_online(CARD_DDEV(card));
+ if (rc)
+ goto retriable;
+retriable:
+ 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;
+ }
+ qeth_determine_capabilities(card);
+ qeth_init_tokens(card);
+ qeth_init_func_level(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;
+ }
+ card->read_or_write_problem = 0;
+ rc = qeth_mpc_initialize(card);
+ if (rc) {
+ QETH_DBF_TEXT_(SETUP, 2, "5err%d", rc);
+ goto out;
+ }
+
+ card->options.ipa4.supported_funcs = 0;
+ card->options.adp.supported_funcs = 0;
+ card->options.sbp.supported_funcs = 0;
+ card->info.diagass_support = 0;
+ qeth_query_ipassists(card, QETH_PROT_IPV4);
+ if (qeth_is_supported(card, IPA_SETADAPTERPARMS))
+ qeth_query_setadapterparms(card);
+ if (qeth_adp_supported(card, IPA_SETADP_SET_DIAG_ASSIST))
+ qeth_query_setdiagass(card);
+ return 0;
+out:
+ dev_warn(&card->gdev->dev, "The qeth device driver failed to recover "
+ "an error on the device\n");
+ QETH_DBF_MESSAGE(2, "%s Initialization in hardsetup failed! rc=%d\n",
+ dev_name(&card->gdev->dev), rc);
+ return rc;
+}
+EXPORT_SYMBOL_GPL(qeth_core_hardsetup_card);
+
+static inline int qeth_create_skb_frag(struct qeth_qdio_buffer *qethbuffer,
+ struct qdio_buffer_element *element,
+ struct sk_buff **pskb, int offset, int *pfrag, int data_len)
+{
+ struct page *page = virt_to_page(element->addr);
+ if (*pskb == NULL) {
+ if (qethbuffer->rx_skb) {
+ /* only if qeth_card.options.cq == QETH_CQ_ENABLED */
+ *pskb = qethbuffer->rx_skb;
+ qethbuffer->rx_skb = NULL;
+ } else {
+ *pskb = dev_alloc_skb(QETH_RX_PULL_LEN + ETH_HLEN);
+ if (!(*pskb))
+ return -ENOMEM;
+ }
+
+ skb_reserve(*pskb, ETH_HLEN);
+ if (data_len <= QETH_RX_PULL_LEN) {
+ memcpy(skb_put(*pskb, data_len), element->addr + offset,
+ data_len);
+ } else {
+ get_page(page);
+ memcpy(skb_put(*pskb, QETH_RX_PULL_LEN),
+ element->addr + offset, QETH_RX_PULL_LEN);
+ skb_fill_page_desc(*pskb, *pfrag, page,
+ offset + QETH_RX_PULL_LEN,
+ data_len - QETH_RX_PULL_LEN);
+ (*pskb)->data_len += data_len - QETH_RX_PULL_LEN;
+ (*pskb)->len += data_len - QETH_RX_PULL_LEN;
+ (*pskb)->truesize += data_len - QETH_RX_PULL_LEN;
+ (*pfrag)++;
+ }
+ } else {
+ get_page(page);
+ skb_fill_page_desc(*pskb, *pfrag, page, offset, data_len);
+ (*pskb)->data_len += data_len;
+ (*pskb)->len += data_len;
+ (*pskb)->truesize += data_len;
+ (*pfrag)++;
+ }
+
+
+ return 0;
+}
+
+struct sk_buff *qeth_core_get_next_skb(struct qeth_card *card,
+ struct qeth_qdio_buffer *qethbuffer,
+ struct qdio_buffer_element **__element, int *__offset,
+ struct qeth_hdr **hdr)
+{
+ struct qdio_buffer_element *element = *__element;
+ struct qdio_buffer *buffer = qethbuffer->buffer;
+ int offset = *__offset;
+ struct sk_buff *skb = NULL;
+ int skb_len = 0;
+ void *data_ptr;
+ int data_len;
+ int headroom = 0;
+ int use_rx_sg = 0;
+ int frag = 0;
+
+ /* 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);
+ switch ((*hdr)->hdr.l2.id) {
+ case QETH_HEADER_TYPE_LAYER2:
+ skb_len = (*hdr)->hdr.l2.pkt_length;
+ break;
+ case QETH_HEADER_TYPE_LAYER3:
+ skb_len = (*hdr)->hdr.l3.length;
+ headroom = ETH_HLEN;
+ break;
+ case QETH_HEADER_TYPE_OSN:
+ skb_len = (*hdr)->hdr.osn.pdu_length;
+ headroom = sizeof(struct qeth_hdr);
+ break;
+ default:
+ break;
+ }
+
+ if (!skb_len)
+ return NULL;
+
+ if (((skb_len >= card->options.rx_sg_cb) &&
+ (!(card->info.type == QETH_CARD_TYPE_OSN)) &&
+ (!atomic_read(&card->force_alloc_skb))) ||
+ (card->options.cq == QETH_CQ_ENABLED)) {
+ use_rx_sg = 1;
+ } else {
+ skb = dev_alloc_skb(skb_len + headroom);
+ if (!skb)
+ goto no_mem;
+ if (headroom)
+ skb_reserve(skb, headroom);
+ }
+
+ data_ptr = element->addr + offset;
+ while (skb_len) {
+ data_len = min(skb_len, (int)(element->length - offset));
+ if (data_len) {
+ if (use_rx_sg) {
+ if (qeth_create_skb_frag(qethbuffer, element,
+ &skb, offset, &frag, data_len))
+ goto no_mem;
+ } else {
+ memcpy(skb_put(skb, data_len), data_ptr,
+ data_len);
+ }
+ }
+ skb_len -= data_len;
+ if (skb_len) {
+ if (qeth_is_last_sbale(element)) {
+ QETH_CARD_TEXT(card, 4, "unexeob");
+ QETH_CARD_HEX(card, 2, buffer, sizeof(void *));
+ dev_kfree_skb_any(skb);
+ card->stats.rx_errors++;
+ return NULL;
+ }
+ element++;
+ offset = 0;
+ data_ptr = element->addr;
+ } else {
+ offset += data_len;
+ }
+ }
+ *__element = element;
+ *__offset = offset;
+ if (use_rx_sg && card->options.performance_stats) {
+ card->perf_stats.sg_skbs_rx++;
+ card->perf_stats.sg_frags_rx += skb_shinfo(skb)->nr_frags;
+ }
+ return skb;
+no_mem:
+ if (net_ratelimit()) {
+ QETH_CARD_TEXT(card, 2, "noskbmem");
+ }
+ card->stats.rx_dropped++;
+ return NULL;
+}
+EXPORT_SYMBOL_GPL(qeth_core_get_next_skb);
+
+static void qeth_unregister_dbf_views(void)
+{
+ int x;
+ for (x = 0; x < QETH_DBF_INFOS; x++) {
+ debug_unregister(qeth_dbf[x].id);
+ qeth_dbf[x].id = NULL;
+ }
+}
+
+void qeth_dbf_longtext(debug_info_t *id, int level, char *fmt, ...)
+{
+ char dbf_txt_buf[32];
+ va_list args;
+
+ if (!debug_level_enabled(id, level))
+ return;
+ va_start(args, fmt);
+ vsnprintf(dbf_txt_buf, sizeof(dbf_txt_buf), fmt, args);
+ va_end(args);
+ debug_text_event(id, level, dbf_txt_buf);
+}
+EXPORT_SYMBOL_GPL(qeth_dbf_longtext);
+
+static int qeth_register_dbf_views(void)
+{
+ int ret;
+ int x;
+
+ for (x = 0; x < QETH_DBF_INFOS; x++) {
+ /* register the areas */
+ qeth_dbf[x].id = debug_register(qeth_dbf[x].name,
+ qeth_dbf[x].pages,
+ qeth_dbf[x].areas,
+ qeth_dbf[x].len);
+ if (qeth_dbf[x].id == NULL) {
+ qeth_unregister_dbf_views();
+ return -ENOMEM;
+ }
+
+ /* register a view */
+ ret = debug_register_view(qeth_dbf[x].id, qeth_dbf[x].view);
+ if (ret) {
+ qeth_unregister_dbf_views();
+ return ret;
+ }
+
+ /* set a passing level */
+ debug_set_level(qeth_dbf[x].id, qeth_dbf[x].level);
+ }
+
+ return 0;
+}
+
+int qeth_core_load_discipline(struct qeth_card *card,
+ enum qeth_discipline_id discipline)
+{
+ int rc = 0;
+ mutex_lock(&qeth_mod_mutex);
+ switch (discipline) {
+ case QETH_DISCIPLINE_LAYER3:
+ card->discipline = try_then_request_module(
+ symbol_get(qeth_l3_discipline), "qeth_l3");
+ break;
+ case QETH_DISCIPLINE_LAYER2:
+ card->discipline = try_then_request_module(
+ symbol_get(qeth_l2_discipline), "qeth_l2");
+ break;
+ }
+ if (!card->discipline) {
+ dev_err(&card->gdev->dev, "There is no kernel module to "
+ "support discipline %d\n", discipline);
+ rc = -EINVAL;
+ }
+ mutex_unlock(&qeth_mod_mutex);
+ return rc;
+}
+
+void qeth_core_free_discipline(struct qeth_card *card)
+{
+ if (card->options.layer2)
+ symbol_put(qeth_l2_discipline);
+ else
+ symbol_put(qeth_l3_discipline);
+ card->discipline = NULL;
+}
+
+static const struct device_type qeth_generic_devtype = {
+ .name = "qeth_generic",
+ .groups = qeth_generic_attr_groups,
+};
+static const struct device_type qeth_osn_devtype = {
+ .name = "qeth_osn",
+ .groups = qeth_osn_attr_groups,
+};
+
+#define DBF_NAME_LEN 20
+
+struct qeth_dbf_entry {
+ char dbf_name[DBF_NAME_LEN];
+ debug_info_t *dbf_info;
+ struct list_head dbf_list;
+};
+
+static LIST_HEAD(qeth_dbf_list);
+static DEFINE_MUTEX(qeth_dbf_list_mutex);
+
+static debug_info_t *qeth_get_dbf_entry(char *name)
+{
+ struct qeth_dbf_entry *entry;
+ debug_info_t *rc = NULL;
+
+ mutex_lock(&qeth_dbf_list_mutex);
+ list_for_each_entry(entry, &qeth_dbf_list, dbf_list) {
+ if (strcmp(entry->dbf_name, name) == 0) {
+ rc = entry->dbf_info;
+ break;
+ }
+ }
+ mutex_unlock(&qeth_dbf_list_mutex);
+ return rc;
+}
+
+static int qeth_add_dbf_entry(struct qeth_card *card, char *name)
+{
+ struct qeth_dbf_entry *new_entry;
+
+ card->debug = debug_register(name, 2, 1, 8);
+ if (!card->debug) {
+ QETH_DBF_TEXT_(SETUP, 2, "%s", "qcdbf");
+ goto err;
+ }
+ if (debug_register_view(card->debug, &debug_hex_ascii_view))
+ goto err_dbg;
+ new_entry = kzalloc(sizeof(struct qeth_dbf_entry), GFP_KERNEL);
+ if (!new_entry)
+ goto err_dbg;
+ strncpy(new_entry->dbf_name, name, DBF_NAME_LEN);
+ new_entry->dbf_info = card->debug;
+ mutex_lock(&qeth_dbf_list_mutex);
+ list_add(&new_entry->dbf_list, &qeth_dbf_list);
+ mutex_unlock(&qeth_dbf_list_mutex);
+
+ return 0;
+
+err_dbg:
+ debug_unregister(card->debug);
+err:
+ return -ENOMEM;
+}
+
+static void qeth_clear_dbf_list(void)
+{
+ struct qeth_dbf_entry *entry, *tmp;
+
+ mutex_lock(&qeth_dbf_list_mutex);
+ list_for_each_entry_safe(entry, tmp, &qeth_dbf_list, dbf_list) {
+ list_del(&entry->dbf_list);
+ debug_unregister(entry->dbf_info);
+ kfree(entry);
+ }
+ mutex_unlock(&qeth_dbf_list_mutex);
+}
+
+static int qeth_core_probe_device(struct ccwgroup_device *gdev)
+{
+ struct qeth_card *card;
+ struct device *dev;
+ int rc;
+ unsigned long flags;
+ char dbf_name[DBF_NAME_LEN];
+
+ QETH_DBF_TEXT(SETUP, 2, "probedev");
+
+ dev = &gdev->dev;
+ if (!get_device(dev))
+ return -ENODEV;
+
+ QETH_DBF_TEXT_(SETUP, 2, "%s", dev_name(&gdev->dev));
+
+ card = qeth_alloc_card();
+ if (!card) {
+ QETH_DBF_TEXT_(SETUP, 2, "1err%d", -ENOMEM);
+ rc = -ENOMEM;
+ goto err_dev;
+ }
+
+ snprintf(dbf_name, sizeof(dbf_name), "qeth_card_%s",
+ dev_name(&gdev->dev));
+ card->debug = qeth_get_dbf_entry(dbf_name);
+ if (!card->debug) {
+ rc = qeth_add_dbf_entry(card, dbf_name);
+ if (rc)
+ goto err_card;
+ }
+
+ card->read.ccwdev = gdev->cdev[0];
+ card->write.ccwdev = gdev->cdev[1];
+ card->data.ccwdev = gdev->cdev[2];
+ dev_set_drvdata(&gdev->dev, card);
+ card->gdev = gdev;
+ gdev->cdev[0]->handler = qeth_irq;
+ gdev->cdev[1]->handler = qeth_irq;
+ gdev->cdev[2]->handler = qeth_irq;
+
+ rc = qeth_determine_card_type(card);
+ if (rc) {
+ QETH_DBF_TEXT_(SETUP, 2, "3err%d", rc);
+ goto err_card;
+ }
+ rc = qeth_setup_card(card);
+ if (rc) {
+ QETH_DBF_TEXT_(SETUP, 2, "2err%d", rc);
+ goto err_card;
+ }
+
+ if (card->info.type == QETH_CARD_TYPE_OSN)
+ gdev->dev.type = &qeth_osn_devtype;
+ else
+ gdev->dev.type = &qeth_generic_devtype;
+
+ switch (card->info.type) {
+ case QETH_CARD_TYPE_OSN:
+ case QETH_CARD_TYPE_OSM:
+ rc = qeth_core_load_discipline(card, QETH_DISCIPLINE_LAYER2);
+ if (rc)
+ goto err_card;
+ rc = card->discipline->setup(card->gdev);
+ if (rc)
+ goto err_disc;
+ case QETH_CARD_TYPE_OSD:
+ case QETH_CARD_TYPE_OSX:
+ default:
+ break;
+ }
+
+ write_lock_irqsave(&qeth_core_card_list.rwlock, flags);
+ list_add_tail(&card->list, &qeth_core_card_list.list);
+ write_unlock_irqrestore(&qeth_core_card_list.rwlock, flags);
+
+ qeth_determine_capabilities(card);
+ return 0;
+
+err_disc:
+ qeth_core_free_discipline(card);
+err_card:
+ qeth_core_free_card(card);
+err_dev:
+ put_device(dev);
+ return rc;
+}
+
+static void qeth_core_remove_device(struct ccwgroup_device *gdev)
+{
+ unsigned long flags;
+ struct qeth_card *card = dev_get_drvdata(&gdev->dev);
+
+ QETH_DBF_TEXT(SETUP, 2, "removedv");
+
+ if (card->discipline) {
+ card->discipline->remove(gdev);
+ qeth_core_free_discipline(card);
+ }
+
+ write_lock_irqsave(&qeth_core_card_list.rwlock, flags);
+ list_del(&card->list);
+ write_unlock_irqrestore(&qeth_core_card_list.rwlock, flags);
+ qeth_core_free_card(card);
+ dev_set_drvdata(&gdev->dev, NULL);
+ put_device(&gdev->dev);
+ return;
+}
+
+static int qeth_core_set_online(struct ccwgroup_device *gdev)
+{
+ struct qeth_card *card = dev_get_drvdata(&gdev->dev);
+ int rc = 0;
+ int def_discipline;
+
+ if (!card->discipline) {
+ if (card->info.type == QETH_CARD_TYPE_IQD)
+ def_discipline = QETH_DISCIPLINE_LAYER3;
+ else
+ def_discipline = QETH_DISCIPLINE_LAYER2;
+ rc = qeth_core_load_discipline(card, def_discipline);
+ if (rc)
+ goto err;
+ rc = card->discipline->setup(card->gdev);
+ if (rc)
+ goto err;
+ }
+ rc = card->discipline->set_online(gdev);
+err:
+ return rc;
+}
+
+static int qeth_core_set_offline(struct ccwgroup_device *gdev)
+{
+ struct qeth_card *card = dev_get_drvdata(&gdev->dev);
+ return card->discipline->set_offline(gdev);
+}
+
+static void qeth_core_shutdown(struct ccwgroup_device *gdev)
+{
+ struct qeth_card *card = dev_get_drvdata(&gdev->dev);
+ if (card->discipline && card->discipline->shutdown)
+ card->discipline->shutdown(gdev);
+}
+
+static int qeth_core_prepare(struct ccwgroup_device *gdev)
+{
+ struct qeth_card *card = dev_get_drvdata(&gdev->dev);
+ if (card->discipline && card->discipline->prepare)
+ return card->discipline->prepare(gdev);
+ return 0;
+}
+
+static void qeth_core_complete(struct ccwgroup_device *gdev)
+{
+ struct qeth_card *card = dev_get_drvdata(&gdev->dev);
+ if (card->discipline && card->discipline->complete)
+ card->discipline->complete(gdev);
+}
+
+static int qeth_core_freeze(struct ccwgroup_device *gdev)
+{
+ struct qeth_card *card = dev_get_drvdata(&gdev->dev);
+ if (card->discipline && card->discipline->freeze)
+ return card->discipline->freeze(gdev);
+ return 0;
+}
+
+static int qeth_core_thaw(struct ccwgroup_device *gdev)
+{
+ struct qeth_card *card = dev_get_drvdata(&gdev->dev);
+ if (card->discipline && card->discipline->thaw)
+ return card->discipline->thaw(gdev);
+ return 0;
+}
+
+static int qeth_core_restore(struct ccwgroup_device *gdev)
+{
+ struct qeth_card *card = dev_get_drvdata(&gdev->dev);
+ if (card->discipline && card->discipline->restore)
+ return card->discipline->restore(gdev);
+ return 0;
+}
+
+static struct ccwgroup_driver qeth_core_ccwgroup_driver = {
+ .driver = {
+ .owner = THIS_MODULE,
+ .name = "qeth",
+ },
+ .setup = qeth_core_probe_device,
+ .remove = qeth_core_remove_device,
+ .set_online = qeth_core_set_online,
+ .set_offline = qeth_core_set_offline,
+ .shutdown = qeth_core_shutdown,
+ .prepare = qeth_core_prepare,
+ .complete = qeth_core_complete,
+ .freeze = qeth_core_freeze,
+ .thaw = qeth_core_thaw,
+ .restore = qeth_core_restore,
+};
+
+static ssize_t qeth_core_driver_group_store(struct device_driver *ddrv,
+ const char *buf, size_t count)
+{
+ int err;
+
+ err = ccwgroup_create_dev(qeth_core_root_dev,
+ &qeth_core_ccwgroup_driver, 3, buf);
+
+ return err ? err : count;
+}
+static DRIVER_ATTR(group, 0200, NULL, qeth_core_driver_group_store);
+
+static struct attribute *qeth_drv_attrs[] = {
+ &driver_attr_group.attr,
+ NULL,
+};
+static struct attribute_group qeth_drv_attr_group = {
+ .attrs = qeth_drv_attrs,
+};
+static const struct attribute_group *qeth_drv_attr_groups[] = {
+ &qeth_drv_attr_group,
+ NULL,
+};
+
+static struct {
+ const char str[ETH_GSTRING_LEN];
+} qeth_ethtool_stats_keys[] = {
+/* 0 */{"rx skbs"},
+ {"rx buffers"},
+ {"tx skbs"},
+ {"tx buffers"},
+ {"tx skbs no packing"},
+ {"tx buffers no packing"},
+ {"tx skbs packing"},
+ {"tx buffers packing"},
+ {"tx sg skbs"},
+ {"tx sg frags"},
+/* 10 */{"rx sg skbs"},
+ {"rx sg frags"},
+ {"rx sg page allocs"},
+ {"tx large kbytes"},
+ {"tx large count"},
+ {"tx pk state ch n->p"},
+ {"tx pk state ch p->n"},
+ {"tx pk watermark low"},
+ {"tx pk watermark high"},
+ {"queue 0 buffer usage"},
+/* 20 */{"queue 1 buffer usage"},
+ {"queue 2 buffer usage"},
+ {"queue 3 buffer usage"},
+ {"rx poll time"},
+ {"rx poll count"},
+ {"rx do_QDIO time"},
+ {"rx do_QDIO count"},
+ {"tx handler time"},
+ {"tx handler count"},
+ {"tx time"},
+/* 30 */{"tx count"},
+ {"tx do_QDIO time"},
+ {"tx do_QDIO count"},
+ {"tx csum"},
+ {"tx lin"},
+ {"cq handler count"},
+ {"cq handler time"}
+};
+
+int qeth_core_get_sset_count(struct net_device *dev, int stringset)
+{
+ switch (stringset) {
+ case ETH_SS_STATS:
+ return (sizeof(qeth_ethtool_stats_keys) / ETH_GSTRING_LEN);
+ default:
+ return -EINVAL;
+ }
+}
+EXPORT_SYMBOL_GPL(qeth_core_get_sset_count);
+
+void qeth_core_get_ethtool_stats(struct net_device *dev,
+ struct ethtool_stats *stats, u64 *data)
+{
+ struct qeth_card *card = dev->ml_priv;
+ data[0] = card->stats.rx_packets -
+ card->perf_stats.initial_rx_packets;
+ data[1] = card->perf_stats.bufs_rec;
+ data[2] = card->stats.tx_packets -
+ card->perf_stats.initial_tx_packets;
+ data[3] = card->perf_stats.bufs_sent;
+ data[4] = card->stats.tx_packets - card->perf_stats.initial_tx_packets
+ - card->perf_stats.skbs_sent_pack;
+ data[5] = card->perf_stats.bufs_sent - card->perf_stats.bufs_sent_pack;
+ data[6] = card->perf_stats.skbs_sent_pack;
+ data[7] = card->perf_stats.bufs_sent_pack;
+ data[8] = card->perf_stats.sg_skbs_sent;
+ data[9] = card->perf_stats.sg_frags_sent;
+ data[10] = card->perf_stats.sg_skbs_rx;
+ data[11] = card->perf_stats.sg_frags_rx;
+ data[12] = card->perf_stats.sg_alloc_page_rx;
+ data[13] = (card->perf_stats.large_send_bytes >> 10);
+ data[14] = card->perf_stats.large_send_cnt;
+ data[15] = card->perf_stats.sc_dp_p;
+ data[16] = card->perf_stats.sc_p_dp;
+ data[17] = QETH_LOW_WATERMARK_PACK;
+ data[18] = QETH_HIGH_WATERMARK_PACK;
+ data[19] = atomic_read(&card->qdio.out_qs[0]->used_buffers);
+ data[20] = (card->qdio.no_out_queues > 1) ?
+ atomic_read(&card->qdio.out_qs[1]->used_buffers) : 0;
+ data[21] = (card->qdio.no_out_queues > 2) ?
+ atomic_read(&card->qdio.out_qs[2]->used_buffers) : 0;
+ data[22] = (card->qdio.no_out_queues > 3) ?
+ atomic_read(&card->qdio.out_qs[3]->used_buffers) : 0;
+ data[23] = card->perf_stats.inbound_time;
+ data[24] = card->perf_stats.inbound_cnt;
+ data[25] = card->perf_stats.inbound_do_qdio_time;
+ data[26] = card->perf_stats.inbound_do_qdio_cnt;
+ data[27] = card->perf_stats.outbound_handler_time;
+ data[28] = card->perf_stats.outbound_handler_cnt;
+ data[29] = card->perf_stats.outbound_time;
+ data[30] = card->perf_stats.outbound_cnt;
+ data[31] = card->perf_stats.outbound_do_qdio_time;
+ data[32] = card->perf_stats.outbound_do_qdio_cnt;
+ data[33] = card->perf_stats.tx_csum;
+ data[34] = card->perf_stats.tx_lin;
+ data[35] = card->perf_stats.cq_cnt;
+ data[36] = card->perf_stats.cq_time;
+}
+EXPORT_SYMBOL_GPL(qeth_core_get_ethtool_stats);
+
+void qeth_core_get_strings(struct net_device *dev, u32 stringset, u8 *data)
+{
+ switch (stringset) {
+ case ETH_SS_STATS:
+ memcpy(data, &qeth_ethtool_stats_keys,
+ sizeof(qeth_ethtool_stats_keys));
+ break;
+ default:
+ WARN_ON(1);
+ break;
+ }
+}
+EXPORT_SYMBOL_GPL(qeth_core_get_strings);
+
+void qeth_core_get_drvinfo(struct net_device *dev,
+ struct ethtool_drvinfo *info)
+{
+ struct qeth_card *card = dev->ml_priv;
+
+ strlcpy(info->driver, card->options.layer2 ? "qeth_l2" : "qeth_l3",
+ sizeof(info->driver));
+ strlcpy(info->version, "1.0", sizeof(info->version));
+ strlcpy(info->fw_version, card->info.mcl_level,
+ sizeof(info->fw_version));
+ snprintf(info->bus_info, sizeof(info->bus_info), "%s/%s/%s",
+ CARD_RDEV_ID(card), CARD_WDEV_ID(card), CARD_DDEV_ID(card));
+}
+EXPORT_SYMBOL_GPL(qeth_core_get_drvinfo);
+
+/* Helper function to fill 'advertizing' and 'supported' which are the same. */
+/* Autoneg and full-duplex are supported and advertized uncondionally. */
+/* Always advertize and support all speeds up to specified, and only one */
+/* specified port type. */
+static void qeth_set_ecmd_adv_sup(struct ethtool_cmd *ecmd,
+ int maxspeed, int porttype)
+{
+ int port_sup, port_adv, spd_sup, spd_adv;
+
+ switch (porttype) {
+ case PORT_TP:
+ port_sup = SUPPORTED_TP;
+ port_adv = ADVERTISED_TP;
+ break;
+ case PORT_FIBRE:
+ port_sup = SUPPORTED_FIBRE;
+ port_adv = ADVERTISED_FIBRE;
+ break;
+ default:
+ port_sup = SUPPORTED_TP;
+ port_adv = ADVERTISED_TP;
+ WARN_ON_ONCE(1);
+ }
+
+ /* "Fallthrough" case'es ordered from high to low result in setting */
+ /* flags cumulatively, starting from the specified speed and down to */
+ /* the lowest possible. */
+ spd_sup = 0;
+ spd_adv = 0;
+ switch (maxspeed) {
+ case SPEED_10000:
+ spd_sup |= SUPPORTED_10000baseT_Full;
+ spd_adv |= ADVERTISED_10000baseT_Full;
+ case SPEED_1000:
+ spd_sup |= SUPPORTED_1000baseT_Half | SUPPORTED_1000baseT_Full;
+ spd_adv |= ADVERTISED_1000baseT_Half |
+ ADVERTISED_1000baseT_Full;
+ case SPEED_100:
+ spd_sup |= SUPPORTED_100baseT_Half | SUPPORTED_100baseT_Full;
+ spd_adv |= ADVERTISED_100baseT_Half | ADVERTISED_100baseT_Full;
+ case SPEED_10:
+ spd_sup |= SUPPORTED_10baseT_Half | SUPPORTED_10baseT_Full;
+ spd_adv |= ADVERTISED_10baseT_Half | ADVERTISED_10baseT_Full;
+ break;
+ default:
+ spd_sup = SUPPORTED_10baseT_Half | SUPPORTED_10baseT_Full;
+ spd_adv = ADVERTISED_10baseT_Half | ADVERTISED_10baseT_Full;
+ WARN_ON_ONCE(1);
+ }
+ ecmd->advertising = ADVERTISED_Autoneg | port_adv | spd_adv;
+ ecmd->supported = SUPPORTED_Autoneg | port_sup | spd_sup;
+}
+
+int qeth_core_ethtool_get_settings(struct net_device *netdev,
+ struct ethtool_cmd *ecmd)
+{
+ struct qeth_card *card = netdev->ml_priv;
+ enum qeth_link_types link_type;
+ struct carrier_info carrier_info;
+ u32 speed;
+
+ if ((card->info.type == QETH_CARD_TYPE_IQD) || (card->info.guestlan))
+ link_type = QETH_LINK_TYPE_10GBIT_ETH;
+ else
+ link_type = card->info.link_type;
+
+ ecmd->transceiver = XCVR_INTERNAL;
+ ecmd->duplex = DUPLEX_FULL;
+ ecmd->autoneg = AUTONEG_ENABLE;
+
+ switch (link_type) {
+ case QETH_LINK_TYPE_FAST_ETH:
+ case QETH_LINK_TYPE_LANE_ETH100:
+ qeth_set_ecmd_adv_sup(ecmd, SPEED_100, PORT_TP);
+ speed = SPEED_100;
+ ecmd->port = PORT_TP;
+ break;
+
+ case QETH_LINK_TYPE_GBIT_ETH:
+ case QETH_LINK_TYPE_LANE_ETH1000:
+ qeth_set_ecmd_adv_sup(ecmd, SPEED_1000, PORT_FIBRE);
+ speed = SPEED_1000;
+ ecmd->port = PORT_FIBRE;
+ break;
+
+ case QETH_LINK_TYPE_10GBIT_ETH:
+ qeth_set_ecmd_adv_sup(ecmd, SPEED_10000, PORT_FIBRE);
+ speed = SPEED_10000;
+ ecmd->port = PORT_FIBRE;
+ break;
+
+ default:
+ qeth_set_ecmd_adv_sup(ecmd, SPEED_10, PORT_TP);
+ speed = SPEED_10;
+ ecmd->port = PORT_TP;
+ }
+ ethtool_cmd_speed_set(ecmd, speed);
+
+ /* Check if we can obtain more accurate information. */
+ /* If QUERY_CARD_INFO command is not supported or fails, */
+ /* just return the heuristics that was filled above. */
+ if (qeth_query_card_info(card, &carrier_info) != 0)
+ return 0;
+
+ netdev_dbg(netdev,
+ "card info: card_type=0x%02x, port_mode=0x%04x, port_speed=0x%08x\n",
+ carrier_info.card_type,
+ carrier_info.port_mode,
+ carrier_info.port_speed);
+
+ /* Update attributes for which we've obtained more authoritative */
+ /* information, leave the rest the way they where filled above. */
+ switch (carrier_info.card_type) {
+ case CARD_INFO_TYPE_1G_COPPER_A:
+ case CARD_INFO_TYPE_1G_COPPER_B:
+ qeth_set_ecmd_adv_sup(ecmd, SPEED_1000, PORT_TP);
+ ecmd->port = PORT_TP;
+ break;
+ case CARD_INFO_TYPE_1G_FIBRE_A:
+ case CARD_INFO_TYPE_1G_FIBRE_B:
+ qeth_set_ecmd_adv_sup(ecmd, SPEED_1000, PORT_FIBRE);
+ ecmd->port = PORT_FIBRE;
+ break;
+ case CARD_INFO_TYPE_10G_FIBRE_A:
+ case CARD_INFO_TYPE_10G_FIBRE_B:
+ qeth_set_ecmd_adv_sup(ecmd, SPEED_10000, PORT_FIBRE);
+ ecmd->port = PORT_FIBRE;
+ break;
+ }
+
+ switch (carrier_info.port_mode) {
+ case CARD_INFO_PORTM_FULLDUPLEX:
+ ecmd->duplex = DUPLEX_FULL;
+ break;
+ case CARD_INFO_PORTM_HALFDUPLEX:
+ ecmd->duplex = DUPLEX_HALF;
+ break;
+ }
+
+ switch (carrier_info.port_speed) {
+ case CARD_INFO_PORTS_10M:
+ speed = SPEED_10;
+ break;
+ case CARD_INFO_PORTS_100M:
+ speed = SPEED_100;
+ break;
+ case CARD_INFO_PORTS_1G:
+ speed = SPEED_1000;
+ break;
+ case CARD_INFO_PORTS_10G:
+ speed = SPEED_10000;
+ break;
+ }
+ ethtool_cmd_speed_set(ecmd, speed);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(qeth_core_ethtool_get_settings);
+
+static int __init qeth_core_init(void)
+{
+ int rc;
+
+ pr_info("loading core functions\n");
+ INIT_LIST_HEAD(&qeth_core_card_list.list);
+ INIT_LIST_HEAD(&qeth_dbf_list);
+ rwlock_init(&qeth_core_card_list.rwlock);
+ mutex_init(&qeth_mod_mutex);
+
+ qeth_wq = create_singlethread_workqueue("qeth_wq");
+
+ rc = qeth_register_dbf_views();
+ if (rc)
+ goto out_err;
+ qeth_core_root_dev = root_device_register("qeth");
+ rc = PTR_ERR_OR_ZERO(qeth_core_root_dev);
+ if (rc)
+ goto register_err;
+ qeth_core_header_cache = kmem_cache_create("qeth_hdr",
+ sizeof(struct qeth_hdr) + ETH_HLEN, 64, 0, NULL);
+ if (!qeth_core_header_cache) {
+ rc = -ENOMEM;
+ goto slab_err;
+ }
+ qeth_qdio_outbuf_cache = kmem_cache_create("qeth_buf",
+ sizeof(struct qeth_qdio_out_buffer), 0, 0, NULL);
+ if (!qeth_qdio_outbuf_cache) {
+ rc = -ENOMEM;
+ goto cqslab_err;
+ }
+ rc = ccw_driver_register(&qeth_ccw_driver);
+ if (rc)
+ goto ccw_err;
+ qeth_core_ccwgroup_driver.driver.groups = qeth_drv_attr_groups;
+ rc = ccwgroup_driver_register(&qeth_core_ccwgroup_driver);
+ if (rc)
+ goto ccwgroup_err;
+
+ return 0;
+
+ccwgroup_err:
+ ccw_driver_unregister(&qeth_ccw_driver);
+ccw_err:
+ kmem_cache_destroy(qeth_qdio_outbuf_cache);
+cqslab_err:
+ kmem_cache_destroy(qeth_core_header_cache);
+slab_err:
+ root_device_unregister(qeth_core_root_dev);
+register_err:
+ qeth_unregister_dbf_views();
+out_err:
+ pr_err("Initializing the qeth device driver failed\n");
+ return rc;
+}
+
+static void __exit qeth_core_exit(void)
+{
+ qeth_clear_dbf_list();
+ destroy_workqueue(qeth_wq);
+ ccwgroup_driver_unregister(&qeth_core_ccwgroup_driver);
+ ccw_driver_unregister(&qeth_ccw_driver);
+ kmem_cache_destroy(qeth_qdio_outbuf_cache);
+ kmem_cache_destroy(qeth_core_header_cache);
+ root_device_unregister(qeth_core_root_dev);
+ qeth_unregister_dbf_views();
+ pr_info("core functions removed\n");
+}
+
+module_init(qeth_core_init);
+module_exit(qeth_core_exit);
+MODULE_AUTHOR("Frank Blaschka <frank.blaschka@de.ibm.com>");
+MODULE_DESCRIPTION("qeth core functions");
+MODULE_LICENSE("GPL");
diff --git a/drivers/s390/net/qeth_core_mpc.c b/drivers/s390/net/qeth_core_mpc.c
new file mode 100644
index 00000000000..7b55768a959
--- /dev/null
+++ b/drivers/s390/net/qeth_core_mpc.c
@@ -0,0 +1,270 @@
+/*
+ * Copyright IBM Corp. 2007
+ * Author(s): Frank Pavlic <fpavlic@de.ibm.com>,
+ * Thomas Spatzier <tspat@de.ibm.com>,
+ * Frank Blaschka <frank.blaschka@de.ibm.com>
+ */
+
+#include <linux/module.h>
+#include <asm/cio.h>
+#include "qeth_core_mpc.h"
+
+unsigned char IDX_ACTIVATE_READ[] = {
+ 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x19, 0x01, 0x01, 0x80, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc8, 0xc1,
+ 0xd3, 0xd3, 0xd6, 0xd3, 0xc5, 0x40, 0x00, 0x00,
+ 0x00, 0x00
+};
+
+unsigned char IDX_ACTIVATE_WRITE[] = {
+ 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x15, 0x01, 0x01, 0x80, 0x00, 0x00, 0x00, 0x00,
+ 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xc8, 0xc1,
+ 0xd3, 0xd3, 0xd6, 0xd3, 0xc5, 0x40, 0x00, 0x00,
+ 0x00, 0x00
+};
+
+unsigned char CM_ENABLE[] = {
+ 0x00, 0xe0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01,
+ 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x63,
+ 0x10, 0x00, 0x00, 0x01,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x81, 0x7e, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x24, 0x00, 0x23,
+ 0x00, 0x00, 0x23, 0x05, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x01, 0x00, 0x00, 0x23, 0x00, 0x00, 0x00, 0x40,
+ 0x00, 0x0c, 0x41, 0x02, 0x00, 0x17, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x0b, 0x04, 0x01,
+ 0x7e, 0x04, 0x05, 0x00, 0x01, 0x01, 0x0f,
+ 0x00,
+ 0x0c, 0x04, 0x02, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff
+};
+
+unsigned char CM_SETUP[] = {
+ 0x00, 0xe0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02,
+ 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x64,
+ 0x10, 0x00, 0x00, 0x01,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x81, 0x7e, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x24, 0x00, 0x24,
+ 0x00, 0x00, 0x24, 0x05, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x01, 0x00, 0x00, 0x24, 0x00, 0x00, 0x00, 0x40,
+ 0x00, 0x0c, 0x41, 0x04, 0x00, 0x18, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x09, 0x04, 0x04,
+ 0x05, 0x00, 0x01, 0x01, 0x11,
+ 0x00, 0x09, 0x04,
+ 0x05, 0x05, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x06,
+ 0x04, 0x06, 0xc8, 0x00
+};
+
+unsigned char ULP_ENABLE[] = {
+ 0x00, 0xe0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03,
+ 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x6b,
+ 0x10, 0x00, 0x00, 0x01,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x41, 0x7e, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x24, 0x00, 0x2b,
+ 0x00, 0x00, 0x2b, 0x05, 0x20, 0x01, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x01, 0x00, 0x00, 0x2b, 0x00, 0x00, 0x00, 0x40,
+ 0x00, 0x0c, 0x41, 0x02, 0x00, 0x1f, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x0b, 0x04, 0x01,
+ 0x03, 0x04, 0x05, 0x00, 0x01, 0x01, 0x12,
+ 0x00,
+ 0x14, 0x04, 0x0a, 0x00, 0x20, 0x00, 0x00, 0xff,
+ 0xff, 0x00, 0x08, 0xc8, 0xe8, 0xc4, 0xf1, 0xc7,
+ 0xf1, 0x00, 0x00
+};
+
+unsigned char ULP_SETUP[] = {
+ 0x00, 0xe0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04,
+ 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x6c,
+ 0x10, 0x00, 0x00, 0x01,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x41, 0x7e, 0x00, 0x01, 0x00, 0x00, 0x00, 0x02,
+ 0x00, 0x00, 0x00, 0x01, 0x00, 0x24, 0x00, 0x2c,
+ 0x00, 0x00, 0x2c, 0x05, 0x20, 0x01, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x01, 0x00, 0x00, 0x2c, 0x00, 0x00, 0x00, 0x40,
+ 0x00, 0x0c, 0x41, 0x04, 0x00, 0x20, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x09, 0x04, 0x04,
+ 0x05, 0x00, 0x01, 0x01, 0x14,
+ 0x00, 0x09, 0x04,
+ 0x05, 0x05, 0x30, 0x01, 0x00, 0x00,
+ 0x00, 0x06,
+ 0x04, 0x06, 0x40, 0x00,
+ 0x00, 0x08, 0x04, 0x0b,
+ 0x00, 0x00, 0x00, 0x00
+};
+
+unsigned char DM_ACT[] = {
+ 0x00, 0xe0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05,
+ 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x55,
+ 0x10, 0x00, 0x00, 0x01,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x41, 0x7e, 0x00, 0x01, 0x00, 0x00, 0x00, 0x03,
+ 0x00, 0x00, 0x00, 0x02, 0x00, 0x24, 0x00, 0x15,
+ 0x00, 0x00, 0x2c, 0x05, 0x20, 0x01, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x01, 0x00, 0x00, 0x15, 0x00, 0x00, 0x00, 0x40,
+ 0x00, 0x0c, 0x43, 0x60, 0x00, 0x09, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x09, 0x04, 0x04,
+ 0x05, 0x40, 0x01, 0x01, 0x00
+};
+
+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 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,
+ sizeof(struct qeth_ipa_cmd) / 256,
+ sizeof(struct qeth_ipa_cmd) % 256,
+ 0x00,
+ sizeof(struct qeth_ipa_cmd) / 256,
+ sizeof(struct qeth_ipa_cmd) % 256,
+ 0x05,
+ 0x77, 0x77, 0x77, 0x77,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x01, 0x00,
+ sizeof(struct qeth_ipa_cmd) / 256,
+ sizeof(struct qeth_ipa_cmd) % 256,
+ 0x00, 0x00, 0x00, 0x40,
+};
+EXPORT_SYMBOL_GPL(IPA_PDU_HEADER);
+
+unsigned char WRITE_CCW[] = {
+ 0x01, CCW_FLAG_SLI, 0, 0,
+ 0, 0, 0, 0
+};
+
+unsigned char READ_CCW[] = {
+ 0x02, CCW_FLAG_SLI, 0, 0,
+ 0, 0, 0, 0
+};
+
+
+struct ipa_rc_msg {
+ enum qeth_ipa_return_codes rc;
+ char *msg;
+};
+
+static struct ipa_rc_msg qeth_ipa_rc_msg[] = {
+ {IPA_RC_SUCCESS, "success"},
+ {IPA_RC_NOTSUPP, "Command not supported"},
+ {IPA_RC_IP_TABLE_FULL, "Add Addr IP Table Full - ipv6"},
+ {IPA_RC_UNKNOWN_ERROR, "IPA command failed - reason unknown"},
+ {IPA_RC_UNSUPPORTED_COMMAND, "Command not supported"},
+ {IPA_RC_DUP_IPV6_REMOTE, "ipv6 address already registered remote"},
+ {IPA_RC_DUP_IPV6_HOME, "ipv6 address already registered"},
+ {IPA_RC_UNREGISTERED_ADDR, "Address not registered"},
+ {IPA_RC_NO_ID_AVAILABLE, "No identifiers available"},
+ {IPA_RC_ID_NOT_FOUND, "Identifier not found"},
+ {IPA_RC_INVALID_IP_VERSION, "IP version incorrect"},
+ {IPA_RC_LAN_FRAME_MISMATCH, "LAN and frame mismatch"},
+ {IPA_RC_L2_UNSUPPORTED_CMD, "Unsupported layer 2 command"},
+ {IPA_RC_L2_DUP_MAC, "Duplicate MAC address"},
+ {IPA_RC_L2_ADDR_TABLE_FULL, "Layer2 address table full"},
+ {IPA_RC_L2_DUP_LAYER3_MAC, "Duplicate with layer 3 MAC"},
+ {IPA_RC_L2_GMAC_NOT_FOUND, "GMAC not found"},
+ {IPA_RC_L2_MAC_NOT_AUTH_BY_HYP, "L2 mac not authorized by hypervisor"},
+ {IPA_RC_L2_MAC_NOT_AUTH_BY_ADP, "L2 mac not authorized by adapter"},
+ {IPA_RC_L2_MAC_NOT_FOUND, "L2 mac address not found"},
+ {IPA_RC_L2_INVALID_VLAN_ID, "L2 invalid vlan id"},
+ {IPA_RC_L2_DUP_VLAN_ID, "L2 duplicate vlan id"},
+ {IPA_RC_L2_VLAN_ID_NOT_FOUND, "L2 vlan id not found"},
+ {IPA_RC_DATA_MISMATCH, "Data field mismatch (v4/v6 mixed)"},
+ {IPA_RC_INVALID_MTU_SIZE, "Invalid MTU size"},
+ {IPA_RC_INVALID_LANTYPE, "Invalid LAN type"},
+ {IPA_RC_INVALID_LANNUM, "Invalid LAN num"},
+ {IPA_RC_DUPLICATE_IP_ADDRESS, "Address already registered"},
+ {IPA_RC_IP_ADDR_TABLE_FULL, "IP address table full"},
+ {IPA_RC_LAN_PORT_STATE_ERROR, "LAN port state error"},
+ {IPA_RC_SETIP_NO_STARTLAN, "Setip no startlan received"},
+ {IPA_RC_SETIP_ALREADY_RECEIVED, "Setip already received"},
+ {IPA_RC_IP_ADDR_ALREADY_USED, "IP address already in use on LAN"},
+ {IPA_RC_MC_ADDR_NOT_FOUND, "Multicast address not found"},
+ {IPA_RC_SETIP_INVALID_VERSION, "SETIP invalid IP version"},
+ {IPA_RC_UNSUPPORTED_SUBCMD, "Unsupported assist subcommand"},
+ {IPA_RC_ARP_ASSIST_NO_ENABLE, "Only partial success, no enable"},
+ {IPA_RC_PRIMARY_ALREADY_DEFINED, "Primary already defined"},
+ {IPA_RC_SECOND_ALREADY_DEFINED, "Secondary already defined"},
+ {IPA_RC_INVALID_SETRTG_INDICATOR, "Invalid SETRTG indicator"},
+ {IPA_RC_MC_ADDR_ALREADY_DEFINED, "Multicast address already defined"},
+ {IPA_RC_LAN_OFFLINE, "STRTLAN_LAN_DISABLED - LAN offline"},
+ {IPA_RC_VEPA_TO_VEB_TRANSITION, "Adj. switch disabled port mode RR"},
+ {IPA_RC_INVALID_IP_VERSION2, "Invalid IP version"},
+ {IPA_RC_ENOMEM, "Memory problem"},
+ {IPA_RC_FFFF, "Unknown Error"}
+};
+
+
+
+char *qeth_get_ipa_msg(enum qeth_ipa_return_codes rc)
+{
+ int x = 0;
+ qeth_ipa_rc_msg[sizeof(qeth_ipa_rc_msg) /
+ sizeof(struct ipa_rc_msg) - 1].rc = rc;
+ while (qeth_ipa_rc_msg[x].rc != rc)
+ x++;
+ return qeth_ipa_rc_msg[x].msg;
+}
+
+
+struct ipa_cmd_names {
+ enum qeth_ipa_cmds cmd;
+ char *name;
+};
+
+static struct ipa_cmd_names qeth_ipa_cmd_names[] = {
+ {IPA_CMD_STARTLAN, "startlan"},
+ {IPA_CMD_STOPLAN, "stoplan"},
+ {IPA_CMD_SETVMAC, "setvmac"},
+ {IPA_CMD_DELVMAC, "delvmac"},
+ {IPA_CMD_SETGMAC, "setgmac"},
+ {IPA_CMD_DELGMAC, "delgmac"},
+ {IPA_CMD_SETVLAN, "setvlan"},
+ {IPA_CMD_DELVLAN, "delvlan"},
+ {IPA_CMD_SETCCID, "setccid"},
+ {IPA_CMD_DELCCID, "delccid"},
+ {IPA_CMD_MODCCID, "modccid"},
+ {IPA_CMD_SETIP, "setip"},
+ {IPA_CMD_QIPASSIST, "qipassist"},
+ {IPA_CMD_SETASSPARMS, "setassparms"},
+ {IPA_CMD_SETIPM, "setipm"},
+ {IPA_CMD_DELIPM, "delipm"},
+ {IPA_CMD_SETRTG, "setrtg"},
+ {IPA_CMD_DELIP, "delip"},
+ {IPA_CMD_SETADAPTERPARMS, "setadapterparms"},
+ {IPA_CMD_SET_DIAG_ASS, "set_diag_ass"},
+ {IPA_CMD_SETBRIDGEPORT, "set_bridge_port"},
+ {IPA_CMD_CREATE_ADDR, "create_addr"},
+ {IPA_CMD_DESTROY_ADDR, "destroy_addr"},
+ {IPA_CMD_REGISTER_LOCAL_ADDR, "register_local_addr"},
+ {IPA_CMD_UNREGISTER_LOCAL_ADDR, "unregister_local_addr"},
+ {IPA_CMD_ADDRESS_CHANGE_NOTIF, "address_change_notification"},
+ {IPA_CMD_UNKNOWN, "unknown"},
+};
+
+char *qeth_get_ipa_cmd_name(enum qeth_ipa_cmds cmd)
+{
+ int x = 0;
+ qeth_ipa_cmd_names[
+ sizeof(qeth_ipa_cmd_names) /
+ sizeof(struct ipa_cmd_names)-1].cmd = cmd;
+ while (qeth_ipa_cmd_names[x].cmd != cmd)
+ x++;
+ return qeth_ipa_cmd_names[x].name;
+}
diff --git a/drivers/s390/net/qeth_mpc.h b/drivers/s390/net/qeth_core_mpc.h
index 6de2da5ed5f..cf6a90ed42a 100644
--- a/drivers/s390/net/qeth_mpc.h
+++ b/drivers/s390/net/qeth_core_mpc.h
@@ -1,27 +1,23 @@
/*
- * linux/drivers/s390/net/qeth_mpc.h
- *
- * Linux on zSeries OSA Express and HiperSockets support
- *
- * Copyright 2000,2003 IBM Corporation
- * Author(s): Utz Bacher <utz.bacher@de.ibm.com>
- * Thomas Spatzier <tspat@de.ibm.com>
- * Frank Pavlic <fpavlic@de.ibm.com>
- *
+ * Copyright IBM Corp. 2007
+ * Author(s): Frank Pavlic <fpavlic@de.ibm.com>,
+ * Thomas Spatzier <tspat@de.ibm.com>,
+ * Frank Blaschka <frank.blaschka@de.ibm.com>
*/
-#ifndef __QETH_MPC_H__
-#define __QETH_MPC_H__
+
+#ifndef __QETH_CORE_MPC_H__
+#define __QETH_CORE_MPC_H__
#include <asm/qeth.h>
#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+0x29)
-#define QETH_IPA_PDU_LEN_PDU3(buffer) (buffer+0x3a)
+#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 + 0x29)
+#define QETH_IPA_PDU_LEN_PDU3(buffer) (buffer + 0x3a)
extern unsigned char IPA_PDU_HEADER[];
-#define QETH_IPA_CMD_DEST_ADDR(buffer) (buffer+0x2c)
+#define QETH_IPA_CMD_DEST_ADDR(buffer) (buffer + 0x2c)
#define IPA_CMD_LENGTH (IPA_PDU_HEADER_SIZE + sizeof(struct qeth_ipa_cmd))
@@ -50,9 +46,11 @@ extern unsigned char IPA_PDU_HEADER[];
enum qeth_card_types {
QETH_CARD_TYPE_UNKNOWN = 0,
- QETH_CARD_TYPE_OSAE = 10,
- QETH_CARD_TYPE_IQD = 1234,
- QETH_CARD_TYPE_OSN = 11,
+ QETH_CARD_TYPE_OSD = 1,
+ QETH_CARD_TYPE_IQD = 5,
+ QETH_CARD_TYPE_OSN = 6,
+ QETH_CARD_TYPE_OSM = 3,
+ QETH_CARD_TYPE_OSX = 2,
};
#define QETH_MPC_DIFINFO_LEN_INDICATES_LINK_TYPE 0x18
@@ -70,30 +68,13 @@ enum qeth_link_types {
QETH_LINK_TYPE_ATM_NATIVE = 0x90,
};
-enum qeth_tr_macaddr_modes {
- QETH_TR_MACADDR_NONCANONICAL = 0,
- QETH_TR_MACADDR_CANONICAL = 1,
-};
-
-enum qeth_tr_broadcast_modes {
- QETH_TR_BROADCAST_ALLRINGS = 0,
- QETH_TR_BROADCAST_LOCAL = 1,
-};
-
-/* 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
-
/*
* 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 */
+ /* TODO: set to bit flag used in IPA Command */
+ NO_ROUTER = 0,
PRIMARY_ROUTER = 1,
SECONDARY_ROUTER = 2,
MULTICAST_ROUTER = 3,
@@ -123,10 +104,12 @@ enum qeth_ipa_cmds {
IPA_CMD_DELIP = 0xb7,
IPA_CMD_SETADAPTERPARMS = 0xb8,
IPA_CMD_SET_DIAG_ASS = 0xb9,
+ IPA_CMD_SETBRIDGEPORT = 0xbe,
IPA_CMD_CREATE_ADDR = 0xc3,
IPA_CMD_DESTROY_ADDR = 0xc4,
IPA_CMD_REGISTER_LOCAL_ADDR = 0xd1,
IPA_CMD_UNREGISTER_LOCAL_ADDR = 0xd2,
+ IPA_CMD_ADDRESS_CHANGE_NOTIF = 0xd3,
IPA_CMD_UNKNOWN = 0x00
};
@@ -157,6 +140,8 @@ enum qeth_ipa_return_codes {
IPA_RC_IP_TABLE_FULL = 0x0002,
IPA_RC_UNKNOWN_ERROR = 0x0003,
IPA_RC_UNSUPPORTED_COMMAND = 0x0004,
+ IPA_RC_TRACE_ALREADY_ACTIVE = 0x0005,
+ IPA_RC_INVALID_FORMAT = 0x0006,
IPA_RC_DUP_IPV6_REMOTE = 0x0008,
IPA_RC_DUP_IPV6_HOME = 0x0010,
IPA_RC_UNREGISTERED_ADDR = 0x0011,
@@ -169,6 +154,8 @@ enum qeth_ipa_return_codes {
IPA_RC_L2_ADDR_TABLE_FULL = 0x2006,
IPA_RC_L2_DUP_LAYER3_MAC = 0x200a,
IPA_RC_L2_GMAC_NOT_FOUND = 0x200b,
+ IPA_RC_L2_MAC_NOT_AUTH_BY_HYP = 0x200c,
+ IPA_RC_L2_MAC_NOT_AUTH_BY_ADP = 0x200d,
IPA_RC_L2_MAC_NOT_FOUND = 0x2010,
IPA_RC_L2_INVALID_VLAN_ID = 0x2015,
IPA_RC_L2_DUP_VLAN_ID = 0x2016,
@@ -183,7 +170,7 @@ enum qeth_ipa_return_codes {
IPA_RC_SETIP_NO_STARTLAN = 0xe008,
IPA_RC_SETIP_ALREADY_RECEIVED = 0xe009,
IPA_RC_IP_ADDR_ALREADY_USED = 0xe00a,
- IPA_RC_MULTICAST_FULL = 0xe00b,
+ IPA_RC_MC_ADDR_NOT_FOUND = 0xe00b,
IPA_RC_SETIP_INVALID_VERSION = 0xe00d,
IPA_RC_UNSUPPORTED_SUBCMD = 0xe00e,
IPA_RC_ARP_ASSIST_NO_ENABLE = 0xe00f,
@@ -192,9 +179,16 @@ enum qeth_ipa_return_codes {
IPA_RC_INVALID_SETRTG_INDICATOR = 0xe012,
IPA_RC_MC_ADDR_ALREADY_DEFINED = 0xe013,
IPA_RC_LAN_OFFLINE = 0xe080,
+ IPA_RC_VEPA_TO_VEB_TRANSITION = 0xe090,
IPA_RC_INVALID_IP_VERSION2 = 0xf001,
+ IPA_RC_ENOMEM = 0xfffe,
IPA_RC_FFFF = 0xffff
};
+/* for DELIP */
+#define IPA_RC_IP_ADDRESS_NOT_DEFINED IPA_RC_PRIMARY_ALREADY_DEFINED
+/* for SET_DIAGNOSTIC_ASSIST */
+#define IPA_RC_INVALID_SUBCMD IPA_RC_IP_TABLE_FULL
+#define IPA_RC_HARDWARE_AUTH_ERROR IPA_RC_UNKNOWN_ERROR
/* IPA function flags; each flag marks availability of respective function */
enum qeth_ipa_funcs {
@@ -233,18 +227,21 @@ enum qeth_ipa_setdelip_flags {
/* 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_QUERY_CARD_INFO = 0x0400,
- IPA_SETADP_SET_PROMISC_MODE = 0x0800,
+ IPA_SETADP_QUERY_COMMANDS_SUPPORTED = 0x00000001L,
+ IPA_SETADP_ALTER_MAC_ADDRESS = 0x00000002L,
+ IPA_SETADP_ADD_DELETE_GROUP_ADDRESS = 0x00000004L,
+ IPA_SETADP_ADD_DELETE_FUNCTIONAL_ADDR = 0x00000008L,
+ IPA_SETADP_SET_ADDRESSING_MODE = 0x00000010L,
+ IPA_SETADP_SET_CONFIG_PARMS = 0x00000020L,
+ IPA_SETADP_SET_CONFIG_PARMS_EXTENDED = 0x00000040L,
+ IPA_SETADP_SET_BROADCAST_MODE = 0x00000080L,
+ IPA_SETADP_SEND_OSA_MESSAGE = 0x00000100L,
+ IPA_SETADP_SET_SNMP_CONTROL = 0x00000200L,
+ IPA_SETADP_QUERY_CARD_INFO = 0x00000400L,
+ IPA_SETADP_SET_PROMISC_MODE = 0x00000800L,
+ IPA_SETADP_SET_DIAG_ASSIST = 0x00002000L,
+ IPA_SETADP_SET_ACCESS_CONTROL = 0x00010000L,
+ IPA_SETADP_QUERY_OAT = 0x00080000L,
};
enum qeth_ipa_mac_ops {
CHANGE_ADDR_READ_MAC = 0,
@@ -263,6 +260,40 @@ enum qeth_ipa_promisc_modes {
SET_PROMISC_MODE_OFF = 0,
SET_PROMISC_MODE_ON = 1,
};
+enum qeth_ipa_isolation_modes {
+ ISOLATION_MODE_NONE = 0x00000000L,
+ ISOLATION_MODE_FWD = 0x00000001L,
+ ISOLATION_MODE_DROP = 0x00000002L,
+};
+enum qeth_ipa_set_access_mode_rc {
+ SET_ACCESS_CTRL_RC_SUCCESS = 0x0000,
+ SET_ACCESS_CTRL_RC_NOT_SUPPORTED = 0x0004,
+ SET_ACCESS_CTRL_RC_ALREADY_NOT_ISOLATED = 0x0008,
+ SET_ACCESS_CTRL_RC_ALREADY_ISOLATED = 0x0010,
+ SET_ACCESS_CTRL_RC_NONE_SHARED_ADAPTER = 0x0014,
+ SET_ACCESS_CTRL_RC_ACTIVE_CHECKSUM_OFF = 0x0018,
+ SET_ACCESS_CTRL_RC_REFLREL_UNSUPPORTED = 0x0022,
+ SET_ACCESS_CTRL_RC_REFLREL_FAILED = 0x0024,
+ SET_ACCESS_CTRL_RC_REFLREL_DEACT_FAILED = 0x0028,
+};
+enum qeth_card_info_card_type {
+ CARD_INFO_TYPE_1G_COPPER_A = 0x61,
+ CARD_INFO_TYPE_1G_FIBRE_A = 0x71,
+ CARD_INFO_TYPE_10G_FIBRE_A = 0x91,
+ CARD_INFO_TYPE_1G_COPPER_B = 0xb1,
+ CARD_INFO_TYPE_1G_FIBRE_B = 0xa1,
+ CARD_INFO_TYPE_10G_FIBRE_B = 0xc1,
+};
+enum qeth_card_info_port_mode {
+ CARD_INFO_PORTM_HALFDUPLEX = 0x0002,
+ CARD_INFO_PORTM_FULLDUPLEX = 0x0003,
+};
+enum qeth_card_info_port_speed {
+ CARD_INFO_PORTS_10M = 0x00000005,
+ CARD_INFO_PORTS_100M = 0x00000006,
+ CARD_INFO_PORTS_1G = 0x00000007,
+ CARD_INFO_PORTS_10G = 0x00000008,
+};
/* (SET)DELIP(M) IPA stuff ***************************************************/
struct qeth_ipacmd_setdelip4 {
@@ -307,7 +338,7 @@ struct qeth_arp_query_data {
__u16 request_bits;
__u16 reply_bits;
__u32 no_entries;
- char data;
+ char data; /* only for replies */
} __attribute__((packed));
/* used as parameter for arp_query reply */
@@ -375,6 +406,31 @@ struct qeth_snmp_ureq {
struct qeth_snmp_cmd cmd;
} __attribute__((packed));
+/* SET_ACCESS_CONTROL: same format for request and reply */
+struct qeth_set_access_ctrl {
+ __u32 subcmd_code;
+ __u8 reserved[8];
+} __attribute__((packed));
+
+struct qeth_query_oat {
+ __u32 subcmd_code;
+ __u8 reserved[12];
+} __packed;
+
+struct qeth_qoat_priv {
+ __u32 buffer_len;
+ __u32 response_len;
+ char *buffer;
+};
+
+struct qeth_query_card_info {
+ __u8 card_type;
+ __u8 reserved1;
+ __u16 port_mode;
+ __u32 port_speed;
+ __u32 reserved2;
+};
+
struct qeth_ipacmd_setadpparms_hdr {
__u32 supp_hw_cmds;
__u32 reserved1;
@@ -393,29 +449,176 @@ struct qeth_ipacmd_setadpparms {
struct qeth_query_cmds_supp query_cmds_supp;
struct qeth_change_addr change_addr;
struct qeth_snmp_cmd snmp;
+ struct qeth_set_access_ctrl set_access_ctrl;
+ struct qeth_query_oat query_oat;
+ struct qeth_query_card_info card_info;
__u32 mode;
} data;
} __attribute__ ((packed));
-/* IPFRAME IPA Command: ***************************************************/
-/* TODO: define in analogy to commands define above */
-
-/* ADD_ADDR_ENTRY IPA Command: ********************************************/
-/* TODO: define in analogy to commands define above */
-
-/* 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 */
+/* SET DIAGNOSTIC ASSIST IPA Command: *************************************/
+
+enum qeth_diags_cmds {
+ QETH_DIAGS_CMD_QUERY = 0x0001,
+ QETH_DIAGS_CMD_TRAP = 0x0002,
+ QETH_DIAGS_CMD_TRACE = 0x0004,
+ QETH_DIAGS_CMD_NOLOG = 0x0008,
+ QETH_DIAGS_CMD_DUMP = 0x0010,
+};
-/* UNREGISTER_LOCAL_ADDR IPA Command: *************************************/
-/* TODO: define in analogy to commands define above */
+enum qeth_diags_trace_types {
+ QETH_DIAGS_TYPE_HIPERSOCKET = 0x02,
+};
+
+enum qeth_diags_trace_cmds {
+ QETH_DIAGS_CMD_TRACE_ENABLE = 0x0001,
+ QETH_DIAGS_CMD_TRACE_DISABLE = 0x0002,
+ QETH_DIAGS_CMD_TRACE_MODIFY = 0x0004,
+ QETH_DIAGS_CMD_TRACE_REPLACE = 0x0008,
+ QETH_DIAGS_CMD_TRACE_QUERY = 0x0010,
+};
+
+enum qeth_diags_trap_action {
+ QETH_DIAGS_TRAP_ARM = 0x01,
+ QETH_DIAGS_TRAP_DISARM = 0x02,
+ QETH_DIAGS_TRAP_CAPTURE = 0x04,
+};
+
+struct qeth_ipacmd_diagass {
+ __u32 host_tod2;
+ __u32:32;
+ __u16 subcmd_len;
+ __u16:16;
+ __u32 subcmd;
+ __u8 type;
+ __u8 action;
+ __u16 options;
+ __u32 ext;
+ __u8 cdata[64];
+} __attribute__ ((packed));
+
+/* SETBRIDGEPORT IPA Command: *********************************************/
+enum qeth_ipa_sbp_cmd {
+ IPA_SBP_QUERY_COMMANDS_SUPPORTED = 0x00000000L,
+ IPA_SBP_RESET_BRIDGE_PORT_ROLE = 0x00000001L,
+ IPA_SBP_SET_PRIMARY_BRIDGE_PORT = 0x00000002L,
+ IPA_SBP_SET_SECONDARY_BRIDGE_PORT = 0x00000004L,
+ IPA_SBP_QUERY_BRIDGE_PORTS = 0x00000008L,
+ IPA_SBP_BRIDGE_PORT_STATE_CHANGE = 0x00000010L,
+};
+
+struct net_if_token {
+ __u16 devnum;
+ __u8 cssid;
+ __u8 iid;
+ __u8 ssid;
+ __u8 chpid;
+ __u16 chid;
+} __packed;
+
+struct mac_addr_lnid {
+ __u8 mac[6];
+ __u16 lnid;
+} __packed;
+
+struct qeth_ipacmd_sbp_hdr {
+ __u32 supported_sbp_cmds;
+ __u32 enabled_sbp_cmds;
+ __u16 cmdlength;
+ __u16 reserved1;
+ __u32 command_code;
+ __u16 return_code;
+ __u8 used_total;
+ __u8 seq_no;
+ __u32 reserved2;
+} __packed;
+
+struct qeth_sbp_query_cmds_supp {
+ __u32 supported_cmds;
+ __u32 reserved;
+} __packed;
+
+struct qeth_sbp_reset_role {
+} __packed;
+
+struct qeth_sbp_set_primary {
+ struct net_if_token token;
+} __packed;
+
+struct qeth_sbp_set_secondary {
+} __packed;
+
+struct qeth_sbp_port_entry {
+ __u8 role;
+ __u8 state;
+ __u8 reserved1;
+ __u8 reserved2;
+ struct net_if_token token;
+} __packed;
+
+struct qeth_sbp_query_ports {
+ __u8 primary_bp_supported;
+ __u8 secondary_bp_supported;
+ __u8 num_entries;
+ __u8 entry_length;
+ struct qeth_sbp_port_entry entry[];
+} __packed;
+
+struct qeth_sbp_state_change {
+ __u8 primary_bp_supported;
+ __u8 secondary_bp_supported;
+ __u8 num_entries;
+ __u8 entry_length;
+ struct qeth_sbp_port_entry entry[];
+} __packed;
+
+struct qeth_ipacmd_setbridgeport {
+ struct qeth_ipacmd_sbp_hdr hdr;
+ union {
+ struct qeth_sbp_query_cmds_supp query_cmds_supp;
+ struct qeth_sbp_reset_role reset_role;
+ struct qeth_sbp_set_primary set_primary;
+ struct qeth_sbp_set_secondary set_secondary;
+ struct qeth_sbp_query_ports query_ports;
+ struct qeth_sbp_state_change state_change;
+ } data;
+} __packed;
+
+/* ADDRESS_CHANGE_NOTIFICATION adapter-initiated "command" *******************/
+/* Bitmask for entry->change_code. Both bits may be raised. */
+enum qeth_ipa_addr_change_code {
+ IPA_ADDR_CHANGE_CODE_VLANID = 0x01,
+ IPA_ADDR_CHANGE_CODE_MACADDR = 0x02,
+ IPA_ADDR_CHANGE_CODE_REMOVAL = 0x80, /* else addition */
+};
+enum qeth_ipa_addr_change_retcode {
+ IPA_ADDR_CHANGE_RETCODE_OK = 0x0000,
+ IPA_ADDR_CHANGE_RETCODE_LOSTEVENTS = 0x0010,
+};
+enum qeth_ipa_addr_change_lostmask {
+ IPA_ADDR_CHANGE_MASK_OVERFLOW = 0x01,
+ IPA_ADDR_CHANGE_MASK_STATECHANGE = 0x02,
+};
+
+struct qeth_ipacmd_addr_change_entry {
+ struct net_if_token token;
+ struct mac_addr_lnid addr_lnid;
+ __u8 change_code;
+ __u8 reserved1;
+ __u16 reserved2;
+} __packed;
+
+struct qeth_ipacmd_addr_change {
+ __u8 lost_event_mask;
+ __u8 reserved;
+ __u16 num_entries;
+ struct qeth_ipacmd_addr_change_entry entry[];
+} __packed;
/* Header for each IPA command */
struct qeth_ipacmd_hdr {
@@ -445,6 +648,9 @@ struct qeth_ipa_cmd {
struct qeth_create_destroy_address create_destroy_addr;
struct qeth_ipacmd_setadpparms setadapterparms;
struct qeth_set_routing setrtg;
+ struct qeth_ipacmd_diagass diagass;
+ struct qeth_ipacmd_setbridgeport sbp;
+ struct qeth_ipacmd_addr_change addrchange;
} data;
} __attribute__ ((packed));
@@ -462,11 +668,8 @@ enum qeth_ipa_arp_return_codes {
QETH_IPA_ARP_RC_Q_NO_DATA = 0x0008,
};
-
-extern char *
-qeth_get_ipa_msg(enum qeth_ipa_return_codes rc);
-extern char *
-qeth_get_ipa_cmd_name(enum qeth_ipa_cmds cmd);
+extern char *qeth_get_ipa_msg(enum qeth_ipa_return_codes rc);
+extern char *qeth_get_ipa_cmd_name(enum qeth_ipa_cmds cmd);
#define QETH_SETASS_BASE_LEN (sizeof(struct qeth_ipacmd_hdr) + \
sizeof(struct qeth_ipacmd_setassparms_hdr))
@@ -492,88 +695,92 @@ extern unsigned char READ_CCW[];
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 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 QETH_CM_ENABLE_RESP_FILTER_TOKEN(buffer) \
- (PDU_ENCAPSULATION(buffer)+ 0x13)
+ (PDU_ENCAPSULATION(buffer) + 0x13)
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)
+#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)
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_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)
+ (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)
-/* Layer 2 defintions */
+ (PDU_ENCAPSULATION(buffer) + 0x2b)
+/* Layer 2 definitions */
#define QETH_PROT_LAYER2 0x08
#define QETH_PROT_TCPIP 0x03
#define QETH_PROT_OSN2 0x0a
-#define QETH_ULP_ENABLE_PROT_TYPE(buffer) (buffer+0x50)
-#define QETH_IPA_CMD_PROT_TYPE(buffer) (buffer+0x19)
+#define QETH_ULP_ENABLE_PROT_TYPE(buffer) (buffer + 0x50)
+#define QETH_IPA_CMD_PROT_TYPE(buffer) (buffer + 0x19)
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_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)
+ (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_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)
+#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 QETH_IDX_ACT_PNO(buffer) (buffer+0x0b)
+#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 QETH_IDX_ACT_CAUSE_CODE(buffer) (buffer)[0x09]
+#define QETH_IDX_ACT_ERR_EXCL 0x19
+#define QETH_IDX_ACT_ERR_AUTH 0x1E
+#define QETH_IDX_ACT_ERR_AUTH_USER 0x20
#define PDU_ENCAPSULATION(buffer) \
- (buffer + *(buffer + (*(buffer+0x0b)) + \
- *(buffer + *(buffer+0x0b)+0x11) +0x07))
+ (buffer + *(buffer + (*(buffer + 0x0b)) + \
+ *(buffer + *(buffer + 0x0b) + 0x11) + 0x07))
#define IS_IPA(buffer) \
((buffer) && \
- ( *(buffer + ((*(buffer+0x0b))+4) )==0xc1) )
+ (*(buffer + ((*(buffer + 0x0b)) + 4)) == 0xc1))
#define ADDR_FRAME_TYPE_DIX 1
#define ADDR_FRAME_TYPE_802_3 2
diff --git a/drivers/s390/net/qeth_core_sys.c b/drivers/s390/net/qeth_core_sys.c
new file mode 100644
index 00000000000..8a25a2be989
--- /dev/null
+++ b/drivers/s390/net/qeth_core_sys.c
@@ -0,0 +1,758 @@
+/*
+ * Copyright IBM Corp. 2007
+ * Author(s): Utz Bacher <utz.bacher@de.ibm.com>,
+ * Frank Pavlic <fpavlic@de.ibm.com>,
+ * Thomas Spatzier <tspat@de.ibm.com>,
+ * Frank Blaschka <frank.blaschka@de.ibm.com>
+ */
+
+#define KMSG_COMPONENT "qeth"
+#define pr_fmt(fmt) KMSG_COMPONENT ": " fmt
+
+#include <linux/list.h>
+#include <linux/rwsem.h>
+#include <asm/ebcdic.h>
+
+#include "qeth_core.h"
+
+static ssize_t qeth_dev_state_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct qeth_card *card = dev_get_drvdata(dev);
+ 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:
+ if (card->lan_online)
+ return sprintf(buf, "UP (LAN ONLINE)\n");
+ else
+ return sprintf(buf, "UP (LAN OFFLINE)\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,
+ struct device_attribute *attr, char *buf)
+{
+ struct qeth_card *card = dev_get_drvdata(dev);
+ 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,
+ struct device_attribute *attr, char *buf)
+{
+ struct qeth_card *card = dev_get_drvdata(dev);
+ if (!card)
+ return -EINVAL;
+ return sprintf(buf, "%s\n", QETH_CARD_IFNAME(card));
+}
+
+static DEVICE_ATTR(if_name, 0444, qeth_dev_if_name_show, NULL);
+
+static ssize_t qeth_dev_card_type_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct qeth_card *card = dev_get_drvdata(dev);
+ 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 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 ssize_t qeth_dev_inbuf_size_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct qeth_card *card = dev_get_drvdata(dev);
+ if (!card)
+ return -EINVAL;
+
+ return sprintf(buf, "%s\n", qeth_get_bufsize_str(card));
+}
+
+static DEVICE_ATTR(inbuf_size, 0444, qeth_dev_inbuf_size_show, NULL);
+
+static ssize_t qeth_dev_portno_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct qeth_card *card = dev_get_drvdata(dev);
+ if (!card)
+ return -EINVAL;
+
+ return sprintf(buf, "%i\n", card->info.portno);
+}
+
+static ssize_t qeth_dev_portno_store(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t count)
+{
+ struct qeth_card *card = dev_get_drvdata(dev);
+ char *tmp;
+ unsigned int portno, limit;
+ int rc = 0;
+
+ if (!card)
+ return -EINVAL;
+
+ mutex_lock(&card->conf_mutex);
+ if ((card->state != CARD_STATE_DOWN) &&
+ (card->state != CARD_STATE_RECOVER)) {
+ rc = -EPERM;
+ goto out;
+ }
+
+ portno = simple_strtoul(buf, &tmp, 16);
+ if (portno > QETH_MAX_PORTNO) {
+ rc = -EINVAL;
+ goto out;
+ }
+ limit = (card->ssqd.pcnt ? card->ssqd.pcnt - 1 : card->ssqd.pcnt);
+ if (portno > limit) {
+ rc = -EINVAL;
+ goto out;
+ }
+ card->info.portno = portno;
+out:
+ mutex_unlock(&card->conf_mutex);
+ return rc ? rc : count;
+}
+
+static DEVICE_ATTR(portno, 0644, qeth_dev_portno_show, qeth_dev_portno_store);
+
+static ssize_t qeth_dev_portname_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct qeth_card *card = dev_get_drvdata(dev);
+ 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,
+ struct device_attribute *attr, const char *buf, size_t count)
+{
+ struct qeth_card *card = dev_get_drvdata(dev);
+ char *tmp;
+ int i, rc = 0;
+
+ if (!card)
+ return -EINVAL;
+
+ mutex_lock(&card->conf_mutex);
+ if ((card->state != CARD_STATE_DOWN) &&
+ (card->state != CARD_STATE_RECOVER)) {
+ rc = -EPERM;
+ goto out;
+ }
+
+ tmp = strsep((char **) &buf, "\n");
+ if ((strlen(tmp) > 8) || (strlen(tmp) == 0)) {
+ rc = -EINVAL;
+ goto out;
+ }
+
+ 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);
+out:
+ mutex_unlock(&card->conf_mutex);
+ return rc ? rc : count;
+}
+
+static DEVICE_ATTR(portname, 0644, qeth_dev_portname_show,
+ qeth_dev_portname_store);
+
+static ssize_t qeth_dev_prioqing_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct qeth_card *card = dev_get_drvdata(dev);
+
+ 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");
+ case QETH_PRIO_Q_ING_SKB:
+ return sprintf(buf, "%s\n", "by skb-priority");
+ case QETH_PRIO_Q_ING_VLAN:
+ return sprintf(buf, "%s\n", "by VLAN headers");
+ default:
+ return sprintf(buf, "always queue %i\n",
+ card->qdio.default_out_queue);
+ }
+}
+
+static ssize_t qeth_dev_prioqing_store(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t count)
+{
+ struct qeth_card *card = dev_get_drvdata(dev);
+ char *tmp;
+ int rc = 0;
+
+ if (!card)
+ return -EINVAL;
+
+ mutex_lock(&card->conf_mutex);
+ if ((card->state != CARD_STATE_DOWN) &&
+ (card->state != CARD_STATE_RECOVER)) {
+ rc = -EPERM;
+ goto out;
+ }
+
+ /* check if 1920 devices are supported ,
+ * if though we have to permit priority queueing
+ */
+ if (card->qdio.no_out_queues == 1) {
+ card->qdio.do_prio_queueing = QETH_PRIOQ_DEFAULT;
+ rc = -EPERM;
+ goto out;
+ }
+
+ tmp = strsep((char **) &buf, "\n");
+ if (!strcmp(tmp, "prio_queueing_prec")) {
+ card->qdio.do_prio_queueing = QETH_PRIO_Q_ING_PREC;
+ card->qdio.default_out_queue = QETH_DEFAULT_QUEUE;
+ } else if (!strcmp(tmp, "prio_queueing_skb")) {
+ card->qdio.do_prio_queueing = QETH_PRIO_Q_ING_SKB;
+ card->qdio.default_out_queue = QETH_DEFAULT_QUEUE;
+ } else if (!strcmp(tmp, "prio_queueing_tos")) {
+ card->qdio.do_prio_queueing = QETH_PRIO_Q_ING_TOS;
+ card->qdio.default_out_queue = QETH_DEFAULT_QUEUE;
+ } else if (!strcmp(tmp, "prio_queueing_vlan")) {
+ if (!card->options.layer2) {
+ rc = -ENOTSUPP;
+ goto out;
+ }
+ card->qdio.do_prio_queueing = QETH_PRIO_Q_ING_VLAN;
+ card->qdio.default_out_queue = QETH_DEFAULT_QUEUE;
+ } 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
+ rc = -EINVAL;
+out:
+ mutex_unlock(&card->conf_mutex);
+ return rc ? rc : 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,
+ struct device_attribute *attr, char *buf)
+{
+ struct qeth_card *card = dev_get_drvdata(dev);
+
+ 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,
+ struct device_attribute *attr, const char *buf, size_t count)
+{
+ struct qeth_card *card = dev_get_drvdata(dev);
+ char *tmp;
+ int cnt, old_cnt;
+ int rc = 0;
+
+ if (!card)
+ return -EINVAL;
+
+ mutex_lock(&card->conf_mutex);
+ if ((card->state != CARD_STATE_DOWN) &&
+ (card->state != CARD_STATE_RECOVER)) {
+ rc = -EPERM;
+ goto out;
+ }
+
+ old_cnt = card->qdio.in_buf_pool.buf_count;
+ cnt = simple_strtoul(buf, &tmp, 10);
+ cnt = (cnt < QETH_IN_BUF_COUNT_MIN) ? QETH_IN_BUF_COUNT_MIN :
+ ((cnt > QETH_IN_BUF_COUNT_MAX) ? QETH_IN_BUF_COUNT_MAX : cnt);
+ if (old_cnt != cnt) {
+ rc = qeth_realloc_buffer_pool(card, cnt);
+ }
+out:
+ mutex_unlock(&card->conf_mutex);
+ return rc ? rc : count;
+}
+
+static DEVICE_ATTR(buffer_count, 0644, qeth_dev_bufcnt_show,
+ qeth_dev_bufcnt_store);
+
+static ssize_t qeth_dev_recover_store(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t count)
+{
+ struct qeth_card *card = dev_get_drvdata(dev);
+ char *tmp;
+ int i;
+
+ if (!card)
+ return -EINVAL;
+
+ if (card->state != CARD_STATE_UP)
+ 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);
+
+static ssize_t qeth_dev_performance_stats_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct qeth_card *card = dev_get_drvdata(dev);
+
+ if (!card)
+ return -EINVAL;
+
+ return sprintf(buf, "%i\n", card->options.performance_stats ? 1:0);
+}
+
+static ssize_t qeth_dev_performance_stats_store(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t count)
+{
+ struct qeth_card *card = dev_get_drvdata(dev);
+ char *tmp;
+ int i, rc = 0;
+
+ if (!card)
+ return -EINVAL;
+
+ mutex_lock(&card->conf_mutex);
+ i = simple_strtoul(buf, &tmp, 16);
+ if ((i == 0) || (i == 1)) {
+ if (i == card->options.performance_stats)
+ goto out;
+ card->options.performance_stats = i;
+ if (i == 0)
+ memset(&card->perf_stats, 0,
+ sizeof(struct qeth_perf_stats));
+ card->perf_stats.initial_rx_packets = card->stats.rx_packets;
+ card->perf_stats.initial_tx_packets = card->stats.tx_packets;
+ } else
+ rc = -EINVAL;
+out:
+ mutex_unlock(&card->conf_mutex);
+ return rc ? rc : count;
+}
+
+static DEVICE_ATTR(performance_stats, 0644, qeth_dev_performance_stats_show,
+ qeth_dev_performance_stats_store);
+
+static ssize_t qeth_dev_layer2_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct qeth_card *card = dev_get_drvdata(dev);
+
+ if (!card)
+ return -EINVAL;
+
+ return sprintf(buf, "%i\n", card->options.layer2);
+}
+
+static ssize_t qeth_dev_layer2_store(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t count)
+{
+ struct qeth_card *card = dev_get_drvdata(dev);
+ char *tmp;
+ int i, rc = 0;
+ enum qeth_discipline_id newdis;
+
+ if (!card)
+ return -EINVAL;
+
+ mutex_lock(&card->discipline_mutex);
+ if (card->state != CARD_STATE_DOWN) {
+ rc = -EPERM;
+ goto out;
+ }
+
+ i = simple_strtoul(buf, &tmp, 16);
+ switch (i) {
+ case 0:
+ newdis = QETH_DISCIPLINE_LAYER3;
+ break;
+ case 1:
+ newdis = QETH_DISCIPLINE_LAYER2;
+ break;
+ default:
+ rc = -EINVAL;
+ goto out;
+ }
+
+ if (card->options.layer2 == newdis)
+ goto out;
+ else {
+ card->info.mac_bits = 0;
+ if (card->discipline) {
+ card->discipline->remove(card->gdev);
+ qeth_core_free_discipline(card);
+ }
+ }
+
+ rc = qeth_core_load_discipline(card, newdis);
+ if (rc)
+ goto out;
+
+ rc = card->discipline->setup(card->gdev);
+out:
+ mutex_unlock(&card->discipline_mutex);
+ return rc ? rc : count;
+}
+
+static DEVICE_ATTR(layer2, 0644, qeth_dev_layer2_show,
+ qeth_dev_layer2_store);
+
+#define ATTR_QETH_ISOLATION_NONE ("none")
+#define ATTR_QETH_ISOLATION_FWD ("forward")
+#define ATTR_QETH_ISOLATION_DROP ("drop")
+
+static ssize_t qeth_dev_isolation_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct qeth_card *card = dev_get_drvdata(dev);
+
+ if (!card)
+ return -EINVAL;
+
+ switch (card->options.isolation) {
+ case ISOLATION_MODE_NONE:
+ return snprintf(buf, 6, "%s\n", ATTR_QETH_ISOLATION_NONE);
+ case ISOLATION_MODE_FWD:
+ return snprintf(buf, 9, "%s\n", ATTR_QETH_ISOLATION_FWD);
+ case ISOLATION_MODE_DROP:
+ return snprintf(buf, 6, "%s\n", ATTR_QETH_ISOLATION_DROP);
+ default:
+ return snprintf(buf, 5, "%s\n", "N/A");
+ }
+}
+
+static ssize_t qeth_dev_isolation_store(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t count)
+{
+ struct qeth_card *card = dev_get_drvdata(dev);
+ enum qeth_ipa_isolation_modes isolation;
+ int rc = 0;
+ char *tmp, *curtoken;
+ curtoken = (char *) buf;
+
+ if (!card)
+ return -EINVAL;
+
+ mutex_lock(&card->conf_mutex);
+ /* check for unknown, too, in case we do not yet know who we are */
+ if (card->info.type != QETH_CARD_TYPE_OSD &&
+ card->info.type != QETH_CARD_TYPE_OSX &&
+ card->info.type != QETH_CARD_TYPE_UNKNOWN) {
+ rc = -EOPNOTSUPP;
+ dev_err(&card->gdev->dev, "Adapter does not "
+ "support QDIO data connection isolation\n");
+ goto out;
+ }
+
+ /* parse input into isolation mode */
+ tmp = strsep(&curtoken, "\n");
+ if (!strcmp(tmp, ATTR_QETH_ISOLATION_NONE)) {
+ isolation = ISOLATION_MODE_NONE;
+ } else if (!strcmp(tmp, ATTR_QETH_ISOLATION_FWD)) {
+ isolation = ISOLATION_MODE_FWD;
+ } else if (!strcmp(tmp, ATTR_QETH_ISOLATION_DROP)) {
+ isolation = ISOLATION_MODE_DROP;
+ } else {
+ rc = -EINVAL;
+ goto out;
+ }
+ rc = count;
+
+ /* defer IP assist if device is offline (until discipline->set_online)*/
+ card->options.prev_isolation = card->options.isolation;
+ card->options.isolation = isolation;
+ if (card->state == CARD_STATE_SOFTSETUP ||
+ card->state == CARD_STATE_UP) {
+ int ipa_rc = qeth_set_access_ctrl_online(card, 1);
+ if (ipa_rc != 0)
+ rc = ipa_rc;
+ }
+out:
+ mutex_unlock(&card->conf_mutex);
+ return rc;
+}
+
+static DEVICE_ATTR(isolation, 0644, qeth_dev_isolation_show,
+ qeth_dev_isolation_store);
+
+static ssize_t qeth_hw_trap_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct qeth_card *card = dev_get_drvdata(dev);
+
+ if (!card)
+ return -EINVAL;
+ if (card->info.hwtrap)
+ return snprintf(buf, 5, "arm\n");
+ else
+ return snprintf(buf, 8, "disarm\n");
+}
+
+static ssize_t qeth_hw_trap_store(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t count)
+{
+ struct qeth_card *card = dev_get_drvdata(dev);
+ int rc = 0;
+ char *tmp, *curtoken;
+ int state = 0;
+ curtoken = (char *)buf;
+
+ if (!card)
+ return -EINVAL;
+
+ mutex_lock(&card->conf_mutex);
+ if (card->state == CARD_STATE_SOFTSETUP || card->state == CARD_STATE_UP)
+ state = 1;
+ tmp = strsep(&curtoken, "\n");
+
+ if (!strcmp(tmp, "arm") && !card->info.hwtrap) {
+ if (state) {
+ if (qeth_is_diagass_supported(card,
+ QETH_DIAGS_CMD_TRAP)) {
+ rc = qeth_hw_trap(card, QETH_DIAGS_TRAP_ARM);
+ if (!rc)
+ card->info.hwtrap = 1;
+ } else
+ rc = -EINVAL;
+ } else
+ card->info.hwtrap = 1;
+ } else if (!strcmp(tmp, "disarm") && card->info.hwtrap) {
+ if (state) {
+ rc = qeth_hw_trap(card, QETH_DIAGS_TRAP_DISARM);
+ if (!rc)
+ card->info.hwtrap = 0;
+ } else
+ card->info.hwtrap = 0;
+ } else if (!strcmp(tmp, "trap") && state && card->info.hwtrap)
+ rc = qeth_hw_trap(card, QETH_DIAGS_TRAP_CAPTURE);
+ else
+ rc = -EINVAL;
+
+ mutex_unlock(&card->conf_mutex);
+ return rc ? rc : count;
+}
+
+static DEVICE_ATTR(hw_trap, 0644, qeth_hw_trap_show,
+ qeth_hw_trap_store);
+
+static ssize_t qeth_dev_blkt_show(char *buf, struct qeth_card *card, int value)
+{
+
+ if (!card)
+ return -EINVAL;
+
+ return sprintf(buf, "%i\n", value);
+}
+
+static ssize_t qeth_dev_blkt_store(struct qeth_card *card,
+ const char *buf, size_t count, int *value, int max_value)
+{
+ char *tmp;
+ int i, rc = 0;
+
+ if (!card)
+ return -EINVAL;
+
+ mutex_lock(&card->conf_mutex);
+ if ((card->state != CARD_STATE_DOWN) &&
+ (card->state != CARD_STATE_RECOVER)) {
+ rc = -EPERM;
+ goto out;
+ }
+ i = simple_strtoul(buf, &tmp, 10);
+ if (i <= max_value)
+ *value = i;
+ else
+ rc = -EINVAL;
+out:
+ mutex_unlock(&card->conf_mutex);
+ return rc ? rc : count;
+}
+
+static ssize_t qeth_dev_blkt_total_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct qeth_card *card = dev_get_drvdata(dev);
+
+ return qeth_dev_blkt_show(buf, card, card->info.blkt.time_total);
+}
+
+static ssize_t qeth_dev_blkt_total_store(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t count)
+{
+ struct qeth_card *card = dev_get_drvdata(dev);
+
+ return qeth_dev_blkt_store(card, buf, count,
+ &card->info.blkt.time_total, 5000);
+}
+
+
+
+static DEVICE_ATTR(total, 0644, qeth_dev_blkt_total_show,
+ qeth_dev_blkt_total_store);
+
+static ssize_t qeth_dev_blkt_inter_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct qeth_card *card = dev_get_drvdata(dev);
+
+ return qeth_dev_blkt_show(buf, card, card->info.blkt.inter_packet);
+}
+
+static ssize_t qeth_dev_blkt_inter_store(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t count)
+{
+ struct qeth_card *card = dev_get_drvdata(dev);
+
+ return qeth_dev_blkt_store(card, buf, count,
+ &card->info.blkt.inter_packet, 1000);
+}
+
+static DEVICE_ATTR(inter, 0644, qeth_dev_blkt_inter_show,
+ qeth_dev_blkt_inter_store);
+
+static ssize_t qeth_dev_blkt_inter_jumbo_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct qeth_card *card = dev_get_drvdata(dev);
+
+ return qeth_dev_blkt_show(buf, card,
+ card->info.blkt.inter_packet_jumbo);
+}
+
+static ssize_t qeth_dev_blkt_inter_jumbo_store(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t count)
+{
+ struct qeth_card *card = dev_get_drvdata(dev);
+
+ return qeth_dev_blkt_store(card, buf, count,
+ &card->info.blkt.inter_packet_jumbo, 1000);
+}
+
+static DEVICE_ATTR(inter_jumbo, 0644, qeth_dev_blkt_inter_jumbo_show,
+ qeth_dev_blkt_inter_jumbo_store);
+
+static struct attribute *qeth_blkt_device_attrs[] = {
+ &dev_attr_total.attr,
+ &dev_attr_inter.attr,
+ &dev_attr_inter_jumbo.attr,
+ NULL,
+};
+static struct attribute_group qeth_device_blkt_group = {
+ .name = "blkt",
+ .attrs = qeth_blkt_device_attrs,
+};
+
+static struct attribute *qeth_device_attrs[] = {
+ &dev_attr_state.attr,
+ &dev_attr_chpid.attr,
+ &dev_attr_if_name.attr,
+ &dev_attr_card_type.attr,
+ &dev_attr_inbuf_size.attr,
+ &dev_attr_portno.attr,
+ &dev_attr_portname.attr,
+ &dev_attr_priority_queueing.attr,
+ &dev_attr_buffer_count.attr,
+ &dev_attr_recover.attr,
+ &dev_attr_performance_stats.attr,
+ &dev_attr_layer2.attr,
+ &dev_attr_isolation.attr,
+ &dev_attr_hw_trap.attr,
+ NULL,
+};
+static struct attribute_group qeth_device_attr_group = {
+ .attrs = qeth_device_attrs,
+};
+
+const struct attribute_group *qeth_generic_attr_groups[] = {
+ &qeth_device_attr_group,
+ &qeth_device_blkt_group,
+ NULL,
+};
+
+static struct attribute *qeth_osn_device_attrs[] = {
+ &dev_attr_state.attr,
+ &dev_attr_chpid.attr,
+ &dev_attr_if_name.attr,
+ &dev_attr_card_type.attr,
+ &dev_attr_buffer_count.attr,
+ &dev_attr_recover.attr,
+ NULL,
+};
+static struct attribute_group qeth_osn_device_attr_group = {
+ .attrs = qeth_osn_device_attrs,
+};
+const struct attribute_group *qeth_osn_attr_groups[] = {
+ &qeth_osn_device_attr_group,
+ NULL,
+};
diff --git a/drivers/s390/net/qeth_eddp.c b/drivers/s390/net/qeth_eddp.c
deleted file mode 100644
index e3c268cfbff..00000000000
--- a/drivers/s390/net/qeth_eddp.c
+++ /dev/null
@@ -1,634 +0,0 @@
-/*
- * linux/drivers/s390/net/qeth_eddp.c
- *
- * Enhanced Device Driver Packing (EDDP) support for the qeth driver.
- *
- * Copyright 2004 IBM Corporation
- *
- * Author(s): Thomas Spatzier <tspat@de.ibm.com>
- *
- */
-#include <linux/errno.h>
-#include <linux/ip.h>
-#include <linux/inetdevice.h>
-#include <linux/netdevice.h>
-#include <linux/kernel.h>
-#include <linux/tcp.h>
-#include <net/tcp.h>
-#include <linux/skbuff.h>
-
-#include <net/ip.h>
-
-#include "qeth.h"
-#include "qeth_mpc.h"
-#include "qeth_eddp.h"
-
-int
-qeth_eddp_check_buffers_for_context(struct qeth_qdio_out_q *queue,
- struct qeth_eddp_context *ctx)
-{
- int index = queue->next_buf_to_fill;
- int elements_needed = ctx->num_elements;
- int elements_in_buffer;
- int skbs_in_buffer;
- int buffers_needed = 0;
-
- QETH_DBF_TEXT(trace, 5, "eddpcbfc");
- while(elements_needed > 0) {
- buffers_needed++;
- if (atomic_read(&queue->bufs[index].state) !=
- QETH_QDIO_BUF_EMPTY)
- return -EBUSY;
-
- elements_in_buffer = QETH_MAX_BUFFER_ELEMENTS(queue->card) -
- queue->bufs[index].next_element_to_fill;
- skbs_in_buffer = elements_in_buffer / ctx->elements_per_skb;
- elements_needed -= skbs_in_buffer * ctx->elements_per_skb;
- index = (index + 1) % QDIO_MAX_BUFFERS_PER_Q;
- }
- return buffers_needed;
-}
-
-static void
-qeth_eddp_free_context(struct qeth_eddp_context *ctx)
-{
- int i;
-
- QETH_DBF_TEXT(trace, 5, "eddpfctx");
- for (i = 0; i < ctx->num_pages; ++i)
- free_page((unsigned long)ctx->pages[i]);
- kfree(ctx->pages);
- kfree(ctx->elements);
- kfree(ctx);
-}
-
-
-static inline void
-qeth_eddp_get_context(struct qeth_eddp_context *ctx)
-{
- atomic_inc(&ctx->refcnt);
-}
-
-void
-qeth_eddp_put_context(struct qeth_eddp_context *ctx)
-{
- if (atomic_dec_return(&ctx->refcnt) == 0)
- qeth_eddp_free_context(ctx);
-}
-
-void
-qeth_eddp_buf_release_contexts(struct qeth_qdio_out_buffer *buf)
-{
- struct qeth_eddp_context_reference *ref;
-
- QETH_DBF_TEXT(trace, 6, "eddprctx");
- while (!list_empty(&buf->ctx_list)){
- ref = list_entry(buf->ctx_list.next,
- struct qeth_eddp_context_reference, list);
- qeth_eddp_put_context(ref->ctx);
- list_del(&ref->list);
- kfree(ref);
- }
-}
-
-static int
-qeth_eddp_buf_ref_context(struct qeth_qdio_out_buffer *buf,
- struct qeth_eddp_context *ctx)
-{
- struct qeth_eddp_context_reference *ref;
-
- QETH_DBF_TEXT(trace, 6, "eddprfcx");
- ref = kmalloc(sizeof(struct qeth_eddp_context_reference), GFP_ATOMIC);
- if (ref == NULL)
- return -ENOMEM;
- qeth_eddp_get_context(ctx);
- ref->ctx = ctx;
- list_add_tail(&ref->list, &buf->ctx_list);
- return 0;
-}
-
-int
-qeth_eddp_fill_buffer(struct qeth_qdio_out_q *queue,
- struct qeth_eddp_context *ctx,
- int index)
-{
- struct qeth_qdio_out_buffer *buf = NULL;
- struct qdio_buffer *buffer;
- int elements = ctx->num_elements;
- int element = 0;
- int flush_cnt = 0;
- int must_refcnt = 1;
- int i;
-
- QETH_DBF_TEXT(trace, 5, "eddpfibu");
- while (elements > 0) {
- buf = &queue->bufs[index];
- if (atomic_read(&buf->state) != QETH_QDIO_BUF_EMPTY){
- /* normally this should not happen since we checked for
- * available elements in qeth_check_elements_for_context
- */
- if (element == 0)
- return -EBUSY;
- else {
- PRINT_WARN("could only partially fill eddp "
- "buffer!\n");
- goto out;
- }
- }
- /* check if the whole next skb fits into current buffer */
- if ((QETH_MAX_BUFFER_ELEMENTS(queue->card) -
- buf->next_element_to_fill)
- < ctx->elements_per_skb){
- /* no -> go to next buffer */
- atomic_set(&buf->state, QETH_QDIO_BUF_PRIMED);
- index = (index + 1) % QDIO_MAX_BUFFERS_PER_Q;
- flush_cnt++;
- /* new buffer, so we have to add ctx to buffer'ctx_list
- * and increment ctx's refcnt */
- must_refcnt = 1;
- continue;
- }
- if (must_refcnt){
- must_refcnt = 0;
- if (qeth_eddp_buf_ref_context(buf, ctx)){
- PRINT_WARN("no memory to create eddp context "
- "reference\n");
- goto out_check;
- }
- }
- buffer = buf->buffer;
- /* fill one skb into buffer */
- for (i = 0; i < ctx->elements_per_skb; ++i){
- if (ctx->elements[element].length != 0) {
- buffer->element[buf->next_element_to_fill].
- addr = ctx->elements[element].addr;
- buffer->element[buf->next_element_to_fill].
- length = ctx->elements[element].length;
- buffer->element[buf->next_element_to_fill].
- flags = ctx->elements[element].flags;
- buf->next_element_to_fill++;
- }
- element++;
- elements--;
- }
- }
-out_check:
- if (!queue->do_pack) {
- QETH_DBF_TEXT(trace, 6, "fillbfnp");
- /* set state to PRIMED -> will be flushed */
- if (buf->next_element_to_fill > 0){
- atomic_set(&buf->state, QETH_QDIO_BUF_PRIMED);
- flush_cnt++;
- }
- } else {
- if (queue->card->options.performance_stats)
- queue->card->perf_stats.skbs_sent_pack++;
- QETH_DBF_TEXT(trace, 6, "fillbfpa");
- if (buf->next_element_to_fill >=
- QETH_MAX_BUFFER_ELEMENTS(queue->card)) {
- /*
- * packed buffer if full -> set state PRIMED
- * -> will be flushed
- */
- atomic_set(&buf->state, QETH_QDIO_BUF_PRIMED);
- flush_cnt++;
- }
- }
-out:
- return flush_cnt;
-}
-
-static void
-qeth_eddp_create_segment_hdrs(struct qeth_eddp_context *ctx,
- struct qeth_eddp_data *eddp, int data_len)
-{
- u8 *page;
- int page_remainder;
- int page_offset;
- int pkt_len;
- struct qeth_eddp_element *element;
-
- QETH_DBF_TEXT(trace, 5, "eddpcrsh");
- page = ctx->pages[ctx->offset >> PAGE_SHIFT];
- page_offset = ctx->offset % PAGE_SIZE;
- element = &ctx->elements[ctx->num_elements];
- pkt_len = eddp->nhl + eddp->thl + data_len;
- /* FIXME: layer2 and VLAN !!! */
- if (eddp->qh.hdr.l2.id == QETH_HEADER_TYPE_LAYER2)
- pkt_len += ETH_HLEN;
- if (eddp->mac.h_proto == __constant_htons(ETH_P_8021Q))
- pkt_len += VLAN_HLEN;
- /* does complete packet fit in current page ? */
- page_remainder = PAGE_SIZE - page_offset;
- if (page_remainder < (sizeof(struct qeth_hdr) + pkt_len)){
- /* no -> go to start of next page */
- ctx->offset += page_remainder;
- page = ctx->pages[ctx->offset >> PAGE_SHIFT];
- page_offset = 0;
- }
- memcpy(page + page_offset, &eddp->qh, sizeof(struct qeth_hdr));
- element->addr = page + page_offset;
- element->length = sizeof(struct qeth_hdr);
- ctx->offset += sizeof(struct qeth_hdr);
- page_offset += sizeof(struct qeth_hdr);
- /* add mac header (?) */
- if (eddp->qh.hdr.l2.id == QETH_HEADER_TYPE_LAYER2){
- memcpy(page + page_offset, &eddp->mac, ETH_HLEN);
- element->length += ETH_HLEN;
- ctx->offset += ETH_HLEN;
- page_offset += ETH_HLEN;
- }
- /* add VLAN tag */
- if (eddp->mac.h_proto == __constant_htons(ETH_P_8021Q)){
- memcpy(page + page_offset, &eddp->vlan, VLAN_HLEN);
- element->length += VLAN_HLEN;
- ctx->offset += VLAN_HLEN;
- page_offset += VLAN_HLEN;
- }
- /* add network header */
- memcpy(page + page_offset, (u8 *)&eddp->nh, eddp->nhl);
- element->length += eddp->nhl;
- eddp->nh_in_ctx = page + page_offset;
- ctx->offset += eddp->nhl;
- page_offset += eddp->nhl;
- /* add transport header */
- memcpy(page + page_offset, (u8 *)&eddp->th, eddp->thl);
- element->length += eddp->thl;
- eddp->th_in_ctx = page + page_offset;
- ctx->offset += eddp->thl;
-}
-
-static void
-qeth_eddp_copy_data_tcp(char *dst, struct qeth_eddp_data *eddp, int len,
- __wsum *hcsum)
-{
- struct skb_frag_struct *frag;
- int left_in_frag;
- int copy_len;
- u8 *src;
-
- QETH_DBF_TEXT(trace, 5, "eddpcdtc");
- if (skb_shinfo(eddp->skb)->nr_frags == 0) {
- skb_copy_from_linear_data_offset(eddp->skb, eddp->skb_offset,
- dst, len);
- *hcsum = csum_partial(eddp->skb->data + eddp->skb_offset, len,
- *hcsum);
- eddp->skb_offset += len;
- } else {
- while (len > 0) {
- if (eddp->frag < 0) {
- /* we're in skb->data */
- left_in_frag = (eddp->skb->len - eddp->skb->data_len)
- - eddp->skb_offset;
- src = eddp->skb->data + eddp->skb_offset;
- } else {
- frag = &skb_shinfo(eddp->skb)->
- frags[eddp->frag];
- left_in_frag = frag->size - eddp->frag_offset;
- src = (u8 *)(
- (page_to_pfn(frag->page) << PAGE_SHIFT)+
- frag->page_offset + eddp->frag_offset);
- }
- if (left_in_frag <= 0) {
- eddp->frag++;
- eddp->frag_offset = 0;
- continue;
- }
- copy_len = min(left_in_frag, len);
- memcpy(dst, src, copy_len);
- *hcsum = csum_partial(src, copy_len, *hcsum);
- dst += copy_len;
- eddp->frag_offset += copy_len;
- eddp->skb_offset += copy_len;
- len -= copy_len;
- }
- }
-}
-
-static void
-qeth_eddp_create_segment_data_tcp(struct qeth_eddp_context *ctx,
- struct qeth_eddp_data *eddp, int data_len,
- __wsum hcsum)
-{
- u8 *page;
- int page_remainder;
- int page_offset;
- struct qeth_eddp_element *element;
- int first_lap = 1;
-
- QETH_DBF_TEXT(trace, 5, "eddpcsdt");
- page = ctx->pages[ctx->offset >> PAGE_SHIFT];
- page_offset = ctx->offset % PAGE_SIZE;
- element = &ctx->elements[ctx->num_elements];
- while (data_len){
- page_remainder = PAGE_SIZE - page_offset;
- if (page_remainder < data_len){
- qeth_eddp_copy_data_tcp(page + page_offset, eddp,
- page_remainder, &hcsum);
- element->length += page_remainder;
- if (first_lap)
- element->flags = SBAL_FLAGS_FIRST_FRAG;
- else
- element->flags = SBAL_FLAGS_MIDDLE_FRAG;
- ctx->num_elements++;
- element++;
- data_len -= page_remainder;
- ctx->offset += page_remainder;
- page = ctx->pages[ctx->offset >> PAGE_SHIFT];
- page_offset = 0;
- element->addr = page + page_offset;
- } else {
- qeth_eddp_copy_data_tcp(page + page_offset, eddp,
- data_len, &hcsum);
- element->length += data_len;
- if (!first_lap)
- element->flags = SBAL_FLAGS_LAST_FRAG;
- ctx->num_elements++;
- ctx->offset += data_len;
- data_len = 0;
- }
- first_lap = 0;
- }
- ((struct tcphdr *)eddp->th_in_ctx)->check = csum_fold(hcsum);
-}
-
-static __wsum
-qeth_eddp_check_tcp4_hdr(struct qeth_eddp_data *eddp, int data_len)
-{
- __wsum phcsum; /* pseudo header checksum */
-
- QETH_DBF_TEXT(trace, 5, "eddpckt4");
- eddp->th.tcp.h.check = 0;
- /* compute pseudo header checksum */
- phcsum = csum_tcpudp_nofold(eddp->nh.ip4.h.saddr, eddp->nh.ip4.h.daddr,
- eddp->thl + data_len, IPPROTO_TCP, 0);
- /* compute checksum of tcp header */
- return csum_partial((u8 *)&eddp->th, eddp->thl, phcsum);
-}
-
-static __wsum
-qeth_eddp_check_tcp6_hdr(struct qeth_eddp_data *eddp, int data_len)
-{
- __be32 proto;
- __wsum phcsum; /* pseudo header checksum */
-
- QETH_DBF_TEXT(trace, 5, "eddpckt6");
- eddp->th.tcp.h.check = 0;
- /* compute pseudo header checksum */
- phcsum = csum_partial((u8 *)&eddp->nh.ip6.h.saddr,
- sizeof(struct in6_addr), 0);
- phcsum = csum_partial((u8 *)&eddp->nh.ip6.h.daddr,
- sizeof(struct in6_addr), phcsum);
- proto = htonl(IPPROTO_TCP);
- phcsum = csum_partial((u8 *)&proto, sizeof(u32), phcsum);
- return phcsum;
-}
-
-static struct qeth_eddp_data *
-qeth_eddp_create_eddp_data(struct qeth_hdr *qh, u8 *nh, u8 nhl, u8 *th, u8 thl)
-{
- struct qeth_eddp_data *eddp;
-
- QETH_DBF_TEXT(trace, 5, "eddpcrda");
- eddp = kzalloc(sizeof(struct qeth_eddp_data), GFP_ATOMIC);
- if (eddp){
- eddp->nhl = nhl;
- eddp->thl = thl;
- memcpy(&eddp->qh, qh, sizeof(struct qeth_hdr));
- memcpy(&eddp->nh, nh, nhl);
- memcpy(&eddp->th, th, thl);
- eddp->frag = -1; /* initially we're in skb->data */
- }
- return eddp;
-}
-
-static void
-__qeth_eddp_fill_context_tcp(struct qeth_eddp_context *ctx,
- struct qeth_eddp_data *eddp)
-{
- struct tcphdr *tcph;
- int data_len;
- __wsum hcsum;
-
- QETH_DBF_TEXT(trace, 5, "eddpftcp");
- eddp->skb_offset = sizeof(struct qeth_hdr) + eddp->nhl + eddp->thl;
- if (eddp->qh.hdr.l2.id == QETH_HEADER_TYPE_LAYER2) {
- eddp->skb_offset += sizeof(struct ethhdr);
-#ifdef CONFIG_QETH_VLAN
- if (eddp->mac.h_proto == __constant_htons(ETH_P_8021Q))
- eddp->skb_offset += VLAN_HLEN;
-#endif /* CONFIG_QETH_VLAN */
- }
- tcph = tcp_hdr(eddp->skb);
- while (eddp->skb_offset < eddp->skb->len) {
- data_len = min((int)skb_shinfo(eddp->skb)->gso_size,
- (int)(eddp->skb->len - eddp->skb_offset));
- /* prepare qdio hdr */
- if (eddp->qh.hdr.l2.id == QETH_HEADER_TYPE_LAYER2){
- eddp->qh.hdr.l2.pkt_length = data_len + ETH_HLEN +
- eddp->nhl + eddp->thl;
-#ifdef CONFIG_QETH_VLAN
- if (eddp->mac.h_proto == __constant_htons(ETH_P_8021Q))
- eddp->qh.hdr.l2.pkt_length += VLAN_HLEN;
-#endif /* CONFIG_QETH_VLAN */
- } else
- eddp->qh.hdr.l3.length = data_len + eddp->nhl +
- eddp->thl;
- /* prepare ip hdr */
- if (eddp->skb->protocol == htons(ETH_P_IP)){
- eddp->nh.ip4.h.tot_len = htons(data_len + eddp->nhl +
- eddp->thl);
- eddp->nh.ip4.h.check = 0;
- eddp->nh.ip4.h.check =
- ip_fast_csum((u8 *)&eddp->nh.ip4.h,
- eddp->nh.ip4.h.ihl);
- } else
- eddp->nh.ip6.h.payload_len = htons(data_len + eddp->thl);
- /* prepare tcp hdr */
- if (data_len == (eddp->skb->len - eddp->skb_offset)){
- /* last segment -> set FIN and PSH flags */
- eddp->th.tcp.h.fin = tcph->fin;
- eddp->th.tcp.h.psh = tcph->psh;
- }
- if (eddp->skb->protocol == htons(ETH_P_IP))
- hcsum = qeth_eddp_check_tcp4_hdr(eddp, data_len);
- else
- hcsum = qeth_eddp_check_tcp6_hdr(eddp, data_len);
- /* fill the next segment into the context */
- qeth_eddp_create_segment_hdrs(ctx, eddp, data_len);
- qeth_eddp_create_segment_data_tcp(ctx, eddp, data_len, hcsum);
- if (eddp->skb_offset >= eddp->skb->len)
- break;
- /* prepare headers for next round */
- if (eddp->skb->protocol == htons(ETH_P_IP))
- eddp->nh.ip4.h.id = htons(ntohs(eddp->nh.ip4.h.id) + 1);
- eddp->th.tcp.h.seq = htonl(ntohl(eddp->th.tcp.h.seq) + data_len);
- }
-}
-
-static int
-qeth_eddp_fill_context_tcp(struct qeth_eddp_context *ctx,
- struct sk_buff *skb, struct qeth_hdr *qhdr)
-{
- struct qeth_eddp_data *eddp = NULL;
-
- QETH_DBF_TEXT(trace, 5, "eddpficx");
- /* create our segmentation headers and copy original headers */
- if (skb->protocol == htons(ETH_P_IP))
- eddp = qeth_eddp_create_eddp_data(qhdr,
- skb_network_header(skb),
- ip_hdrlen(skb),
- skb_transport_header(skb),
- tcp_hdrlen(skb));
- else
- eddp = qeth_eddp_create_eddp_data(qhdr,
- skb_network_header(skb),
- sizeof(struct ipv6hdr),
- skb_transport_header(skb),
- tcp_hdrlen(skb));
-
- if (eddp == NULL) {
- QETH_DBF_TEXT(trace, 2, "eddpfcnm");
- return -ENOMEM;
- }
- if (qhdr->hdr.l2.id == QETH_HEADER_TYPE_LAYER2) {
- skb_set_mac_header(skb, sizeof(struct qeth_hdr));
- memcpy(&eddp->mac, eth_hdr(skb), ETH_HLEN);
-#ifdef CONFIG_QETH_VLAN
- if (eddp->mac.h_proto == __constant_htons(ETH_P_8021Q)) {
- eddp->vlan[0] = skb->protocol;
- eddp->vlan[1] = htons(vlan_tx_tag_get(skb));
- }
-#endif /* CONFIG_QETH_VLAN */
- }
- /* the next flags will only be set on the last segment */
- eddp->th.tcp.h.fin = 0;
- eddp->th.tcp.h.psh = 0;
- eddp->skb = skb;
- /* begin segmentation and fill context */
- __qeth_eddp_fill_context_tcp(ctx, eddp);
- kfree(eddp);
- return 0;
-}
-
-static void
-qeth_eddp_calc_num_pages(struct qeth_eddp_context *ctx, struct sk_buff *skb,
- int hdr_len)
-{
- int skbs_per_page;
-
- QETH_DBF_TEXT(trace, 5, "eddpcanp");
- /* can we put multiple skbs in one page? */
- skbs_per_page = PAGE_SIZE / (skb_shinfo(skb)->gso_size + hdr_len);
- if (skbs_per_page > 1){
- ctx->num_pages = (skb_shinfo(skb)->gso_segs + 1) /
- skbs_per_page + 1;
- ctx->elements_per_skb = 1;
- } else {
- /* no -> how many elements per skb? */
- ctx->elements_per_skb = (skb_shinfo(skb)->gso_size + hdr_len +
- PAGE_SIZE) >> PAGE_SHIFT;
- ctx->num_pages = ctx->elements_per_skb *
- (skb_shinfo(skb)->gso_segs + 1);
- }
- ctx->num_elements = ctx->elements_per_skb *
- (skb_shinfo(skb)->gso_segs + 1);
-}
-
-static struct qeth_eddp_context *
-qeth_eddp_create_context_generic(struct qeth_card *card, struct sk_buff *skb,
- int hdr_len)
-{
- struct qeth_eddp_context *ctx = NULL;
- u8 *addr;
- int i;
-
- QETH_DBF_TEXT(trace, 5, "creddpcg");
- /* create the context and allocate pages */
- ctx = kzalloc(sizeof(struct qeth_eddp_context), GFP_ATOMIC);
- if (ctx == NULL){
- QETH_DBF_TEXT(trace, 2, "ceddpcn1");
- return NULL;
- }
- ctx->type = QETH_LARGE_SEND_EDDP;
- qeth_eddp_calc_num_pages(ctx, skb, hdr_len);
- if (ctx->elements_per_skb > QETH_MAX_BUFFER_ELEMENTS(card)){
- QETH_DBF_TEXT(trace, 2, "ceddpcis");
- kfree(ctx);
- return NULL;
- }
- ctx->pages = kcalloc(ctx->num_pages, sizeof(u8 *), GFP_ATOMIC);
- if (ctx->pages == NULL){
- QETH_DBF_TEXT(trace, 2, "ceddpcn2");
- kfree(ctx);
- return NULL;
- }
- for (i = 0; i < ctx->num_pages; ++i){
- addr = (u8 *)__get_free_page(GFP_ATOMIC);
- if (addr == NULL){
- QETH_DBF_TEXT(trace, 2, "ceddpcn3");
- ctx->num_pages = i;
- qeth_eddp_free_context(ctx);
- return NULL;
- }
- memset(addr, 0, PAGE_SIZE);
- ctx->pages[i] = addr;
- }
- ctx->elements = kcalloc(ctx->num_elements,
- sizeof(struct qeth_eddp_element), GFP_ATOMIC);
- if (ctx->elements == NULL){
- QETH_DBF_TEXT(trace, 2, "ceddpcn4");
- qeth_eddp_free_context(ctx);
- return NULL;
- }
- /* reset num_elements; will be incremented again in fill_buffer to
- * reflect number of actually used elements */
- ctx->num_elements = 0;
- return ctx;
-}
-
-static struct qeth_eddp_context *
-qeth_eddp_create_context_tcp(struct qeth_card *card, struct sk_buff *skb,
- struct qeth_hdr *qhdr)
-{
- struct qeth_eddp_context *ctx = NULL;
-
- QETH_DBF_TEXT(trace, 5, "creddpct");
- if (skb->protocol == htons(ETH_P_IP))
- ctx = qeth_eddp_create_context_generic(card, skb,
- (sizeof(struct qeth_hdr) +
- ip_hdrlen(skb) +
- tcp_hdrlen(skb)));
- else if (skb->protocol == htons(ETH_P_IPV6))
- ctx = qeth_eddp_create_context_generic(card, skb,
- sizeof(struct qeth_hdr) + sizeof(struct ipv6hdr) +
- tcp_hdrlen(skb));
- else
- QETH_DBF_TEXT(trace, 2, "cetcpinv");
-
- if (ctx == NULL) {
- QETH_DBF_TEXT(trace, 2, "creddpnl");
- return NULL;
- }
- if (qeth_eddp_fill_context_tcp(ctx, skb, qhdr)){
- QETH_DBF_TEXT(trace, 2, "ceddptfe");
- qeth_eddp_free_context(ctx);
- return NULL;
- }
- atomic_set(&ctx->refcnt, 1);
- return ctx;
-}
-
-struct qeth_eddp_context *
-qeth_eddp_create_context(struct qeth_card *card, struct sk_buff *skb,
- struct qeth_hdr *qhdr, unsigned char sk_protocol)
-{
- QETH_DBF_TEXT(trace, 5, "creddpc");
- switch (sk_protocol) {
- case IPPROTO_TCP:
- return qeth_eddp_create_context_tcp(card, skb, qhdr);
- default:
- QETH_DBF_TEXT(trace, 2, "eddpinvp");
- }
- return NULL;
-}
diff --git a/drivers/s390/net/qeth_eddp.h b/drivers/s390/net/qeth_eddp.h
deleted file mode 100644
index 52910c9252c..00000000000
--- a/drivers/s390/net/qeth_eddp.h
+++ /dev/null
@@ -1,84 +0,0 @@
-/*
- * linux/drivers/s390/net/qeth_eddp.h
- *
- * Header file for qeth enhanced device driver packing.
- *
- * Copyright 2004 IBM Corporation
- *
- * Author(s): Thomas Spatzier <tspat@de.ibm.com>
- *
- */
-#ifndef __QETH_EDDP_H__
-#define __QETH_EDDP_H__
-
-struct qeth_eddp_element {
- u32 flags;
- u32 length;
- void *addr;
-};
-
-struct qeth_eddp_context {
- atomic_t refcnt;
- enum qeth_large_send_types type;
- int num_pages; /* # of allocated pages */
- u8 **pages; /* pointers to pages */
- int offset; /* offset in ctx during creation */
- int num_elements; /* # of required 'SBALEs' */
- struct qeth_eddp_element *elements; /* array of 'SBALEs' */
- int elements_per_skb; /* # of 'SBALEs' per skb **/
-};
-
-struct qeth_eddp_context_reference {
- struct list_head list;
- struct qeth_eddp_context *ctx;
-};
-
-extern struct qeth_eddp_context *
-qeth_eddp_create_context(struct qeth_card *,struct sk_buff *,
- struct qeth_hdr *, unsigned char);
-
-extern void
-qeth_eddp_put_context(struct qeth_eddp_context *);
-
-extern int
-qeth_eddp_fill_buffer(struct qeth_qdio_out_q *,struct qeth_eddp_context *,int);
-
-extern void
-qeth_eddp_buf_release_contexts(struct qeth_qdio_out_buffer *);
-
-extern int
-qeth_eddp_check_buffers_for_context(struct qeth_qdio_out_q *,
- struct qeth_eddp_context *);
-/*
- * Data used for fragmenting a IP packet.
- */
-struct qeth_eddp_data {
- struct qeth_hdr qh;
- struct ethhdr mac;
- __be16 vlan[2];
- union {
- struct {
- struct iphdr h;
- u8 options[40];
- } ip4;
- struct {
- struct ipv6hdr h;
- } ip6;
- } nh;
- u8 nhl;
- void *nh_in_ctx; /* address of nh within the ctx */
- union {
- struct {
- struct tcphdr h;
- u8 options[40];
- } tcp;
- } th;
- u8 thl;
- void *th_in_ctx; /* address of th within the ctx */
- struct sk_buff *skb;
- int skb_offset;
- int frag;
- int frag_offset;
-} __attribute__ ((packed));
-
-#endif /* __QETH_EDDP_H__ */
diff --git a/drivers/s390/net/qeth_fs.h b/drivers/s390/net/qeth_fs.h
deleted file mode 100644
index 61faf05517d..00000000000
--- a/drivers/s390/net/qeth_fs.h
+++ /dev/null
@@ -1,168 +0,0 @@
-/*
- * 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 <tspat@de.ibm.com>
- *
- */
-#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_device_attributes_osn(struct device *dev);
-
-extern void
-qeth_remove_device_attributes_osn(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";
- case QETH_CARD_TYPE_OSN:
- return " OSN QDIO";
- 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_10GBIT_ETH:
- return "OSD_10GIG";
- 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";
- case QETH_CARD_TYPE_OSN:
- return "OSN";
- default:
- return "unknown";
- }
- }
- return "n/a";
-}
-
-#endif /* __QETH_FS_H__ */
diff --git a/drivers/s390/net/qeth_l2.h b/drivers/s390/net/qeth_l2.h
new file mode 100644
index 00000000000..0767556404b
--- /dev/null
+++ b/drivers/s390/net/qeth_l2.h
@@ -0,0 +1,15 @@
+/*
+ * Copyright IBM Corp. 2013
+ * Author(s): Eugene Crosser <eugene.crosser@ru.ibm.com>
+ */
+
+#ifndef __QETH_L2_H__
+#define __QETH_L2_H__
+
+#include "qeth_core.h"
+
+int qeth_l2_create_device_attributes(struct device *);
+void qeth_l2_remove_device_attributes(struct device *);
+void qeth_l2_setup_bridgeport_attrs(struct qeth_card *card);
+
+#endif /* __QETH_L2_H__ */
diff --git a/drivers/s390/net/qeth_l2_main.c b/drivers/s390/net/qeth_l2_main.c
new file mode 100644
index 00000000000..5ef5b4f4575
--- /dev/null
+++ b/drivers/s390/net/qeth_l2_main.c
@@ -0,0 +1,1984 @@
+/*
+ * Copyright IBM Corp. 2007, 2009
+ * Author(s): Utz Bacher <utz.bacher@de.ibm.com>,
+ * Frank Pavlic <fpavlic@de.ibm.com>,
+ * Thomas Spatzier <tspat@de.ibm.com>,
+ * Frank Blaschka <frank.blaschka@de.ibm.com>
+ */
+
+#define KMSG_COMPONENT "qeth"
+#define pr_fmt(fmt) KMSG_COMPONENT ": " fmt
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/string.h>
+#include <linux/errno.h>
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/etherdevice.h>
+#include <linux/mii.h>
+#include <linux/ip.h>
+#include <linux/list.h>
+
+#include "qeth_core.h"
+#include "qeth_l2.h"
+
+static int qeth_l2_set_offline(struct ccwgroup_device *);
+static int qeth_l2_stop(struct net_device *);
+static int qeth_l2_send_delmac(struct qeth_card *, __u8 *);
+static int qeth_l2_send_setdelmac(struct qeth_card *, __u8 *,
+ enum qeth_ipa_cmds,
+ int (*reply_cb) (struct qeth_card *,
+ struct qeth_reply*,
+ unsigned long));
+static void qeth_l2_set_multicast_list(struct net_device *);
+static int qeth_l2_recover(void *);
+static void qeth_bridgeport_query_support(struct qeth_card *card);
+static void qeth_bridge_state_change(struct qeth_card *card,
+ struct qeth_ipa_cmd *cmd);
+static void qeth_bridge_host_event(struct qeth_card *card,
+ struct qeth_ipa_cmd *cmd);
+
+static int qeth_l2_do_ioctl(struct net_device *dev, struct ifreq *rq, int cmd)
+{
+ struct qeth_card *card = dev->ml_priv;
+ struct mii_ioctl_data *mii_data;
+ int rc = 0;
+
+ if (!card)
+ return -ENODEV;
+
+ if ((card->state != CARD_STATE_UP) &&
+ (card->state != CARD_STATE_SOFTSETUP))
+ return -ENODEV;
+
+ if (card->info.type == QETH_CARD_TYPE_OSN)
+ return -EPERM;
+
+ switch (cmd) {
+ case SIOC_QETH_ADP_SET_SNMP_CONTROL:
+ rc = qeth_snmp_command(card, rq->ifr_ifru.ifru_data);
+ break;
+ case SIOC_QETH_GET_CARD_TYPE:
+ if ((card->info.type == QETH_CARD_TYPE_OSD ||
+ card->info.type == QETH_CARD_TYPE_OSM ||
+ card->info.type == QETH_CARD_TYPE_OSX) &&
+ !card->info.guestlan)
+ return 1;
+ return 0;
+ break;
+ case SIOCGMIIPHY:
+ mii_data = if_mii(rq);
+ mii_data->phy_id = 0;
+ break;
+ case SIOCGMIIREG:
+ mii_data = if_mii(rq);
+ 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 SIOC_QETH_QUERY_OAT:
+ rc = qeth_query_oat_command(card, rq->ifr_ifru.ifru_data);
+ break;
+ default:
+ rc = -EOPNOTSUPP;
+ }
+ if (rc)
+ QETH_CARD_TEXT_(card, 2, "ioce%d", rc);
+ return rc;
+}
+
+static int qeth_l2_verify_dev(struct net_device *dev)
+{
+ struct qeth_card *card;
+ unsigned long flags;
+ int rc = 0;
+
+ read_lock_irqsave(&qeth_core_card_list.rwlock, flags);
+ list_for_each_entry(card, &qeth_core_card_list.list, list) {
+ if (card->dev == dev) {
+ rc = QETH_REAL_CARD;
+ break;
+ }
+ }
+ read_unlock_irqrestore(&qeth_core_card_list.rwlock, flags);
+
+ return rc;
+}
+
+static struct net_device *qeth_l2_netdev_by_devno(unsigned char *read_dev_no)
+{
+ struct qeth_card *card;
+ struct net_device *ndev;
+ __u16 temp_dev_no;
+ unsigned long flags;
+ struct ccw_dev_id read_devid;
+
+ ndev = NULL;
+ memcpy(&temp_dev_no, read_dev_no, 2);
+ read_lock_irqsave(&qeth_core_card_list.rwlock, flags);
+ list_for_each_entry(card, &qeth_core_card_list.list, list) {
+ ccw_device_get_id(CARD_RDEV(card), &read_devid);
+ if (read_devid.devno == temp_dev_no) {
+ ndev = card->dev;
+ break;
+ }
+ }
+ read_unlock_irqrestore(&qeth_core_card_list.rwlock, flags);
+ return ndev;
+}
+
+static int qeth_l2_send_setgroupmac_cb(struct qeth_card *card,
+ struct qeth_reply *reply,
+ unsigned long data)
+{
+ struct qeth_ipa_cmd *cmd;
+ __u8 *mac;
+
+ QETH_CARD_TEXT(card, 2, "L2Sgmacb");
+ cmd = (struct qeth_ipa_cmd *) data;
+ mac = &cmd->data.setdelmac.mac[0];
+ /* MAC already registered, needed in couple/uncouple case */
+ if (cmd->hdr.return_code == IPA_RC_L2_DUP_MAC) {
+ QETH_DBF_MESSAGE(2, "Group MAC %pM already existing on %s \n",
+ mac, QETH_CARD_IFNAME(card));
+ cmd->hdr.return_code = 0;
+ }
+ if (cmd->hdr.return_code)
+ QETH_DBF_MESSAGE(2, "Could not set group MAC %pM on %s: %x\n",
+ mac, QETH_CARD_IFNAME(card), cmd->hdr.return_code);
+ return 0;
+}
+
+static int qeth_l2_send_setgroupmac(struct qeth_card *card, __u8 *mac)
+{
+ QETH_CARD_TEXT(card, 2, "L2Sgmac");
+ return qeth_l2_send_setdelmac(card, mac, IPA_CMD_SETGMAC,
+ qeth_l2_send_setgroupmac_cb);
+}
+
+static int qeth_l2_send_delgroupmac_cb(struct qeth_card *card,
+ struct qeth_reply *reply,
+ unsigned long data)
+{
+ struct qeth_ipa_cmd *cmd;
+ __u8 *mac;
+
+ QETH_CARD_TEXT(card, 2, "L2Dgmacb");
+ cmd = (struct qeth_ipa_cmd *) data;
+ mac = &cmd->data.setdelmac.mac[0];
+ if (cmd->hdr.return_code)
+ QETH_DBF_MESSAGE(2, "Could not delete group MAC %pM on %s: %x\n",
+ mac, QETH_CARD_IFNAME(card), cmd->hdr.return_code);
+ return 0;
+}
+
+static int qeth_l2_send_delgroupmac(struct qeth_card *card, __u8 *mac)
+{
+ QETH_CARD_TEXT(card, 2, "L2Dgmac");
+ return qeth_l2_send_setdelmac(card, mac, IPA_CMD_DELGMAC,
+ qeth_l2_send_delgroupmac_cb);
+}
+
+static void qeth_l2_add_mc(struct qeth_card *card, __u8 *mac, int vmac)
+{
+ struct qeth_mc_mac *mc;
+ int rc;
+
+ mc = kmalloc(sizeof(struct qeth_mc_mac), GFP_ATOMIC);
+
+ if (!mc)
+ return;
+
+ memcpy(mc->mc_addr, mac, OSA_ADDR_LEN);
+ mc->mc_addrlen = OSA_ADDR_LEN;
+ mc->is_vmac = vmac;
+
+ if (vmac) {
+ rc = qeth_l2_send_setdelmac(card, mac, IPA_CMD_SETVMAC,
+ NULL);
+ } else {
+ rc = qeth_l2_send_setgroupmac(card, mac);
+ }
+
+ if (!rc)
+ list_add_tail(&mc->list, &card->mc_list);
+ else
+ kfree(mc);
+}
+
+static void qeth_l2_del_all_mc(struct qeth_card *card, int del)
+{
+ struct qeth_mc_mac *mc, *tmp;
+
+ spin_lock_bh(&card->mclock);
+ list_for_each_entry_safe(mc, tmp, &card->mc_list, list) {
+ if (del) {
+ if (mc->is_vmac)
+ qeth_l2_send_setdelmac(card, mc->mc_addr,
+ IPA_CMD_DELVMAC, NULL);
+ else
+ qeth_l2_send_delgroupmac(card, mc->mc_addr);
+ }
+ list_del(&mc->list);
+ kfree(mc);
+ }
+ spin_unlock_bh(&card->mclock);
+}
+
+static inline int qeth_l2_get_cast_type(struct qeth_card *card,
+ struct sk_buff *skb)
+{
+ if (card->info.type == QETH_CARD_TYPE_OSN)
+ return RTN_UNSPEC;
+ if (is_broadcast_ether_addr(skb->data))
+ return RTN_BROADCAST;
+ if (is_multicast_ether_addr(skb->data))
+ return RTN_MULTICAST;
+ return RTN_UNSPEC;
+}
+
+static void qeth_l2_fill_header(struct qeth_card *card, struct qeth_hdr *hdr,
+ struct sk_buff *skb, int cast_type)
+{
+ struct vlan_ethhdr *veth = (struct vlan_ethhdr *)skb_mac_header(skb);
+
+ memset(hdr, 0, sizeof(struct qeth_hdr));
+ hdr->hdr.l2.id = QETH_HEADER_TYPE_LAYER2;
+
+ /* set byte byte 3 to casting flags */
+ if (cast_type == RTN_MULTICAST)
+ hdr->hdr.l2.flags[2] |= QETH_LAYER2_FLAG_MULTICAST;
+ else if (cast_type == RTN_BROADCAST)
+ hdr->hdr.l2.flags[2] |= QETH_LAYER2_FLAG_BROADCAST;
+ else
+ hdr->hdr.l2.flags[2] |= QETH_LAYER2_FLAG_UNICAST;
+
+ hdr->hdr.l2.pkt_length = skb->len-QETH_HEADER_SIZE;
+ /* VSWITCH relies on the VLAN
+ * information to be present in
+ * the QDIO header */
+ if (veth->h_vlan_proto == __constant_htons(ETH_P_8021Q)) {
+ hdr->hdr.l2.flags[2] |= QETH_LAYER2_FLAG_VLAN;
+ hdr->hdr.l2.vlan_id = ntohs(veth->h_vlan_TCI);
+ }
+}
+
+static int qeth_l2_send_setdelvlan_cb(struct qeth_card *card,
+ struct qeth_reply *reply, unsigned long data)
+{
+ struct qeth_ipa_cmd *cmd;
+
+ QETH_CARD_TEXT(card, 2, "L2sdvcb");
+ cmd = (struct qeth_ipa_cmd *) data;
+ if (cmd->hdr.return_code) {
+ QETH_DBF_MESSAGE(2, "Error in processing VLAN %i on %s: 0x%x. "
+ "Continuing\n", cmd->data.setdelvlan.vlan_id,
+ QETH_CARD_IFNAME(card), cmd->hdr.return_code);
+ QETH_CARD_TEXT_(card, 2, "L2VL%4x", cmd->hdr.command);
+ QETH_CARD_TEXT_(card, 2, "err%d", cmd->hdr.return_code);
+ }
+ return 0;
+}
+
+static int qeth_l2_send_setdelvlan(struct qeth_card *card, __u16 i,
+ enum qeth_ipa_cmds ipacmd)
+{
+ struct qeth_ipa_cmd *cmd;
+ struct qeth_cmd_buffer *iob;
+
+ QETH_CARD_TEXT_(card, 4, "L2sdv%x", ipacmd);
+ iob = qeth_get_ipacmd_buffer(card, ipacmd, QETH_PROT_IPV4);
+ cmd = (struct qeth_ipa_cmd *)(iob->data+IPA_PDU_HEADER_SIZE);
+ cmd->data.setdelvlan.vlan_id = i;
+ return qeth_send_ipa_cmd(card, iob,
+ qeth_l2_send_setdelvlan_cb, NULL);
+}
+
+static void qeth_l2_process_vlans(struct qeth_card *card)
+{
+ struct qeth_vlan_vid *id;
+ QETH_CARD_TEXT(card, 3, "L2prcvln");
+ spin_lock_bh(&card->vlanlock);
+ list_for_each_entry(id, &card->vid_list, list) {
+ qeth_l2_send_setdelvlan(card, id->vid, IPA_CMD_SETVLAN);
+ }
+ spin_unlock_bh(&card->vlanlock);
+}
+
+static int qeth_l2_vlan_rx_add_vid(struct net_device *dev,
+ __be16 proto, u16 vid)
+{
+ struct qeth_card *card = dev->ml_priv;
+ struct qeth_vlan_vid *id;
+
+ QETH_CARD_TEXT_(card, 4, "aid:%d", vid);
+ if (!vid)
+ return 0;
+ if (card->info.type == QETH_CARD_TYPE_OSM) {
+ QETH_CARD_TEXT(card, 3, "aidOSM");
+ return 0;
+ }
+ if (qeth_wait_for_threads(card, QETH_RECOVER_THREAD)) {
+ QETH_CARD_TEXT(card, 3, "aidREC");
+ return 0;
+ }
+ id = kmalloc(sizeof(struct qeth_vlan_vid), GFP_ATOMIC);
+ if (id) {
+ id->vid = vid;
+ qeth_l2_send_setdelvlan(card, vid, IPA_CMD_SETVLAN);
+ spin_lock_bh(&card->vlanlock);
+ list_add_tail(&id->list, &card->vid_list);
+ spin_unlock_bh(&card->vlanlock);
+ } else {
+ return -ENOMEM;
+ }
+ return 0;
+}
+
+static int qeth_l2_vlan_rx_kill_vid(struct net_device *dev,
+ __be16 proto, u16 vid)
+{
+ struct qeth_vlan_vid *id, *tmpid = NULL;
+ struct qeth_card *card = dev->ml_priv;
+
+ QETH_CARD_TEXT_(card, 4, "kid:%d", vid);
+ if (card->info.type == QETH_CARD_TYPE_OSM) {
+ QETH_CARD_TEXT(card, 3, "kidOSM");
+ return 0;
+ }
+ if (qeth_wait_for_threads(card, QETH_RECOVER_THREAD)) {
+ QETH_CARD_TEXT(card, 3, "kidREC");
+ return 0;
+ }
+ spin_lock_bh(&card->vlanlock);
+ list_for_each_entry(id, &card->vid_list, list) {
+ if (id->vid == vid) {
+ list_del(&id->list);
+ tmpid = id;
+ break;
+ }
+ }
+ spin_unlock_bh(&card->vlanlock);
+ if (tmpid) {
+ qeth_l2_send_setdelvlan(card, vid, IPA_CMD_DELVLAN);
+ kfree(tmpid);
+ }
+ qeth_l2_set_multicast_list(card->dev);
+ return 0;
+}
+
+static int qeth_l2_stop_card(struct qeth_card *card, int recovery_mode)
+{
+ 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 (card->read.state == CH_STATE_UP &&
+ card->write.state == CH_STATE_UP &&
+ (card->state == CARD_STATE_UP)) {
+ if (recovery_mode &&
+ card->info.type != QETH_CARD_TYPE_OSN) {
+ qeth_l2_stop(card->dev);
+ } else {
+ rtnl_lock();
+ dev_close(card->dev);
+ rtnl_unlock();
+ }
+ card->info.mac_bits &= ~QETH_LAYER2_MAC_REGISTERED;
+ card->state = CARD_STATE_SOFTSETUP;
+ }
+ if (card->state == CARD_STATE_SOFTSETUP) {
+ qeth_l2_del_all_mc(card, 0);
+ qeth_clear_ipacmd_list(card);
+ card->state = CARD_STATE_HARDSETUP;
+ }
+ if (card->state == CARD_STATE_HARDSETUP) {
+ 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);
+ }
+ return rc;
+}
+
+static int qeth_l2_process_inbound_buffer(struct qeth_card *card,
+ int budget, int *done)
+{
+ int work_done = 0;
+ struct sk_buff *skb;
+ struct qeth_hdr *hdr;
+ unsigned int len;
+
+ *done = 0;
+ WARN_ON_ONCE(!budget);
+ while (budget) {
+ skb = qeth_core_get_next_skb(card,
+ &card->qdio.in_q->bufs[card->rx.b_index],
+ &card->rx.b_element, &card->rx.e_offset, &hdr);
+ if (!skb) {
+ *done = 1;
+ break;
+ }
+ skb->dev = card->dev;
+ switch (hdr->hdr.l2.id) {
+ case QETH_HEADER_TYPE_LAYER2:
+ skb->pkt_type = PACKET_HOST;
+ skb->protocol = eth_type_trans(skb, skb->dev);
+ skb->ip_summed = CHECKSUM_NONE;
+ if (skb->protocol == htons(ETH_P_802_2))
+ *((__u32 *)skb->cb) = ++card->seqno.pkt_seqno;
+ len = skb->len;
+ netif_receive_skb(skb);
+ break;
+ case QETH_HEADER_TYPE_OSN:
+ if (card->info.type == QETH_CARD_TYPE_OSN) {
+ skb_push(skb, sizeof(struct qeth_hdr));
+ skb_copy_to_linear_data(skb, hdr,
+ sizeof(struct qeth_hdr));
+ len = skb->len;
+ card->osn_info.data_cb(skb);
+ break;
+ }
+ /* else unknown */
+ default:
+ dev_kfree_skb_any(skb);
+ QETH_CARD_TEXT(card, 3, "inbunkno");
+ QETH_DBF_HEX(CTRL, 3, hdr, QETH_DBF_CTRL_LEN);
+ continue;
+ }
+ work_done++;
+ budget--;
+ card->stats.rx_packets++;
+ card->stats.rx_bytes += len;
+ }
+ return work_done;
+}
+
+static int qeth_l2_poll(struct napi_struct *napi, int budget)
+{
+ struct qeth_card *card = container_of(napi, struct qeth_card, napi);
+ int work_done = 0;
+ struct qeth_qdio_buffer *buffer;
+ int done;
+ int new_budget = budget;
+
+ if (card->options.performance_stats) {
+ card->perf_stats.inbound_cnt++;
+ card->perf_stats.inbound_start_time = qeth_get_micros();
+ }
+
+ while (1) {
+ if (!card->rx.b_count) {
+ card->rx.qdio_err = 0;
+ card->rx.b_count = qdio_get_next_buffers(
+ card->data.ccwdev, 0, &card->rx.b_index,
+ &card->rx.qdio_err);
+ if (card->rx.b_count <= 0) {
+ card->rx.b_count = 0;
+ break;
+ }
+ card->rx.b_element =
+ &card->qdio.in_q->bufs[card->rx.b_index]
+ .buffer->element[0];
+ card->rx.e_offset = 0;
+ }
+
+ while (card->rx.b_count) {
+ buffer = &card->qdio.in_q->bufs[card->rx.b_index];
+ if (!(card->rx.qdio_err &&
+ qeth_check_qdio_errors(card, buffer->buffer,
+ card->rx.qdio_err, "qinerr")))
+ work_done += qeth_l2_process_inbound_buffer(
+ card, new_budget, &done);
+ else
+ done = 1;
+
+ if (done) {
+ if (card->options.performance_stats)
+ card->perf_stats.bufs_rec++;
+ qeth_put_buffer_pool_entry(card,
+ buffer->pool_entry);
+ qeth_queue_input_buffer(card, card->rx.b_index);
+ card->rx.b_count--;
+ if (card->rx.b_count) {
+ card->rx.b_index =
+ (card->rx.b_index + 1) %
+ QDIO_MAX_BUFFERS_PER_Q;
+ card->rx.b_element =
+ &card->qdio.in_q
+ ->bufs[card->rx.b_index]
+ .buffer->element[0];
+ card->rx.e_offset = 0;
+ }
+ }
+
+ if (work_done >= budget)
+ goto out;
+ else
+ new_budget = budget - work_done;
+ }
+ }
+
+ napi_complete(napi);
+ if (qdio_start_irq(card->data.ccwdev, 0))
+ napi_schedule(&card->napi);
+out:
+ if (card->options.performance_stats)
+ card->perf_stats.inbound_time += qeth_get_micros() -
+ card->perf_stats.inbound_start_time;
+ return work_done;
+}
+
+static int qeth_l2_send_setdelmac(struct qeth_card *card, __u8 *mac,
+ enum qeth_ipa_cmds ipacmd,
+ int (*reply_cb) (struct qeth_card *,
+ struct qeth_reply*,
+ unsigned long))
+{
+ struct qeth_ipa_cmd *cmd;
+ struct qeth_cmd_buffer *iob;
+
+ QETH_CARD_TEXT(card, 2, "L2sdmac");
+ iob = qeth_get_ipacmd_buffer(card, ipacmd, QETH_PROT_IPV4);
+ cmd = (struct qeth_ipa_cmd *)(iob->data+IPA_PDU_HEADER_SIZE);
+ cmd->data.setdelmac.mac_length = OSA_ADDR_LEN;
+ memcpy(&cmd->data.setdelmac.mac, mac, OSA_ADDR_LEN);
+ return qeth_send_ipa_cmd(card, iob, reply_cb, NULL);
+}
+
+static int qeth_l2_send_setmac_cb(struct qeth_card *card,
+ struct qeth_reply *reply,
+ unsigned long data)
+{
+ struct qeth_ipa_cmd *cmd;
+
+ QETH_CARD_TEXT(card, 2, "L2Smaccb");
+ cmd = (struct qeth_ipa_cmd *) data;
+ if (cmd->hdr.return_code) {
+ QETH_CARD_TEXT_(card, 2, "L2er%x", cmd->hdr.return_code);
+ card->info.mac_bits &= ~QETH_LAYER2_MAC_REGISTERED;
+ switch (cmd->hdr.return_code) {
+ case IPA_RC_L2_DUP_MAC:
+ case IPA_RC_L2_DUP_LAYER3_MAC:
+ dev_warn(&card->gdev->dev,
+ "MAC address %pM already exists\n",
+ cmd->data.setdelmac.mac);
+ break;
+ case IPA_RC_L2_MAC_NOT_AUTH_BY_HYP:
+ case IPA_RC_L2_MAC_NOT_AUTH_BY_ADP:
+ dev_warn(&card->gdev->dev,
+ "MAC address %pM is not authorized\n",
+ cmd->data.setdelmac.mac);
+ break;
+ default:
+ break;
+ }
+ } else {
+ card->info.mac_bits |= QETH_LAYER2_MAC_REGISTERED;
+ memcpy(card->dev->dev_addr, cmd->data.setdelmac.mac,
+ OSA_ADDR_LEN);
+ dev_info(&card->gdev->dev,
+ "MAC address %pM successfully registered on device %s\n",
+ card->dev->dev_addr, card->dev->name);
+ }
+ return 0;
+}
+
+static int qeth_l2_send_setmac(struct qeth_card *card, __u8 *mac)
+{
+ QETH_CARD_TEXT(card, 2, "L2Setmac");
+ return qeth_l2_send_setdelmac(card, mac, IPA_CMD_SETVMAC,
+ qeth_l2_send_setmac_cb);
+}
+
+static int qeth_l2_send_delmac_cb(struct qeth_card *card,
+ struct qeth_reply *reply,
+ unsigned long data)
+{
+ struct qeth_ipa_cmd *cmd;
+
+ QETH_CARD_TEXT(card, 2, "L2Dmaccb");
+ cmd = (struct qeth_ipa_cmd *) data;
+ if (cmd->hdr.return_code) {
+ QETH_CARD_TEXT_(card, 2, "err%d", cmd->hdr.return_code);
+ return 0;
+ }
+ card->info.mac_bits &= ~QETH_LAYER2_MAC_REGISTERED;
+
+ return 0;
+}
+
+static int qeth_l2_send_delmac(struct qeth_card *card, __u8 *mac)
+{
+ QETH_CARD_TEXT(card, 2, "L2Delmac");
+ if (!(card->info.mac_bits & QETH_LAYER2_MAC_REGISTERED))
+ return 0;
+ return qeth_l2_send_setdelmac(card, mac, IPA_CMD_DELVMAC,
+ qeth_l2_send_delmac_cb);
+}
+
+static int qeth_l2_request_initial_mac(struct qeth_card *card)
+{
+ int rc = 0;
+ char vendor_pre[] = {0x02, 0x00, 0x00};
+
+ QETH_DBF_TEXT(SETUP, 2, "doL2init");
+ QETH_DBF_TEXT_(SETUP, 2, "doL2%s", CARD_BUS_ID(card));
+
+ if (qeth_is_supported(card, IPA_SETADAPTERPARMS)) {
+ rc = qeth_query_setadapterparms(card);
+ if (rc) {
+ QETH_DBF_MESSAGE(2, "could not query adapter "
+ "parameters on device %s: x%x\n",
+ CARD_BUS_ID(card), rc);
+ }
+ }
+
+ if (card->info.type == QETH_CARD_TYPE_IQD ||
+ card->info.type == QETH_CARD_TYPE_OSM ||
+ card->info.type == QETH_CARD_TYPE_OSX ||
+ card->info.guestlan) {
+ rc = qeth_setadpparms_change_macaddr(card);
+ if (rc) {
+ QETH_DBF_MESSAGE(2, "couldn't get MAC address on "
+ "device %s: x%x\n", CARD_BUS_ID(card), rc);
+ QETH_DBF_TEXT_(SETUP, 2, "1err%d", rc);
+ return rc;
+ }
+ QETH_DBF_HEX(SETUP, 2, card->dev->dev_addr, OSA_ADDR_LEN);
+ } else {
+ eth_random_addr(card->dev->dev_addr);
+ memcpy(card->dev->dev_addr, vendor_pre, 3);
+ }
+ return 0;
+}
+
+static int qeth_l2_set_mac_address(struct net_device *dev, void *p)
+{
+ struct sockaddr *addr = p;
+ struct qeth_card *card = dev->ml_priv;
+ int rc = 0;
+
+ QETH_CARD_TEXT(card, 3, "setmac");
+
+ if (qeth_l2_verify_dev(dev) != QETH_REAL_CARD) {
+ QETH_CARD_TEXT(card, 3, "setmcINV");
+ return -EOPNOTSUPP;
+ }
+
+ if (card->info.type == QETH_CARD_TYPE_OSN ||
+ card->info.type == QETH_CARD_TYPE_OSM ||
+ card->info.type == QETH_CARD_TYPE_OSX) {
+ QETH_CARD_TEXT(card, 3, "setmcTYP");
+ return -EOPNOTSUPP;
+ }
+ QETH_CARD_HEX(card, 3, addr->sa_data, OSA_ADDR_LEN);
+ if (qeth_wait_for_threads(card, QETH_RECOVER_THREAD)) {
+ QETH_CARD_TEXT(card, 3, "setmcREC");
+ return -ERESTARTSYS;
+ }
+ rc = qeth_l2_send_delmac(card, &card->dev->dev_addr[0]);
+ if (!rc || (rc == IPA_RC_L2_MAC_NOT_FOUND))
+ rc = qeth_l2_send_setmac(card, addr->sa_data);
+ return rc ? -EINVAL : 0;
+}
+
+static void qeth_l2_set_multicast_list(struct net_device *dev)
+{
+ struct qeth_card *card = dev->ml_priv;
+ struct netdev_hw_addr *ha;
+
+ if (card->info.type == QETH_CARD_TYPE_OSN)
+ return ;
+
+ QETH_CARD_TEXT(card, 3, "setmulti");
+ if (qeth_threads_running(card, QETH_RECOVER_THREAD) &&
+ (card->state != CARD_STATE_UP))
+ return;
+ qeth_l2_del_all_mc(card, 1);
+ spin_lock_bh(&card->mclock);
+ netdev_for_each_mc_addr(ha, dev)
+ qeth_l2_add_mc(card, ha->addr, 0);
+
+ netdev_for_each_uc_addr(ha, dev)
+ qeth_l2_add_mc(card, ha->addr, 1);
+
+ spin_unlock_bh(&card->mclock);
+ if (!qeth_adp_supported(card, IPA_SETADP_SET_PROMISC_MODE))
+ return;
+ qeth_setadp_promisc_mode(card);
+}
+
+static int qeth_l2_hard_start_xmit(struct sk_buff *skb, struct net_device *dev)
+{
+ int rc;
+ struct qeth_hdr *hdr = NULL;
+ int elements = 0;
+ struct qeth_card *card = dev->ml_priv;
+ struct sk_buff *new_skb = skb;
+ int cast_type = qeth_l2_get_cast_type(card, skb);
+ struct qeth_qdio_out_q *queue;
+ int tx_bytes = skb->len;
+ int data_offset = -1;
+ int elements_needed = 0;
+ int hd_len = 0;
+
+ if (card->qdio.do_prio_queueing || (cast_type &&
+ card->info.is_multicast_different))
+ queue = card->qdio.out_qs[qeth_get_priority_queue(card, skb,
+ qeth_get_ip_version(skb), cast_type)];
+ else
+ queue = card->qdio.out_qs[card->qdio.default_out_queue];
+
+ if ((card->state != CARD_STATE_UP) || !card->lan_online) {
+ card->stats.tx_carrier_errors++;
+ goto tx_drop;
+ }
+
+ if ((card->info.type == QETH_CARD_TYPE_OSN) &&
+ (skb->protocol == htons(ETH_P_IPV6)))
+ goto tx_drop;
+
+ if (card->options.performance_stats) {
+ card->perf_stats.outbound_cnt++;
+ card->perf_stats.outbound_start_time = qeth_get_micros();
+ }
+ netif_stop_queue(dev);
+
+ if (card->info.type == QETH_CARD_TYPE_OSN)
+ hdr = (struct qeth_hdr *)skb->data;
+ else {
+ if (card->info.type == QETH_CARD_TYPE_IQD) {
+ new_skb = skb;
+ data_offset = ETH_HLEN;
+ hd_len = ETH_HLEN;
+ hdr = kmem_cache_alloc(qeth_core_header_cache,
+ GFP_ATOMIC);
+ if (!hdr)
+ goto tx_drop;
+ elements_needed++;
+ skb_reset_mac_header(new_skb);
+ qeth_l2_fill_header(card, hdr, new_skb, cast_type);
+ hdr->hdr.l2.pkt_length = new_skb->len;
+ memcpy(((char *)hdr) + sizeof(struct qeth_hdr),
+ skb_mac_header(new_skb), ETH_HLEN);
+ } else {
+ /* create a clone with writeable headroom */
+ new_skb = skb_realloc_headroom(skb,
+ sizeof(struct qeth_hdr));
+ if (!new_skb)
+ goto tx_drop;
+ hdr = (struct qeth_hdr *)skb_push(new_skb,
+ sizeof(struct qeth_hdr));
+ skb_set_mac_header(new_skb, sizeof(struct qeth_hdr));
+ qeth_l2_fill_header(card, hdr, new_skb, cast_type);
+ }
+ }
+
+ elements = qeth_get_elements_no(card, new_skb, elements_needed);
+ if (!elements) {
+ if (data_offset >= 0)
+ kmem_cache_free(qeth_core_header_cache, hdr);
+ goto tx_drop;
+ }
+
+ if (card->info.type != QETH_CARD_TYPE_IQD) {
+ if (qeth_hdr_chk_and_bounce(new_skb, &hdr,
+ sizeof(struct qeth_hdr_layer2)))
+ goto tx_drop;
+ rc = qeth_do_send_packet(card, queue, new_skb, hdr,
+ elements);
+ } else
+ rc = qeth_do_send_packet_fast(card, queue, new_skb, hdr,
+ elements, data_offset, hd_len);
+ if (!rc) {
+ card->stats.tx_packets++;
+ card->stats.tx_bytes += tx_bytes;
+ if (new_skb != skb)
+ dev_kfree_skb_any(skb);
+ rc = NETDEV_TX_OK;
+ } else {
+ if (data_offset >= 0)
+ kmem_cache_free(qeth_core_header_cache, hdr);
+
+ if (rc == -EBUSY) {
+ if (new_skb != skb)
+ dev_kfree_skb_any(new_skb);
+ return NETDEV_TX_BUSY;
+ } else
+ goto tx_drop;
+ }
+
+ netif_wake_queue(dev);
+ if (card->options.performance_stats)
+ card->perf_stats.outbound_time += qeth_get_micros() -
+ card->perf_stats.outbound_start_time;
+ return rc;
+
+tx_drop:
+ card->stats.tx_dropped++;
+ card->stats.tx_errors++;
+ if ((new_skb != skb) && new_skb)
+ dev_kfree_skb_any(new_skb);
+ dev_kfree_skb_any(skb);
+ netif_wake_queue(dev);
+ return NETDEV_TX_OK;
+}
+
+static int __qeth_l2_open(struct net_device *dev)
+{
+ struct qeth_card *card = dev->ml_priv;
+ int rc = 0;
+
+ QETH_CARD_TEXT(card, 4, "qethopen");
+ if (card->state == CARD_STATE_UP)
+ return rc;
+ if (card->state != CARD_STATE_SOFTSETUP)
+ return -ENODEV;
+
+ if ((card->info.type != QETH_CARD_TYPE_OSN) &&
+ (!(card->info.mac_bits & QETH_LAYER2_MAC_REGISTERED))) {
+ QETH_CARD_TEXT(card, 4, "nomacadr");
+ return -EPERM;
+ }
+ card->data.state = CH_STATE_UP;
+ card->state = CARD_STATE_UP;
+ netif_start_queue(dev);
+
+ if (qdio_stop_irq(card->data.ccwdev, 0) >= 0) {
+ napi_enable(&card->napi);
+ napi_schedule(&card->napi);
+ } else
+ rc = -EIO;
+ return rc;
+}
+
+static int qeth_l2_open(struct net_device *dev)
+{
+ struct qeth_card *card = dev->ml_priv;
+
+ QETH_CARD_TEXT(card, 5, "qethope_");
+ if (qeth_wait_for_threads(card, QETH_RECOVER_THREAD)) {
+ QETH_CARD_TEXT(card, 3, "openREC");
+ return -ERESTARTSYS;
+ }
+ return __qeth_l2_open(dev);
+}
+
+static int qeth_l2_stop(struct net_device *dev)
+{
+ struct qeth_card *card = dev->ml_priv;
+
+ QETH_CARD_TEXT(card, 4, "qethstop");
+ netif_tx_disable(dev);
+ if (card->state == CARD_STATE_UP) {
+ card->state = CARD_STATE_SOFTSETUP;
+ napi_disable(&card->napi);
+ }
+ return 0;
+}
+
+static int qeth_l2_probe_device(struct ccwgroup_device *gdev)
+{
+ struct qeth_card *card = dev_get_drvdata(&gdev->dev);
+
+ qeth_l2_create_device_attributes(&gdev->dev);
+ INIT_LIST_HEAD(&card->vid_list);
+ INIT_LIST_HEAD(&card->mc_list);
+ card->options.layer2 = 1;
+ card->info.hwtrap = 0;
+ return 0;
+}
+
+static void qeth_l2_remove_device(struct ccwgroup_device *cgdev)
+{
+ struct qeth_card *card = dev_get_drvdata(&cgdev->dev);
+
+ qeth_l2_remove_device_attributes(&cgdev->dev);
+ qeth_set_allowed_threads(card, 0, 1);
+ wait_event(card->wait_q, qeth_threads_running(card, 0xffffffff) == 0);
+
+ if (cgdev->state == CCWGROUP_ONLINE)
+ qeth_l2_set_offline(cgdev);
+
+ if (card->dev) {
+ unregister_netdev(card->dev);
+ card->dev = NULL;
+ }
+ return;
+}
+
+static const struct ethtool_ops qeth_l2_ethtool_ops = {
+ .get_link = ethtool_op_get_link,
+ .get_strings = qeth_core_get_strings,
+ .get_ethtool_stats = qeth_core_get_ethtool_stats,
+ .get_sset_count = qeth_core_get_sset_count,
+ .get_drvinfo = qeth_core_get_drvinfo,
+ .get_settings = qeth_core_ethtool_get_settings,
+};
+
+static const struct ethtool_ops qeth_l2_osn_ops = {
+ .get_strings = qeth_core_get_strings,
+ .get_ethtool_stats = qeth_core_get_ethtool_stats,
+ .get_sset_count = qeth_core_get_sset_count,
+ .get_drvinfo = qeth_core_get_drvinfo,
+};
+
+static const struct net_device_ops qeth_l2_netdev_ops = {
+ .ndo_open = qeth_l2_open,
+ .ndo_stop = qeth_l2_stop,
+ .ndo_get_stats = qeth_get_stats,
+ .ndo_start_xmit = qeth_l2_hard_start_xmit,
+ .ndo_validate_addr = eth_validate_addr,
+ .ndo_set_rx_mode = qeth_l2_set_multicast_list,
+ .ndo_do_ioctl = qeth_l2_do_ioctl,
+ .ndo_set_mac_address = qeth_l2_set_mac_address,
+ .ndo_change_mtu = qeth_change_mtu,
+ .ndo_vlan_rx_add_vid = qeth_l2_vlan_rx_add_vid,
+ .ndo_vlan_rx_kill_vid = qeth_l2_vlan_rx_kill_vid,
+ .ndo_tx_timeout = qeth_tx_timeout,
+};
+
+static int qeth_l2_setup_netdev(struct qeth_card *card)
+{
+ switch (card->info.type) {
+ case QETH_CARD_TYPE_IQD:
+ card->dev = alloc_netdev(0, "hsi%d", ether_setup);
+ break;
+ case QETH_CARD_TYPE_OSN:
+ card->dev = alloc_netdev(0, "osn%d", ether_setup);
+ card->dev->flags |= IFF_NOARP;
+ break;
+ default:
+ card->dev = alloc_etherdev(0);
+ }
+
+ if (!card->dev)
+ return -ENODEV;
+
+ card->dev->ml_priv = card;
+ card->dev->watchdog_timeo = QETH_TX_TIMEOUT;
+ card->dev->mtu = card->info.initial_mtu;
+ card->dev->netdev_ops = &qeth_l2_netdev_ops;
+ card->dev->ethtool_ops =
+ (card->info.type != QETH_CARD_TYPE_OSN) ?
+ &qeth_l2_ethtool_ops : &qeth_l2_osn_ops;
+ card->dev->features |= NETIF_F_HW_VLAN_CTAG_FILTER;
+ card->info.broadcast_capable = 1;
+ qeth_l2_request_initial_mac(card);
+ SET_NETDEV_DEV(card->dev, &card->gdev->dev);
+ netif_napi_add(card->dev, &card->napi, qeth_l2_poll, QETH_NAPI_WEIGHT);
+ return register_netdev(card->dev);
+}
+
+static int __qeth_l2_set_online(struct ccwgroup_device *gdev, int recovery_mode)
+{
+ struct qeth_card *card = dev_get_drvdata(&gdev->dev);
+ int rc = 0;
+ enum qeth_card_states recover_flag;
+
+ mutex_lock(&card->discipline_mutex);
+ mutex_lock(&card->conf_mutex);
+ QETH_DBF_TEXT(SETUP, 2, "setonlin");
+ QETH_DBF_HEX(SETUP, 2, &card, sizeof(void *));
+
+ recover_flag = card->state;
+ rc = qeth_core_hardsetup_card(card);
+ if (rc) {
+ QETH_DBF_TEXT_(SETUP, 2, "2err%d", rc);
+ rc = -ENODEV;
+ goto out_remove;
+ }
+ qeth_bridgeport_query_support(card);
+ if (card->options.sbp.supported_funcs)
+ dev_info(&card->gdev->dev,
+ "The device represents a HiperSockets Bridge Capable Port\n");
+ qeth_trace_features(card);
+
+ if (!card->dev && qeth_l2_setup_netdev(card)) {
+ rc = -ENODEV;
+ goto out_remove;
+ }
+
+ if (card->info.type != QETH_CARD_TYPE_OSN)
+ qeth_l2_send_setmac(card, &card->dev->dev_addr[0]);
+
+ if (qeth_is_diagass_supported(card, QETH_DIAGS_CMD_TRAP)) {
+ if (card->info.hwtrap &&
+ qeth_hw_trap(card, QETH_DIAGS_TRAP_ARM))
+ card->info.hwtrap = 0;
+ } else
+ card->info.hwtrap = 0;
+
+ qeth_l2_setup_bridgeport_attrs(card);
+
+ card->state = CARD_STATE_HARDSETUP;
+ memset(&card->rx, 0, sizeof(struct qeth_rx));
+ qeth_print_status_message(card);
+
+ /* softsetup */
+ QETH_DBF_TEXT(SETUP, 2, "softsetp");
+
+ rc = qeth_send_startlan(card);
+ if (rc) {
+ QETH_DBF_TEXT_(SETUP, 2, "1err%d", rc);
+ if (rc == 0xe080) {
+ dev_warn(&card->gdev->dev,
+ "The LAN is offline\n");
+ card->lan_online = 0;
+ goto contin;
+ }
+ rc = -ENODEV;
+ goto out_remove;
+ } else
+ card->lan_online = 1;
+
+contin:
+ if ((card->info.type == QETH_CARD_TYPE_OSD) ||
+ (card->info.type == QETH_CARD_TYPE_OSX)) {
+ /* configure isolation level */
+ rc = qeth_set_access_ctrl_online(card, 0);
+ if (rc) {
+ rc = -ENODEV;
+ goto out_remove;
+ }
+ }
+
+ if (card->info.type != QETH_CARD_TYPE_OSN &&
+ card->info.type != QETH_CARD_TYPE_OSM)
+ qeth_l2_process_vlans(card);
+
+ netif_tx_disable(card->dev);
+
+ rc = qeth_init_qdio_queues(card);
+ if (rc) {
+ QETH_DBF_TEXT_(SETUP, 2, "6err%d", rc);
+ rc = -ENODEV;
+ goto out_remove;
+ }
+ card->state = CARD_STATE_SOFTSETUP;
+ if (card->lan_online)
+ netif_carrier_on(card->dev);
+ else
+ netif_carrier_off(card->dev);
+
+ qeth_set_allowed_threads(card, 0xffffffff, 0);
+ if (recover_flag == CARD_STATE_RECOVER) {
+ if (recovery_mode &&
+ card->info.type != QETH_CARD_TYPE_OSN) {
+ __qeth_l2_open(card->dev);
+ } else {
+ rtnl_lock();
+ dev_open(card->dev);
+ rtnl_unlock();
+ }
+ /* this also sets saved unicast addresses */
+ qeth_l2_set_multicast_list(card->dev);
+ }
+ /* let user_space know that device is online */
+ kobject_uevent(&gdev->dev.kobj, KOBJ_CHANGE);
+ mutex_unlock(&card->conf_mutex);
+ mutex_unlock(&card->discipline_mutex);
+ return 0;
+
+out_remove:
+ qeth_l2_stop_card(card, 0);
+ ccw_device_set_offline(CARD_DDEV(card));
+ ccw_device_set_offline(CARD_WDEV(card));
+ ccw_device_set_offline(CARD_RDEV(card));
+ qdio_free(CARD_DDEV(card));
+ if (recover_flag == CARD_STATE_RECOVER)
+ card->state = CARD_STATE_RECOVER;
+ else
+ card->state = CARD_STATE_DOWN;
+ mutex_unlock(&card->conf_mutex);
+ mutex_unlock(&card->discipline_mutex);
+ return rc;
+}
+
+static int qeth_l2_set_online(struct ccwgroup_device *gdev)
+{
+ return __qeth_l2_set_online(gdev, 0);
+}
+
+static int __qeth_l2_set_offline(struct ccwgroup_device *cgdev,
+ int recovery_mode)
+{
+ struct qeth_card *card = dev_get_drvdata(&cgdev->dev);
+ int rc = 0, rc2 = 0, rc3 = 0;
+ enum qeth_card_states recover_flag;
+
+ mutex_lock(&card->discipline_mutex);
+ mutex_lock(&card->conf_mutex);
+ QETH_DBF_TEXT(SETUP, 3, "setoffl");
+ QETH_DBF_HEX(SETUP, 3, &card, sizeof(void *));
+
+ if (card->dev && netif_carrier_ok(card->dev))
+ netif_carrier_off(card->dev);
+ recover_flag = card->state;
+ if ((!recovery_mode && card->info.hwtrap) || card->info.hwtrap == 2) {
+ qeth_hw_trap(card, QETH_DIAGS_TRAP_DISARM);
+ card->info.hwtrap = 1;
+ }
+ qeth_l2_stop_card(card, recovery_mode);
+ rc = ccw_device_set_offline(CARD_DDEV(card));
+ rc2 = ccw_device_set_offline(CARD_WDEV(card));
+ rc3 = ccw_device_set_offline(CARD_RDEV(card));
+ if (!rc)
+ rc = (rc2) ? rc2 : rc3;
+ if (rc)
+ QETH_DBF_TEXT_(SETUP, 2, "1err%d", rc);
+ qdio_free(CARD_DDEV(card));
+ if (recover_flag == CARD_STATE_UP)
+ card->state = CARD_STATE_RECOVER;
+ /* let user_space know that device is offline */
+ kobject_uevent(&cgdev->dev.kobj, KOBJ_CHANGE);
+ mutex_unlock(&card->conf_mutex);
+ mutex_unlock(&card->discipline_mutex);
+ return 0;
+}
+
+static int qeth_l2_set_offline(struct ccwgroup_device *cgdev)
+{
+ return __qeth_l2_set_offline(cgdev, 0);
+}
+
+static int qeth_l2_recover(void *ptr)
+{
+ struct qeth_card *card;
+ int rc = 0;
+
+ card = (struct qeth_card *) ptr;
+ QETH_CARD_TEXT(card, 2, "recover1");
+ if (!qeth_do_run_thread(card, QETH_RECOVER_THREAD))
+ return 0;
+ QETH_CARD_TEXT(card, 2, "recover2");
+ dev_warn(&card->gdev->dev,
+ "A recovery process has been started for the device\n");
+ qeth_set_recovery_task(card);
+ __qeth_l2_set_offline(card->gdev, 1);
+ rc = __qeth_l2_set_online(card->gdev, 1);
+ if (!rc)
+ dev_info(&card->gdev->dev,
+ "Device successfully recovered!\n");
+ else {
+ qeth_close_dev(card);
+ dev_warn(&card->gdev->dev, "The qeth device driver "
+ "failed to recover an error on the device\n");
+ }
+ qeth_clear_recovery_task(card);
+ qeth_clear_thread_start_bit(card, QETH_RECOVER_THREAD);
+ qeth_clear_thread_running_bit(card, QETH_RECOVER_THREAD);
+ return 0;
+}
+
+static int __init qeth_l2_init(void)
+{
+ pr_info("register layer 2 discipline\n");
+ return 0;
+}
+
+static void __exit qeth_l2_exit(void)
+{
+ pr_info("unregister layer 2 discipline\n");
+}
+
+static void qeth_l2_shutdown(struct ccwgroup_device *gdev)
+{
+ struct qeth_card *card = dev_get_drvdata(&gdev->dev);
+ qeth_set_allowed_threads(card, 0, 1);
+ if ((gdev->state == CCWGROUP_ONLINE) && card->info.hwtrap)
+ qeth_hw_trap(card, QETH_DIAGS_TRAP_DISARM);
+ qeth_qdio_clear_card(card, 0);
+ qeth_clear_qdio_buffers(card);
+ qdio_free(CARD_DDEV(card));
+}
+
+static int qeth_l2_pm_suspend(struct ccwgroup_device *gdev)
+{
+ struct qeth_card *card = dev_get_drvdata(&gdev->dev);
+
+ if (card->dev)
+ netif_device_detach(card->dev);
+ qeth_set_allowed_threads(card, 0, 1);
+ wait_event(card->wait_q, qeth_threads_running(card, 0xffffffff) == 0);
+ if (gdev->state == CCWGROUP_OFFLINE)
+ return 0;
+ if (card->state == CARD_STATE_UP) {
+ if (card->info.hwtrap)
+ qeth_hw_trap(card, QETH_DIAGS_TRAP_DISARM);
+ __qeth_l2_set_offline(card->gdev, 1);
+ } else
+ __qeth_l2_set_offline(card->gdev, 0);
+ return 0;
+}
+
+static int qeth_l2_pm_resume(struct ccwgroup_device *gdev)
+{
+ struct qeth_card *card = dev_get_drvdata(&gdev->dev);
+ int rc = 0;
+
+ if (gdev->state == CCWGROUP_OFFLINE)
+ goto out;
+
+ if (card->state == CARD_STATE_RECOVER) {
+ rc = __qeth_l2_set_online(card->gdev, 1);
+ if (rc) {
+ rtnl_lock();
+ dev_close(card->dev);
+ rtnl_unlock();
+ }
+ } else
+ rc = __qeth_l2_set_online(card->gdev, 0);
+out:
+ qeth_set_allowed_threads(card, 0xffffffff, 0);
+ if (card->dev)
+ netif_device_attach(card->dev);
+ if (rc)
+ dev_warn(&card->gdev->dev, "The qeth device driver "
+ "failed to recover an error on the device\n");
+ return rc;
+}
+
+/* Returns zero if the command is successfully "consumed" */
+static int qeth_l2_control_event(struct qeth_card *card,
+ struct qeth_ipa_cmd *cmd)
+{
+ switch (cmd->hdr.command) {
+ case IPA_CMD_SETBRIDGEPORT:
+ if (cmd->data.sbp.hdr.command_code ==
+ IPA_SBP_BRIDGE_PORT_STATE_CHANGE) {
+ qeth_bridge_state_change(card, cmd);
+ return 0;
+ } else
+ return 1;
+ case IPA_CMD_ADDRESS_CHANGE_NOTIF:
+ qeth_bridge_host_event(card, cmd);
+ return 0;
+ default:
+ return 1;
+ }
+}
+
+struct qeth_discipline qeth_l2_discipline = {
+ .start_poll = qeth_qdio_start_poll,
+ .input_handler = (qdio_handler_t *) qeth_qdio_input_handler,
+ .output_handler = (qdio_handler_t *) qeth_qdio_output_handler,
+ .recover = qeth_l2_recover,
+ .setup = qeth_l2_probe_device,
+ .remove = qeth_l2_remove_device,
+ .set_online = qeth_l2_set_online,
+ .set_offline = qeth_l2_set_offline,
+ .shutdown = qeth_l2_shutdown,
+ .freeze = qeth_l2_pm_suspend,
+ .thaw = qeth_l2_pm_resume,
+ .restore = qeth_l2_pm_resume,
+ .control_event_handler = qeth_l2_control_event,
+};
+EXPORT_SYMBOL_GPL(qeth_l2_discipline);
+
+static int qeth_osn_send_control_data(struct qeth_card *card, int len,
+ struct qeth_cmd_buffer *iob)
+{
+ unsigned long flags;
+ int rc = 0;
+
+ QETH_CARD_TEXT(card, 5, "osndctrd");
+
+ wait_event(card->wait_q,
+ atomic_cmpxchg(&card->write.irq_pending, 0, 1) == 0);
+ qeth_prepare_control_data(card, len, iob);
+ QETH_CARD_TEXT(card, 6, "osnoirqp");
+ 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) {
+ QETH_DBF_MESSAGE(2, "qeth_osn_send_control_data: "
+ "ccw_device_start rc = %i\n", rc);
+ QETH_CARD_TEXT_(card, 2, " err%d", rc);
+ qeth_release_buffer(iob->channel, iob);
+ atomic_set(&card->write.irq_pending, 0);
+ wake_up(&card->wait_q);
+ }
+ return rc;
+}
+
+static int qeth_osn_send_ipa_cmd(struct qeth_card *card,
+ struct qeth_cmd_buffer *iob, int data_len)
+{
+ u16 s1, s2;
+
+ QETH_CARD_TEXT(card, 4, "osndipa");
+
+ qeth_prepare_ipa_cmd(card, iob, QETH_PROT_OSN2);
+ s1 = (u16)(IPA_PDU_HEADER_SIZE + data_len);
+ s2 = (u16)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);
+ return qeth_osn_send_control_data(card, s1, iob);
+}
+
+int qeth_osn_assist(struct net_device *dev, void *data, int data_len)
+{
+ struct qeth_cmd_buffer *iob;
+ struct qeth_card *card;
+ int rc;
+
+ if (!dev)
+ return -ENODEV;
+ card = dev->ml_priv;
+ if (!card)
+ return -ENODEV;
+ QETH_CARD_TEXT(card, 2, "osnsdmc");
+ if ((card->state != CARD_STATE_UP) &&
+ (card->state != CARD_STATE_SOFTSETUP))
+ return -ENODEV;
+ iob = qeth_wait_for_buffer(&card->write);
+ memcpy(iob->data+IPA_PDU_HEADER_SIZE, data, data_len);
+ rc = qeth_osn_send_ipa_cmd(card, iob, data_len);
+ return rc;
+}
+EXPORT_SYMBOL(qeth_osn_assist);
+
+int qeth_osn_register(unsigned char *read_dev_no, struct net_device **dev,
+ int (*assist_cb)(struct net_device *, void *),
+ int (*data_cb)(struct sk_buff *))
+{
+ struct qeth_card *card;
+
+ *dev = qeth_l2_netdev_by_devno(read_dev_no);
+ if (*dev == NULL)
+ return -ENODEV;
+ card = (*dev)->ml_priv;
+ if (!card)
+ return -ENODEV;
+ QETH_CARD_TEXT(card, 2, "osnreg");
+ if ((assist_cb == NULL) || (data_cb == NULL))
+ return -EINVAL;
+ card->osn_info.assist_cb = assist_cb;
+ card->osn_info.data_cb = data_cb;
+ return 0;
+}
+EXPORT_SYMBOL(qeth_osn_register);
+
+void qeth_osn_deregister(struct net_device *dev)
+{
+ struct qeth_card *card;
+
+ if (!dev)
+ return;
+ card = dev->ml_priv;
+ if (!card)
+ return;
+ QETH_CARD_TEXT(card, 2, "osndereg");
+ card->osn_info.assist_cb = NULL;
+ card->osn_info.data_cb = NULL;
+ return;
+}
+EXPORT_SYMBOL(qeth_osn_deregister);
+
+/* SETBRIDGEPORT support, async notifications */
+
+enum qeth_an_event_type {anev_reg_unreg, anev_abort, anev_reset};
+
+/**
+ * qeth_bridge_emit_host_event() - bridgeport address change notification
+ * @card: qeth_card structure pointer, for udev events.
+ * @evtype: "normal" register/unregister, or abort, or reset. For abort
+ * and reset token and addr_lnid are unused and may be NULL.
+ * @code: event bitmask: high order bit 0x80 value 1 means removal of an
+ * object, 0 - addition of an object.
+ * 0x01 - VLAN, 0x02 - MAC, 0x03 - VLAN and MAC.
+ * @token: "network token" structure identifying physical address of the port.
+ * @addr_lnid: pointer to structure with MAC address and VLAN ID.
+ *
+ * This function is called when registrations and deregistrations are
+ * reported by the hardware, and also when notifications are enabled -
+ * for all currently registered addresses.
+ */
+static void qeth_bridge_emit_host_event(struct qeth_card *card,
+ enum qeth_an_event_type evtype,
+ u8 code, struct net_if_token *token, struct mac_addr_lnid *addr_lnid)
+{
+ char str[7][32];
+ char *env[8];
+ int i = 0;
+
+ switch (evtype) {
+ case anev_reg_unreg:
+ snprintf(str[i], sizeof(str[i]), "BRIDGEDHOST=%s",
+ (code & IPA_ADDR_CHANGE_CODE_REMOVAL)
+ ? "deregister" : "register");
+ env[i] = str[i]; i++;
+ if (code & IPA_ADDR_CHANGE_CODE_VLANID) {
+ snprintf(str[i], sizeof(str[i]), "VLAN=%d",
+ addr_lnid->lnid);
+ env[i] = str[i]; i++;
+ }
+ if (code & IPA_ADDR_CHANGE_CODE_MACADDR) {
+ snprintf(str[i], sizeof(str[i]), "MAC=%pM6",
+ &addr_lnid->mac);
+ env[i] = str[i]; i++;
+ }
+ snprintf(str[i], sizeof(str[i]), "NTOK_BUSID=%x.%x.%04x",
+ token->cssid, token->ssid, token->devnum);
+ env[i] = str[i]; i++;
+ snprintf(str[i], sizeof(str[i]), "NTOK_IID=%02x", token->iid);
+ env[i] = str[i]; i++;
+ snprintf(str[i], sizeof(str[i]), "NTOK_CHPID=%02x",
+ token->chpid);
+ env[i] = str[i]; i++;
+ snprintf(str[i], sizeof(str[i]), "NTOK_CHID=%04x", token->chid);
+ env[i] = str[i]; i++;
+ break;
+ case anev_abort:
+ snprintf(str[i], sizeof(str[i]), "BRIDGEDHOST=abort");
+ env[i] = str[i]; i++;
+ break;
+ case anev_reset:
+ snprintf(str[i], sizeof(str[i]), "BRIDGEDHOST=reset");
+ env[i] = str[i]; i++;
+ break;
+ }
+ env[i] = NULL;
+ kobject_uevent_env(&card->gdev->dev.kobj, KOBJ_CHANGE, env);
+}
+
+struct qeth_bridge_state_data {
+ struct work_struct worker;
+ struct qeth_card *card;
+ struct qeth_sbp_state_change qports;
+};
+
+static void qeth_bridge_state_change_worker(struct work_struct *work)
+{
+ struct qeth_bridge_state_data *data =
+ container_of(work, struct qeth_bridge_state_data, worker);
+ /* We are only interested in the first entry - local port */
+ struct qeth_sbp_port_entry *entry = &data->qports.entry[0];
+ char env_locrem[32];
+ char env_role[32];
+ char env_state[32];
+ char *env[] = {
+ env_locrem,
+ env_role,
+ env_state,
+ NULL
+ };
+
+ /* Role should not change by itself, but if it did, */
+ /* information from the hardware is authoritative. */
+ mutex_lock(&data->card->conf_mutex);
+ data->card->options.sbp.role = entry->role;
+ mutex_unlock(&data->card->conf_mutex);
+
+ snprintf(env_locrem, sizeof(env_locrem), "BRIDGEPORT=statechange");
+ snprintf(env_role, sizeof(env_role), "ROLE=%s",
+ (entry->role == QETH_SBP_ROLE_NONE) ? "none" :
+ (entry->role == QETH_SBP_ROLE_PRIMARY) ? "primary" :
+ (entry->role == QETH_SBP_ROLE_SECONDARY) ? "secondary" :
+ "<INVALID>");
+ snprintf(env_state, sizeof(env_state), "STATE=%s",
+ (entry->state == QETH_SBP_STATE_INACTIVE) ? "inactive" :
+ (entry->state == QETH_SBP_STATE_STANDBY) ? "standby" :
+ (entry->state == QETH_SBP_STATE_ACTIVE) ? "active" :
+ "<INVALID>");
+ kobject_uevent_env(&data->card->gdev->dev.kobj,
+ KOBJ_CHANGE, env);
+ kfree(data);
+}
+
+static void qeth_bridge_state_change(struct qeth_card *card,
+ struct qeth_ipa_cmd *cmd)
+{
+ struct qeth_sbp_state_change *qports =
+ &cmd->data.sbp.data.state_change;
+ struct qeth_bridge_state_data *data;
+ int extrasize;
+
+ QETH_CARD_TEXT(card, 2, "brstchng");
+ if (qports->entry_length != sizeof(struct qeth_sbp_port_entry)) {
+ QETH_CARD_TEXT_(card, 2, "BPsz%.8d", qports->entry_length);
+ return;
+ }
+ extrasize = sizeof(struct qeth_sbp_port_entry) * qports->num_entries;
+ data = kzalloc(sizeof(struct qeth_bridge_state_data) + extrasize,
+ GFP_ATOMIC);
+ if (!data) {
+ QETH_CARD_TEXT(card, 2, "BPSalloc");
+ return;
+ }
+ INIT_WORK(&data->worker, qeth_bridge_state_change_worker);
+ data->card = card;
+ memcpy(&data->qports, qports,
+ sizeof(struct qeth_sbp_state_change) + extrasize);
+ queue_work(qeth_wq, &data->worker);
+}
+
+struct qeth_bridge_host_data {
+ struct work_struct worker;
+ struct qeth_card *card;
+ struct qeth_ipacmd_addr_change hostevs;
+};
+
+static void qeth_bridge_host_event_worker(struct work_struct *work)
+{
+ struct qeth_bridge_host_data *data =
+ container_of(work, struct qeth_bridge_host_data, worker);
+ int i;
+
+ if (data->hostevs.lost_event_mask) {
+ dev_info(&data->card->gdev->dev,
+"Address notification from the HiperSockets Bridge Port stopped %s (%s)\n",
+ data->card->dev->name,
+ (data->hostevs.lost_event_mask == 0x01)
+ ? "Overflow"
+ : (data->hostevs.lost_event_mask == 0x02)
+ ? "Bridge port state change"
+ : "Unknown reason");
+ mutex_lock(&data->card->conf_mutex);
+ data->card->options.sbp.hostnotification = 0;
+ mutex_unlock(&data->card->conf_mutex);
+ qeth_bridge_emit_host_event(data->card, anev_abort,
+ 0, NULL, NULL);
+ } else
+ for (i = 0; i < data->hostevs.num_entries; i++) {
+ struct qeth_ipacmd_addr_change_entry *entry =
+ &data->hostevs.entry[i];
+ qeth_bridge_emit_host_event(data->card,
+ anev_reg_unreg,
+ entry->change_code,
+ &entry->token, &entry->addr_lnid);
+ }
+ kfree(data);
+}
+
+static void qeth_bridge_host_event(struct qeth_card *card,
+ struct qeth_ipa_cmd *cmd)
+{
+ struct qeth_ipacmd_addr_change *hostevs =
+ &cmd->data.addrchange;
+ struct qeth_bridge_host_data *data;
+ int extrasize;
+
+ QETH_CARD_TEXT(card, 2, "brhostev");
+ if (cmd->hdr.return_code != 0x0000) {
+ if (cmd->hdr.return_code == 0x0010) {
+ if (hostevs->lost_event_mask == 0x00)
+ hostevs->lost_event_mask = 0xff;
+ } else {
+ QETH_CARD_TEXT_(card, 2, "BPHe%04x",
+ cmd->hdr.return_code);
+ return;
+ }
+ }
+ extrasize = sizeof(struct qeth_ipacmd_addr_change_entry) *
+ hostevs->num_entries;
+ data = kzalloc(sizeof(struct qeth_bridge_host_data) + extrasize,
+ GFP_ATOMIC);
+ if (!data) {
+ QETH_CARD_TEXT(card, 2, "BPHalloc");
+ return;
+ }
+ INIT_WORK(&data->worker, qeth_bridge_host_event_worker);
+ data->card = card;
+ memcpy(&data->hostevs, hostevs,
+ sizeof(struct qeth_ipacmd_addr_change) + extrasize);
+ queue_work(qeth_wq, &data->worker);
+}
+
+/* SETBRIDGEPORT support; sending commands */
+
+struct _qeth_sbp_cbctl {
+ u16 ipa_rc;
+ u16 cmd_rc;
+ union {
+ u32 supported;
+ struct {
+ enum qeth_sbp_roles *role;
+ enum qeth_sbp_states *state;
+ } qports;
+ } data;
+};
+
+/**
+ * qeth_bridgeport_makerc() - derive "traditional" error from hardware codes.
+ * @card: qeth_card structure pointer, for debug messages.
+ * @cbctl: state structure with hardware return codes.
+ * @setcmd: IPA command code
+ *
+ * Returns negative errno-compatible error indication or 0 on success.
+ */
+static int qeth_bridgeport_makerc(struct qeth_card *card,
+ struct _qeth_sbp_cbctl *cbctl, enum qeth_ipa_sbp_cmd setcmd)
+{
+ int rc;
+
+ switch (cbctl->ipa_rc) {
+ case IPA_RC_SUCCESS:
+ switch (cbctl->cmd_rc) {
+ case 0x0000:
+ rc = 0;
+ break;
+ case 0x0004:
+ rc = -ENOSYS;
+ break;
+ case 0x000C: /* Not configured as bridge Port */
+ rc = -ENODEV; /* maybe not the best code here? */
+ dev_err(&card->gdev->dev,
+ "The HiperSockets device is not configured as a Bridge Port\n");
+ break;
+ case 0x0014: /* Another device is Primary */
+ switch (setcmd) {
+ case IPA_SBP_SET_PRIMARY_BRIDGE_PORT:
+ rc = -EEXIST;
+ dev_err(&card->gdev->dev,
+ "The HiperSockets LAN already has a primary Bridge Port\n");
+ break;
+ case IPA_SBP_SET_SECONDARY_BRIDGE_PORT:
+ rc = -EBUSY;
+ dev_err(&card->gdev->dev,
+ "The HiperSockets device is already a primary Bridge Port\n");
+ break;
+ default:
+ rc = -EIO;
+ }
+ break;
+ case 0x0018: /* This device is currently Secondary */
+ rc = -EBUSY;
+ dev_err(&card->gdev->dev,
+ "The HiperSockets device is already a secondary Bridge Port\n");
+ break;
+ case 0x001C: /* Limit for Secondary devices reached */
+ rc = -EEXIST;
+ dev_err(&card->gdev->dev,
+ "The HiperSockets LAN cannot have more secondary Bridge Ports\n");
+ break;
+ case 0x0024: /* This device is currently Primary */
+ rc = -EBUSY;
+ dev_err(&card->gdev->dev,
+ "The HiperSockets device is already a primary Bridge Port\n");
+ break;
+ case 0x0020: /* Not authorized by zManager */
+ rc = -EACCES;
+ dev_err(&card->gdev->dev,
+ "The HiperSockets device is not authorized to be a Bridge Port\n");
+ break;
+ default:
+ rc = -EIO;
+ }
+ break;
+ case IPA_RC_NOTSUPP:
+ rc = -ENOSYS;
+ break;
+ case IPA_RC_UNSUPPORTED_COMMAND:
+ rc = -ENOSYS;
+ break;
+ default:
+ rc = -EIO;
+ }
+ if (rc) {
+ QETH_CARD_TEXT_(card, 2, "SBPi%04x", cbctl->ipa_rc);
+ QETH_CARD_TEXT_(card, 2, "SBPc%04x", cbctl->cmd_rc);
+ }
+ return rc;
+}
+
+static int qeth_bridgeport_query_support_cb(struct qeth_card *card,
+ struct qeth_reply *reply, unsigned long data)
+{
+ struct qeth_ipa_cmd *cmd = (struct qeth_ipa_cmd *) data;
+ struct _qeth_sbp_cbctl *cbctl = (struct _qeth_sbp_cbctl *)reply->param;
+ QETH_CARD_TEXT(card, 2, "brqsupcb");
+ cbctl->ipa_rc = cmd->hdr.return_code;
+ cbctl->cmd_rc = cmd->data.sbp.hdr.return_code;
+ if ((cbctl->ipa_rc == 0) && (cbctl->cmd_rc == 0)) {
+ cbctl->data.supported =
+ cmd->data.sbp.data.query_cmds_supp.supported_cmds;
+ } else {
+ cbctl->data.supported = 0;
+ }
+ return 0;
+}
+
+/**
+ * qeth_bridgeport_query_support() - store bitmask of supported subfunctions.
+ * @card: qeth_card structure pointer.
+ *
+ * Sets bitmask of supported setbridgeport subfunctions in the qeth_card
+ * strucutre: card->options.sbp.supported_funcs.
+ */
+static void qeth_bridgeport_query_support(struct qeth_card *card)
+{
+ struct qeth_cmd_buffer *iob;
+ struct qeth_ipa_cmd *cmd;
+ struct _qeth_sbp_cbctl cbctl;
+
+ QETH_CARD_TEXT(card, 2, "brqsuppo");
+ iob = qeth_get_ipacmd_buffer(card, IPA_CMD_SETBRIDGEPORT, 0);
+ cmd = (struct qeth_ipa_cmd *)(iob->data+IPA_PDU_HEADER_SIZE);
+ cmd->data.sbp.hdr.cmdlength =
+ sizeof(struct qeth_ipacmd_sbp_hdr) +
+ sizeof(struct qeth_sbp_query_cmds_supp);
+ cmd->data.sbp.hdr.command_code =
+ IPA_SBP_QUERY_COMMANDS_SUPPORTED;
+ cmd->data.sbp.hdr.used_total = 1;
+ cmd->data.sbp.hdr.seq_no = 1;
+ if (qeth_send_ipa_cmd(card, iob, qeth_bridgeport_query_support_cb,
+ (void *)&cbctl) ||
+ qeth_bridgeport_makerc(card, &cbctl,
+ IPA_SBP_QUERY_COMMANDS_SUPPORTED)) {
+ /* non-zero makerc signifies failure, and produce messages */
+ card->options.sbp.role = QETH_SBP_ROLE_NONE;
+ return;
+ }
+ card->options.sbp.supported_funcs = cbctl.data.supported;
+}
+
+static int qeth_bridgeport_query_ports_cb(struct qeth_card *card,
+ struct qeth_reply *reply, unsigned long data)
+{
+ struct qeth_ipa_cmd *cmd = (struct qeth_ipa_cmd *) data;
+ struct qeth_sbp_query_ports *qports = &cmd->data.sbp.data.query_ports;
+ struct _qeth_sbp_cbctl *cbctl = (struct _qeth_sbp_cbctl *)reply->param;
+
+ QETH_CARD_TEXT(card, 2, "brqprtcb");
+ cbctl->ipa_rc = cmd->hdr.return_code;
+ cbctl->cmd_rc = cmd->data.sbp.hdr.return_code;
+ if ((cbctl->ipa_rc != 0) || (cbctl->cmd_rc != 0))
+ return 0;
+ if (qports->entry_length != sizeof(struct qeth_sbp_port_entry)) {
+ cbctl->cmd_rc = 0xffff;
+ QETH_CARD_TEXT_(card, 2, "SBPs%04x", qports->entry_length);
+ return 0;
+ }
+ /* first entry contains the state of the local port */
+ if (qports->num_entries > 0) {
+ if (cbctl->data.qports.role)
+ *cbctl->data.qports.role = qports->entry[0].role;
+ if (cbctl->data.qports.state)
+ *cbctl->data.qports.state = qports->entry[0].state;
+ }
+ return 0;
+}
+
+/**
+ * qeth_bridgeport_query_ports() - query local bridgeport status.
+ * @card: qeth_card structure pointer.
+ * @role: Role of the port: 0-none, 1-primary, 2-secondary.
+ * @state: State of the port: 0-inactive, 1-standby, 2-active.
+ *
+ * Returns negative errno-compatible error indication or 0 on success.
+ *
+ * 'role' and 'state' are not updated in case of hardware operation failure.
+ */
+int qeth_bridgeport_query_ports(struct qeth_card *card,
+ enum qeth_sbp_roles *role, enum qeth_sbp_states *state)
+{
+ int rc = 0;
+ struct qeth_cmd_buffer *iob;
+ struct qeth_ipa_cmd *cmd;
+ struct _qeth_sbp_cbctl cbctl = {
+ .data = {
+ .qports = {
+ .role = role,
+ .state = state,
+ },
+ },
+ };
+
+ QETH_CARD_TEXT(card, 2, "brqports");
+ if (!(card->options.sbp.supported_funcs & IPA_SBP_QUERY_BRIDGE_PORTS))
+ return -EOPNOTSUPP;
+ iob = qeth_get_ipacmd_buffer(card, IPA_CMD_SETBRIDGEPORT, 0);
+ cmd = (struct qeth_ipa_cmd *)(iob->data+IPA_PDU_HEADER_SIZE);
+ cmd->data.sbp.hdr.cmdlength =
+ sizeof(struct qeth_ipacmd_sbp_hdr);
+ cmd->data.sbp.hdr.command_code =
+ IPA_SBP_QUERY_BRIDGE_PORTS;
+ cmd->data.sbp.hdr.used_total = 1;
+ cmd->data.sbp.hdr.seq_no = 1;
+ rc = qeth_send_ipa_cmd(card, iob, qeth_bridgeport_query_ports_cb,
+ (void *)&cbctl);
+ if (rc)
+ return rc;
+ rc = qeth_bridgeport_makerc(card, &cbctl, IPA_SBP_QUERY_BRIDGE_PORTS);
+ if (rc)
+ return rc;
+ return 0;
+}
+EXPORT_SYMBOL_GPL(qeth_bridgeport_query_ports);
+
+static int qeth_bridgeport_set_cb(struct qeth_card *card,
+ struct qeth_reply *reply, unsigned long data)
+{
+ struct qeth_ipa_cmd *cmd = (struct qeth_ipa_cmd *)data;
+ struct _qeth_sbp_cbctl *cbctl = (struct _qeth_sbp_cbctl *)reply->param;
+ QETH_CARD_TEXT(card, 2, "brsetrcb");
+ cbctl->ipa_rc = cmd->hdr.return_code;
+ cbctl->cmd_rc = cmd->data.sbp.hdr.return_code;
+ return 0;
+}
+
+/**
+ * qeth_bridgeport_setrole() - Assign primary role to the port.
+ * @card: qeth_card structure pointer.
+ * @role: Role to assign.
+ *
+ * Returns negative errno-compatible error indication or 0 on success.
+ */
+int qeth_bridgeport_setrole(struct qeth_card *card, enum qeth_sbp_roles role)
+{
+ int rc = 0;
+ int cmdlength;
+ struct qeth_cmd_buffer *iob;
+ struct qeth_ipa_cmd *cmd;
+ struct _qeth_sbp_cbctl cbctl;
+ enum qeth_ipa_sbp_cmd setcmd;
+
+ QETH_CARD_TEXT(card, 2, "brsetrol");
+ switch (role) {
+ case QETH_SBP_ROLE_NONE:
+ setcmd = IPA_SBP_RESET_BRIDGE_PORT_ROLE;
+ cmdlength = sizeof(struct qeth_ipacmd_sbp_hdr) +
+ sizeof(struct qeth_sbp_reset_role);
+ break;
+ case QETH_SBP_ROLE_PRIMARY:
+ setcmd = IPA_SBP_SET_PRIMARY_BRIDGE_PORT;
+ cmdlength = sizeof(struct qeth_ipacmd_sbp_hdr) +
+ sizeof(struct qeth_sbp_set_primary);
+ break;
+ case QETH_SBP_ROLE_SECONDARY:
+ setcmd = IPA_SBP_SET_SECONDARY_BRIDGE_PORT;
+ cmdlength = sizeof(struct qeth_ipacmd_sbp_hdr) +
+ sizeof(struct qeth_sbp_set_secondary);
+ break;
+ default:
+ return -EINVAL;
+ }
+ if (!(card->options.sbp.supported_funcs & setcmd))
+ return -EOPNOTSUPP;
+ iob = qeth_get_ipacmd_buffer(card, IPA_CMD_SETBRIDGEPORT, 0);
+ cmd = (struct qeth_ipa_cmd *)(iob->data+IPA_PDU_HEADER_SIZE);
+ cmd->data.sbp.hdr.cmdlength = cmdlength;
+ cmd->data.sbp.hdr.command_code = setcmd;
+ cmd->data.sbp.hdr.used_total = 1;
+ cmd->data.sbp.hdr.seq_no = 1;
+ rc = qeth_send_ipa_cmd(card, iob, qeth_bridgeport_set_cb,
+ (void *)&cbctl);
+ if (rc)
+ return rc;
+ rc = qeth_bridgeport_makerc(card, &cbctl, setcmd);
+ return rc;
+}
+
+/**
+ * qeth_anset_makerc() - derive "traditional" error from hardware codes.
+ * @card: qeth_card structure pointer, for debug messages.
+ *
+ * Returns negative errno-compatible error indication or 0 on success.
+ */
+static int qeth_anset_makerc(struct qeth_card *card, int pnso_rc, u16 response)
+{
+ int rc;
+
+ if (pnso_rc == 0)
+ switch (response) {
+ case 0x0001:
+ rc = 0;
+ break;
+ case 0x0004:
+ case 0x0100:
+ case 0x0106:
+ rc = -ENOSYS;
+ dev_err(&card->gdev->dev,
+ "Setting address notification failed\n");
+ break;
+ case 0x0107:
+ rc = -EAGAIN;
+ break;
+ default:
+ rc = -EIO;
+ }
+ else
+ rc = -EIO;
+
+ if (rc) {
+ QETH_CARD_TEXT_(card, 2, "SBPp%04x", pnso_rc);
+ QETH_CARD_TEXT_(card, 2, "SBPr%04x", response);
+ }
+ return rc;
+}
+
+static void qeth_bridgeport_an_set_cb(void *priv,
+ enum qdio_brinfo_entry_type type, void *entry)
+{
+ struct qeth_card *card = (struct qeth_card *)priv;
+ struct qdio_brinfo_entry_l2 *l2entry;
+ u8 code;
+
+ if (type != l2_addr_lnid) {
+ WARN_ON_ONCE(1);
+ return;
+ }
+
+ l2entry = (struct qdio_brinfo_entry_l2 *)entry;
+ code = IPA_ADDR_CHANGE_CODE_MACADDR;
+ if (l2entry->addr_lnid.lnid)
+ code |= IPA_ADDR_CHANGE_CODE_VLANID;
+ qeth_bridge_emit_host_event(card, anev_reg_unreg, code,
+ (struct net_if_token *)&l2entry->nit,
+ (struct mac_addr_lnid *)&l2entry->addr_lnid);
+}
+
+/**
+ * qeth_bridgeport_an_set() - Enable or disable bridgeport address notification
+ * @card: qeth_card structure pointer.
+ * @enable: 0 - disable, non-zero - enable notifications
+ *
+ * Returns negative errno-compatible error indication or 0 on success.
+ *
+ * On enable, emits a series of address notifications udev events for all
+ * currently registered hosts.
+ */
+int qeth_bridgeport_an_set(struct qeth_card *card, int enable)
+{
+ int rc;
+ u16 response;
+ struct ccw_device *ddev;
+ struct subchannel_id schid;
+
+ if (!card)
+ return -EINVAL;
+ if (!card->options.sbp.supported_funcs)
+ return -EOPNOTSUPP;
+ ddev = CARD_DDEV(card);
+ ccw_device_get_schid(ddev, &schid);
+
+ if (enable) {
+ qeth_bridge_emit_host_event(card, anev_reset, 0, NULL, NULL);
+ rc = qdio_pnso_brinfo(schid, 1, &response,
+ qeth_bridgeport_an_set_cb, card);
+ } else
+ rc = qdio_pnso_brinfo(schid, 0, &response, NULL, NULL);
+ return qeth_anset_makerc(card, rc, response);
+}
+EXPORT_SYMBOL_GPL(qeth_bridgeport_an_set);
+
+module_init(qeth_l2_init);
+module_exit(qeth_l2_exit);
+MODULE_AUTHOR("Frank Blaschka <frank.blaschka@de.ibm.com>");
+MODULE_DESCRIPTION("qeth layer 2 discipline");
+MODULE_LICENSE("GPL");
diff --git a/drivers/s390/net/qeth_l2_sys.c b/drivers/s390/net/qeth_l2_sys.c
new file mode 100644
index 00000000000..ae1bc04b865
--- /dev/null
+++ b/drivers/s390/net/qeth_l2_sys.c
@@ -0,0 +1,223 @@
+/*
+ * Copyright IBM Corp. 2013
+ * Author(s): Eugene Crosser <eugene.crosser@ru.ibm.com>
+ */
+
+#include <linux/slab.h>
+#include <asm/ebcdic.h>
+#include "qeth_l2.h"
+
+#define QETH_DEVICE_ATTR(_id, _name, _mode, _show, _store) \
+struct device_attribute dev_attr_##_id = __ATTR(_name, _mode, _show, _store)
+
+static int qeth_card_hw_is_reachable(struct qeth_card *card)
+{
+ return (card->state == CARD_STATE_SOFTSETUP) ||
+ (card->state == CARD_STATE_UP);
+}
+
+static ssize_t qeth_bridge_port_role_state_show(struct device *dev,
+ struct device_attribute *attr, char *buf,
+ int show_state)
+{
+ struct qeth_card *card = dev_get_drvdata(dev);
+ enum qeth_sbp_states state = QETH_SBP_STATE_INACTIVE;
+ int rc = 0;
+ char *word;
+
+ if (!card)
+ return -EINVAL;
+
+ mutex_lock(&card->conf_mutex);
+
+ if (qeth_card_hw_is_reachable(card) &&
+ card->options.sbp.supported_funcs)
+ rc = qeth_bridgeport_query_ports(card,
+ &card->options.sbp.role, &state);
+ if (!rc) {
+ if (show_state)
+ switch (state) {
+ case QETH_SBP_STATE_INACTIVE:
+ word = "inactive"; break;
+ case QETH_SBP_STATE_STANDBY:
+ word = "standby"; break;
+ case QETH_SBP_STATE_ACTIVE:
+ word = "active"; break;
+ default:
+ rc = -EIO;
+ }
+ else
+ switch (card->options.sbp.role) {
+ case QETH_SBP_ROLE_NONE:
+ word = "none"; break;
+ case QETH_SBP_ROLE_PRIMARY:
+ word = "primary"; break;
+ case QETH_SBP_ROLE_SECONDARY:
+ word = "secondary"; break;
+ default:
+ rc = -EIO;
+ }
+ if (rc)
+ QETH_CARD_TEXT_(card, 2, "SBP%02x:%02x",
+ card->options.sbp.role, state);
+ else
+ rc = sprintf(buf, "%s\n", word);
+ }
+
+ mutex_unlock(&card->conf_mutex);
+
+ return rc;
+}
+
+static ssize_t qeth_bridge_port_role_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ return qeth_bridge_port_role_state_show(dev, attr, buf, 0);
+}
+
+static ssize_t qeth_bridge_port_role_store(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t count)
+{
+ struct qeth_card *card = dev_get_drvdata(dev);
+ int rc = 0;
+ enum qeth_sbp_roles role;
+
+ if (!card)
+ return -EINVAL;
+ if (sysfs_streq(buf, "primary"))
+ role = QETH_SBP_ROLE_PRIMARY;
+ else if (sysfs_streq(buf, "secondary"))
+ role = QETH_SBP_ROLE_SECONDARY;
+ else if (sysfs_streq(buf, "none"))
+ role = QETH_SBP_ROLE_NONE;
+ else
+ return -EINVAL;
+
+ mutex_lock(&card->conf_mutex);
+
+ if (qeth_card_hw_is_reachable(card)) {
+ rc = qeth_bridgeport_setrole(card, role);
+ if (!rc)
+ card->options.sbp.role = role;
+ } else
+ card->options.sbp.role = role;
+
+ mutex_unlock(&card->conf_mutex);
+
+ return rc ? rc : count;
+}
+
+static DEVICE_ATTR(bridge_role, 0644, qeth_bridge_port_role_show,
+ qeth_bridge_port_role_store);
+
+static ssize_t qeth_bridge_port_state_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ return qeth_bridge_port_role_state_show(dev, attr, buf, 1);
+}
+
+static DEVICE_ATTR(bridge_state, 0644, qeth_bridge_port_state_show,
+ NULL);
+
+static ssize_t qeth_bridgeport_hostnotification_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct qeth_card *card = dev_get_drvdata(dev);
+ int enabled;
+
+ if (!card)
+ return -EINVAL;
+
+ mutex_lock(&card->conf_mutex);
+
+ enabled = card->options.sbp.hostnotification;
+
+ mutex_unlock(&card->conf_mutex);
+
+ return sprintf(buf, "%d\n", enabled);
+}
+
+static ssize_t qeth_bridgeport_hostnotification_store(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t count)
+{
+ struct qeth_card *card = dev_get_drvdata(dev);
+ int rc = 0;
+ int enable;
+
+ if (!card)
+ return -EINVAL;
+
+ if (sysfs_streq(buf, "0"))
+ enable = 0;
+ else if (sysfs_streq(buf, "1"))
+ enable = 1;
+ else
+ return -EINVAL;
+
+ mutex_lock(&card->conf_mutex);
+
+ if (qeth_card_hw_is_reachable(card)) {
+ rc = qeth_bridgeport_an_set(card, enable);
+ if (!rc)
+ card->options.sbp.hostnotification = enable;
+ } else
+ card->options.sbp.hostnotification = enable;
+
+ mutex_unlock(&card->conf_mutex);
+
+ return rc ? rc : count;
+}
+
+static DEVICE_ATTR(bridge_hostnotify, 0644,
+ qeth_bridgeport_hostnotification_show,
+ qeth_bridgeport_hostnotification_store);
+
+static struct attribute *qeth_l2_bridgeport_attrs[] = {
+ &dev_attr_bridge_role.attr,
+ &dev_attr_bridge_state.attr,
+ &dev_attr_bridge_hostnotify.attr,
+ NULL,
+};
+
+static struct attribute_group qeth_l2_bridgeport_attr_group = {
+ .attrs = qeth_l2_bridgeport_attrs,
+};
+
+int qeth_l2_create_device_attributes(struct device *dev)
+{
+ return sysfs_create_group(&dev->kobj, &qeth_l2_bridgeport_attr_group);
+}
+
+void qeth_l2_remove_device_attributes(struct device *dev)
+{
+ sysfs_remove_group(&dev->kobj, &qeth_l2_bridgeport_attr_group);
+}
+
+/**
+ * qeth_l2_setup_bridgeport_attrs() - set/restore attrs when turning online.
+ * @card: qeth_card structure pointer
+ *
+ * Note: this function is called with conf_mutex held by the caller
+ */
+void qeth_l2_setup_bridgeport_attrs(struct qeth_card *card)
+{
+ int rc;
+
+ if (!card)
+ return;
+ if (!card->options.sbp.supported_funcs)
+ return;
+ if (card->options.sbp.role != QETH_SBP_ROLE_NONE) {
+ /* Conditional to avoid spurious error messages */
+ qeth_bridgeport_setrole(card, card->options.sbp.role);
+ /* Let the callback function refresh the stored role value. */
+ qeth_bridgeport_query_ports(card,
+ &card->options.sbp.role, NULL);
+ }
+ if (card->options.sbp.hostnotification) {
+ rc = qeth_bridgeport_an_set(card, 1);
+ if (rc)
+ card->options.sbp.hostnotification = 0;
+ } else
+ qeth_bridgeport_an_set(card, 0);
+}
diff --git a/drivers/s390/net/qeth_l3.h b/drivers/s390/net/qeth_l3.h
new file mode 100644
index 00000000000..29c1c00e3a0
--- /dev/null
+++ b/drivers/s390/net/qeth_l3.h
@@ -0,0 +1,69 @@
+/*
+ * Copyright IBM Corp. 2007
+ * Author(s): Utz Bacher <utz.bacher@de.ibm.com>,
+ * Frank Pavlic <fpavlic@de.ibm.com>,
+ * Thomas Spatzier <tspat@de.ibm.com>,
+ * Frank Blaschka <frank.blaschka@de.ibm.com>
+ */
+
+#ifndef __QETH_L3_H__
+#define __QETH_L3_H__
+
+#include "qeth_core.h"
+
+#define QETH_SNIFF_AVAIL 0x0008
+
+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;
+ 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;
+};
+
+
+void qeth_l3_ipaddr4_to_string(const __u8 *, char *);
+int qeth_l3_string_to_ipaddr4(const char *, __u8 *);
+void qeth_l3_ipaddr6_to_string(const __u8 *, char *);
+int qeth_l3_string_to_ipaddr6(const char *, __u8 *);
+void qeth_l3_ipaddr_to_string(enum qeth_prot_versions, const __u8 *, char *);
+int qeth_l3_string_to_ipaddr(const char *, enum qeth_prot_versions, __u8 *);
+int qeth_l3_create_device_attributes(struct device *);
+void qeth_l3_remove_device_attributes(struct device *);
+int qeth_l3_setrouting_v4(struct qeth_card *);
+int qeth_l3_setrouting_v6(struct qeth_card *);
+int qeth_l3_add_ipato_entry(struct qeth_card *, struct qeth_ipato_entry *);
+void qeth_l3_del_ipato_entry(struct qeth_card *, enum qeth_prot_versions,
+ u8 *, int);
+int qeth_l3_add_vipa(struct qeth_card *, enum qeth_prot_versions, const u8 *);
+void qeth_l3_del_vipa(struct qeth_card *, enum qeth_prot_versions, const u8 *);
+int qeth_l3_add_rxip(struct qeth_card *, enum qeth_prot_versions, const u8 *);
+void qeth_l3_del_rxip(struct qeth_card *card, enum qeth_prot_versions,
+ const u8 *);
+int qeth_l3_is_addr_covered_by_ipato(struct qeth_card *, struct qeth_ipaddr *);
+struct qeth_ipaddr *qeth_l3_get_addr_buffer(enum qeth_prot_versions);
+int qeth_l3_add_ip(struct qeth_card *, struct qeth_ipaddr *);
+int qeth_l3_delete_ip(struct qeth_card *, struct qeth_ipaddr *);
+void qeth_l3_set_ip_addr_list(struct qeth_card *);
+
+#endif /* __QETH_L3_H__ */
diff --git a/drivers/s390/net/qeth_l3_main.c b/drivers/s390/net/qeth_l3_main.c
new file mode 100644
index 00000000000..14e0b5810e8
--- /dev/null
+++ b/drivers/s390/net/qeth_l3_main.c
@@ -0,0 +1,3771 @@
+/*
+ * Copyright IBM Corp. 2007, 2009
+ * Author(s): Utz Bacher <utz.bacher@de.ibm.com>,
+ * Frank Pavlic <fpavlic@de.ibm.com>,
+ * Thomas Spatzier <tspat@de.ibm.com>,
+ * Frank Blaschka <frank.blaschka@de.ibm.com>
+ */
+
+#define KMSG_COMPONENT "qeth"
+#define pr_fmt(fmt) KMSG_COMPONENT ": " fmt
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/bitops.h>
+#include <linux/string.h>
+#include <linux/errno.h>
+#include <linux/kernel.h>
+#include <linux/etherdevice.h>
+#include <linux/mii.h>
+#include <linux/ip.h>
+#include <linux/ipv6.h>
+#include <linux/inetdevice.h>
+#include <linux/igmp.h>
+#include <linux/slab.h>
+#include <linux/if_vlan.h>
+
+#include <net/ip.h>
+#include <net/arp.h>
+#include <net/route.h>
+#include <net/ip6_fib.h>
+#include <net/ip6_checksum.h>
+#include <net/iucv/af_iucv.h>
+
+#include "qeth_l3.h"
+
+
+static int qeth_l3_set_offline(struct ccwgroup_device *);
+static int qeth_l3_recover(void *);
+static int qeth_l3_stop(struct net_device *);
+static void qeth_l3_set_multicast_list(struct net_device *);
+static int qeth_l3_neigh_setup(struct net_device *, struct neigh_parms *);
+static int qeth_l3_register_addr_entry(struct qeth_card *,
+ struct qeth_ipaddr *);
+static int qeth_l3_deregister_addr_entry(struct qeth_card *,
+ struct qeth_ipaddr *);
+static int __qeth_l3_set_online(struct ccwgroup_device *, int);
+static int __qeth_l3_set_offline(struct ccwgroup_device *, int);
+
+static int qeth_l3_isxdigit(char *buf)
+{
+ while (*buf) {
+ if (!isxdigit(*buf++))
+ return 0;
+ }
+ return 1;
+}
+
+void qeth_l3_ipaddr4_to_string(const __u8 *addr, char *buf)
+{
+ sprintf(buf, "%i.%i.%i.%i", addr[0], addr[1], addr[2], addr[3]);
+}
+
+int qeth_l3_string_to_ipaddr4(const char *buf, __u8 *addr)
+{
+ int count = 0, rc = 0;
+ unsigned int in[4];
+ char c;
+
+ rc = sscanf(buf, "%u.%u.%u.%u%c",
+ &in[0], &in[1], &in[2], &in[3], &c);
+ if (rc != 4 && (rc != 5 || c != '\n'))
+ return -EINVAL;
+ for (count = 0; count < 4; count++) {
+ if (in[count] > 255)
+ return -EINVAL;
+ addr[count] = in[count];
+ }
+ return 0;
+}
+
+void qeth_l3_ipaddr6_to_string(const __u8 *addr, char *buf)
+{
+ sprintf(buf, "%pI6", addr);
+}
+
+int qeth_l3_string_to_ipaddr6(const char *buf, __u8 *addr)
+{
+ const char *end, *end_tmp, *start;
+ __u16 *in;
+ char num[5];
+ int num2, cnt, out, found, save_cnt;
+ unsigned short in_tmp[8] = {0, };
+
+ cnt = out = found = save_cnt = num2 = 0;
+ end = start = buf;
+ in = (__u16 *) addr;
+ memset(in, 0, 16);
+ while (*end) {
+ end = strchr(start, ':');
+ if (end == NULL) {
+ end = buf + strlen(buf);
+ end_tmp = strchr(start, '\n');
+ if (end_tmp != NULL)
+ end = end_tmp;
+ out = 1;
+ }
+ if ((end - start)) {
+ memset(num, 0, 5);
+ if ((end - start) > 4)
+ return -EINVAL;
+ memcpy(num, start, end - start);
+ if (!qeth_l3_isxdigit(num))
+ return -EINVAL;
+ sscanf(start, "%x", &num2);
+ if (found)
+ in_tmp[save_cnt++] = num2;
+ else
+ in[cnt++] = num2;
+ if (out)
+ break;
+ } else {
+ if (found)
+ return -EINVAL;
+ found = 1;
+ }
+ start = ++end;
+ }
+ if (cnt + save_cnt > 8)
+ return -EINVAL;
+ cnt = 7;
+ while (save_cnt)
+ in[cnt--] = in_tmp[--save_cnt];
+ return 0;
+}
+
+void qeth_l3_ipaddr_to_string(enum qeth_prot_versions proto, const __u8 *addr,
+ char *buf)
+{
+ if (proto == QETH_PROT_IPV4)
+ qeth_l3_ipaddr4_to_string(addr, buf);
+ else if (proto == QETH_PROT_IPV6)
+ qeth_l3_ipaddr6_to_string(addr, buf);
+}
+
+int qeth_l3_string_to_ipaddr(const char *buf, enum qeth_prot_versions proto,
+ __u8 *addr)
+{
+ if (proto == QETH_PROT_IPV4)
+ return qeth_l3_string_to_ipaddr4(buf, addr);
+ else if (proto == QETH_PROT_IPV6)
+ return qeth_l3_string_to_ipaddr6(buf, addr);
+ else
+ return -EINVAL;
+}
+
+static void qeth_l3_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;
+ }
+ }
+}
+
+int qeth_l3_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_l3_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_l3_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;
+}
+
+/*
+ * 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_l3_insert_ip_todo(struct qeth_card *card,
+ struct qeth_ipaddr *addr, int add)
+{
+ struct qeth_ipaddr *tmp, *t;
+ int found = 0;
+
+ if (card->options.sniffer)
+ return 0;
+ list_for_each_entry_safe(tmp, t, card->ip_tbd_list, entry) {
+ if ((addr->type == QETH_IP_TYPE_DEL_ALL_MC) &&
+ (tmp->type == QETH_IP_TYPE_DEL_ALL_MC))
+ return 0;
+ if ((tmp->proto == QETH_PROT_IPV4) &&
+ (addr->proto == QETH_PROT_IPV4) &&
+ (tmp->type == addr->type) &&
+ (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->type == QETH_IP_TYPE_DEL_ALL_MC)
+ list_add(&addr->entry, card->ip_tbd_list);
+ else {
+ if (addr->users == 0)
+ addr->users += add ? 1 : -1;
+ if (add && (addr->type == QETH_IP_TYPE_NORMAL) &&
+ qeth_l3_is_addr_covered_by_ipato(card, addr)) {
+ QETH_CARD_TEXT(card, 2, "tkovaddr");
+ addr->set_flags |= QETH_IPA_SETIP_TAKEOVER_FLAG;
+ }
+ list_add_tail(&addr->entry, card->ip_tbd_list);
+ }
+ return 1;
+ }
+}
+
+int qeth_l3_delete_ip(struct qeth_card *card, struct qeth_ipaddr *addr)
+{
+ unsigned long flags;
+ int rc = 0;
+
+ QETH_CARD_TEXT(card, 4, "delip");
+
+ if (addr->proto == QETH_PROT_IPV4)
+ QETH_CARD_HEX(card, 4, &addr->u.a4.addr, 4);
+ else {
+ QETH_CARD_HEX(card, 4, &addr->u.a6.addr, 8);
+ QETH_CARD_HEX(card, 4, ((char *)&addr->u.a6.addr) + 8, 8);
+ }
+ spin_lock_irqsave(&card->ip_lock, flags);
+ rc = __qeth_l3_insert_ip_todo(card, addr, 0);
+ spin_unlock_irqrestore(&card->ip_lock, flags);
+ return rc;
+}
+
+int qeth_l3_add_ip(struct qeth_card *card, struct qeth_ipaddr *addr)
+{
+ unsigned long flags;
+ int rc = 0;
+
+ QETH_CARD_TEXT(card, 4, "addip");
+ if (addr->proto == QETH_PROT_IPV4)
+ QETH_CARD_HEX(card, 4, &addr->u.a4.addr, 4);
+ else {
+ QETH_CARD_HEX(card, 4, &addr->u.a6.addr, 8);
+ QETH_CARD_HEX(card, 4, ((char *)&addr->u.a6.addr) + 8, 8);
+ }
+ spin_lock_irqsave(&card->ip_lock, flags);
+ rc = __qeth_l3_insert_ip_todo(card, addr, 1);
+ spin_unlock_irqrestore(&card->ip_lock, flags);
+ return rc;
+}
+
+
+struct qeth_ipaddr *qeth_l3_get_addr_buffer(
+ enum qeth_prot_versions prot)
+{
+ struct qeth_ipaddr *addr;
+
+ addr = kzalloc(sizeof(struct qeth_ipaddr), GFP_ATOMIC);
+ if (addr == NULL) {
+ return NULL;
+ }
+ addr->type = QETH_IP_TYPE_NORMAL;
+ addr->proto = prot;
+ return addr;
+}
+
+static void qeth_l3_delete_mc_addresses(struct qeth_card *card)
+{
+ struct qeth_ipaddr *iptodo;
+ unsigned long flags;
+
+ QETH_CARD_TEXT(card, 4, "delmc");
+ iptodo = qeth_l3_get_addr_buffer(QETH_PROT_IPV4);
+ if (!iptodo) {
+ QETH_CARD_TEXT(card, 2, "dmcnomem");
+ return;
+ }
+ iptodo->type = QETH_IP_TYPE_DEL_ALL_MC;
+ spin_lock_irqsave(&card->ip_lock, flags);
+ if (!__qeth_l3_insert_ip_todo(card, iptodo, 0))
+ kfree(iptodo);
+ spin_unlock_irqrestore(&card->ip_lock, flags);
+}
+
+/*
+ * 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_l3_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)
+ todo->users = 1;
+ return 1;
+ } else
+ return 0;
+}
+
+static void __qeth_l3_delete_all_mc(struct qeth_card *card,
+ unsigned long *flags)
+{
+ struct list_head fail_list;
+ struct qeth_ipaddr *addr, *tmp;
+ int rc;
+
+ INIT_LIST_HEAD(&fail_list);
+again:
+ list_for_each_entry_safe(addr, tmp, &card->ip_list, entry) {
+ if (addr->is_multicast) {
+ list_del(&addr->entry);
+ spin_unlock_irqrestore(&card->ip_lock, *flags);
+ rc = qeth_l3_deregister_addr_entry(card, addr);
+ spin_lock_irqsave(&card->ip_lock, *flags);
+ if (!rc || (rc == IPA_RC_MC_ADDR_NOT_FOUND))
+ kfree(addr);
+ else
+ list_add_tail(&addr->entry, &fail_list);
+ goto again;
+ }
+ }
+ list_splice(&fail_list, &card->ip_list);
+}
+
+void qeth_l3_set_ip_addr_list(struct qeth_card *card)
+{
+ struct list_head *tbd_list;
+ struct qeth_ipaddr *todo, *addr;
+ unsigned long flags;
+ int rc;
+
+ QETH_CARD_TEXT(card, 2, "sdiplist");
+ QETH_CARD_HEX(card, 2, &card, sizeof(void *));
+
+ if ((card->state != CARD_STATE_UP &&
+ card->state != CARD_STATE_SOFTSETUP) || card->options.sniffer) {
+ return;
+ }
+
+ spin_lock_irqsave(&card->ip_lock, flags);
+ tbd_list = card->ip_tbd_list;
+ card->ip_tbd_list = kzalloc(sizeof(struct list_head), GFP_ATOMIC);
+ if (!card->ip_tbd_list) {
+ QETH_CARD_TEXT(card, 0, "silnomem");
+ card->ip_tbd_list = tbd_list;
+ spin_unlock_irqrestore(&card->ip_lock, flags);
+ return;
+ } else
+ INIT_LIST_HEAD(card->ip_tbd_list);
+
+ while (!list_empty(tbd_list)) {
+ todo = list_entry(tbd_list->next, struct qeth_ipaddr, entry);
+ list_del(&todo->entry);
+ if (todo->type == QETH_IP_TYPE_DEL_ALL_MC) {
+ __qeth_l3_delete_all_mc(card, &flags);
+ kfree(todo);
+ continue;
+ }
+ rc = __qeth_l3_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_l3_register_addr_entry(card, todo);
+ spin_lock_irqsave(&card->ip_lock, flags);
+ if (!rc || (rc == IPA_RC_LAN_OFFLINE))
+ list_add_tail(&todo->entry, &card->ip_list);
+ else
+ kfree(todo);
+ } else if (rc == -1) {
+ /* on-card entry to be removed */
+ list_del_init(&addr->entry);
+ spin_unlock_irqrestore(&card->ip_lock, flags);
+ rc = qeth_l3_deregister_addr_entry(card, addr);
+ spin_lock_irqsave(&card->ip_lock, flags);
+ if (!rc || (rc == IPA_RC_IP_ADDRESS_NOT_DEFINED))
+ kfree(addr);
+ else
+ list_add_tail(&addr->entry, &card->ip_list);
+ kfree(todo);
+ }
+ }
+ spin_unlock_irqrestore(&card->ip_lock, flags);
+ kfree(tbd_list);
+}
+
+static void qeth_l3_clear_ip_list(struct qeth_card *card, int recover)
+{
+ struct qeth_ipaddr *addr, *tmp;
+ unsigned long flags;
+
+ QETH_CARD_TEXT(card, 4, "clearip");
+ if (recover && card->options.sniffer)
+ return;
+ 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);
+ }
+
+ while (!list_empty(&card->ip_list)) {
+ addr = list_entry(card->ip_list.next,
+ struct qeth_ipaddr, entry);
+ list_del_init(&addr->entry);
+ if (!recover || addr->is_multicast) {
+ kfree(addr);
+ continue;
+ }
+ list_add_tail(&addr->entry, card->ip_tbd_list);
+ }
+ spin_unlock_irqrestore(&card->ip_lock, flags);
+}
+
+static int qeth_l3_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;
+}
+
+static int qeth_l3_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_CARD_TEXT(card, 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 void qeth_l3_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_l3_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_CARD_TEXT(card, 4, "setdelip");
+ QETH_CARD_TEXT_(card, 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_l3_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_l3_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_CARD_TEXT(card, 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;
+}
+
+static int qeth_l3_correct_routing_type(struct qeth_card *card,
+ enum qeth_routing_types *type, enum qeth_prot_versions prot)
+{
+ if (card->info.type == QETH_CARD_TYPE_IQD) {
+ switch (*type) {
+ case NO_ROUTER:
+ case PRIMARY_CONNECTOR:
+ case SECONDARY_CONNECTOR:
+ case MULTICAST_ROUTER:
+ return 0;
+ default:
+ goto out_inval;
+ }
+ } else {
+ switch (*type) {
+ case NO_ROUTER:
+ case PRIMARY_ROUTER:
+ case SECONDARY_ROUTER:
+ return 0;
+ case MULTICAST_ROUTER:
+ if (qeth_is_ipafunc_supported(card, prot,
+ IPA_OSA_MC_ROUTER))
+ return 0;
+ default:
+ goto out_inval;
+ }
+ }
+out_inval:
+ *type = NO_ROUTER;
+ return -EINVAL;
+}
+
+int qeth_l3_setrouting_v4(struct qeth_card *card)
+{
+ int rc;
+
+ QETH_CARD_TEXT(card, 3, "setrtg4");
+
+ rc = qeth_l3_correct_routing_type(card, &card->options.route4.type,
+ QETH_PROT_IPV4);
+ if (rc)
+ return rc;
+
+ rc = qeth_l3_send_setrouting(card, card->options.route4.type,
+ QETH_PROT_IPV4);
+ if (rc) {
+ card->options.route4.type = NO_ROUTER;
+ QETH_DBF_MESSAGE(2, "Error (0x%04x) while setting routing type"
+ " on %s. Type set to 'no router'.\n", rc,
+ QETH_CARD_IFNAME(card));
+ }
+ return rc;
+}
+
+int qeth_l3_setrouting_v6(struct qeth_card *card)
+{
+ int rc = 0;
+
+ QETH_CARD_TEXT(card, 3, "setrtg6");
+#ifdef CONFIG_QETH_IPV6
+
+ if (!qeth_is_supported(card, IPA_IPV6))
+ return 0;
+ rc = qeth_l3_correct_routing_type(card, &card->options.route6.type,
+ QETH_PROT_IPV6);
+ if (rc)
+ return rc;
+
+ rc = qeth_l3_send_setrouting(card, card->options.route6.type,
+ QETH_PROT_IPV6);
+ if (rc) {
+ card->options.route6.type = NO_ROUTER;
+ QETH_DBF_MESSAGE(2, "Error (0x%04x) while setting routing type"
+ " on %s. Type set to 'no router'.\n", rc,
+ QETH_CARD_IFNAME(card));
+ }
+#endif
+ return rc;
+}
+
+/*
+ * IP address takeover related functions
+ */
+static void qeth_l3_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_l3_add_ipato_entry(struct qeth_card *card,
+ struct qeth_ipato_entry *new)
+{
+ struct qeth_ipato_entry *ipatoe;
+ unsigned long flags;
+ int rc = 0;
+
+ QETH_CARD_TEXT(card, 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)) {
+ rc = -EEXIST;
+ break;
+ }
+ }
+ if (!rc)
+ list_add_tail(&new->entry, &card->ipato.entries);
+
+ spin_unlock_irqrestore(&card->ip_lock, flags);
+ return rc;
+}
+
+void qeth_l3_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_CARD_TEXT(card, 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);
+}
+
+/*
+ * VIPA related functions
+ */
+int qeth_l3_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_l3_get_addr_buffer(proto);
+ if (ipaddr) {
+ if (proto == QETH_PROT_IPV4) {
+ QETH_CARD_TEXT(card, 2, "addvipa4");
+ memcpy(&ipaddr->u.a4.addr, addr, 4);
+ ipaddr->u.a4.mask = 0;
+ } else if (proto == QETH_PROT_IPV6) {
+ QETH_CARD_TEXT(card, 2, "addvipa6");
+ memcpy(&ipaddr->u.a6.addr, addr, 16);
+ ipaddr->u.a6.pfxlen = 0;
+ }
+ 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_l3_address_exists_in_list(&card->ip_list, ipaddr, 0) ||
+ qeth_l3_address_exists_in_list(card->ip_tbd_list, ipaddr, 0))
+ rc = -EEXIST;
+ spin_unlock_irqrestore(&card->ip_lock, flags);
+ if (rc) {
+ kfree(ipaddr);
+ return rc;
+ }
+ if (!qeth_l3_add_ip(card, ipaddr))
+ kfree(ipaddr);
+ qeth_l3_set_ip_addr_list(card);
+ return rc;
+}
+
+void qeth_l3_del_vipa(struct qeth_card *card, enum qeth_prot_versions proto,
+ const u8 *addr)
+{
+ struct qeth_ipaddr *ipaddr;
+
+ ipaddr = qeth_l3_get_addr_buffer(proto);
+ if (ipaddr) {
+ if (proto == QETH_PROT_IPV4) {
+ QETH_CARD_TEXT(card, 2, "delvipa4");
+ memcpy(&ipaddr->u.a4.addr, addr, 4);
+ ipaddr->u.a4.mask = 0;
+ } else if (proto == QETH_PROT_IPV6) {
+ QETH_CARD_TEXT(card, 2, "delvipa6");
+ memcpy(&ipaddr->u.a6.addr, addr, 16);
+ ipaddr->u.a6.pfxlen = 0;
+ }
+ ipaddr->type = QETH_IP_TYPE_VIPA;
+ } else
+ return;
+ if (!qeth_l3_delete_ip(card, ipaddr))
+ kfree(ipaddr);
+ qeth_l3_set_ip_addr_list(card);
+}
+
+/*
+ * proxy ARP related functions
+ */
+int qeth_l3_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_l3_get_addr_buffer(proto);
+ if (ipaddr) {
+ if (proto == QETH_PROT_IPV4) {
+ QETH_CARD_TEXT(card, 2, "addrxip4");
+ memcpy(&ipaddr->u.a4.addr, addr, 4);
+ ipaddr->u.a4.mask = 0;
+ } else if (proto == QETH_PROT_IPV6) {
+ QETH_CARD_TEXT(card, 2, "addrxip6");
+ memcpy(&ipaddr->u.a6.addr, addr, 16);
+ ipaddr->u.a6.pfxlen = 0;
+ }
+ 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_l3_address_exists_in_list(&card->ip_list, ipaddr, 0) ||
+ qeth_l3_address_exists_in_list(card->ip_tbd_list, ipaddr, 0))
+ rc = -EEXIST;
+ spin_unlock_irqrestore(&card->ip_lock, flags);
+ if (rc) {
+ kfree(ipaddr);
+ return rc;
+ }
+ if (!qeth_l3_add_ip(card, ipaddr))
+ kfree(ipaddr);
+ qeth_l3_set_ip_addr_list(card);
+ return 0;
+}
+
+void qeth_l3_del_rxip(struct qeth_card *card, enum qeth_prot_versions proto,
+ const u8 *addr)
+{
+ struct qeth_ipaddr *ipaddr;
+
+ ipaddr = qeth_l3_get_addr_buffer(proto);
+ if (ipaddr) {
+ if (proto == QETH_PROT_IPV4) {
+ QETH_CARD_TEXT(card, 2, "addrxip4");
+ memcpy(&ipaddr->u.a4.addr, addr, 4);
+ ipaddr->u.a4.mask = 0;
+ } else if (proto == QETH_PROT_IPV6) {
+ QETH_CARD_TEXT(card, 2, "addrxip6");
+ memcpy(&ipaddr->u.a6.addr, addr, 16);
+ ipaddr->u.a6.pfxlen = 0;
+ }
+ ipaddr->type = QETH_IP_TYPE_RXIP;
+ } else
+ return;
+ if (!qeth_l3_delete_ip(card, ipaddr))
+ kfree(ipaddr);
+ qeth_l3_set_ip_addr_list(card);
+}
+
+static int qeth_l3_register_addr_entry(struct qeth_card *card,
+ struct qeth_ipaddr *addr)
+{
+ char buf[50];
+ int rc = 0;
+ int cnt = 3;
+
+ if (addr->proto == QETH_PROT_IPV4) {
+ QETH_CARD_TEXT(card, 2, "setaddr4");
+ QETH_CARD_HEX(card, 3, &addr->u.a4.addr, sizeof(int));
+ } else if (addr->proto == QETH_PROT_IPV6) {
+ QETH_CARD_TEXT(card, 2, "setaddr6");
+ QETH_CARD_HEX(card, 3, &addr->u.a6.addr, 8);
+ QETH_CARD_HEX(card, 3, ((char *)&addr->u.a6.addr) + 8, 8);
+ } else {
+ QETH_CARD_TEXT(card, 2, "setaddr?");
+ QETH_CARD_HEX(card, 3, addr, sizeof(struct qeth_ipaddr));
+ }
+ do {
+ if (addr->is_multicast)
+ rc = qeth_l3_send_setdelmc(card, addr, IPA_CMD_SETIPM);
+ else
+ rc = qeth_l3_send_setdelip(card, addr, IPA_CMD_SETIP,
+ addr->set_flags);
+ if (rc)
+ QETH_CARD_TEXT(card, 2, "failed");
+ } while ((--cnt > 0) && rc);
+ if (rc) {
+ QETH_CARD_TEXT(card, 2, "FAILED");
+ qeth_l3_ipaddr_to_string(addr->proto, (u8 *)&addr->u, buf);
+ dev_warn(&card->gdev->dev,
+ "Registering IP address %s failed\n", buf);
+ }
+ return rc;
+}
+
+static int qeth_l3_deregister_addr_entry(struct qeth_card *card,
+ struct qeth_ipaddr *addr)
+{
+ int rc = 0;
+
+ if (addr->proto == QETH_PROT_IPV4) {
+ QETH_CARD_TEXT(card, 2, "deladdr4");
+ QETH_CARD_HEX(card, 3, &addr->u.a4.addr, sizeof(int));
+ } else if (addr->proto == QETH_PROT_IPV6) {
+ QETH_CARD_TEXT(card, 2, "deladdr6");
+ QETH_CARD_HEX(card, 3, &addr->u.a6.addr, 8);
+ QETH_CARD_HEX(card, 3, ((char *)&addr->u.a6.addr) + 8, 8);
+ } else {
+ QETH_CARD_TEXT(card, 2, "deladdr?");
+ QETH_CARD_HEX(card, 3, addr, sizeof(struct qeth_ipaddr));
+ }
+ if (addr->is_multicast)
+ rc = qeth_l3_send_setdelmc(card, addr, IPA_CMD_DELIPM);
+ else
+ rc = qeth_l3_send_setdelip(card, addr, IPA_CMD_DELIP,
+ addr->del_flags);
+ if (rc)
+ QETH_CARD_TEXT(card, 2, "failed");
+
+ return rc;
+}
+
+static inline u8 qeth_l3_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_l3_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 int qeth_l3_setadapter_parms(struct qeth_card *card)
+{
+ int rc;
+
+ QETH_DBF_TEXT(SETUP, 2, "setadprm");
+
+ if (!qeth_is_supported(card, IPA_SETADAPTERPARMS)) {
+ dev_info(&card->gdev->dev,
+ "set adapter parameters not supported.\n");
+ QETH_DBF_TEXT(SETUP, 2, " notsupp");
+ return 0;
+ }
+ rc = qeth_query_setadapterparms(card);
+ if (rc) {
+ QETH_DBF_MESSAGE(2, "%s couldn't set adapter parameters: "
+ "0x%x\n", dev_name(&card->gdev->dev), rc);
+ return rc;
+ }
+ if (qeth_adp_supported(card, IPA_SETADP_ALTER_MAC_ADDRESS)) {
+ rc = qeth_setadpparms_change_macaddr(card);
+ if (rc)
+ dev_warn(&card->gdev->dev, "Reading the adapter MAC"
+ " address failed\n");
+ }
+
+ return rc;
+}
+
+static int qeth_l3_default_setassparms_cb(struct qeth_card *card,
+ struct qeth_reply *reply, unsigned long data)
+{
+ struct qeth_ipa_cmd *cmd;
+
+ QETH_CARD_TEXT(card, 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;
+ if (cmd->hdr.prot_version == QETH_PROT_IPV6)
+ card->options.ipa6.enabled_funcs = cmd->hdr.ipa_enabled;
+ }
+ 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_CARD_TEXT_(card, 3, "csum:%d", card->info.csum_mask);
+ }
+ if (cmd->data.setassparms.hdr.assist_no == IPA_OUTBOUND_CHECKSUM &&
+ cmd->data.setassparms.hdr.command_code == IPA_CMD_ASS_START) {
+ card->info.tx_csum_mask =
+ cmd->data.setassparms.data.flags_32bit;
+ QETH_CARD_TEXT_(card, 3, "tcsu:%d", card->info.tx_csum_mask);
+ }
+
+ return 0;
+}
+
+static struct qeth_cmd_buffer *qeth_l3_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_CARD_TEXT(card, 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_l3_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_CARD_TEXT(card, 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 /* (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_l3_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_CARD_TEXT(card, 4, "simassp6");
+ iob = qeth_l3_get_setassparms_cmd(card, ipa_func, cmd_code,
+ 0, QETH_PROT_IPV6);
+ rc = qeth_l3_send_setassparms(card, iob, 0, 0,
+ qeth_l3_default_setassparms_cb, NULL);
+ return rc;
+}
+#endif
+
+static int qeth_l3_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_CARD_TEXT(card, 4, "simassp4");
+ if (data)
+ length = sizeof(__u32);
+ iob = qeth_l3_get_setassparms_cmd(card, ipa_func, cmd_code,
+ length, QETH_PROT_IPV4);
+ rc = qeth_l3_send_setassparms(card, iob, length, data,
+ qeth_l3_default_setassparms_cb, NULL);
+ return rc;
+}
+
+static int qeth_l3_start_ipa_arp_processing(struct qeth_card *card)
+{
+ int rc;
+
+ QETH_CARD_TEXT(card, 3, "ipaarp");
+
+ if (!qeth_is_supported(card, IPA_ARP_PROCESSING)) {
+ dev_info(&card->gdev->dev,
+ "ARP processing not supported on %s!\n",
+ QETH_CARD_IFNAME(card));
+ return 0;
+ }
+ rc = qeth_l3_send_simple_setassparms(card, IPA_ARP_PROCESSING,
+ IPA_CMD_ASS_START, 0);
+ if (rc) {
+ dev_warn(&card->gdev->dev,
+ "Starting ARP processing support for %s failed\n",
+ QETH_CARD_IFNAME(card));
+ }
+ return rc;
+}
+
+static int qeth_l3_start_ipa_ip_fragmentation(struct qeth_card *card)
+{
+ int rc;
+
+ QETH_CARD_TEXT(card, 3, "ipaipfrg");
+
+ if (!qeth_is_supported(card, IPA_IP_FRAGMENTATION)) {
+ dev_info(&card->gdev->dev,
+ "Hardware IP fragmentation not supported on %s\n",
+ QETH_CARD_IFNAME(card));
+ return -EOPNOTSUPP;
+ }
+
+ rc = qeth_l3_send_simple_setassparms(card, IPA_IP_FRAGMENTATION,
+ IPA_CMD_ASS_START, 0);
+ if (rc) {
+ dev_warn(&card->gdev->dev,
+ "Starting IP fragmentation support for %s failed\n",
+ QETH_CARD_IFNAME(card));
+ } else
+ dev_info(&card->gdev->dev,
+ "Hardware IP fragmentation enabled \n");
+ return rc;
+}
+
+static int qeth_l3_start_ipa_source_mac(struct qeth_card *card)
+{
+ int rc;
+
+ QETH_CARD_TEXT(card, 3, "stsrcmac");
+
+ if (!qeth_is_supported(card, IPA_SOURCE_MAC)) {
+ dev_info(&card->gdev->dev,
+ "Inbound source MAC-address not supported on %s\n",
+ QETH_CARD_IFNAME(card));
+ return -EOPNOTSUPP;
+ }
+
+ rc = qeth_l3_send_simple_setassparms(card, IPA_SOURCE_MAC,
+ IPA_CMD_ASS_START, 0);
+ if (rc)
+ dev_warn(&card->gdev->dev,
+ "Starting source MAC-address support for %s failed\n",
+ QETH_CARD_IFNAME(card));
+ return rc;
+}
+
+static int qeth_l3_start_ipa_vlan(struct qeth_card *card)
+{
+ int rc = 0;
+
+ QETH_CARD_TEXT(card, 3, "strtvlan");
+
+ if (!qeth_is_supported(card, IPA_FULL_VLAN)) {
+ dev_info(&card->gdev->dev,
+ "VLAN not supported on %s\n", QETH_CARD_IFNAME(card));
+ return -EOPNOTSUPP;
+ }
+
+ rc = qeth_l3_send_simple_setassparms(card, IPA_VLAN_PRIO,
+ IPA_CMD_ASS_START, 0);
+ if (rc) {
+ dev_warn(&card->gdev->dev,
+ "Starting VLAN support for %s failed\n",
+ QETH_CARD_IFNAME(card));
+ } else {
+ dev_info(&card->gdev->dev, "VLAN enabled\n");
+ }
+ return rc;
+}
+
+static int qeth_l3_start_ipa_multicast(struct qeth_card *card)
+{
+ int rc;
+
+ QETH_CARD_TEXT(card, 3, "stmcast");
+
+ if (!qeth_is_supported(card, IPA_MULTICASTING)) {
+ dev_info(&card->gdev->dev,
+ "Multicast not supported on %s\n",
+ QETH_CARD_IFNAME(card));
+ return -EOPNOTSUPP;
+ }
+
+ rc = qeth_l3_send_simple_setassparms(card, IPA_MULTICASTING,
+ IPA_CMD_ASS_START, 0);
+ if (rc) {
+ dev_warn(&card->gdev->dev,
+ "Starting multicast support for %s failed\n",
+ QETH_CARD_IFNAME(card));
+ } else {
+ dev_info(&card->gdev->dev, "Multicast enabled\n");
+ card->dev->flags |= IFF_MULTICAST;
+ }
+ return rc;
+}
+
+#ifdef CONFIG_QETH_IPV6
+static int qeth_l3_softsetup_ipv6(struct qeth_card *card)
+{
+ int rc;
+
+ QETH_CARD_TEXT(card, 3, "softipv6");
+
+ if (card->info.type == QETH_CARD_TYPE_IQD)
+ goto out;
+
+ rc = qeth_query_ipassists(card, QETH_PROT_IPV6);
+ if (rc) {
+ dev_err(&card->gdev->dev,
+ "Activating IPv6 support for %s failed\n",
+ QETH_CARD_IFNAME(card));
+ return rc;
+ }
+ rc = qeth_l3_send_simple_setassparms(card, IPA_IPV6,
+ IPA_CMD_ASS_START, 3);
+ if (rc) {
+ dev_err(&card->gdev->dev,
+ "Activating IPv6 support for %s failed\n",
+ QETH_CARD_IFNAME(card));
+ return rc;
+ }
+ rc = qeth_l3_send_simple_setassparms_ipv6(card, IPA_IPV6,
+ IPA_CMD_ASS_START);
+ if (rc) {
+ dev_err(&card->gdev->dev,
+ "Activating IPv6 support for %s failed\n",
+ QETH_CARD_IFNAME(card));
+ return rc;
+ }
+ rc = qeth_l3_send_simple_setassparms_ipv6(card, IPA_PASSTHRU,
+ IPA_CMD_ASS_START);
+ if (rc) {
+ dev_warn(&card->gdev->dev,
+ "Enabling the passthrough mode for %s failed\n",
+ QETH_CARD_IFNAME(card));
+ return rc;
+ }
+out:
+ dev_info(&card->gdev->dev, "IPV6 enabled\n");
+ return 0;
+}
+#endif
+
+static int qeth_l3_start_ipa_ipv6(struct qeth_card *card)
+{
+ int rc = 0;
+
+ QETH_CARD_TEXT(card, 3, "strtipv6");
+
+ if (!qeth_is_supported(card, IPA_IPV6)) {
+ dev_info(&card->gdev->dev,
+ "IPv6 not supported on %s\n", QETH_CARD_IFNAME(card));
+ return 0;
+ }
+#ifdef CONFIG_QETH_IPV6
+ rc = qeth_l3_softsetup_ipv6(card);
+#endif
+ return rc ;
+}
+
+static int qeth_l3_start_ipa_broadcast(struct qeth_card *card)
+{
+ int rc;
+
+ QETH_CARD_TEXT(card, 3, "stbrdcst");
+ card->info.broadcast_capable = 0;
+ if (!qeth_is_supported(card, IPA_FILTERING)) {
+ dev_info(&card->gdev->dev,
+ "Broadcast not supported on %s\n",
+ QETH_CARD_IFNAME(card));
+ rc = -EOPNOTSUPP;
+ goto out;
+ }
+ rc = qeth_l3_send_simple_setassparms(card, IPA_FILTERING,
+ IPA_CMD_ASS_START, 0);
+ if (rc) {
+ dev_warn(&card->gdev->dev, "Enabling broadcast filtering for "
+ "%s failed\n", QETH_CARD_IFNAME(card));
+ goto out;
+ }
+
+ rc = qeth_l3_send_simple_setassparms(card, IPA_FILTERING,
+ IPA_CMD_ASS_CONFIGURE, 1);
+ if (rc) {
+ dev_warn(&card->gdev->dev,
+ "Setting up broadcast filtering for %s failed\n",
+ QETH_CARD_IFNAME(card));
+ goto out;
+ }
+ card->info.broadcast_capable = QETH_BROADCAST_WITH_ECHO;
+ dev_info(&card->gdev->dev, "Broadcast enabled\n");
+ rc = qeth_l3_send_simple_setassparms(card, IPA_FILTERING,
+ IPA_CMD_ASS_ENABLE, 1);
+ if (rc) {
+ dev_warn(&card->gdev->dev, "Setting up broadcast echo "
+ "filtering for %s failed\n", QETH_CARD_IFNAME(card));
+ goto out;
+ }
+ card->info.broadcast_capable = QETH_BROADCAST_WITHOUT_ECHO;
+out:
+ if (card->info.broadcast_capable)
+ card->dev->flags |= IFF_BROADCAST;
+ else
+ card->dev->flags &= ~IFF_BROADCAST;
+ return rc;
+}
+
+static int qeth_l3_send_checksum_command(struct qeth_card *card)
+{
+ int rc;
+
+ rc = qeth_l3_send_simple_setassparms(card, IPA_INBOUND_CHECKSUM,
+ IPA_CMD_ASS_START, 0);
+ if (rc) {
+ dev_warn(&card->gdev->dev, "Starting HW checksumming for %s "
+ "failed, using SW checksumming\n",
+ QETH_CARD_IFNAME(card));
+ return rc;
+ }
+ rc = qeth_l3_send_simple_setassparms(card, IPA_INBOUND_CHECKSUM,
+ IPA_CMD_ASS_ENABLE,
+ card->info.csum_mask);
+ if (rc) {
+ dev_warn(&card->gdev->dev, "Enabling HW checksumming for %s "
+ "failed, using SW checksumming\n",
+ QETH_CARD_IFNAME(card));
+ return rc;
+ }
+ return 0;
+}
+
+static int qeth_l3_set_rx_csum(struct qeth_card *card, int on)
+{
+ int rc = 0;
+
+ if (on) {
+ rc = qeth_l3_send_checksum_command(card);
+ if (rc)
+ return -EIO;
+ dev_info(&card->gdev->dev,
+ "HW Checksumming (inbound) enabled\n");
+ } else {
+ rc = qeth_l3_send_simple_setassparms(card,
+ IPA_INBOUND_CHECKSUM, IPA_CMD_ASS_STOP, 0);
+ if (rc)
+ return -EIO;
+ }
+
+ return 0;
+}
+
+static int qeth_l3_start_ipa_checksum(struct qeth_card *card)
+{
+ QETH_CARD_TEXT(card, 3, "strtcsum");
+
+ if (card->dev->features & NETIF_F_RXCSUM) {
+ rtnl_lock();
+ /* force set_features call */
+ card->dev->features &= ~NETIF_F_RXCSUM;
+ netdev_update_features(card->dev);
+ rtnl_unlock();
+ }
+ return 0;
+}
+
+static int qeth_l3_start_ipa_tx_checksum(struct qeth_card *card)
+{
+ int rc = 0;
+
+ if (!qeth_is_supported(card, IPA_OUTBOUND_CHECKSUM))
+ return rc;
+ rc = qeth_l3_send_simple_setassparms(card, IPA_OUTBOUND_CHECKSUM,
+ IPA_CMD_ASS_START, 0);
+ if (rc)
+ goto err_out;
+ rc = qeth_l3_send_simple_setassparms(card, IPA_OUTBOUND_CHECKSUM,
+ IPA_CMD_ASS_ENABLE, card->info.tx_csum_mask);
+ if (rc)
+ goto err_out;
+ dev_info(&card->gdev->dev, "HW TX Checksumming enabled\n");
+ return rc;
+err_out:
+ dev_warn(&card->gdev->dev, "Enabling HW TX checksumming for %s "
+ "failed, using SW TX checksumming\n", QETH_CARD_IFNAME(card));
+ return rc;
+}
+
+static int qeth_l3_start_ipa_tso(struct qeth_card *card)
+{
+ int rc;
+
+ QETH_CARD_TEXT(card, 3, "sttso");
+
+ if (!qeth_is_supported(card, IPA_OUTBOUND_TSO)) {
+ dev_info(&card->gdev->dev,
+ "Outbound TSO not supported on %s\n",
+ QETH_CARD_IFNAME(card));
+ rc = -EOPNOTSUPP;
+ } else {
+ rc = qeth_l3_send_simple_setassparms(card, IPA_OUTBOUND_TSO,
+ IPA_CMD_ASS_START, 0);
+ if (rc)
+ dev_warn(&card->gdev->dev, "Starting outbound TCP "
+ "segmentation offload for %s failed\n",
+ QETH_CARD_IFNAME(card));
+ else
+ dev_info(&card->gdev->dev,
+ "Outbound TSO enabled\n");
+ }
+ if (rc)
+ card->dev->features &= ~NETIF_F_TSO;
+ return rc;
+}
+
+static int qeth_l3_start_ipassists(struct qeth_card *card)
+{
+ QETH_CARD_TEXT(card, 3, "strtipas");
+
+ if (qeth_set_access_ctrl_online(card, 0))
+ return -EIO;
+ qeth_l3_start_ipa_arp_processing(card); /* go on*/
+ qeth_l3_start_ipa_ip_fragmentation(card); /* go on*/
+ qeth_l3_start_ipa_source_mac(card); /* go on*/
+ qeth_l3_start_ipa_vlan(card); /* go on*/
+ qeth_l3_start_ipa_multicast(card); /* go on*/
+ qeth_l3_start_ipa_ipv6(card); /* go on*/
+ qeth_l3_start_ipa_broadcast(card); /* go on*/
+ qeth_l3_start_ipa_checksum(card); /* go on*/
+ qeth_l3_start_ipa_tx_checksum(card);
+ qeth_l3_start_ipa_tso(card); /* go on*/
+ return 0;
+}
+
+static int qeth_l3_iqd_read_initial_mac_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)
+ memcpy(card->dev->dev_addr,
+ cmd->data.create_destroy_addr.unique_id, ETH_ALEN);
+ else
+ eth_random_addr(card->dev->dev_addr);
+
+ return 0;
+}
+
+static int qeth_l3_iqd_read_initial_mac(struct qeth_card *card)
+{
+ int rc = 0;
+ struct qeth_cmd_buffer *iob;
+ struct qeth_ipa_cmd *cmd;
+
+ QETH_DBF_TEXT(SETUP, 2, "hsrmac");
+
+ 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_l3_iqd_read_initial_mac_cb,
+ NULL);
+ return rc;
+}
+
+static int qeth_l3_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;
+ dev_warn(&card->gdev->dev, "The network adapter failed to "
+ "generate a unique ID\n");
+ }
+ return 0;
+}
+
+static int qeth_l3_get_unique_id(struct qeth_card *card)
+{
+ int rc = 0;
+ 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_l3_get_unique_id_cb, NULL);
+ return rc;
+}
+
+static int
+qeth_diags_trace_cb(struct qeth_card *card, struct qeth_reply *reply,
+ unsigned long data)
+{
+ struct qeth_ipa_cmd *cmd;
+ __u16 rc;
+
+ QETH_DBF_TEXT(SETUP, 2, "diastrcb");
+
+ cmd = (struct qeth_ipa_cmd *)data;
+ rc = cmd->hdr.return_code;
+ if (rc)
+ QETH_CARD_TEXT_(card, 2, "dxter%x", rc);
+ switch (cmd->data.diagass.action) {
+ case QETH_DIAGS_CMD_TRACE_QUERY:
+ break;
+ case QETH_DIAGS_CMD_TRACE_DISABLE:
+ switch (rc) {
+ case 0:
+ case IPA_RC_INVALID_SUBCMD:
+ card->info.promisc_mode = SET_PROMISC_MODE_OFF;
+ dev_info(&card->gdev->dev, "The HiperSockets network "
+ "traffic analyzer is deactivated\n");
+ break;
+ default:
+ break;
+ }
+ break;
+ case QETH_DIAGS_CMD_TRACE_ENABLE:
+ switch (rc) {
+ case 0:
+ card->info.promisc_mode = SET_PROMISC_MODE_ON;
+ dev_info(&card->gdev->dev, "The HiperSockets network "
+ "traffic analyzer is activated\n");
+ break;
+ case IPA_RC_HARDWARE_AUTH_ERROR:
+ dev_warn(&card->gdev->dev, "The device is not "
+ "authorized to run as a HiperSockets network "
+ "traffic analyzer\n");
+ break;
+ case IPA_RC_TRACE_ALREADY_ACTIVE:
+ dev_warn(&card->gdev->dev, "A HiperSockets "
+ "network traffic analyzer is already "
+ "active in the HiperSockets LAN\n");
+ break;
+ default:
+ break;
+ }
+ break;
+ default:
+ QETH_DBF_MESSAGE(2, "Unknown sniffer action (0x%04x) on %s\n",
+ cmd->data.diagass.action, QETH_CARD_IFNAME(card));
+ }
+
+ return 0;
+}
+
+static int
+qeth_diags_trace(struct qeth_card *card, enum qeth_diags_trace_cmds diags_cmd)
+{
+ struct qeth_cmd_buffer *iob;
+ struct qeth_ipa_cmd *cmd;
+
+ QETH_DBF_TEXT(SETUP, 2, "diagtrac");
+
+ iob = qeth_get_ipacmd_buffer(card, IPA_CMD_SET_DIAG_ASS, 0);
+ cmd = (struct qeth_ipa_cmd *)(iob->data+IPA_PDU_HEADER_SIZE);
+ cmd->data.diagass.subcmd_len = 16;
+ cmd->data.diagass.subcmd = QETH_DIAGS_CMD_TRACE;
+ cmd->data.diagass.type = QETH_DIAGS_TYPE_HIPERSOCKET;
+ cmd->data.diagass.action = diags_cmd;
+ return qeth_send_ipa_cmd(card, iob, qeth_diags_trace_cb, NULL);
+}
+
+static void qeth_l3_get_mac_for_ipm(__u32 ipm, char *mac,
+ struct net_device *dev)
+{
+ ip_eth_mc_map(ipm, mac);
+}
+
+static void qeth_l3_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_CARD_TEXT(card, 4, "addmc");
+ for (im4 = rcu_dereference(in4_dev->mc_list); im4 != NULL;
+ im4 = rcu_dereference(im4->next_rcu)) {
+ qeth_l3_get_mac_for_ipm(im4->multiaddr, buf, in4_dev->dev);
+ ipm = qeth_l3_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_l3_add_ip(card, ipm))
+ kfree(ipm);
+ }
+}
+
+/* called with rcu_read_lock */
+static void qeth_l3_add_vlan_mc(struct qeth_card *card)
+{
+ struct in_device *in_dev;
+ u16 vid;
+
+ QETH_CARD_TEXT(card, 4, "addmcvl");
+ if (!qeth_is_supported(card, IPA_FULL_VLAN))
+ return;
+
+ for_each_set_bit(vid, card->active_vlans, VLAN_N_VID) {
+ struct net_device *netdev;
+
+ netdev = __vlan_find_dev_deep_rcu(card->dev, htons(ETH_P_8021Q),
+ vid);
+ if (netdev == NULL ||
+ !(netdev->flags & IFF_UP))
+ continue;
+ in_dev = __in_dev_get_rcu(netdev);
+ if (!in_dev)
+ continue;
+ qeth_l3_add_mc(card, in_dev);
+ }
+}
+
+static void qeth_l3_add_multicast_ipv4(struct qeth_card *card)
+{
+ struct in_device *in4_dev;
+
+ QETH_CARD_TEXT(card, 4, "chkmcv4");
+ rcu_read_lock();
+ in4_dev = __in_dev_get_rcu(card->dev);
+ if (in4_dev == NULL)
+ goto unlock;
+ qeth_l3_add_mc(card, in4_dev);
+ qeth_l3_add_vlan_mc(card);
+unlock:
+ rcu_read_unlock();
+}
+
+#ifdef CONFIG_QETH_IPV6
+static void qeth_l3_add_mc6(struct qeth_card *card, struct inet6_dev *in6_dev)
+{
+ struct qeth_ipaddr *ipm;
+ struct ifmcaddr6 *im6;
+ char buf[MAX_ADDR_LEN];
+
+ QETH_CARD_TEXT(card, 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_l3_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_l3_add_ip(card, ipm))
+ kfree(ipm);
+ }
+}
+
+/* called with rcu_read_lock */
+static void qeth_l3_add_vlan_mc6(struct qeth_card *card)
+{
+ struct inet6_dev *in_dev;
+ u16 vid;
+
+ QETH_CARD_TEXT(card, 4, "admc6vl");
+ if (!qeth_is_supported(card, IPA_FULL_VLAN))
+ return;
+
+ for_each_set_bit(vid, card->active_vlans, VLAN_N_VID) {
+ struct net_device *netdev;
+
+ netdev = __vlan_find_dev_deep_rcu(card->dev, htons(ETH_P_8021Q),
+ vid);
+ if (netdev == NULL ||
+ !(netdev->flags & IFF_UP))
+ continue;
+ in_dev = in6_dev_get(netdev);
+ if (!in_dev)
+ continue;
+ read_lock_bh(&in_dev->lock);
+ qeth_l3_add_mc6(card, in_dev);
+ read_unlock_bh(&in_dev->lock);
+ in6_dev_put(in_dev);
+ }
+}
+
+static void qeth_l3_add_multicast_ipv6(struct qeth_card *card)
+{
+ struct inet6_dev *in6_dev;
+
+ QETH_CARD_TEXT(card, 4, "chkmcv6");
+ if (!qeth_is_supported(card, IPA_IPV6))
+ return ;
+ in6_dev = in6_dev_get(card->dev);
+ if (in6_dev == NULL)
+ return;
+ rcu_read_lock();
+ read_lock_bh(&in6_dev->lock);
+ qeth_l3_add_mc6(card, in6_dev);
+ qeth_l3_add_vlan_mc6(card);
+ read_unlock_bh(&in6_dev->lock);
+ rcu_read_unlock();
+ in6_dev_put(in6_dev);
+}
+#endif /* CONFIG_QETH_IPV6 */
+
+static void qeth_l3_free_vlan_addresses4(struct qeth_card *card,
+ unsigned short vid)
+{
+ struct in_device *in_dev;
+ struct in_ifaddr *ifa;
+ struct qeth_ipaddr *addr;
+ struct net_device *netdev;
+
+ QETH_CARD_TEXT(card, 4, "frvaddr4");
+
+ netdev = __vlan_find_dev_deep_rcu(card->dev, htons(ETH_P_8021Q), vid);
+ if (!netdev)
+ return;
+ in_dev = in_dev_get(netdev);
+ if (!in_dev)
+ return;
+ for (ifa = in_dev->ifa_list; ifa; ifa = ifa->ifa_next) {
+ addr = qeth_l3_get_addr_buffer(QETH_PROT_IPV4);
+ if (addr) {
+ addr->u.a4.addr = ifa->ifa_address;
+ addr->u.a4.mask = ifa->ifa_mask;
+ addr->type = QETH_IP_TYPE_NORMAL;
+ if (!qeth_l3_delete_ip(card, addr))
+ kfree(addr);
+ }
+ }
+ in_dev_put(in_dev);
+}
+
+static void qeth_l3_free_vlan_addresses6(struct qeth_card *card,
+ unsigned short vid)
+{
+#ifdef CONFIG_QETH_IPV6
+ struct inet6_dev *in6_dev;
+ struct inet6_ifaddr *ifa;
+ struct qeth_ipaddr *addr;
+ struct net_device *netdev;
+
+ QETH_CARD_TEXT(card, 4, "frvaddr6");
+
+ netdev = __vlan_find_dev_deep_rcu(card->dev, htons(ETH_P_8021Q), vid);
+ if (!netdev)
+ return;
+ in6_dev = in6_dev_get(netdev);
+ if (!in6_dev)
+ return;
+ list_for_each_entry(ifa, &in6_dev->addr_list, if_list) {
+ addr = qeth_l3_get_addr_buffer(QETH_PROT_IPV6);
+ if (addr) {
+ memcpy(&addr->u.a6.addr, &ifa->addr,
+ sizeof(struct in6_addr));
+ addr->u.a6.pfxlen = ifa->prefix_len;
+ addr->type = QETH_IP_TYPE_NORMAL;
+ if (!qeth_l3_delete_ip(card, addr))
+ kfree(addr);
+ }
+ }
+ in6_dev_put(in6_dev);
+#endif /* CONFIG_QETH_IPV6 */
+}
+
+static void qeth_l3_free_vlan_addresses(struct qeth_card *card,
+ unsigned short vid)
+{
+ rcu_read_lock();
+ qeth_l3_free_vlan_addresses4(card, vid);
+ qeth_l3_free_vlan_addresses6(card, vid);
+ rcu_read_unlock();
+}
+
+static int qeth_l3_vlan_rx_add_vid(struct net_device *dev,
+ __be16 proto, u16 vid)
+{
+ struct qeth_card *card = dev->ml_priv;
+
+ set_bit(vid, card->active_vlans);
+ return 0;
+}
+
+static int qeth_l3_vlan_rx_kill_vid(struct net_device *dev,
+ __be16 proto, u16 vid)
+{
+ struct qeth_card *card = dev->ml_priv;
+ unsigned long flags;
+
+ QETH_CARD_TEXT_(card, 4, "kid:%d", vid);
+ if (qeth_wait_for_threads(card, QETH_RECOVER_THREAD)) {
+ QETH_CARD_TEXT(card, 3, "kidREC");
+ return 0;
+ }
+ spin_lock_irqsave(&card->vlanlock, flags);
+ /* unregister IP addresses of vlan device */
+ qeth_l3_free_vlan_addresses(card, vid);
+ clear_bit(vid, card->active_vlans);
+ spin_unlock_irqrestore(&card->vlanlock, flags);
+ qeth_l3_set_multicast_list(card->dev);
+ return 0;
+}
+
+static inline int qeth_l3_rebuild_skb(struct qeth_card *card,
+ struct sk_buff *skb, struct qeth_hdr *hdr,
+ unsigned short *vlan_id)
+{
+ __be16 prot;
+ struct iphdr *ip_hdr;
+ unsigned char tg_addr[MAX_ADDR_LEN];
+ int is_vlan = 0;
+
+ if (!(hdr->hdr.l3.flags & QETH_HDR_PASSTHRU)) {
+ prot = htons((hdr->hdr.l3.flags & QETH_HDR_IPV6)? ETH_P_IPV6 :
+ ETH_P_IP);
+ switch (hdr->hdr.l3.flags & QETH_HDR_CAST_MASK) {
+ case QETH_CAST_MULTICAST:
+ switch (prot) {
+#ifdef CONFIG_QETH_IPV6
+ case __constant_htons(ETH_P_IPV6):
+ ndisc_mc_map((struct in6_addr *)
+ skb->data + 24,
+ tg_addr, card->dev, 0);
+ break;
+#endif
+ case __constant_htons(ETH_P_IP):
+ ip_hdr = (struct iphdr *)skb->data;
+ ip_eth_mc_map(ip_hdr->daddr, tg_addr);
+ break;
+ default:
+ memcpy(tg_addr, card->dev->broadcast,
+ card->dev->addr_len);
+ }
+ card->stats.multicast++;
+ skb->pkt_type = PACKET_MULTICAST;
+ break;
+ case QETH_CAST_BROADCAST:
+ memcpy(tg_addr, card->dev->broadcast,
+ card->dev->addr_len);
+ card->stats.multicast++;
+ skb->pkt_type = PACKET_BROADCAST;
+ break;
+ case QETH_CAST_UNICAST:
+ case QETH_CAST_ANYCAST:
+ case QETH_CAST_NOCAST:
+ default:
+ if (card->options.sniffer)
+ skb->pkt_type = PACKET_OTHERHOST;
+ else
+ skb->pkt_type = PACKET_HOST;
+ memcpy(tg_addr, card->dev->dev_addr,
+ card->dev->addr_len);
+ }
+ if (hdr->hdr.l3.ext_flags & QETH_HDR_EXT_SRC_MAC_ADDR)
+ card->dev->header_ops->create(skb, card->dev, prot,
+ tg_addr, &hdr->hdr.l3.dest_addr[2],
+ card->dev->addr_len);
+ else
+ card->dev->header_ops->create(skb, card->dev, prot,
+ tg_addr, "FAKELL", card->dev->addr_len);
+ }
+
+ skb->protocol = eth_type_trans(skb, card->dev);
+
+ if (hdr->hdr.l3.ext_flags &
+ (QETH_HDR_EXT_VLAN_FRAME | QETH_HDR_EXT_INCLUDE_VLAN_TAG)) {
+ *vlan_id = (hdr->hdr.l3.ext_flags & QETH_HDR_EXT_VLAN_FRAME) ?
+ hdr->hdr.l3.vlan_id : *((u16 *)&hdr->hdr.l3.dest_addr[12]);
+ is_vlan = 1;
+ }
+
+ if (card->dev->features & NETIF_F_RXCSUM) {
+ if ((hdr->hdr.l3.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 = CHECKSUM_NONE;
+ } else
+ skb->ip_summed = CHECKSUM_NONE;
+
+ return is_vlan;
+}
+
+static int qeth_l3_process_inbound_buffer(struct qeth_card *card,
+ int budget, int *done)
+{
+ int work_done = 0;
+ struct sk_buff *skb;
+ struct qeth_hdr *hdr;
+ __u16 vlan_tag = 0;
+ int is_vlan;
+ unsigned int len;
+ __u16 magic;
+
+ *done = 0;
+ WARN_ON_ONCE(!budget);
+ while (budget) {
+ skb = qeth_core_get_next_skb(card,
+ &card->qdio.in_q->bufs[card->rx.b_index],
+ &card->rx.b_element, &card->rx.e_offset, &hdr);
+ if (!skb) {
+ *done = 1;
+ break;
+ }
+ skb->dev = card->dev;
+ switch (hdr->hdr.l3.id) {
+ case QETH_HEADER_TYPE_LAYER3:
+ magic = *(__u16 *)skb->data;
+ if ((card->info.type == QETH_CARD_TYPE_IQD) &&
+ (magic == ETH_P_AF_IUCV)) {
+ skb->protocol = ETH_P_AF_IUCV;
+ skb->pkt_type = PACKET_HOST;
+ skb->mac_header = NET_SKB_PAD;
+ skb->dev = card->dev;
+ len = skb->len;
+ card->dev->header_ops->create(skb, card->dev, 0,
+ card->dev->dev_addr, "FAKELL",
+ card->dev->addr_len);
+ netif_receive_skb(skb);
+ } else {
+ is_vlan = qeth_l3_rebuild_skb(card, skb, hdr,
+ &vlan_tag);
+ len = skb->len;
+ if (is_vlan && !card->options.sniffer)
+ __vlan_hwaccel_put_tag(skb,
+ htons(ETH_P_8021Q), vlan_tag);
+ napi_gro_receive(&card->napi, skb);
+ }
+ break;
+ case QETH_HEADER_TYPE_LAYER2: /* for HiperSockets sniffer */
+ skb->pkt_type = PACKET_HOST;
+ skb->protocol = eth_type_trans(skb, skb->dev);
+ len = skb->len;
+ netif_receive_skb(skb);
+ break;
+ default:
+ dev_kfree_skb_any(skb);
+ QETH_CARD_TEXT(card, 3, "inbunkno");
+ QETH_DBF_HEX(CTRL, 3, hdr, QETH_DBF_CTRL_LEN);
+ continue;
+ }
+ work_done++;
+ budget--;
+ card->stats.rx_packets++;
+ card->stats.rx_bytes += len;
+ }
+ return work_done;
+}
+
+static int qeth_l3_poll(struct napi_struct *napi, int budget)
+{
+ struct qeth_card *card = container_of(napi, struct qeth_card, napi);
+ int work_done = 0;
+ struct qeth_qdio_buffer *buffer;
+ int done;
+ int new_budget = budget;
+
+ if (card->options.performance_stats) {
+ card->perf_stats.inbound_cnt++;
+ card->perf_stats.inbound_start_time = qeth_get_micros();
+ }
+
+ while (1) {
+ if (!card->rx.b_count) {
+ card->rx.qdio_err = 0;
+ card->rx.b_count = qdio_get_next_buffers(
+ card->data.ccwdev, 0, &card->rx.b_index,
+ &card->rx.qdio_err);
+ if (card->rx.b_count <= 0) {
+ card->rx.b_count = 0;
+ break;
+ }
+ card->rx.b_element =
+ &card->qdio.in_q->bufs[card->rx.b_index]
+ .buffer->element[0];
+ card->rx.e_offset = 0;
+ }
+
+ while (card->rx.b_count) {
+ buffer = &card->qdio.in_q->bufs[card->rx.b_index];
+ if (!(card->rx.qdio_err &&
+ qeth_check_qdio_errors(card, buffer->buffer,
+ card->rx.qdio_err, "qinerr")))
+ work_done += qeth_l3_process_inbound_buffer(
+ card, new_budget, &done);
+ else
+ done = 1;
+
+ if (done) {
+ if (card->options.performance_stats)
+ card->perf_stats.bufs_rec++;
+ qeth_put_buffer_pool_entry(card,
+ buffer->pool_entry);
+ qeth_queue_input_buffer(card, card->rx.b_index);
+ card->rx.b_count--;
+ if (card->rx.b_count) {
+ card->rx.b_index =
+ (card->rx.b_index + 1) %
+ QDIO_MAX_BUFFERS_PER_Q;
+ card->rx.b_element =
+ &card->qdio.in_q
+ ->bufs[card->rx.b_index]
+ .buffer->element[0];
+ card->rx.e_offset = 0;
+ }
+ }
+
+ if (work_done >= budget)
+ goto out;
+ else
+ new_budget = budget - work_done;
+ }
+ }
+
+ napi_complete(napi);
+ if (qdio_start_irq(card->data.ccwdev, 0))
+ napi_schedule(&card->napi);
+out:
+ if (card->options.performance_stats)
+ card->perf_stats.inbound_time += qeth_get_micros() -
+ card->perf_stats.inbound_start_time;
+ return work_done;
+}
+
+static int qeth_l3_verify_vlan_dev(struct net_device *dev,
+ struct qeth_card *card)
+{
+ int rc = 0;
+ u16 vid;
+
+ for_each_set_bit(vid, card->active_vlans, VLAN_N_VID) {
+ struct net_device *netdev;
+
+ rcu_read_lock();
+ netdev = __vlan_find_dev_deep_rcu(card->dev, htons(ETH_P_8021Q),
+ vid);
+ rcu_read_unlock();
+ if (netdev == dev) {
+ rc = QETH_VLAN_CARD;
+ break;
+ }
+ }
+
+ if (rc && !(vlan_dev_real_dev(dev)->ml_priv == (void *)card))
+ return 0;
+
+ return rc;
+}
+
+static int qeth_l3_verify_dev(struct net_device *dev)
+{
+ struct qeth_card *card;
+ unsigned long flags;
+ int rc = 0;
+
+ read_lock_irqsave(&qeth_core_card_list.rwlock, flags);
+ list_for_each_entry(card, &qeth_core_card_list.list, list) {
+ if (card->dev == dev) {
+ rc = QETH_REAL_CARD;
+ break;
+ }
+ rc = qeth_l3_verify_vlan_dev(dev, card);
+ if (rc)
+ break;
+ }
+ read_unlock_irqrestore(&qeth_core_card_list.rwlock, flags);
+
+ return rc;
+}
+
+static struct qeth_card *qeth_l3_get_card_from_dev(struct net_device *dev)
+{
+ struct qeth_card *card = NULL;
+ int rc;
+
+ rc = qeth_l3_verify_dev(dev);
+ if (rc == QETH_REAL_CARD)
+ card = dev->ml_priv;
+ else if (rc == QETH_VLAN_CARD)
+ card = vlan_dev_real_dev(dev)->ml_priv;
+ if (card && card->options.layer2)
+ card = NULL;
+ if (card)
+ QETH_CARD_TEXT_(card, 4, "%d", rc);
+ return card ;
+}
+
+static int qeth_l3_stop_card(struct qeth_card *card, int recovery_mode)
+{
+ 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 (card->options.sniffer &&
+ (card->info.promisc_mode == SET_PROMISC_MODE_ON))
+ qeth_diags_trace(card, QETH_DIAGS_CMD_TRACE_DISABLE);
+ if (card->read.state == CH_STATE_UP &&
+ card->write.state == CH_STATE_UP &&
+ (card->state == CARD_STATE_UP)) {
+ if (recovery_mode)
+ qeth_l3_stop(card->dev);
+ else {
+ rtnl_lock();
+ dev_close(card->dev);
+ rtnl_unlock();
+ }
+ card->state = CARD_STATE_SOFTSETUP;
+ }
+ if (card->state == CARD_STATE_SOFTSETUP) {
+ qeth_l3_clear_ip_list(card, 1);
+ qeth_clear_ipacmd_list(card);
+ card->state = CARD_STATE_HARDSETUP;
+ }
+ if (card->state == CARD_STATE_HARDSETUP) {
+ 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);
+ }
+ return rc;
+}
+
+/*
+ * test for and Switch promiscuous mode (on or off)
+ * either for guestlan or HiperSocket Sniffer
+ */
+static void
+qeth_l3_handle_promisc_mode(struct qeth_card *card)
+{
+ struct net_device *dev = card->dev;
+
+ if (((dev->flags & IFF_PROMISC) &&
+ (card->info.promisc_mode == SET_PROMISC_MODE_ON)) ||
+ (!(dev->flags & IFF_PROMISC) &&
+ (card->info.promisc_mode == SET_PROMISC_MODE_OFF)))
+ return;
+
+ if (card->info.guestlan) { /* Guestlan trace */
+ if (qeth_adp_supported(card, IPA_SETADP_SET_PROMISC_MODE))
+ qeth_setadp_promisc_mode(card);
+ } else if (card->options.sniffer && /* HiperSockets trace */
+ qeth_adp_supported(card, IPA_SETADP_SET_DIAG_ASSIST)) {
+ if (dev->flags & IFF_PROMISC) {
+ QETH_CARD_TEXT(card, 3, "+promisc");
+ qeth_diags_trace(card, QETH_DIAGS_CMD_TRACE_ENABLE);
+ } else {
+ QETH_CARD_TEXT(card, 3, "-promisc");
+ qeth_diags_trace(card, QETH_DIAGS_CMD_TRACE_DISABLE);
+ }
+ }
+}
+
+static void qeth_l3_set_multicast_list(struct net_device *dev)
+{
+ struct qeth_card *card = dev->ml_priv;
+
+ QETH_CARD_TEXT(card, 3, "setmulti");
+ if (qeth_threads_running(card, QETH_RECOVER_THREAD) &&
+ (card->state != CARD_STATE_UP))
+ return;
+ if (!card->options.sniffer) {
+ qeth_l3_delete_mc_addresses(card);
+ qeth_l3_add_multicast_ipv4(card);
+#ifdef CONFIG_QETH_IPV6
+ qeth_l3_add_multicast_ipv6(card);
+#endif
+ qeth_l3_set_ip_addr_list(card);
+ if (!qeth_adp_supported(card, IPA_SETADP_SET_PROMISC_MODE))
+ return;
+ }
+ qeth_l3_handle_promisc_mode(card);
+}
+
+static const char *qeth_l3_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_l3_arp_set_no_entries(struct qeth_card *card, int no_entries)
+{
+ int tmp;
+ int rc;
+
+ QETH_CARD_TEXT(card, 3, "arpstnoe");
+
+ /*
+ * currently GuestLAN only supports the ARP assist function
+ * IPA_CMD_ASS_ARP_QUERY_INFO, but not IPA_CMD_ASS_ARP_SET_NO_ENTRIES;
+ * thus we say EOPNOTSUPP for this ARP function
+ */
+ if (card->info.guestlan)
+ return -EOPNOTSUPP;
+ if (!qeth_is_supported(card, IPA_ARP_PROCESSING)) {
+ return -EOPNOTSUPP;
+ }
+ rc = qeth_l3_send_simple_setassparms(card, IPA_ARP_PROCESSING,
+ IPA_CMD_ASS_ARP_SET_NO_ENTRIES,
+ no_entries);
+ if (rc) {
+ tmp = rc;
+ QETH_DBF_MESSAGE(2, "Could not set number of ARP entries on "
+ "%s: %s (0x%x/%d)\n", QETH_CARD_IFNAME(card),
+ qeth_l3_arp_get_error_cause(&rc), tmp, tmp);
+ }
+ return rc;
+}
+
+static __u32 get_arp_entry_size(struct qeth_card *card,
+ struct qeth_arp_query_data *qdata,
+ struct qeth_arp_entrytype *type, __u8 strip_entries)
+{
+ __u32 rc;
+ __u8 is_hsi;
+
+ is_hsi = qdata->reply_bits == 5;
+ if (type->ip == QETHARP_IP_ADDR_V4) {
+ QETH_CARD_TEXT(card, 4, "arpev4");
+ if (strip_entries) {
+ rc = is_hsi ? sizeof(struct qeth_arp_qi_entry5_short) :
+ sizeof(struct qeth_arp_qi_entry7_short);
+ } else {
+ rc = is_hsi ? sizeof(struct qeth_arp_qi_entry5) :
+ sizeof(struct qeth_arp_qi_entry7);
+ }
+ } else if (type->ip == QETHARP_IP_ADDR_V6) {
+ QETH_CARD_TEXT(card, 4, "arpev6");
+ if (strip_entries) {
+ rc = is_hsi ?
+ sizeof(struct qeth_arp_qi_entry5_short_ipv6) :
+ sizeof(struct qeth_arp_qi_entry7_short_ipv6);
+ } else {
+ rc = is_hsi ?
+ sizeof(struct qeth_arp_qi_entry5_ipv6) :
+ sizeof(struct qeth_arp_qi_entry7_ipv6);
+ }
+ } else {
+ QETH_CARD_TEXT(card, 4, "arpinv");
+ rc = 0;
+ }
+
+ return rc;
+}
+
+static int arpentry_matches_prot(struct qeth_arp_entrytype *type, __u16 prot)
+{
+ return (type->ip == QETHARP_IP_ADDR_V4 && prot == QETH_PROT_IPV4) ||
+ (type->ip == QETHARP_IP_ADDR_V6 && prot == QETH_PROT_IPV6);
+}
+
+static int qeth_l3_arp_query_cb(struct qeth_card *card,
+ struct qeth_reply *reply, unsigned long data)
+{
+ struct qeth_ipa_cmd *cmd;
+ struct qeth_arp_query_data *qdata;
+ struct qeth_arp_query_info *qinfo;
+ int i;
+ int e;
+ int entrybytes_done;
+ int stripped_bytes;
+ __u8 do_strip_entries;
+
+ QETH_CARD_TEXT(card, 3, "arpquecb");
+
+ qinfo = (struct qeth_arp_query_info *) reply->param;
+ cmd = (struct qeth_ipa_cmd *) data;
+ QETH_CARD_TEXT_(card, 4, "%i", cmd->hdr.prot_version);
+ if (cmd->hdr.return_code) {
+ QETH_CARD_TEXT(card, 4, "arpcberr");
+ QETH_CARD_TEXT_(card, 4, "%i", cmd->hdr.return_code);
+ return 0;
+ }
+ if (cmd->data.setassparms.hdr.return_code) {
+ cmd->hdr.return_code = cmd->data.setassparms.hdr.return_code;
+ QETH_CARD_TEXT(card, 4, "setaperr");
+ QETH_CARD_TEXT_(card, 4, "%i", cmd->hdr.return_code);
+ return 0;
+ }
+ qdata = &cmd->data.setassparms.data.query_arp;
+ QETH_CARD_TEXT_(card, 4, "anoen%i", qdata->no_entries);
+
+ do_strip_entries = (qinfo->mask_bits & QETH_QARP_STRIP_ENTRIES) > 0;
+ stripped_bytes = do_strip_entries ? QETH_QARP_MEDIASPECIFIC_BYTES : 0;
+ entrybytes_done = 0;
+ for (e = 0; e < qdata->no_entries; ++e) {
+ char *cur_entry;
+ __u32 esize;
+ struct qeth_arp_entrytype *etype;
+
+ cur_entry = &qdata->data + entrybytes_done;
+ etype = &((struct qeth_arp_qi_entry5 *) cur_entry)->type;
+ if (!arpentry_matches_prot(etype, cmd->hdr.prot_version)) {
+ QETH_CARD_TEXT(card, 4, "pmis");
+ QETH_CARD_TEXT_(card, 4, "%i", etype->ip);
+ break;
+ }
+ esize = get_arp_entry_size(card, qdata, etype,
+ do_strip_entries);
+ QETH_CARD_TEXT_(card, 5, "esz%i", esize);
+ if (!esize)
+ break;
+
+ if ((qinfo->udata_len - qinfo->udata_offset) < esize) {
+ QETH_CARD_TEXT_(card, 4, "qaer3%i", -ENOMEM);
+ cmd->hdr.return_code = IPA_RC_ENOMEM;
+ goto out_error;
+ }
+
+ memcpy(qinfo->udata + qinfo->udata_offset,
+ &qdata->data + entrybytes_done + stripped_bytes,
+ esize);
+ entrybytes_done += esize + stripped_bytes;
+ qinfo->udata_offset += esize;
+ ++qinfo->no_entries;
+ }
+ /* check if all replies received ... */
+ if (cmd->data.setassparms.hdr.seq_no <
+ cmd->data.setassparms.hdr.number_of_replies)
+ return 1;
+ QETH_CARD_TEXT_(card, 4, "nove%i", qinfo->no_entries);
+ memcpy(qinfo->udata, &qinfo->no_entries, 4);
+ /* keep STRIP_ENTRIES flag so the user program can distinguish
+ * stripped entries from normal ones */
+ if (qinfo->mask_bits & QETH_QARP_STRIP_ENTRIES)
+ qdata->reply_bits |= QETH_QARP_STRIP_ENTRIES;
+ memcpy(qinfo->udata + QETH_QARP_MASK_OFFSET, &qdata->reply_bits, 2);
+ QETH_CARD_TEXT_(card, 4, "rc%i", 0);
+ return 0;
+out_error:
+ i = 0;
+ memcpy(qinfo->udata, &i, 4);
+ return 0;
+}
+
+static int qeth_l3_send_ipa_arp_cmd(struct qeth_card *card,
+ struct qeth_cmd_buffer *iob, int len,
+ int (*reply_cb)(struct qeth_card *, struct qeth_reply *,
+ unsigned long),
+ void *reply_param)
+{
+ QETH_CARD_TEXT(card, 4, "sendarp");
+
+ 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);
+ return qeth_send_control_data(card, IPA_PDU_HEADER_SIZE + len, iob,
+ reply_cb, reply_param);
+}
+
+static int qeth_l3_query_arp_cache_info(struct qeth_card *card,
+ enum qeth_prot_versions prot,
+ struct qeth_arp_query_info *qinfo)
+{
+ struct qeth_cmd_buffer *iob;
+ struct qeth_ipa_cmd *cmd;
+ int tmp;
+ int rc;
+
+ QETH_CARD_TEXT_(card, 3, "qarpipv%i", prot);
+
+ iob = qeth_l3_get_setassparms_cmd(card, IPA_ARP_PROCESSING,
+ IPA_CMD_ASS_ARP_QUERY_INFO,
+ sizeof(struct qeth_arp_query_data) - sizeof(char),
+ prot);
+ cmd = (struct qeth_ipa_cmd *)(iob->data+IPA_PDU_HEADER_SIZE);
+ cmd->data.setassparms.data.query_arp.request_bits = 0x000F;
+ cmd->data.setassparms.data.query_arp.reply_bits = 0;
+ cmd->data.setassparms.data.query_arp.no_entries = 0;
+ rc = qeth_l3_send_ipa_arp_cmd(card, iob,
+ QETH_SETASS_BASE_LEN+QETH_ARP_CMD_LEN,
+ qeth_l3_arp_query_cb, (void *)qinfo);
+ if (rc) {
+ tmp = rc;
+ QETH_DBF_MESSAGE(2,
+ "Error while querying ARP cache on %s: %s "
+ "(0x%x/%d)\n", QETH_CARD_IFNAME(card),
+ qeth_l3_arp_get_error_cause(&rc), tmp, tmp);
+ }
+
+ return rc;
+}
+
+static int qeth_l3_arp_query(struct qeth_card *card, char __user *udata)
+{
+ struct qeth_arp_query_info qinfo = {0, };
+ int rc;
+
+ QETH_CARD_TEXT(card, 3, "arpquery");
+
+ if (!qeth_is_supported(card,/*IPA_QUERY_ARP_ADDR_INFO*/
+ IPA_ARP_PROCESSING)) {
+ QETH_CARD_TEXT(card, 3, "arpqnsup");
+ rc = -EOPNOTSUPP;
+ goto out;
+ }
+ /* get size of userspace buffer and mask_bits -> 6 bytes */
+ if (copy_from_user(&qinfo, udata, 6)) {
+ rc = -EFAULT;
+ goto out;
+ }
+ qinfo.udata = kzalloc(qinfo.udata_len, GFP_KERNEL);
+ if (!qinfo.udata) {
+ rc = -ENOMEM;
+ goto out;
+ }
+ qinfo.udata_offset = QETH_QARP_ENTRIES_OFFSET;
+ rc = qeth_l3_query_arp_cache_info(card, QETH_PROT_IPV4, &qinfo);
+ if (rc) {
+ if (copy_to_user(udata, qinfo.udata, 4))
+ rc = -EFAULT;
+ goto free_and_out;
+ } else {
+#ifdef CONFIG_QETH_IPV6
+ if (qinfo.mask_bits & QETH_QARP_WITH_IPV6) {
+ /* fails in case of GuestLAN QDIO mode */
+ qeth_l3_query_arp_cache_info(card, QETH_PROT_IPV6,
+ &qinfo);
+ }
+#endif
+ if (copy_to_user(udata, qinfo.udata, qinfo.udata_len)) {
+ QETH_CARD_TEXT(card, 4, "qactf");
+ rc = -EFAULT;
+ goto free_and_out;
+ }
+ QETH_CARD_TEXT_(card, 4, "qacts");
+ }
+free_and_out:
+ kfree(qinfo.udata);
+out:
+ return rc;
+}
+
+static int qeth_l3_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_CARD_TEXT(card, 3, "arpadent");
+
+ /*
+ * currently GuestLAN only supports the ARP assist function
+ * IPA_CMD_ASS_ARP_QUERY_INFO, but not IPA_CMD_ASS_ARP_ADD_ENTRY;
+ * thus we say EOPNOTSUPP for this ARP function
+ */
+ if (card->info.guestlan)
+ return -EOPNOTSUPP;
+ if (!qeth_is_supported(card, IPA_ARP_PROCESSING)) {
+ return -EOPNOTSUPP;
+ }
+
+ iob = qeth_l3_get_setassparms_cmd(card, IPA_ARP_PROCESSING,
+ IPA_CMD_ASS_ARP_ADD_ENTRY,
+ sizeof(struct qeth_arp_cache_entry),
+ QETH_PROT_IPV4);
+ rc = qeth_l3_send_setassparms(card, iob,
+ sizeof(struct qeth_arp_cache_entry),
+ (unsigned long) entry,
+ qeth_l3_default_setassparms_cb, NULL);
+ if (rc) {
+ tmp = rc;
+ qeth_l3_ipaddr4_to_string((u8 *)entry->ipaddr, buf);
+ QETH_DBF_MESSAGE(2, "Could not add ARP entry for address %s "
+ "on %s: %s (0x%x/%d)\n", buf, QETH_CARD_IFNAME(card),
+ qeth_l3_arp_get_error_cause(&rc), tmp, tmp);
+ }
+ return rc;
+}
+
+static int qeth_l3_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_CARD_TEXT(card, 3, "arprment");
+
+ /*
+ * currently GuestLAN only supports the ARP assist function
+ * IPA_CMD_ASS_ARP_QUERY_INFO, but not IPA_CMD_ASS_ARP_REMOVE_ENTRY;
+ * thus we say EOPNOTSUPP for this ARP function
+ */
+ if (card->info.guestlan)
+ return -EOPNOTSUPP;
+ if (!qeth_is_supported(card, IPA_ARP_PROCESSING)) {
+ return -EOPNOTSUPP;
+ }
+ memcpy(buf, entry, 12);
+ iob = qeth_l3_get_setassparms_cmd(card, IPA_ARP_PROCESSING,
+ IPA_CMD_ASS_ARP_REMOVE_ENTRY,
+ 12,
+ QETH_PROT_IPV4);
+ rc = qeth_l3_send_setassparms(card, iob,
+ 12, (unsigned long)buf,
+ qeth_l3_default_setassparms_cb, NULL);
+ if (rc) {
+ tmp = rc;
+ memset(buf, 0, 16);
+ qeth_l3_ipaddr4_to_string((u8 *)entry->ipaddr, buf);
+ QETH_DBF_MESSAGE(2, "Could not delete ARP entry for address %s"
+ " on %s: %s (0x%x/%d)\n", buf, QETH_CARD_IFNAME(card),
+ qeth_l3_arp_get_error_cause(&rc), tmp, tmp);
+ }
+ return rc;
+}
+
+static int qeth_l3_arp_flush_cache(struct qeth_card *card)
+{
+ int rc;
+ int tmp;
+
+ QETH_CARD_TEXT(card, 3, "arpflush");
+
+ /*
+ * currently GuestLAN only supports the ARP assist function
+ * IPA_CMD_ASS_ARP_QUERY_INFO, but not IPA_CMD_ASS_ARP_FLUSH_CACHE;
+ * thus we say EOPNOTSUPP for this ARP function
+ */
+ if (card->info.guestlan || (card->info.type == QETH_CARD_TYPE_IQD))
+ return -EOPNOTSUPP;
+ if (!qeth_is_supported(card, IPA_ARP_PROCESSING)) {
+ return -EOPNOTSUPP;
+ }
+ rc = qeth_l3_send_simple_setassparms(card, IPA_ARP_PROCESSING,
+ IPA_CMD_ASS_ARP_FLUSH_CACHE, 0);
+ if (rc) {
+ tmp = rc;
+ QETH_DBF_MESSAGE(2, "Could not flush ARP cache on %s: %s "
+ "(0x%x/%d)\n", QETH_CARD_IFNAME(card),
+ qeth_l3_arp_get_error_cause(&rc), tmp, tmp);
+ }
+ return rc;
+}
+
+static int qeth_l3_do_ioctl(struct net_device *dev, struct ifreq *rq, int cmd)
+{
+ struct qeth_card *card = dev->ml_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) &&
+ (card->state != CARD_STATE_SOFTSETUP))
+ return -ENODEV;
+
+ switch (cmd) {
+ case SIOC_QETH_ARP_SET_NO_ENTRIES:
+ if (!capable(CAP_NET_ADMIN)) {
+ rc = -EPERM;
+ break;
+ }
+ rc = qeth_l3_arp_set_no_entries(card, rq->ifr_ifru.ifru_ivalue);
+ break;
+ case SIOC_QETH_ARP_QUERY_INFO:
+ if (!capable(CAP_NET_ADMIN)) {
+ rc = -EPERM;
+ break;
+ }
+ rc = qeth_l3_arp_query(card, rq->ifr_ifru.ifru_data);
+ break;
+ 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_l3_arp_add_entry(card, &arp_entry);
+ break;
+ 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_l3_arp_remove_entry(card, &arp_entry);
+ break;
+ case SIOC_QETH_ARP_FLUSH_CACHE:
+ if (!capable(CAP_NET_ADMIN)) {
+ rc = -EPERM;
+ break;
+ }
+ rc = qeth_l3_arp_flush_cache(card);
+ break;
+ case SIOC_QETH_ADP_SET_SNMP_CONTROL:
+ rc = qeth_snmp_command(card, rq->ifr_ifru.ifru_data);
+ break;
+ case SIOC_QETH_GET_CARD_TYPE:
+ if ((card->info.type == QETH_CARD_TYPE_OSD ||
+ card->info.type == QETH_CARD_TYPE_OSX) &&
+ !card->info.guestlan)
+ return 1;
+ return 0;
+ break;
+ case SIOCGMIIPHY:
+ mii_data = if_mii(rq);
+ mii_data->phy_id = 0;
+ break;
+ case SIOCGMIIREG:
+ mii_data = if_mii(rq);
+ 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 SIOC_QETH_QUERY_OAT:
+ rc = qeth_query_oat_command(card, rq->ifr_ifru.ifru_data);
+ break;
+ default:
+ rc = -EOPNOTSUPP;
+ }
+ if (rc)
+ QETH_CARD_TEXT_(card, 2, "ioce%d", rc);
+ return rc;
+}
+
+int inline qeth_l3_get_cast_type(struct qeth_card *card, struct sk_buff *skb)
+{
+ int cast_type = RTN_UNSPEC;
+ struct neighbour *n = NULL;
+ struct dst_entry *dst;
+
+ rcu_read_lock();
+ dst = skb_dst(skb);
+ if (dst)
+ n = dst_neigh_lookup_skb(dst, skb);
+ if (n) {
+ cast_type = n->type;
+ rcu_read_unlock();
+ neigh_release(n);
+ if ((cast_type == RTN_BROADCAST) ||
+ (cast_type == RTN_MULTICAST) ||
+ (cast_type == RTN_ANYCAST))
+ return cast_type;
+ else
+ return RTN_UNSPEC;
+ }
+ rcu_read_unlock();
+
+ /* try something else */
+ if (skb->protocol == ETH_P_IPV6)
+ return (skb_network_header(skb)[24] == 0xff) ?
+ RTN_MULTICAST : 0;
+ else if (skb->protocol == ETH_P_IP)
+ return ((skb_network_header(skb)[16] & 0xf0) == 0xe0) ?
+ RTN_MULTICAST : 0;
+ /* ... */
+ if (!memcmp(skb->data, skb->dev->broadcast, 6))
+ return RTN_BROADCAST;
+ else {
+ u16 hdr_mac;
+
+ hdr_mac = *((u16 *)skb->data);
+ /* tr multicast? */
+ switch (card->info.link_type) {
+ case QETH_LINK_TYPE_HSTR:
+ case QETH_LINK_TYPE_LANE_TR:
+ if ((hdr_mac == QETH_TR_MAC_NC) ||
+ (hdr_mac == QETH_TR_MAC_C))
+ return RTN_MULTICAST;
+ break;
+ /* 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 void qeth_l3_fill_af_iucv_hdr(struct qeth_card *card,
+ struct qeth_hdr *hdr, struct sk_buff *skb)
+{
+ char daddr[16];
+ struct af_iucv_trans_hdr *iucv_hdr;
+
+ skb_pull(skb, 14);
+ card->dev->header_ops->create(skb, card->dev, 0,
+ card->dev->dev_addr, card->dev->dev_addr,
+ card->dev->addr_len);
+ skb_pull(skb, 14);
+ iucv_hdr = (struct af_iucv_trans_hdr *)skb->data;
+ memset(hdr, 0, sizeof(struct qeth_hdr));
+ hdr->hdr.l3.id = QETH_HEADER_TYPE_LAYER3;
+ hdr->hdr.l3.ext_flags = 0;
+ hdr->hdr.l3.length = skb->len;
+ hdr->hdr.l3.flags = QETH_HDR_IPV6 | QETH_CAST_UNICAST;
+ memset(daddr, 0, sizeof(daddr));
+ daddr[0] = 0xfe;
+ daddr[1] = 0x80;
+ memcpy(&daddr[8], iucv_hdr->destUserID, 8);
+ memcpy(hdr->hdr.l3.dest_addr, daddr, 16);
+}
+
+static void qeth_l3_fill_header(struct qeth_card *card, struct qeth_hdr *hdr,
+ struct sk_buff *skb, int ipv, int cast_type)
+{
+ struct dst_entry *dst;
+
+ memset(hdr, 0, sizeof(struct qeth_hdr));
+ hdr->hdr.l3.id = QETH_HEADER_TYPE_LAYER3;
+ hdr->hdr.l3.ext_flags = 0;
+
+ /*
+ * before we're going to overwrite this location with next hop ip.
+ * v6 uses passthrough, v4 sets the tag in the QDIO header.
+ */
+ if (vlan_tx_tag_present(skb)) {
+ if ((ipv == 4) || (card->info.type == QETH_CARD_TYPE_IQD))
+ hdr->hdr.l3.ext_flags = QETH_HDR_EXT_VLAN_FRAME;
+ else
+ hdr->hdr.l3.ext_flags = QETH_HDR_EXT_INCLUDE_VLAN_TAG;
+ hdr->hdr.l3.vlan_id = vlan_tx_tag_get(skb);
+ }
+
+ hdr->hdr.l3.length = skb->len - sizeof(struct qeth_hdr);
+
+ rcu_read_lock();
+ dst = skb_dst(skb);
+ if (ipv == 4) {
+ struct rtable *rt = (struct rtable *) dst;
+ __be32 *pkey = &ip_hdr(skb)->daddr;
+
+ if (rt->rt_gateway)
+ pkey = &rt->rt_gateway;
+
+ /* IPv4 */
+ hdr->hdr.l3.flags = qeth_l3_get_qeth_hdr_flags4(cast_type);
+ memset(hdr->hdr.l3.dest_addr, 0, 12);
+ *((__be32 *) (&hdr->hdr.l3.dest_addr[12])) = *pkey;
+ } else if (ipv == 6) {
+ struct rt6_info *rt = (struct rt6_info *) dst;
+ struct in6_addr *pkey = &ipv6_hdr(skb)->daddr;
+
+ if (!ipv6_addr_any(&rt->rt6i_gateway))
+ pkey = &rt->rt6i_gateway;
+
+ /* IPv6 */
+ hdr->hdr.l3.flags = qeth_l3_get_qeth_hdr_flags6(cast_type);
+ if (card->info.type == QETH_CARD_TYPE_IQD)
+ hdr->hdr.l3.flags &= ~QETH_HDR_PASSTHRU;
+ memcpy(hdr->hdr.l3.dest_addr, pkey, 16);
+ } else {
+ if (!memcmp(skb->data + sizeof(struct qeth_hdr),
+ skb->dev->broadcast, 6)) {
+ /* broadcast? */
+ hdr->hdr.l3.flags = QETH_CAST_BROADCAST |
+ QETH_HDR_PASSTHRU;
+ } else {
+ hdr->hdr.l3.flags = (cast_type == RTN_MULTICAST) ?
+ QETH_CAST_MULTICAST | QETH_HDR_PASSTHRU :
+ QETH_CAST_UNICAST | QETH_HDR_PASSTHRU;
+ }
+ }
+ rcu_read_unlock();
+}
+
+static inline void qeth_l3_hdr_csum(struct qeth_card *card,
+ struct qeth_hdr *hdr, struct sk_buff *skb)
+{
+ struct iphdr *iph = ip_hdr(skb);
+
+ /* tcph->check contains already the pseudo hdr checksum
+ * so just set the header flags
+ */
+ if (iph->protocol == IPPROTO_UDP)
+ hdr->hdr.l3.ext_flags |= QETH_HDR_EXT_UDP;
+ hdr->hdr.l3.ext_flags |= QETH_HDR_EXT_CSUM_TRANSP_REQ |
+ QETH_HDR_EXT_CSUM_HDR_REQ;
+ iph->check = 0;
+ if (card->options.performance_stats)
+ card->perf_stats.tx_csum++;
+}
+
+static void qeth_tso_fill_header(struct qeth_card *card,
+ struct qeth_hdr *qhdr, struct sk_buff *skb)
+{
+ struct qeth_hdr_tso *hdr = (struct qeth_hdr_tso *)qhdr;
+ struct tcphdr *tcph = tcp_hdr(skb);
+ struct iphdr *iph = ip_hdr(skb);
+ struct ipv6hdr *ip6h = ipv6_hdr(skb);
+
+ /*fix header to TSO values ...*/
+ hdr->hdr.hdr.l3.id = QETH_HEADER_TYPE_TSO;
+ hdr->hdr.hdr.l3.length = skb->len - sizeof(struct qeth_hdr_tso);
+ /*set values which are fix for the first approach ...*/
+ hdr->ext.hdr_tot_len = (__u16) sizeof(struct qeth_hdr_ext_tso);
+ hdr->ext.imb_hdr_no = 1;
+ hdr->ext.hdr_type = 1;
+ hdr->ext.hdr_version = 1;
+ hdr->ext.hdr_len = 28;
+ /*insert non-fix values */
+ hdr->ext.mss = skb_shinfo(skb)->gso_size;
+ hdr->ext.dg_hdr_len = (__u16)(iph->ihl*4 + tcph->doff*4);
+ hdr->ext.payload_len = (__u16)(skb->len - hdr->ext.dg_hdr_len -
+ sizeof(struct qeth_hdr_tso));
+ tcph->check = 0;
+ if (skb->protocol == ETH_P_IPV6) {
+ ip6h->payload_len = 0;
+ tcph->check = ~csum_ipv6_magic(&ip6h->saddr, &ip6h->daddr,
+ 0, IPPROTO_TCP, 0);
+ } else {
+ /*OSA want us to set these values ...*/
+ tcph->check = ~csum_tcpudp_magic(iph->saddr, iph->daddr,
+ 0, IPPROTO_TCP, 0);
+ iph->tot_len = 0;
+ iph->check = 0;
+ }
+}
+
+static inline int qeth_l3_tso_elements(struct sk_buff *skb)
+{
+ unsigned long tcpd = (unsigned long)tcp_hdr(skb) +
+ tcp_hdr(skb)->doff * 4;
+ int tcpd_len = skb->len - (tcpd - (unsigned long)skb->data);
+ int elements = PFN_UP(tcpd + tcpd_len - 1) - PFN_DOWN(tcpd);
+
+ elements += qeth_get_elements_for_frags(skb);
+
+ return elements;
+}
+
+static int qeth_l3_hard_start_xmit(struct sk_buff *skb, struct net_device *dev)
+{
+ int rc;
+ u16 *tag;
+ struct qeth_hdr *hdr = NULL;
+ int elements_needed = 0;
+ int elems;
+ struct qeth_card *card = dev->ml_priv;
+ struct sk_buff *new_skb = NULL;
+ int ipv = qeth_get_ip_version(skb);
+ int cast_type = qeth_l3_get_cast_type(card, skb);
+ struct qeth_qdio_out_q *queue =
+ card->qdio.out_qs[card->qdio.do_prio_queueing
+ || (cast_type && card->info.is_multicast_different) ?
+ qeth_get_priority_queue(card, skb, ipv, cast_type) :
+ card->qdio.default_out_queue];
+ int tx_bytes = skb->len;
+ bool large_send;
+ int data_offset = -1;
+ int nr_frags;
+
+ if (((card->info.type == QETH_CARD_TYPE_IQD) &&
+ (((card->options.cq != QETH_CQ_ENABLED) && !ipv) ||
+ ((card->options.cq == QETH_CQ_ENABLED) &&
+ (skb->protocol != ETH_P_AF_IUCV)))) ||
+ card->options.sniffer)
+ goto tx_drop;
+
+ if ((card->state != CARD_STATE_UP) || !card->lan_online) {
+ card->stats.tx_carrier_errors++;
+ goto tx_drop;
+ }
+
+ if ((cast_type == RTN_BROADCAST) &&
+ (card->info.broadcast_capable == 0))
+ goto tx_drop;
+
+ if (card->options.performance_stats) {
+ card->perf_stats.outbound_cnt++;
+ card->perf_stats.outbound_start_time = qeth_get_micros();
+ }
+
+ large_send = skb_is_gso(skb);
+
+ if ((card->info.type == QETH_CARD_TYPE_IQD) && (!large_send) &&
+ (skb_shinfo(skb)->nr_frags == 0)) {
+ new_skb = skb;
+ if (new_skb->protocol == ETH_P_AF_IUCV)
+ data_offset = 0;
+ else
+ data_offset = ETH_HLEN;
+ hdr = kmem_cache_alloc(qeth_core_header_cache, GFP_ATOMIC);
+ if (!hdr)
+ goto tx_drop;
+ elements_needed++;
+ } else {
+ /* create a clone with writeable headroom */
+ new_skb = skb_realloc_headroom(skb, sizeof(struct qeth_hdr_tso)
+ + VLAN_HLEN);
+ if (!new_skb)
+ goto tx_drop;
+ }
+
+ if (card->info.type == QETH_CARD_TYPE_IQD) {
+ if (data_offset < 0)
+ skb_pull(new_skb, ETH_HLEN);
+ } else {
+ if (ipv == 4) {
+ skb_pull(new_skb, ETH_HLEN);
+ }
+
+ if (ipv != 4 && vlan_tx_tag_present(new_skb)) {
+ skb_push(new_skb, VLAN_HLEN);
+ skb_copy_to_linear_data(new_skb, new_skb->data + 4, 4);
+ skb_copy_to_linear_data_offset(new_skb, 4,
+ new_skb->data + 8, 4);
+ skb_copy_to_linear_data_offset(new_skb, 8,
+ new_skb->data + 12, 4);
+ tag = (u16 *)(new_skb->data + 12);
+ *tag = __constant_htons(ETH_P_8021Q);
+ *(tag + 1) = htons(vlan_tx_tag_get(new_skb));
+ }
+ }
+
+ netif_stop_queue(dev);
+
+ /* fix hardware limitation: as long as we do not have sbal
+ * chaining we can not send long frag lists
+ */
+ if (large_send) {
+ if (qeth_l3_tso_elements(new_skb) + 1 > 16) {
+ if (skb_linearize(new_skb))
+ goto tx_drop;
+ if (card->options.performance_stats)
+ card->perf_stats.tx_lin++;
+ }
+ }
+
+ if (large_send && (cast_type == RTN_UNSPEC)) {
+ hdr = (struct qeth_hdr *)skb_push(new_skb,
+ sizeof(struct qeth_hdr_tso));
+ memset(hdr, 0, sizeof(struct qeth_hdr_tso));
+ qeth_l3_fill_header(card, hdr, new_skb, ipv, cast_type);
+ qeth_tso_fill_header(card, hdr, new_skb);
+ elements_needed++;
+ } else {
+ if (data_offset < 0) {
+ hdr = (struct qeth_hdr *)skb_push(new_skb,
+ sizeof(struct qeth_hdr));
+ qeth_l3_fill_header(card, hdr, new_skb, ipv,
+ cast_type);
+ } else {
+ if (new_skb->protocol == ETH_P_AF_IUCV)
+ qeth_l3_fill_af_iucv_hdr(card, hdr, new_skb);
+ else {
+ qeth_l3_fill_header(card, hdr, new_skb, ipv,
+ cast_type);
+ hdr->hdr.l3.length = new_skb->len - data_offset;
+ }
+ }
+
+ if (skb->ip_summed == CHECKSUM_PARTIAL)
+ qeth_l3_hdr_csum(card, hdr, new_skb);
+ }
+
+ elems = qeth_get_elements_no(card, new_skb, elements_needed);
+ if (!elems) {
+ if (data_offset >= 0)
+ kmem_cache_free(qeth_core_header_cache, hdr);
+ goto tx_drop;
+ }
+ elements_needed += elems;
+ nr_frags = skb_shinfo(new_skb)->nr_frags;
+
+ if (card->info.type != QETH_CARD_TYPE_IQD) {
+ int len;
+ if (large_send)
+ len = ((unsigned long)tcp_hdr(new_skb) +
+ tcp_hdr(new_skb)->doff * 4) -
+ (unsigned long)new_skb->data;
+ else
+ len = sizeof(struct qeth_hdr_layer3);
+
+ if (qeth_hdr_chk_and_bounce(new_skb, &hdr, len))
+ goto tx_drop;
+ rc = qeth_do_send_packet(card, queue, new_skb, hdr,
+ elements_needed);
+ } else
+ rc = qeth_do_send_packet_fast(card, queue, new_skb, hdr,
+ elements_needed, data_offset, 0);
+
+ if (!rc) {
+ card->stats.tx_packets++;
+ card->stats.tx_bytes += tx_bytes;
+ if (new_skb != skb)
+ dev_kfree_skb_any(skb);
+ if (card->options.performance_stats) {
+ if (large_send) {
+ card->perf_stats.large_send_bytes += tx_bytes;
+ card->perf_stats.large_send_cnt++;
+ }
+ if (nr_frags) {
+ card->perf_stats.sg_skbs_sent++;
+ /* nr_frags + skb->data */
+ card->perf_stats.sg_frags_sent += nr_frags + 1;
+ }
+ }
+ rc = NETDEV_TX_OK;
+ } else {
+ if (data_offset >= 0)
+ kmem_cache_free(qeth_core_header_cache, hdr);
+
+ if (rc == -EBUSY) {
+ if (new_skb != skb)
+ dev_kfree_skb_any(new_skb);
+ return NETDEV_TX_BUSY;
+ } else
+ goto tx_drop;
+ }
+
+ netif_wake_queue(dev);
+ if (card->options.performance_stats)
+ card->perf_stats.outbound_time += qeth_get_micros() -
+ card->perf_stats.outbound_start_time;
+ return rc;
+
+tx_drop:
+ card->stats.tx_dropped++;
+ card->stats.tx_errors++;
+ if ((new_skb != skb) && new_skb)
+ dev_kfree_skb_any(new_skb);
+ dev_kfree_skb_any(skb);
+ netif_wake_queue(dev);
+ return NETDEV_TX_OK;
+}
+
+static int __qeth_l3_open(struct net_device *dev)
+{
+ struct qeth_card *card = dev->ml_priv;
+ int rc = 0;
+
+ QETH_CARD_TEXT(card, 4, "qethopen");
+ if (card->state == CARD_STATE_UP)
+ return rc;
+ if (card->state != CARD_STATE_SOFTSETUP)
+ return -ENODEV;
+ card->data.state = CH_STATE_UP;
+ card->state = CARD_STATE_UP;
+ netif_start_queue(dev);
+
+ if (qdio_stop_irq(card->data.ccwdev, 0) >= 0) {
+ napi_enable(&card->napi);
+ napi_schedule(&card->napi);
+ } else
+ rc = -EIO;
+ return rc;
+}
+
+static int qeth_l3_open(struct net_device *dev)
+{
+ struct qeth_card *card = dev->ml_priv;
+
+ QETH_CARD_TEXT(card, 5, "qethope_");
+ if (qeth_wait_for_threads(card, QETH_RECOVER_THREAD)) {
+ QETH_CARD_TEXT(card, 3, "openREC");
+ return -ERESTARTSYS;
+ }
+ return __qeth_l3_open(dev);
+}
+
+static int qeth_l3_stop(struct net_device *dev)
+{
+ struct qeth_card *card = dev->ml_priv;
+
+ QETH_CARD_TEXT(card, 4, "qethstop");
+ netif_tx_disable(dev);
+ if (card->state == CARD_STATE_UP) {
+ card->state = CARD_STATE_SOFTSETUP;
+ napi_disable(&card->napi);
+ }
+ return 0;
+}
+
+static netdev_features_t qeth_l3_fix_features(struct net_device *dev,
+ netdev_features_t features)
+{
+ struct qeth_card *card = dev->ml_priv;
+
+ if (!qeth_is_supported(card, IPA_OUTBOUND_CHECKSUM))
+ features &= ~NETIF_F_IP_CSUM;
+ if (!qeth_is_supported(card, IPA_OUTBOUND_TSO))
+ features &= ~NETIF_F_TSO;
+ if (!qeth_is_supported(card, IPA_INBOUND_CHECKSUM))
+ features &= ~NETIF_F_RXCSUM;
+
+ return features;
+}
+
+static int qeth_l3_set_features(struct net_device *dev,
+ netdev_features_t features)
+{
+ struct qeth_card *card = dev->ml_priv;
+ u32 changed = dev->features ^ features;
+ int err;
+
+ if (!(changed & NETIF_F_RXCSUM))
+ return 0;
+
+ if (card->state == CARD_STATE_DOWN ||
+ card->state == CARD_STATE_RECOVER)
+ return 0;
+
+ err = qeth_l3_set_rx_csum(card, features & NETIF_F_RXCSUM);
+ if (err)
+ dev->features = features ^ NETIF_F_RXCSUM;
+
+ return err;
+}
+
+static const struct ethtool_ops qeth_l3_ethtool_ops = {
+ .get_link = ethtool_op_get_link,
+ .get_strings = qeth_core_get_strings,
+ .get_ethtool_stats = qeth_core_get_ethtool_stats,
+ .get_sset_count = qeth_core_get_sset_count,
+ .get_drvinfo = qeth_core_get_drvinfo,
+ .get_settings = qeth_core_ethtool_get_settings,
+};
+
+/*
+ * we need NOARP for IPv4 but we want neighbor solicitation for IPv6. Setting
+ * NOARP on the netdevice is no option because it also turns off neighbor
+ * solicitation. For IPv4 we install a neighbor_setup function. We don't want
+ * arp resolution but we want the hard header (packet socket will work
+ * e.g. tcpdump)
+ */
+static int qeth_l3_neigh_setup_noarp(struct neighbour *n)
+{
+ n->nud_state = NUD_NOARP;
+ memcpy(n->ha, "FAKELL", 6);
+ n->output = n->ops->connected_output;
+ return 0;
+}
+
+static int
+qeth_l3_neigh_setup(struct net_device *dev, struct neigh_parms *np)
+{
+ if (np->tbl->family == AF_INET)
+ np->neigh_setup = qeth_l3_neigh_setup_noarp;
+
+ return 0;
+}
+
+static const struct net_device_ops qeth_l3_netdev_ops = {
+ .ndo_open = qeth_l3_open,
+ .ndo_stop = qeth_l3_stop,
+ .ndo_get_stats = qeth_get_stats,
+ .ndo_start_xmit = qeth_l3_hard_start_xmit,
+ .ndo_validate_addr = eth_validate_addr,
+ .ndo_set_rx_mode = qeth_l3_set_multicast_list,
+ .ndo_do_ioctl = qeth_l3_do_ioctl,
+ .ndo_change_mtu = qeth_change_mtu,
+ .ndo_fix_features = qeth_l3_fix_features,
+ .ndo_set_features = qeth_l3_set_features,
+ .ndo_vlan_rx_add_vid = qeth_l3_vlan_rx_add_vid,
+ .ndo_vlan_rx_kill_vid = qeth_l3_vlan_rx_kill_vid,
+ .ndo_tx_timeout = qeth_tx_timeout,
+};
+
+static const struct net_device_ops qeth_l3_osa_netdev_ops = {
+ .ndo_open = qeth_l3_open,
+ .ndo_stop = qeth_l3_stop,
+ .ndo_get_stats = qeth_get_stats,
+ .ndo_start_xmit = qeth_l3_hard_start_xmit,
+ .ndo_validate_addr = eth_validate_addr,
+ .ndo_set_rx_mode = qeth_l3_set_multicast_list,
+ .ndo_do_ioctl = qeth_l3_do_ioctl,
+ .ndo_change_mtu = qeth_change_mtu,
+ .ndo_fix_features = qeth_l3_fix_features,
+ .ndo_set_features = qeth_l3_set_features,
+ .ndo_vlan_rx_add_vid = qeth_l3_vlan_rx_add_vid,
+ .ndo_vlan_rx_kill_vid = qeth_l3_vlan_rx_kill_vid,
+ .ndo_tx_timeout = qeth_tx_timeout,
+ .ndo_neigh_setup = qeth_l3_neigh_setup,
+};
+
+static int qeth_l3_setup_netdev(struct qeth_card *card)
+{
+ if (card->info.type == QETH_CARD_TYPE_OSD ||
+ card->info.type == QETH_CARD_TYPE_OSX) {
+ if ((card->info.link_type == QETH_LINK_TYPE_LANE_TR) ||
+ (card->info.link_type == QETH_LINK_TYPE_HSTR)) {
+ pr_info("qeth_l3: ignoring TR device\n");
+ return -ENODEV;
+ } else {
+ card->dev = alloc_etherdev(0);
+ if (!card->dev)
+ return -ENODEV;
+ card->dev->netdev_ops = &qeth_l3_osa_netdev_ops;
+
+ /*IPv6 address autoconfiguration stuff*/
+ qeth_l3_get_unique_id(card);
+ if (!(card->info.unique_id & UNIQUE_ID_NOT_BY_CARD))
+ card->dev->dev_id = card->info.unique_id &
+ 0xffff;
+ if (!card->info.guestlan) {
+ card->dev->hw_features = NETIF_F_SG |
+ NETIF_F_RXCSUM | NETIF_F_IP_CSUM |
+ NETIF_F_TSO;
+ card->dev->features = NETIF_F_RXCSUM;
+ }
+ }
+ } else if (card->info.type == QETH_CARD_TYPE_IQD) {
+ card->dev = alloc_netdev(0, "hsi%d", ether_setup);
+ if (!card->dev)
+ return -ENODEV;
+ card->dev->flags |= IFF_NOARP;
+ card->dev->netdev_ops = &qeth_l3_netdev_ops;
+ qeth_l3_iqd_read_initial_mac(card);
+ if (card->options.hsuid[0])
+ memcpy(card->dev->perm_addr, card->options.hsuid, 9);
+ } else
+ return -ENODEV;
+
+ card->dev->ml_priv = card;
+ card->dev->watchdog_timeo = QETH_TX_TIMEOUT;
+ card->dev->mtu = card->info.initial_mtu;
+ card->dev->ethtool_ops = &qeth_l3_ethtool_ops;
+ card->dev->features |= NETIF_F_HW_VLAN_CTAG_TX |
+ NETIF_F_HW_VLAN_CTAG_RX |
+ NETIF_F_HW_VLAN_CTAG_FILTER;
+ card->dev->priv_flags &= ~IFF_XMIT_DST_RELEASE;
+ card->dev->gso_max_size = 15 * PAGE_SIZE;
+
+ SET_NETDEV_DEV(card->dev, &card->gdev->dev);
+ netif_napi_add(card->dev, &card->napi, qeth_l3_poll, QETH_NAPI_WEIGHT);
+ return register_netdev(card->dev);
+}
+
+static int qeth_l3_probe_device(struct ccwgroup_device *gdev)
+{
+ struct qeth_card *card = dev_get_drvdata(&gdev->dev);
+
+ qeth_l3_create_device_attributes(&gdev->dev);
+ card->options.layer2 = 0;
+ card->info.hwtrap = 0;
+ return 0;
+}
+
+static void qeth_l3_remove_device(struct ccwgroup_device *cgdev)
+{
+ struct qeth_card *card = dev_get_drvdata(&cgdev->dev);
+
+ qeth_l3_remove_device_attributes(&cgdev->dev);
+
+ qeth_set_allowed_threads(card, 0, 1);
+ wait_event(card->wait_q, qeth_threads_running(card, 0xffffffff) == 0);
+
+ if (cgdev->state == CCWGROUP_ONLINE)
+ qeth_l3_set_offline(cgdev);
+
+ if (card->dev) {
+ unregister_netdev(card->dev);
+ card->dev = NULL;
+ }
+
+ qeth_l3_clear_ip_list(card, 0);
+ qeth_l3_clear_ipato_list(card);
+ return;
+}
+
+static int __qeth_l3_set_online(struct ccwgroup_device *gdev, int recovery_mode)
+{
+ struct qeth_card *card = dev_get_drvdata(&gdev->dev);
+ int rc = 0;
+ enum qeth_card_states recover_flag;
+
+ mutex_lock(&card->discipline_mutex);
+ mutex_lock(&card->conf_mutex);
+ QETH_DBF_TEXT(SETUP, 2, "setonlin");
+ QETH_DBF_HEX(SETUP, 2, &card, sizeof(void *));
+
+ recover_flag = card->state;
+ rc = qeth_core_hardsetup_card(card);
+ if (rc) {
+ QETH_DBF_TEXT_(SETUP, 2, "2err%d", rc);
+ rc = -ENODEV;
+ goto out_remove;
+ }
+
+ if (!card->dev && qeth_l3_setup_netdev(card)) {
+ rc = -ENODEV;
+ goto out_remove;
+ }
+
+ if (qeth_is_diagass_supported(card, QETH_DIAGS_CMD_TRAP)) {
+ if (card->info.hwtrap &&
+ qeth_hw_trap(card, QETH_DIAGS_TRAP_ARM))
+ card->info.hwtrap = 0;
+ } else
+ card->info.hwtrap = 0;
+
+ card->state = CARD_STATE_HARDSETUP;
+ memset(&card->rx, 0, sizeof(struct qeth_rx));
+ qeth_print_status_message(card);
+
+ /* softsetup */
+ QETH_DBF_TEXT(SETUP, 2, "softsetp");
+
+ rc = qeth_send_startlan(card);
+ if (rc) {
+ QETH_DBF_TEXT_(SETUP, 2, "1err%d", rc);
+ if (rc == 0xe080) {
+ dev_warn(&card->gdev->dev,
+ "The LAN is offline\n");
+ card->lan_online = 0;
+ goto contin;
+ }
+ rc = -ENODEV;
+ goto out_remove;
+ } else
+ card->lan_online = 1;
+
+contin:
+ rc = qeth_l3_setadapter_parms(card);
+ if (rc)
+ QETH_DBF_TEXT_(SETUP, 2, "2err%d", rc);
+ if (!card->options.sniffer) {
+ rc = qeth_l3_start_ipassists(card);
+ if (rc) {
+ QETH_DBF_TEXT_(SETUP, 2, "3err%d", rc);
+ goto out_remove;
+ }
+ rc = qeth_l3_setrouting_v4(card);
+ if (rc)
+ QETH_DBF_TEXT_(SETUP, 2, "4err%d", rc);
+ rc = qeth_l3_setrouting_v6(card);
+ if (rc)
+ QETH_DBF_TEXT_(SETUP, 2, "5err%d", rc);
+ }
+ netif_tx_disable(card->dev);
+
+ rc = qeth_init_qdio_queues(card);
+ if (rc) {
+ QETH_DBF_TEXT_(SETUP, 2, "6err%d", rc);
+ rc = -ENODEV;
+ goto out_remove;
+ }
+ card->state = CARD_STATE_SOFTSETUP;
+
+ qeth_set_allowed_threads(card, 0xffffffff, 0);
+ qeth_l3_set_ip_addr_list(card);
+ if (card->lan_online)
+ netif_carrier_on(card->dev);
+ else
+ netif_carrier_off(card->dev);
+ if (recover_flag == CARD_STATE_RECOVER) {
+ rtnl_lock();
+ if (recovery_mode)
+ __qeth_l3_open(card->dev);
+ else
+ dev_open(card->dev);
+ qeth_l3_set_multicast_list(card->dev);
+ rtnl_unlock();
+ }
+ qeth_trace_features(card);
+ /* let user_space know that device is online */
+ kobject_uevent(&gdev->dev.kobj, KOBJ_CHANGE);
+ mutex_unlock(&card->conf_mutex);
+ mutex_unlock(&card->discipline_mutex);
+ return 0;
+out_remove:
+ qeth_l3_stop_card(card, 0);
+ ccw_device_set_offline(CARD_DDEV(card));
+ ccw_device_set_offline(CARD_WDEV(card));
+ ccw_device_set_offline(CARD_RDEV(card));
+ qdio_free(CARD_DDEV(card));
+ if (recover_flag == CARD_STATE_RECOVER)
+ card->state = CARD_STATE_RECOVER;
+ else
+ card->state = CARD_STATE_DOWN;
+ mutex_unlock(&card->conf_mutex);
+ mutex_unlock(&card->discipline_mutex);
+ return rc;
+}
+
+static int qeth_l3_set_online(struct ccwgroup_device *gdev)
+{
+ return __qeth_l3_set_online(gdev, 0);
+}
+
+static int __qeth_l3_set_offline(struct ccwgroup_device *cgdev,
+ int recovery_mode)
+{
+ struct qeth_card *card = dev_get_drvdata(&cgdev->dev);
+ int rc = 0, rc2 = 0, rc3 = 0;
+ enum qeth_card_states recover_flag;
+
+ mutex_lock(&card->discipline_mutex);
+ mutex_lock(&card->conf_mutex);
+ QETH_DBF_TEXT(SETUP, 3, "setoffl");
+ QETH_DBF_HEX(SETUP, 3, &card, sizeof(void *));
+
+ if (card->dev && netif_carrier_ok(card->dev))
+ netif_carrier_off(card->dev);
+ recover_flag = card->state;
+ if ((!recovery_mode && card->info.hwtrap) || card->info.hwtrap == 2) {
+ qeth_hw_trap(card, QETH_DIAGS_TRAP_DISARM);
+ card->info.hwtrap = 1;
+ }
+ qeth_l3_stop_card(card, recovery_mode);
+ if ((card->options.cq == QETH_CQ_ENABLED) && card->dev) {
+ rtnl_lock();
+ call_netdevice_notifiers(NETDEV_REBOOT, card->dev);
+ rtnl_unlock();
+ }
+ rc = ccw_device_set_offline(CARD_DDEV(card));
+ rc2 = ccw_device_set_offline(CARD_WDEV(card));
+ rc3 = ccw_device_set_offline(CARD_RDEV(card));
+ if (!rc)
+ rc = (rc2) ? rc2 : rc3;
+ if (rc)
+ QETH_DBF_TEXT_(SETUP, 2, "1err%d", rc);
+ qdio_free(CARD_DDEV(card));
+ if (recover_flag == CARD_STATE_UP)
+ card->state = CARD_STATE_RECOVER;
+ /* let user_space know that device is offline */
+ kobject_uevent(&cgdev->dev.kobj, KOBJ_CHANGE);
+ mutex_unlock(&card->conf_mutex);
+ mutex_unlock(&card->discipline_mutex);
+ return 0;
+}
+
+static int qeth_l3_set_offline(struct ccwgroup_device *cgdev)
+{
+ return __qeth_l3_set_offline(cgdev, 0);
+}
+
+static int qeth_l3_recover(void *ptr)
+{
+ struct qeth_card *card;
+ int rc = 0;
+
+ card = (struct qeth_card *) ptr;
+ QETH_CARD_TEXT(card, 2, "recover1");
+ QETH_CARD_HEX(card, 2, &card, sizeof(void *));
+ if (!qeth_do_run_thread(card, QETH_RECOVER_THREAD))
+ return 0;
+ QETH_CARD_TEXT(card, 2, "recover2");
+ dev_warn(&card->gdev->dev,
+ "A recovery process has been started for the device\n");
+ qeth_set_recovery_task(card);
+ __qeth_l3_set_offline(card->gdev, 1);
+ rc = __qeth_l3_set_online(card->gdev, 1);
+ if (!rc)
+ dev_info(&card->gdev->dev,
+ "Device successfully recovered!\n");
+ else {
+ qeth_close_dev(card);
+ dev_warn(&card->gdev->dev, "The qeth device driver "
+ "failed to recover an error on the device\n");
+ }
+ qeth_clear_recovery_task(card);
+ qeth_clear_thread_start_bit(card, QETH_RECOVER_THREAD);
+ qeth_clear_thread_running_bit(card, QETH_RECOVER_THREAD);
+ return 0;
+}
+
+static void qeth_l3_shutdown(struct ccwgroup_device *gdev)
+{
+ struct qeth_card *card = dev_get_drvdata(&gdev->dev);
+ qeth_set_allowed_threads(card, 0, 1);
+ if ((gdev->state == CCWGROUP_ONLINE) && card->info.hwtrap)
+ qeth_hw_trap(card, QETH_DIAGS_TRAP_DISARM);
+ qeth_qdio_clear_card(card, 0);
+ qeth_clear_qdio_buffers(card);
+ qdio_free(CARD_DDEV(card));
+}
+
+static int qeth_l3_pm_suspend(struct ccwgroup_device *gdev)
+{
+ struct qeth_card *card = dev_get_drvdata(&gdev->dev);
+
+ if (card->dev)
+ netif_device_detach(card->dev);
+ qeth_set_allowed_threads(card, 0, 1);
+ wait_event(card->wait_q, qeth_threads_running(card, 0xffffffff) == 0);
+ if (gdev->state == CCWGROUP_OFFLINE)
+ return 0;
+ if (card->state == CARD_STATE_UP) {
+ if (card->info.hwtrap)
+ qeth_hw_trap(card, QETH_DIAGS_TRAP_DISARM);
+ __qeth_l3_set_offline(card->gdev, 1);
+ } else
+ __qeth_l3_set_offline(card->gdev, 0);
+ return 0;
+}
+
+static int qeth_l3_pm_resume(struct ccwgroup_device *gdev)
+{
+ struct qeth_card *card = dev_get_drvdata(&gdev->dev);
+ int rc = 0;
+
+ if (gdev->state == CCWGROUP_OFFLINE)
+ goto out;
+
+ if (card->state == CARD_STATE_RECOVER) {
+ rc = __qeth_l3_set_online(card->gdev, 1);
+ if (rc) {
+ rtnl_lock();
+ dev_close(card->dev);
+ rtnl_unlock();
+ }
+ } else
+ rc = __qeth_l3_set_online(card->gdev, 0);
+out:
+ qeth_set_allowed_threads(card, 0xffffffff, 0);
+ if (card->dev)
+ netif_device_attach(card->dev);
+ if (rc)
+ dev_warn(&card->gdev->dev, "The qeth device driver "
+ "failed to recover an error on the device\n");
+ return rc;
+}
+
+/* Returns zero if the command is successfully "consumed" */
+static int qeth_l3_control_event(struct qeth_card *card,
+ struct qeth_ipa_cmd *cmd)
+{
+ return 1;
+}
+
+struct qeth_discipline qeth_l3_discipline = {
+ .start_poll = qeth_qdio_start_poll,
+ .input_handler = (qdio_handler_t *) qeth_qdio_input_handler,
+ .output_handler = (qdio_handler_t *) qeth_qdio_output_handler,
+ .recover = qeth_l3_recover,
+ .setup = qeth_l3_probe_device,
+ .remove = qeth_l3_remove_device,
+ .set_online = qeth_l3_set_online,
+ .set_offline = qeth_l3_set_offline,
+ .shutdown = qeth_l3_shutdown,
+ .freeze = qeth_l3_pm_suspend,
+ .thaw = qeth_l3_pm_resume,
+ .restore = qeth_l3_pm_resume,
+ .control_event_handler = qeth_l3_control_event,
+};
+EXPORT_SYMBOL_GPL(qeth_l3_discipline);
+
+static int qeth_l3_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;
+
+ if (dev_net(dev) != &init_net)
+ return NOTIFY_DONE;
+
+ card = qeth_l3_get_card_from_dev(dev);
+ if (!card)
+ return NOTIFY_DONE;
+ QETH_CARD_TEXT(card, 3, "ipevent");
+
+ addr = qeth_l3_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;
+ } else
+ goto out;
+
+ switch (event) {
+ case NETDEV_UP:
+ if (!qeth_l3_add_ip(card, addr))
+ kfree(addr);
+ break;
+ case NETDEV_DOWN:
+ if (!qeth_l3_delete_ip(card, addr))
+ kfree(addr);
+ break;
+ default:
+ break;
+ }
+ qeth_l3_set_ip_addr_list(card);
+out:
+ return NOTIFY_DONE;
+}
+
+static struct notifier_block qeth_l3_ip_notifier = {
+ qeth_l3_ip_event,
+ NULL,
+};
+
+#ifdef CONFIG_QETH_IPV6
+/**
+ * IPv6 event handler
+ */
+static int qeth_l3_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;
+
+ card = qeth_l3_get_card_from_dev(dev);
+ if (!card)
+ return NOTIFY_DONE;
+ QETH_CARD_TEXT(card, 3, "ip6event");
+ if (!qeth_is_supported(card, IPA_IPV6))
+ return NOTIFY_DONE;
+
+ addr = qeth_l3_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;
+ } else
+ goto out;
+
+ switch (event) {
+ case NETDEV_UP:
+ if (!qeth_l3_add_ip(card, addr))
+ kfree(addr);
+ break;
+ case NETDEV_DOWN:
+ if (!qeth_l3_delete_ip(card, addr))
+ kfree(addr);
+ break;
+ default:
+ break;
+ }
+ qeth_l3_set_ip_addr_list(card);
+out:
+ return NOTIFY_DONE;
+}
+
+static struct notifier_block qeth_l3_ip6_notifier = {
+ qeth_l3_ip6_event,
+ NULL,
+};
+#endif
+
+static int qeth_l3_register_notifiers(void)
+{
+ int rc;
+
+ QETH_DBF_TEXT(SETUP, 5, "regnotif");
+ rc = register_inetaddr_notifier(&qeth_l3_ip_notifier);
+ if (rc)
+ return rc;
+#ifdef CONFIG_QETH_IPV6
+ rc = register_inet6addr_notifier(&qeth_l3_ip6_notifier);
+ if (rc) {
+ unregister_inetaddr_notifier(&qeth_l3_ip_notifier);
+ return rc;
+ }
+#else
+ pr_warning("There is no IPv6 support for the layer 3 discipline\n");
+#endif
+ return 0;
+}
+
+static void qeth_l3_unregister_notifiers(void)
+{
+
+ QETH_DBF_TEXT(SETUP, 5, "unregnot");
+ WARN_ON(unregister_inetaddr_notifier(&qeth_l3_ip_notifier));
+#ifdef CONFIG_QETH_IPV6
+ WARN_ON(unregister_inet6addr_notifier(&qeth_l3_ip6_notifier));
+#endif /* QETH_IPV6 */
+}
+
+static int __init qeth_l3_init(void)
+{
+ int rc = 0;
+
+ pr_info("register layer 3 discipline\n");
+ rc = qeth_l3_register_notifiers();
+ return rc;
+}
+
+static void __exit qeth_l3_exit(void)
+{
+ qeth_l3_unregister_notifiers();
+ pr_info("unregister layer 3 discipline\n");
+}
+
+module_init(qeth_l3_init);
+module_exit(qeth_l3_exit);
+MODULE_AUTHOR("Frank Blaschka <frank.blaschka@de.ibm.com>");
+MODULE_DESCRIPTION("qeth layer 3 discipline");
+MODULE_LICENSE("GPL");
diff --git a/drivers/s390/net/qeth_l3_sys.c b/drivers/s390/net/qeth_l3_sys.c
new file mode 100644
index 00000000000..adef5f5de11
--- /dev/null
+++ b/drivers/s390/net/qeth_l3_sys.c
@@ -0,0 +1,1058 @@
+/*
+ * Copyright IBM Corp. 2007
+ * Author(s): Utz Bacher <utz.bacher@de.ibm.com>,
+ * Frank Pavlic <fpavlic@de.ibm.com>,
+ * Thomas Spatzier <tspat@de.ibm.com>,
+ * Frank Blaschka <frank.blaschka@de.ibm.com>
+ */
+
+#include <linux/slab.h>
+#include <asm/ebcdic.h>
+#include "qeth_l3.h"
+
+#define QETH_DEVICE_ATTR(_id, _name, _mode, _show, _store) \
+struct device_attribute dev_attr_##_id = __ATTR(_name, _mode, _show, _store)
+
+static ssize_t qeth_l3_dev_route_show(struct qeth_card *card,
+ 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:
+ if (card->info.broadcast_capable == QETH_BROADCAST_WITHOUT_ECHO)
+ return sprintf(buf, "%s\n", "multicast router+");
+ else
+ return sprintf(buf, "%s\n", "multicast router");
+ case PRIMARY_CONNECTOR:
+ if (card->info.broadcast_capable == QETH_BROADCAST_WITHOUT_ECHO)
+ return sprintf(buf, "%s\n", "primary connector+");
+ else
+ return sprintf(buf, "%s\n", "primary connector");
+ case SECONDARY_CONNECTOR:
+ if (card->info.broadcast_capable == QETH_BROADCAST_WITHOUT_ECHO)
+ return sprintf(buf, "%s\n", "secondary connector+");
+ else
+ return sprintf(buf, "%s\n", "secondary connector");
+ default:
+ return sprintf(buf, "%s\n", "no");
+ }
+}
+
+static ssize_t qeth_l3_dev_route4_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct qeth_card *card = dev_get_drvdata(dev);
+
+ if (!card)
+ return -EINVAL;
+
+ return qeth_l3_dev_route_show(card, &card->options.route4, buf);
+}
+
+static ssize_t qeth_l3_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 = 0;
+
+ tmp = strsep((char **) &buf, "\n");
+ mutex_lock(&card->conf_mutex);
+ if (!strcmp(tmp, "no_router")) {
+ route->type = NO_ROUTER;
+ } else if (!strcmp(tmp, "primary_connector")) {
+ route->type = PRIMARY_CONNECTOR;
+ } else if (!strcmp(tmp, "secondary_connector")) {
+ route->type = SECONDARY_CONNECTOR;
+ } 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")) {
+ route->type = MULTICAST_ROUTER;
+ } else {
+ rc = -EINVAL;
+ goto out;
+ }
+ if (((card->state == CARD_STATE_SOFTSETUP) ||
+ (card->state == CARD_STATE_UP)) &&
+ (old_route_type != route->type)) {
+ if (prot == QETH_PROT_IPV4)
+ rc = qeth_l3_setrouting_v4(card);
+ else if (prot == QETH_PROT_IPV6)
+ rc = qeth_l3_setrouting_v6(card);
+ }
+out:
+ if (rc)
+ route->type = old_route_type;
+ mutex_unlock(&card->conf_mutex);
+ return rc ? rc : count;
+}
+
+static ssize_t qeth_l3_dev_route4_store(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t count)
+{
+ struct qeth_card *card = dev_get_drvdata(dev);
+
+ if (!card)
+ return -EINVAL;
+
+ return qeth_l3_dev_route_store(card, &card->options.route4,
+ QETH_PROT_IPV4, buf, count);
+}
+
+static DEVICE_ATTR(route4, 0644, qeth_l3_dev_route4_show,
+ qeth_l3_dev_route4_store);
+
+static ssize_t qeth_l3_dev_route6_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct qeth_card *card = dev_get_drvdata(dev);
+
+ if (!card)
+ return -EINVAL;
+
+ return qeth_l3_dev_route_show(card, &card->options.route6, buf);
+}
+
+static ssize_t qeth_l3_dev_route6_store(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t count)
+{
+ struct qeth_card *card = dev_get_drvdata(dev);
+
+ if (!card)
+ return -EINVAL;
+
+ return qeth_l3_dev_route_store(card, &card->options.route6,
+ QETH_PROT_IPV6, buf, count);
+}
+
+static DEVICE_ATTR(route6, 0644, qeth_l3_dev_route6_show,
+ qeth_l3_dev_route6_store);
+
+static ssize_t qeth_l3_dev_fake_broadcast_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct qeth_card *card = dev_get_drvdata(dev);
+
+ if (!card)
+ return -EINVAL;
+
+ return sprintf(buf, "%i\n", card->options.fake_broadcast? 1:0);
+}
+
+static ssize_t qeth_l3_dev_fake_broadcast_store(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t count)
+{
+ struct qeth_card *card = dev_get_drvdata(dev);
+ char *tmp;
+ int i, rc = 0;
+
+ if (!card)
+ return -EINVAL;
+
+ mutex_lock(&card->conf_mutex);
+ if ((card->state != CARD_STATE_DOWN) &&
+ (card->state != CARD_STATE_RECOVER)) {
+ rc = -EPERM;
+ goto out;
+ }
+
+ i = simple_strtoul(buf, &tmp, 16);
+ if ((i == 0) || (i == 1))
+ card->options.fake_broadcast = i;
+ else
+ rc = -EINVAL;
+out:
+ mutex_unlock(&card->conf_mutex);
+ return rc ? rc : count;
+}
+
+static DEVICE_ATTR(fake_broadcast, 0644, qeth_l3_dev_fake_broadcast_show,
+ qeth_l3_dev_fake_broadcast_store);
+
+static ssize_t qeth_l3_dev_sniffer_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct qeth_card *card = dev_get_drvdata(dev);
+
+ if (!card)
+ return -EINVAL;
+
+ return sprintf(buf, "%i\n", card->options.sniffer ? 1 : 0);
+}
+
+static ssize_t qeth_l3_dev_sniffer_store(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t count)
+{
+ struct qeth_card *card = dev_get_drvdata(dev);
+ int rc = 0;
+ unsigned long i;
+
+ if (!card)
+ return -EINVAL;
+
+ if (card->info.type != QETH_CARD_TYPE_IQD)
+ return -EPERM;
+ if (card->options.cq == QETH_CQ_ENABLED)
+ return -EPERM;
+
+ mutex_lock(&card->conf_mutex);
+ if ((card->state != CARD_STATE_DOWN) &&
+ (card->state != CARD_STATE_RECOVER)) {
+ rc = -EPERM;
+ goto out;
+ }
+
+ rc = kstrtoul(buf, 16, &i);
+ if (rc) {
+ rc = -EINVAL;
+ goto out;
+ }
+ switch (i) {
+ case 0:
+ card->options.sniffer = i;
+ break;
+ case 1:
+ qdio_get_ssqd_desc(CARD_DDEV(card), &card->ssqd);
+ if (card->ssqd.qdioac2 & QETH_SNIFF_AVAIL) {
+ card->options.sniffer = i;
+ if (card->qdio.init_pool.buf_count !=
+ QETH_IN_BUF_COUNT_MAX)
+ qeth_realloc_buffer_pool(card,
+ QETH_IN_BUF_COUNT_MAX);
+ } else
+ rc = -EPERM;
+ break;
+ default:
+ rc = -EINVAL;
+ }
+out:
+ mutex_unlock(&card->conf_mutex);
+ return rc ? rc : count;
+}
+
+static DEVICE_ATTR(sniffer, 0644, qeth_l3_dev_sniffer_show,
+ qeth_l3_dev_sniffer_store);
+
+
+static ssize_t qeth_l3_dev_hsuid_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct qeth_card *card = dev_get_drvdata(dev);
+ char tmp_hsuid[9];
+
+ if (!card)
+ return -EINVAL;
+
+ if (card->info.type != QETH_CARD_TYPE_IQD)
+ return -EPERM;
+
+ if (card->state == CARD_STATE_DOWN)
+ return -EPERM;
+
+ memcpy(tmp_hsuid, card->options.hsuid, sizeof(tmp_hsuid));
+ EBCASC(tmp_hsuid, 8);
+ return sprintf(buf, "%s\n", tmp_hsuid);
+}
+
+static ssize_t qeth_l3_dev_hsuid_store(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t count)
+{
+ struct qeth_card *card = dev_get_drvdata(dev);
+ struct qeth_ipaddr *addr;
+ char *tmp;
+ int i;
+
+ if (!card)
+ return -EINVAL;
+
+ if (card->info.type != QETH_CARD_TYPE_IQD)
+ return -EPERM;
+ if (card->state != CARD_STATE_DOWN &&
+ card->state != CARD_STATE_RECOVER)
+ return -EPERM;
+ if (card->options.sniffer)
+ return -EPERM;
+ if (card->options.cq == QETH_CQ_NOTAVAILABLE)
+ return -EPERM;
+
+ tmp = strsep((char **)&buf, "\n");
+ if (strlen(tmp) > 8)
+ return -EINVAL;
+
+ if (card->options.hsuid[0]) {
+ /* delete old ip address */
+ addr = qeth_l3_get_addr_buffer(QETH_PROT_IPV6);
+ if (addr != NULL) {
+ addr->u.a6.addr.s6_addr32[0] = 0xfe800000;
+ addr->u.a6.addr.s6_addr32[1] = 0x00000000;
+ for (i = 8; i < 16; i++)
+ addr->u.a6.addr.s6_addr[i] =
+ card->options.hsuid[i - 8];
+ addr->u.a6.pfxlen = 0;
+ addr->type = QETH_IP_TYPE_NORMAL;
+ } else
+ return -ENOMEM;
+ if (!qeth_l3_delete_ip(card, addr))
+ kfree(addr);
+ qeth_l3_set_ip_addr_list(card);
+ }
+
+ if (strlen(tmp) == 0) {
+ /* delete ip address only */
+ card->options.hsuid[0] = '\0';
+ if (card->dev)
+ memcpy(card->dev->perm_addr, card->options.hsuid, 9);
+ qeth_configure_cq(card, QETH_CQ_DISABLED);
+ return count;
+ }
+
+ if (qeth_configure_cq(card, QETH_CQ_ENABLED))
+ return -EPERM;
+
+ snprintf(card->options.hsuid, sizeof(card->options.hsuid),
+ "%-8s", tmp);
+ ASCEBC(card->options.hsuid, 8);
+ if (card->dev)
+ memcpy(card->dev->perm_addr, card->options.hsuid, 9);
+
+ addr = qeth_l3_get_addr_buffer(QETH_PROT_IPV6);
+ if (addr != NULL) {
+ addr->u.a6.addr.s6_addr32[0] = 0xfe800000;
+ addr->u.a6.addr.s6_addr32[1] = 0x00000000;
+ for (i = 8; i < 16; i++)
+ addr->u.a6.addr.s6_addr[i] = card->options.hsuid[i - 8];
+ addr->u.a6.pfxlen = 0;
+ addr->type = QETH_IP_TYPE_NORMAL;
+ } else
+ return -ENOMEM;
+ if (!qeth_l3_add_ip(card, addr))
+ kfree(addr);
+ qeth_l3_set_ip_addr_list(card);
+
+ return count;
+}
+
+static DEVICE_ATTR(hsuid, 0644, qeth_l3_dev_hsuid_show,
+ qeth_l3_dev_hsuid_store);
+
+
+static struct attribute *qeth_l3_device_attrs[] = {
+ &dev_attr_route4.attr,
+ &dev_attr_route6.attr,
+ &dev_attr_fake_broadcast.attr,
+ &dev_attr_sniffer.attr,
+ &dev_attr_hsuid.attr,
+ NULL,
+};
+
+static struct attribute_group qeth_l3_device_attr_group = {
+ .attrs = qeth_l3_device_attrs,
+};
+
+static ssize_t qeth_l3_dev_ipato_enable_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct qeth_card *card = dev_get_drvdata(dev);
+
+ if (!card)
+ return -EINVAL;
+
+ return sprintf(buf, "%i\n", card->ipato.enabled? 1:0);
+}
+
+static ssize_t qeth_l3_dev_ipato_enable_store(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t count)
+{
+ struct qeth_card *card = dev_get_drvdata(dev);
+ struct qeth_ipaddr *tmpipa, *t;
+ char *tmp;
+ int rc = 0;
+
+ if (!card)
+ return -EINVAL;
+
+ mutex_lock(&card->conf_mutex);
+ if ((card->state != CARD_STATE_DOWN) &&
+ (card->state != CARD_STATE_RECOVER)) {
+ rc = -EPERM;
+ goto out;
+ }
+
+ 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;
+ list_for_each_entry_safe(tmpipa, t, card->ip_tbd_list, entry) {
+ if ((tmpipa->type == QETH_IP_TYPE_NORMAL) &&
+ qeth_l3_is_addr_covered_by_ipato(card, tmpipa))
+ tmpipa->set_flags |=
+ QETH_IPA_SETIP_TAKEOVER_FLAG;
+ }
+
+ } else if (!strcmp(tmp, "0")) {
+ card->ipato.enabled = 0;
+ list_for_each_entry_safe(tmpipa, t, card->ip_tbd_list, entry) {
+ if (tmpipa->set_flags &
+ QETH_IPA_SETIP_TAKEOVER_FLAG)
+ tmpipa->set_flags &=
+ ~QETH_IPA_SETIP_TAKEOVER_FLAG;
+ }
+ } else
+ rc = -EINVAL;
+out:
+ mutex_unlock(&card->conf_mutex);
+ return rc ? rc : count;
+}
+
+static QETH_DEVICE_ATTR(ipato_enable, enable, 0644,
+ qeth_l3_dev_ipato_enable_show,
+ qeth_l3_dev_ipato_enable_store);
+
+static ssize_t qeth_l3_dev_ipato_invert4_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct qeth_card *card = dev_get_drvdata(dev);
+
+ if (!card)
+ return -EINVAL;
+
+ return sprintf(buf, "%i\n", card->ipato.invert4? 1:0);
+}
+
+static ssize_t qeth_l3_dev_ipato_invert4_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct qeth_card *card = dev_get_drvdata(dev);
+ char *tmp;
+ int rc = 0;
+
+ if (!card)
+ return -EINVAL;
+
+ mutex_lock(&card->conf_mutex);
+ 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
+ rc = -EINVAL;
+ mutex_unlock(&card->conf_mutex);
+ return rc ? rc : count;
+}
+
+static QETH_DEVICE_ATTR(ipato_invert4, invert4, 0644,
+ qeth_l3_dev_ipato_invert4_show,
+ qeth_l3_dev_ipato_invert4_store);
+
+static ssize_t qeth_l3_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[40];
+ int entry_len; /* length of 1 entry string, differs between v4 and v6 */
+ int i = 0;
+
+ entry_len = (proto == QETH_PROT_IPV4)? 12 : 40;
+ /* add strlen for "/<mask>\n" */
+ entry_len += (proto == QETH_PROT_IPV4)? 5 : 6;
+ spin_lock_irqsave(&card->ip_lock, flags);
+ list_for_each_entry(ipatoe, &card->ipato.entries, entry) {
+ if (ipatoe->proto != proto)
+ continue;
+ /* String must not be longer than PAGE_SIZE. So we check if
+ * string length gets near PAGE_SIZE. Then we can savely display
+ * the next IPv6 address (worst case, compared to IPv4) */
+ if ((PAGE_SIZE - i) <= entry_len)
+ break;
+ qeth_l3_ipaddr_to_string(proto, ipatoe->addr, addr_str);
+ i += snprintf(buf + i, PAGE_SIZE - i,
+ "%s/%i\n", addr_str, ipatoe->mask_bits);
+ }
+ spin_unlock_irqrestore(&card->ip_lock, flags);
+ i += snprintf(buf + i, PAGE_SIZE - i, "\n");
+
+ return i;
+}
+
+static ssize_t qeth_l3_dev_ipato_add4_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct qeth_card *card = dev_get_drvdata(dev);
+
+ if (!card)
+ return -EINVAL;
+
+ return qeth_l3_dev_ipato_add_show(buf, card, QETH_PROT_IPV4);
+}
+
+static int qeth_l3_parse_ipatoe(const char *buf, enum qeth_prot_versions proto,
+ u8 *addr, int *mask_bits)
+{
+ const char *start, *end;
+ char *tmp;
+ char buffer[40] = {0, };
+
+ start = buf;
+ /* get address string */
+ end = strchr(start, '/');
+ if (!end || (end - start >= 40)) {
+ return -EINVAL;
+ }
+ strncpy(buffer, start, end - start);
+ if (qeth_l3_string_to_ipaddr(buffer, proto, addr)) {
+ return -EINVAL;
+ }
+ start = end + 1;
+ *mask_bits = simple_strtoul(start, &tmp, 10);
+ if (!strlen(start) ||
+ (tmp == start) ||
+ (*mask_bits > ((proto == QETH_PROT_IPV4) ? 32 : 128))) {
+ return -EINVAL;
+ }
+ return 0;
+}
+
+static ssize_t qeth_l3_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 = 0;
+
+ mutex_lock(&card->conf_mutex);
+ rc = qeth_l3_parse_ipatoe(buf, proto, addr, &mask_bits);
+ if (rc)
+ goto out;
+
+ ipatoe = kzalloc(sizeof(struct qeth_ipato_entry), GFP_KERNEL);
+ if (!ipatoe) {
+ rc = -ENOMEM;
+ goto out;
+ }
+ ipatoe->proto = proto;
+ memcpy(ipatoe->addr, addr, (proto == QETH_PROT_IPV4)? 4:16);
+ ipatoe->mask_bits = mask_bits;
+
+ rc = qeth_l3_add_ipato_entry(card, ipatoe);
+ if (rc)
+ kfree(ipatoe);
+out:
+ mutex_unlock(&card->conf_mutex);
+ return rc ? rc : count;
+}
+
+static ssize_t qeth_l3_dev_ipato_add4_store(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t count)
+{
+ struct qeth_card *card = dev_get_drvdata(dev);
+
+ if (!card)
+ return -EINVAL;
+
+ return qeth_l3_dev_ipato_add_store(buf, count, card, QETH_PROT_IPV4);
+}
+
+static QETH_DEVICE_ATTR(ipato_add4, add4, 0644,
+ qeth_l3_dev_ipato_add4_show,
+ qeth_l3_dev_ipato_add4_store);
+
+static ssize_t qeth_l3_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 = 0;
+
+ mutex_lock(&card->conf_mutex);
+ rc = qeth_l3_parse_ipatoe(buf, proto, addr, &mask_bits);
+ if (!rc)
+ qeth_l3_del_ipato_entry(card, proto, addr, mask_bits);
+ mutex_unlock(&card->conf_mutex);
+ return rc ? rc : count;
+}
+
+static ssize_t qeth_l3_dev_ipato_del4_store(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t count)
+{
+ struct qeth_card *card = dev_get_drvdata(dev);
+
+ if (!card)
+ return -EINVAL;
+
+ return qeth_l3_dev_ipato_del_store(buf, count, card, QETH_PROT_IPV4);
+}
+
+static QETH_DEVICE_ATTR(ipato_del4, del4, 0200, NULL,
+ qeth_l3_dev_ipato_del4_store);
+
+static ssize_t qeth_l3_dev_ipato_invert6_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct qeth_card *card = dev_get_drvdata(dev);
+
+ if (!card)
+ return -EINVAL;
+
+ return sprintf(buf, "%i\n", card->ipato.invert6? 1:0);
+}
+
+static ssize_t qeth_l3_dev_ipato_invert6_store(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t count)
+{
+ struct qeth_card *card = dev_get_drvdata(dev);
+ char *tmp;
+ int rc = 0;
+
+ if (!card)
+ return -EINVAL;
+
+ mutex_lock(&card->conf_mutex);
+ 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
+ rc = -EINVAL;
+ mutex_unlock(&card->conf_mutex);
+ return rc ? rc : count;
+}
+
+static QETH_DEVICE_ATTR(ipato_invert6, invert6, 0644,
+ qeth_l3_dev_ipato_invert6_show,
+ qeth_l3_dev_ipato_invert6_store);
+
+
+static ssize_t qeth_l3_dev_ipato_add6_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct qeth_card *card = dev_get_drvdata(dev);
+
+ if (!card)
+ return -EINVAL;
+
+ return qeth_l3_dev_ipato_add_show(buf, card, QETH_PROT_IPV6);
+}
+
+static ssize_t qeth_l3_dev_ipato_add6_store(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t count)
+{
+ struct qeth_card *card = dev_get_drvdata(dev);
+
+ if (!card)
+ return -EINVAL;
+
+ return qeth_l3_dev_ipato_add_store(buf, count, card, QETH_PROT_IPV6);
+}
+
+static QETH_DEVICE_ATTR(ipato_add6, add6, 0644,
+ qeth_l3_dev_ipato_add6_show,
+ qeth_l3_dev_ipato_add6_store);
+
+static ssize_t qeth_l3_dev_ipato_del6_store(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t count)
+{
+ struct qeth_card *card = dev_get_drvdata(dev);
+
+ if (!card)
+ return -EINVAL;
+
+ return qeth_l3_dev_ipato_del_store(buf, count, card, QETH_PROT_IPV6);
+}
+
+static QETH_DEVICE_ATTR(ipato_del6, del6, 0200, NULL,
+ qeth_l3_dev_ipato_del6_store);
+
+static struct attribute *qeth_ipato_device_attrs[] = {
+ &dev_attr_ipato_enable.attr,
+ &dev_attr_ipato_invert4.attr,
+ &dev_attr_ipato_add4.attr,
+ &dev_attr_ipato_del4.attr,
+ &dev_attr_ipato_invert6.attr,
+ &dev_attr_ipato_add6.attr,
+ &dev_attr_ipato_del6.attr,
+ NULL,
+};
+
+static struct attribute_group qeth_device_ipato_group = {
+ .name = "ipa_takeover",
+ .attrs = qeth_ipato_device_attrs,
+};
+
+static ssize_t qeth_l3_dev_vipa_add_show(char *buf, struct qeth_card *card,
+ enum qeth_prot_versions proto)
+{
+ struct qeth_ipaddr *ipaddr;
+ char addr_str[40];
+ int entry_len; /* length of 1 entry string, differs between v4 and v6 */
+ unsigned long flags;
+ int i = 0;
+
+ entry_len = (proto == QETH_PROT_IPV4)? 12 : 40;
+ entry_len += 2; /* \n + terminator */
+ 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;
+ /* String must not be longer than PAGE_SIZE. So we check if
+ * string length gets near PAGE_SIZE. Then we can savely display
+ * the next IPv6 address (worst case, compared to IPv4) */
+ if ((PAGE_SIZE - i) <= entry_len)
+ break;
+ qeth_l3_ipaddr_to_string(proto, (const u8 *)&ipaddr->u,
+ addr_str);
+ i += snprintf(buf + i, PAGE_SIZE - i, "%s\n", addr_str);
+ }
+ spin_unlock_irqrestore(&card->ip_lock, flags);
+ i += snprintf(buf + i, PAGE_SIZE - i, "\n");
+
+ return i;
+}
+
+static ssize_t qeth_l3_dev_vipa_add4_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct qeth_card *card = dev_get_drvdata(dev);
+
+ if (!card)
+ return -EINVAL;
+
+ return qeth_l3_dev_vipa_add_show(buf, card, QETH_PROT_IPV4);
+}
+
+static int qeth_l3_parse_vipae(const char *buf, enum qeth_prot_versions proto,
+ u8 *addr)
+{
+ if (qeth_l3_string_to_ipaddr(buf, proto, addr)) {
+ return -EINVAL;
+ }
+ return 0;
+}
+
+static ssize_t qeth_l3_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;
+
+ mutex_lock(&card->conf_mutex);
+ rc = qeth_l3_parse_vipae(buf, proto, addr);
+ if (!rc)
+ rc = qeth_l3_add_vipa(card, proto, addr);
+ mutex_unlock(&card->conf_mutex);
+ return rc ? rc : count;
+}
+
+static ssize_t qeth_l3_dev_vipa_add4_store(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t count)
+{
+ struct qeth_card *card = dev_get_drvdata(dev);
+
+ if (!card)
+ return -EINVAL;
+
+ return qeth_l3_dev_vipa_add_store(buf, count, card, QETH_PROT_IPV4);
+}
+
+static QETH_DEVICE_ATTR(vipa_add4, add4, 0644,
+ qeth_l3_dev_vipa_add4_show,
+ qeth_l3_dev_vipa_add4_store);
+
+static ssize_t qeth_l3_dev_vipa_del_store(const char *buf, size_t count,
+ struct qeth_card *card, enum qeth_prot_versions proto)
+{
+ u8 addr[16];
+ int rc;
+
+ mutex_lock(&card->conf_mutex);
+ rc = qeth_l3_parse_vipae(buf, proto, addr);
+ if (!rc)
+ qeth_l3_del_vipa(card, proto, addr);
+ mutex_unlock(&card->conf_mutex);
+ return rc ? rc : count;
+}
+
+static ssize_t qeth_l3_dev_vipa_del4_store(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t count)
+{
+ struct qeth_card *card = dev_get_drvdata(dev);
+
+ if (!card)
+ return -EINVAL;
+
+ return qeth_l3_dev_vipa_del_store(buf, count, card, QETH_PROT_IPV4);
+}
+
+static QETH_DEVICE_ATTR(vipa_del4, del4, 0200, NULL,
+ qeth_l3_dev_vipa_del4_store);
+
+static ssize_t qeth_l3_dev_vipa_add6_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct qeth_card *card = dev_get_drvdata(dev);
+
+ if (!card)
+ return -EINVAL;
+
+ return qeth_l3_dev_vipa_add_show(buf, card, QETH_PROT_IPV6);
+}
+
+static ssize_t qeth_l3_dev_vipa_add6_store(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t count)
+{
+ struct qeth_card *card = dev_get_drvdata(dev);
+
+ if (!card)
+ return -EINVAL;
+
+ return qeth_l3_dev_vipa_add_store(buf, count, card, QETH_PROT_IPV6);
+}
+
+static QETH_DEVICE_ATTR(vipa_add6, add6, 0644,
+ qeth_l3_dev_vipa_add6_show,
+ qeth_l3_dev_vipa_add6_store);
+
+static ssize_t qeth_l3_dev_vipa_del6_store(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t count)
+{
+ struct qeth_card *card = dev_get_drvdata(dev);
+
+ if (!card)
+ return -EINVAL;
+
+ return qeth_l3_dev_vipa_del_store(buf, count, card, QETH_PROT_IPV6);
+}
+
+static QETH_DEVICE_ATTR(vipa_del6, del6, 0200, NULL,
+ qeth_l3_dev_vipa_del6_store);
+
+static struct attribute *qeth_vipa_device_attrs[] = {
+ &dev_attr_vipa_add4.attr,
+ &dev_attr_vipa_del4.attr,
+ &dev_attr_vipa_add6.attr,
+ &dev_attr_vipa_del6.attr,
+ NULL,
+};
+
+static struct attribute_group qeth_device_vipa_group = {
+ .name = "vipa",
+ .attrs = qeth_vipa_device_attrs,
+};
+
+static ssize_t qeth_l3_dev_rxip_add_show(char *buf, struct qeth_card *card,
+ enum qeth_prot_versions proto)
+{
+ struct qeth_ipaddr *ipaddr;
+ char addr_str[40];
+ int entry_len; /* length of 1 entry string, differs between v4 and v6 */
+ unsigned long flags;
+ int i = 0;
+
+ entry_len = (proto == QETH_PROT_IPV4)? 12 : 40;
+ entry_len += 2; /* \n + terminator */
+ 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;
+ /* String must not be longer than PAGE_SIZE. So we check if
+ * string length gets near PAGE_SIZE. Then we can savely display
+ * the next IPv6 address (worst case, compared to IPv4) */
+ if ((PAGE_SIZE - i) <= entry_len)
+ break;
+ qeth_l3_ipaddr_to_string(proto, (const u8 *)&ipaddr->u,
+ addr_str);
+ i += snprintf(buf + i, PAGE_SIZE - i, "%s\n", addr_str);
+ }
+ spin_unlock_irqrestore(&card->ip_lock, flags);
+ i += snprintf(buf + i, PAGE_SIZE - i, "\n");
+
+ return i;
+}
+
+static ssize_t qeth_l3_dev_rxip_add4_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct qeth_card *card = dev_get_drvdata(dev);
+
+ if (!card)
+ return -EINVAL;
+
+ return qeth_l3_dev_rxip_add_show(buf, card, QETH_PROT_IPV4);
+}
+
+static int qeth_l3_parse_rxipe(const char *buf, enum qeth_prot_versions proto,
+ u8 *addr)
+{
+ if (qeth_l3_string_to_ipaddr(buf, proto, addr)) {
+ return -EINVAL;
+ }
+ return 0;
+}
+
+static ssize_t qeth_l3_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;
+
+ mutex_lock(&card->conf_mutex);
+ rc = qeth_l3_parse_rxipe(buf, proto, addr);
+ if (!rc)
+ rc = qeth_l3_add_rxip(card, proto, addr);
+ mutex_unlock(&card->conf_mutex);
+ return rc ? rc : count;
+}
+
+static ssize_t qeth_l3_dev_rxip_add4_store(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t count)
+{
+ struct qeth_card *card = dev_get_drvdata(dev);
+
+ if (!card)
+ return -EINVAL;
+
+ return qeth_l3_dev_rxip_add_store(buf, count, card, QETH_PROT_IPV4);
+}
+
+static QETH_DEVICE_ATTR(rxip_add4, add4, 0644,
+ qeth_l3_dev_rxip_add4_show,
+ qeth_l3_dev_rxip_add4_store);
+
+static ssize_t qeth_l3_dev_rxip_del_store(const char *buf, size_t count,
+ struct qeth_card *card, enum qeth_prot_versions proto)
+{
+ u8 addr[16];
+ int rc;
+
+ mutex_lock(&card->conf_mutex);
+ rc = qeth_l3_parse_rxipe(buf, proto, addr);
+ if (!rc)
+ qeth_l3_del_rxip(card, proto, addr);
+ mutex_unlock(&card->conf_mutex);
+ return rc ? rc : count;
+}
+
+static ssize_t qeth_l3_dev_rxip_del4_store(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t count)
+{
+ struct qeth_card *card = dev_get_drvdata(dev);
+
+ if (!card)
+ return -EINVAL;
+
+ return qeth_l3_dev_rxip_del_store(buf, count, card, QETH_PROT_IPV4);
+}
+
+static QETH_DEVICE_ATTR(rxip_del4, del4, 0200, NULL,
+ qeth_l3_dev_rxip_del4_store);
+
+static ssize_t qeth_l3_dev_rxip_add6_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct qeth_card *card = dev_get_drvdata(dev);
+
+ if (!card)
+ return -EINVAL;
+
+ return qeth_l3_dev_rxip_add_show(buf, card, QETH_PROT_IPV6);
+}
+
+static ssize_t qeth_l3_dev_rxip_add6_store(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t count)
+{
+ struct qeth_card *card = dev_get_drvdata(dev);
+
+ if (!card)
+ return -EINVAL;
+
+ return qeth_l3_dev_rxip_add_store(buf, count, card, QETH_PROT_IPV6);
+}
+
+static QETH_DEVICE_ATTR(rxip_add6, add6, 0644,
+ qeth_l3_dev_rxip_add6_show,
+ qeth_l3_dev_rxip_add6_store);
+
+static ssize_t qeth_l3_dev_rxip_del6_store(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t count)
+{
+ struct qeth_card *card = dev_get_drvdata(dev);
+
+ if (!card)
+ return -EINVAL;
+
+ return qeth_l3_dev_rxip_del_store(buf, count, card, QETH_PROT_IPV6);
+}
+
+static QETH_DEVICE_ATTR(rxip_del6, del6, 0200, NULL,
+ qeth_l3_dev_rxip_del6_store);
+
+static struct attribute *qeth_rxip_device_attrs[] = {
+ &dev_attr_rxip_add4.attr,
+ &dev_attr_rxip_del4.attr,
+ &dev_attr_rxip_add6.attr,
+ &dev_attr_rxip_del6.attr,
+ NULL,
+};
+
+static struct attribute_group qeth_device_rxip_group = {
+ .name = "rxip",
+ .attrs = qeth_rxip_device_attrs,
+};
+
+int qeth_l3_create_device_attributes(struct device *dev)
+{
+ int ret;
+
+ ret = sysfs_create_group(&dev->kobj, &qeth_l3_device_attr_group);
+ if (ret)
+ return ret;
+
+ ret = sysfs_create_group(&dev->kobj, &qeth_device_ipato_group);
+ if (ret) {
+ sysfs_remove_group(&dev->kobj, &qeth_l3_device_attr_group);
+ return ret;
+ }
+
+ ret = sysfs_create_group(&dev->kobj, &qeth_device_vipa_group);
+ if (ret) {
+ sysfs_remove_group(&dev->kobj, &qeth_l3_device_attr_group);
+ sysfs_remove_group(&dev->kobj, &qeth_device_ipato_group);
+ return ret;
+ }
+
+ ret = sysfs_create_group(&dev->kobj, &qeth_device_rxip_group);
+ if (ret) {
+ sysfs_remove_group(&dev->kobj, &qeth_l3_device_attr_group);
+ sysfs_remove_group(&dev->kobj, &qeth_device_ipato_group);
+ sysfs_remove_group(&dev->kobj, &qeth_device_vipa_group);
+ return ret;
+ }
+ return 0;
+}
+
+void qeth_l3_remove_device_attributes(struct device *dev)
+{
+ sysfs_remove_group(&dev->kobj, &qeth_l3_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);
+}
diff --git a/drivers/s390/net/qeth_main.c b/drivers/s390/net/qeth_main.c
deleted file mode 100644
index 62606ce26e5..00000000000
--- a/drivers/s390/net/qeth_main.c
+++ /dev/null
@@ -1,8956 +0,0 @@
-/*
- * linux/drivers/s390/net/qeth_main.c
- *
- * 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 (fpavlic@de.ibm.com) and
- * Thomas Spatzier <tspat@de.ibm.com>
- *
- * 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.
- */
-
-
-#include <linux/module.h>
-#include <linux/moduleparam.h>
-#include <linux/string.h>
-#include <linux/errno.h>
-#include <linux/mm.h>
-#include <linux/ip.h>
-#include <linux/inetdevice.h>
-#include <linux/netdevice.h>
-#include <linux/sched.h>
-#include <linux/workqueue.h>
-#include <linux/kernel.h>
-#include <linux/slab.h>
-#include <linux/interrupt.h>
-#include <linux/tcp.h>
-#include <linux/icmp.h>
-#include <linux/skbuff.h>
-#include <linux/in.h>
-#include <linux/igmp.h>
-#include <linux/init.h>
-#include <linux/reboot.h>
-#include <linux/mii.h>
-#include <linux/rcupdate.h>
-#include <linux/ethtool.h>
-
-#include <net/arp.h>
-#include <net/ip.h>
-#include <net/route.h>
-
-#include <asm/ebcdic.h>
-#include <asm/io.h>
-#include <asm/qeth.h>
-#include <asm/timex.h>
-#include <asm/semaphore.h>
-#include <asm/uaccess.h>
-#include <asm/s390_rdev.h>
-
-#include "qeth.h"
-#include "qeth_mpc.h"
-#include "qeth_fs.h"
-#include "qeth_eddp.h"
-#include "qeth_tso.h"
-
-static const char *version = "qeth S/390 OSA-Express driver";
-
-/**
- * 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;
-debug_info_t *qeth_dbf_trace = NULL;
-static debug_info_t *qeth_dbf_sense = NULL;
-static debug_info_t *qeth_dbf_qerr = NULL;
-
-DEFINE_PER_CPU(char[256], qeth_dbf_txt_buf);
-
-static struct lock_class_key qdio_out_skb_queue_key;
-
-/**
- * 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;
-/*process list want to be notified*/
-spinlock_t qeth_notify_lock;
-struct list_head qeth_notify_list;
-
-static void qeth_send_control_data_cb(struct qeth_channel *,
- struct qeth_cmd_buffer *);
-
-/**
- * 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 *);
-
-static int
-__qeth_set_online(struct ccwgroup_device *gdev, int recovery_mode);
-
-static struct qeth_ipaddr *
-qeth_get_addr_buffer(enum qeth_prot_versions);
-
-static void
-qeth_set_multicast_list(struct net_device *);
-
-static void
-qeth_setadp_promisc_mode(struct qeth_card *);
-
-static int
-qeth_hard_header_parse(const struct sk_buff *skb, unsigned char *haddr);
-
-static void
-qeth_notify_processes(void)
-{
- /*notify all registered processes */
- struct qeth_notify_list_struct *n_entry;
-
- QETH_DBF_TEXT(trace,3,"procnoti");
- spin_lock(&qeth_notify_lock);
- list_for_each_entry(n_entry, &qeth_notify_list, list) {
- send_sig(n_entry->signum, n_entry->task, 1);
- }
- spin_unlock(&qeth_notify_lock);
-
-}
-int
-qeth_notifier_unregister(struct task_struct *p)
-{
- struct qeth_notify_list_struct *n_entry, *tmp;
-
- QETH_DBF_TEXT(trace, 2, "notunreg");
- spin_lock(&qeth_notify_lock);
- list_for_each_entry_safe(n_entry, tmp, &qeth_notify_list, list) {
- if (n_entry->task == p) {
- list_del(&n_entry->list);
- kfree(n_entry);
- goto out;
- }
- }
-out:
- spin_unlock(&qeth_notify_lock);
- return 0;
-}
-int
-qeth_notifier_register(struct task_struct *p, int signum)
-{
- struct qeth_notify_list_struct *n_entry;
-
- /*check first if entry already exists*/
- spin_lock(&qeth_notify_lock);
- list_for_each_entry(n_entry, &qeth_notify_list, list) {
- if (n_entry->task == p) {
- n_entry->signum = signum;
- spin_unlock(&qeth_notify_lock);
- return 0;
- }
- }
- spin_unlock(&qeth_notify_lock);
-
- n_entry = (struct qeth_notify_list_struct *)
- kmalloc(sizeof(struct qeth_notify_list_struct),GFP_KERNEL);
- if (!n_entry)
- return -ENOMEM;
- n_entry->task = p;
- n_entry->signum = signum;
- spin_lock(&qeth_notify_lock);
- list_add(&n_entry->list,&qeth_notify_list);
- spin_unlock(&qeth_notify_lock);
- return 0;
-}
-
-
-/**
- * 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);
- kfree(card->ip_tbd_list);
- 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 = kzalloc(sizeof(struct qeth_card), GFP_DMA|GFP_KERNEL);
- if (!card)
- return NULL;
- QETH_DBF_HEX(setup, 2, &card, sizeof(void *));
- 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, unsigned long intparm,
- 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);
- if (intparm == QETH_RCD_PARM) {
- struct qeth_card *card = CARD_FROM_CDEV(cdev);
-
- if (card && (card->data.ccwdev == cdev)) {
- card->data.state = CH_STATE_DOWN;
- wake_up(&card->wait_q);
- }
- }
- 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, intparm, 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) &&
- (intparm != QETH_RCD_PARM))
- 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);
- }
- if (intparm == QETH_RCD_PARM) {
- channel->state = CH_STATE_DOWN;
- goto out;
- }
- rc = qeth_get_problem(cdev,irb);
- if (rc) {
- qeth_schedule_recovery(card);
- goto out;
- }
- }
-
- if (intparm == QETH_RCD_PARM) {
- channel->state = CH_STATE_RCD_DONE;
- 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);
-
- qeth_irq_tasklet((unsigned long)channel);
- 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 *, int);
-
-static int
-__qeth_set_offline(struct ccwgroup_device *cgdev, int recovery_mode)
-{
- struct qeth_card *card = (struct qeth_card *) cgdev->dev.driver_data;
- int rc = 0, rc2 = 0, rc3 = 0;
- enum qeth_card_states recover_flag;
-
- QETH_DBF_TEXT(setup, 3, "setoffl");
- QETH_DBF_HEX(setup, 3, &card, sizeof(void *));
-
- if (card->dev && netif_carrier_ok(card->dev))
- netif_carrier_off(card->dev);
- recover_flag = card->state;
- if (qeth_stop_card(card, recovery_mode) == -ERESTARTSYS){
- PRINT_WARN("Stopping card %s interrupted by user!\n",
- CARD_BUS_ID(card));
- return -ERESTARTSYS;
- }
- rc = ccw_device_set_offline(CARD_DDEV(card));
- rc2 = ccw_device_set_offline(CARD_WDEV(card));
- rc3 = ccw_device_set_offline(CARD_RDEV(card));
- if (!rc)
- rc = (rc2) ? rc2 : rc3;
- if (rc)
- QETH_DBF_TEXT_(setup, 2, "1err%d", rc);
- if (recover_flag == CARD_STATE_UP)
- card->state = CARD_STATE_RECOVER;
- qeth_notify_processes();
- return 0;
-}
-
-static int
-qeth_set_offline(struct ccwgroup_device *cgdev)
-{
- return __qeth_set_offline(cgdev, 0);
-}
-
-static int
-qeth_threads_running(struct qeth_card *card, unsigned long threads);
-
-
-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;
-
- wait_event(card->wait_q, qeth_threads_running(card, 0xffffffff) == 0);
-
- if (cgdev->state == CCWGROUP_ONLINE){
- card->use_hard_stop = 1;
- qeth_set_offline(cgdev);
- }
- /* 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);
- if (card->dev)
- unregister_netdev(card->dev);
- qeth_remove_device_attributes(&cgdev->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 (card->options.layer2) {
- if ((addr->type == todo->type) &&
- (memcmp(&addr->mac, &todo->mac,
- OSA_ADDR_LEN) == 0)) {
- found = 1;
- break;
- }
- continue;
- }
- 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)
- todo->users = 1;
- return 1;
- } else
- return 0;
-}
-
-static 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 ((addr->type == QETH_IP_TYPE_DEL_ALL_MC) &&
- (tmp->type == QETH_IP_TYPE_DEL_ALL_MC))
- return 0;
- if (card->options.layer2) {
- if ((tmp->type == addr->type) &&
- (tmp->is_multicast == addr->is_multicast) &&
- (memcmp(&tmp->mac, &addr->mac,
- OSA_ADDR_LEN) == 0)) {
- found = 1;
- break;
- }
- continue;
- }
- 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->type == QETH_IP_TYPE_DEL_ALL_MC)
- list_add(&addr->entry, card->ip_tbd_list);
- else {
- if (addr->users == 0)
- addr->users += add? 1:-1;
- if (add && (addr->type == QETH_IP_TYPE_NORMAL) &&
- qeth_is_addr_covered_by_ipato(card, addr)){
- QETH_DBF_TEXT(trace, 2, "tkovaddr");
- addr->set_flags |= QETH_IPA_SETIP_TAKEOVER_FLAG;
- }
- list_add_tail(&addr->entry, card->ip_tbd_list);
- }
- 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 (card->options.layer2)
- QETH_DBF_HEX(trace, 4, &addr->mac, 6);
- else 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, 8);
- QETH_DBF_HEX(trace, 4, ((char *)&addr->u.a6.addr) + 8, 8);
- }
- spin_lock_irqsave(&card->ip_lock, flags);
- rc = __qeth_insert_ip_todo(card, addr, 0);
- 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 (card->options.layer2)
- QETH_DBF_HEX(trace, 4, &addr->mac, 6);
- else 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, 8);
- QETH_DBF_HEX(trace, 4, ((char *)&addr->u.a6.addr) + 8, 8);
- }
- spin_lock_irqsave(&card->ip_lock, flags);
- rc = __qeth_insert_ip_todo(card, addr, 1);
- spin_unlock_irqrestore(&card->ip_lock, flags);
- return rc;
-}
-
-static void
-__qeth_delete_all_mc(struct qeth_card *card, unsigned long *flags)
-{
- struct qeth_ipaddr *addr, *tmp;
- int rc;
-again:
- list_for_each_entry_safe(addr, tmp, &card->ip_list, entry) {
- if (addr->is_multicast) {
- list_del(&addr->entry);
- spin_unlock_irqrestore(&card->ip_lock, *flags);
- rc = qeth_deregister_addr_entry(card, addr);
- spin_lock_irqsave(&card->ip_lock, *flags);
- if (!rc) {
- kfree(addr);
- goto again;
- } else
- list_add(&addr->entry, &card->ip_list);
- }
- }
-}
-
-static void
-qeth_set_ip_addr_list(struct qeth_card *card)
-{
- struct list_head *tbd_list;
- struct qeth_ipaddr *todo, *addr;
- unsigned long flags;
- int rc;
-
- QETH_DBF_TEXT(trace, 2, "sdiplist");
- QETH_DBF_HEX(trace, 2, &card, sizeof(void *));
-
- spin_lock_irqsave(&card->ip_lock, flags);
- tbd_list = card->ip_tbd_list;
- card->ip_tbd_list = kmalloc(sizeof(struct list_head), GFP_ATOMIC);
- if (!card->ip_tbd_list) {
- QETH_DBF_TEXT(trace, 0, "silnomem");
- card->ip_tbd_list = tbd_list;
- spin_unlock_irqrestore(&card->ip_lock, flags);
- return;
- } else
- INIT_LIST_HEAD(card->ip_tbd_list);
-
- while (!list_empty(tbd_list)){
- todo = list_entry(tbd_list->next, struct qeth_ipaddr, entry);
- list_del(&todo->entry);
- if (todo->type == QETH_IP_TYPE_DEL_ALL_MC){
- __qeth_delete_all_mc(card, &flags);
- kfree(todo);
- continue;
- }
- rc = __qeth_ref_ip_on_card(card, todo, &addr);
- if (rc == 0) {
- /* nothing to be done; only adjusted refcount */
- 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);
- spin_lock_irqsave(&card->ip_lock, flags);
- if (!rc)
- list_add_tail(&todo->entry, &card->ip_list);
- else
- kfree(todo);
- } else if (rc == -1) {
- /* on-card entry to be removed */
- list_del_init(&addr->entry);
- spin_unlock_irqrestore(&card->ip_lock, flags);
- rc = qeth_deregister_addr_entry(card, addr);
- spin_lock_irqsave(&card->ip_lock, flags);
- if (!rc)
- kfree(addr);
- else
- list_add_tail(&addr->entry, &card->ip_list);
- kfree(todo);
- }
- }
- spin_unlock_irqrestore(&card->ip_lock, flags);
- kfree(tbd_list);
-}
-
-static void qeth_delete_mc_addresses(struct qeth_card *);
-static void qeth_add_multicast_ipv4(struct qeth_card *);
-static void qeth_layer2_add_multicast(struct qeth_card *);
-#ifdef CONFIG_QETH_IPV6
-static void qeth_add_multicast_ipv6(struct qeth_card *);
-#endif
-
-static int
-qeth_set_thread_start_bit(struct qeth_card *card, unsigned long thread)
-{
- unsigned long flags;
-
- spin_lock_irqsave(&card->thread_mask_lock, flags);
- if ( !(card->thread_allowed_mask & thread) ||
- (card->thread_start_mask & thread) ) {
- spin_unlock_irqrestore(&card->thread_mask_lock, flags);
- return -EPERM;
- }
- card->thread_start_mask |= thread;
- spin_unlock_irqrestore(&card->thread_mask_lock, flags);
- return 0;
-}
-
-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 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_recover(void *ptr)
-{
- struct qeth_card *card;
- int rc = 0;
-
- card = (struct qeth_card *) ptr;
- daemonize("qeth_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,1);
- rc = __qeth_set_online(card->gdev,1);
- /* don't run another scheduled recovery */
- qeth_clear_thread_start_bit(card, QETH_RECOVER_THREAD);
- qeth_clear_thread_running_bit(card, QETH_RECOVER_THREAD);
- 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));
- return 0;
-}
-
-void
-qeth_schedule_recovery(struct qeth_card *card)
-{
- QETH_DBF_TEXT(trace,2,"startrec");
- if (qeth_set_thread_start_bit(card, QETH_RECOVER_THREAD) == 0)
- 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 work_struct *work)
-{
- struct qeth_card *card = container_of(work, struct qeth_card, kernel_thread_starter);
- 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_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.fake_broadcast = 0;
- card->options.add_hhlen = DEFAULT_ADD_HHLEN;
- card->options.fake_ll = 0;
- if (card->info.type == QETH_CARD_TYPE_OSN)
- card->options.layer2 = 1;
- else
- card->options.layer2 = 0;
- card->options.performance_stats = 0;
- card->options.rx_sg_cb = QETH_RX_SG_CB;
-}
-
-/**
- * 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->lock);
- 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, qeth_start_kernel_thread);
- INIT_LIST_HEAD(&card->ip_list);
- card->ip_tbd_list = kmalloc(sizeof(struct list_head), GFP_KERNEL);
- if (!card->ip_tbd_list) {
- QETH_DBF_TEXT(setup, 0, "iptbdnom");
- return -ENOMEM;
- }
- INIT_LIST_HEAD(card->ip_tbd_list);
- INIT_LIST_HEAD(&card->cmd_waiter_list);
- init_waitqueue_head(&card->wait_q);
- /* intial options */
- 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
-is_1920_device (struct qeth_card *card)
-{
- int single_queue = 0;
- struct ccw_device *ccwdev;
- struct channelPath_dsc {
- u8 flags;
- u8 lsn;
- u8 desc;
- u8 chpid;
- u8 swla;
- u8 zeroes;
- u8 chla;
- u8 chpp;
- } *chp_dsc;
-
- QETH_DBF_TEXT(setup, 2, "chk_1920");
-
- ccwdev = card->data.ccwdev;
- chp_dsc = (struct channelPath_dsc *)ccw_device_get_chp_desc(ccwdev, 0);
- if (chp_dsc != NULL) {
- /* CHPP field bit 6 == 1 -> single queue */
- single_queue = ((chp_dsc->chpp & 0x02) == 0x02);
- kfree(chp_dsc);
- }
- QETH_DBF_TEXT_(setup, 2, "rc:%x", single_queue);
- return single_queue;
-}
-
-static int
-qeth_determine_card_type(struct qeth_card *card)
-{
- int i = 0;
-
- QETH_DBF_TEXT(setup, 2, "detcdtyp");
-
- card->qdio.do_prio_queueing = QETH_PRIOQ_DEFAULT;
- card->qdio.default_out_queue = QETH_DEFAULT_QUEUE;
- 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];
- card->qdio.no_out_queues = known_devices[i][8];
- card->info.is_multicast_different = known_devices[i][9];
- if (is_1920_device(card)) {
- PRINT_INFO("Priority Queueing not able "
- "due to hardware limitations!\n");
- card->qdio.no_out_queues = 1;
- card->qdio.default_out_queue = 0;
- }
- 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;
-
- QETH_DBF_TEXT_(setup, 2, "%s", gdev->dev.bus_id);
-
- card = qeth_alloc_card();
- if (!card) {
- put_device(dev);
- QETH_DBF_TEXT_(setup, 2, "1err%d", -ENOMEM);
- return -ENOMEM;
- }
- card->read.ccwdev = gdev->cdev[0];
- card->write.ccwdev = gdev->cdev[1];
- card->data.ccwdev = gdev->cdev[2];
- 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;
-
- 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;
- }
- if ((rc = qeth_setup_card(card))){
- QETH_DBF_TEXT_(setup, 2, "2err%d", rc);
- put_device(dev);
- qeth_free_card(card);
- return rc;
- }
- rc = qeth_create_device_attributes(dev);
- if (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_read_conf_data(struct qeth_card *card, void **buffer,
- int *length)
-{
- struct ciw *ciw;
- char *rcd_buf;
- int ret;
- struct qeth_channel *channel = &card->data;
- unsigned long flags;
-
- /*
- * scan for RCD command in extended SenseID data
- */
- ciw = ccw_device_get_ciw(channel->ccwdev, CIW_TYPE_RCD);
- if (!ciw || ciw->cmd == 0)
- return -EOPNOTSUPP;
- rcd_buf = kzalloc(ciw->count, GFP_KERNEL | GFP_DMA);
- if (!rcd_buf)
- return -ENOMEM;
-
- channel->ccw.cmd_code = ciw->cmd;
- channel->ccw.cda = (__u32) __pa (rcd_buf);
- channel->ccw.count = ciw->count;
- channel->ccw.flags = CCW_FLAG_SLI;
- channel->state = CH_STATE_RCD;
- spin_lock_irqsave(get_ccwdev_lock(channel->ccwdev), flags);
- ret = ccw_device_start_timeout(channel->ccwdev, &channel->ccw,
- QETH_RCD_PARM, LPM_ANYPATH, 0,
- QETH_RCD_TIMEOUT);
- spin_unlock_irqrestore(get_ccwdev_lock(channel->ccwdev), flags);
- if (!ret)
- wait_event(card->wait_q,
- (channel->state == CH_STATE_RCD_DONE ||
- channel->state == CH_STATE_DOWN));
- if (channel->state == CH_STATE_DOWN)
- ret = -EIO;
- else
- channel->state = CH_STATE_DOWN;
- if (ret) {
- kfree(rcd_buf);
- *buffer = NULL;
- *length = 0;
- } else {
- *length = ciw->count;
- *buffer = rcd_buf;
- }
- return ret;
-}
-
-static int
-qeth_get_unitaddr(struct qeth_card *card)
-{
- int length;
- char *prcd;
- int rc;
-
- QETH_DBF_TEXT(setup, 2, "getunit");
- rc = qeth_read_conf_data(card, (void **) &prcd, &length);
- if (rc) {
- PRINT_ERR("qeth_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']));
- kfree(prcd);
- 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;
-
- 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_cmpxchg(&channel->irq_pending, 0, 1) == 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);
- qeth_clear_cmd_buffers(channel);
- } 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_cmpxchg(&channel->irq_pending, 0, 1) == 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);
- qeth_clear_cmd_buffers(channel);
- 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))) {
- if (QETH_IDX_ACT_CAUSE_CODE(iob->data) == 0x19)
- PRINT_ERR("IDX_ACTIVATE on write channel device %s: "
- "adapter exclusively used by another host\n",
- CARD_WDEV_ID(card));
- else
- 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))) {
- if (QETH_IDX_ACT_CAUSE_CODE(iob->data) == 0x19)
- PRINT_ERR("IDX_ACTIVATE on read channel device %s: "
- "adapter exclusively used by another host\n",
- CARD_RDEV_ID(card));
- else
- 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);
- 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 = kzalloc(sizeof(struct qeth_reply), GFP_ATOMIC);
- if (reply){
- atomic_set(&reply->refcnt, 1);
- atomic_set(&reply->received, 0);
- 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_issue_ipa_msg(struct qeth_ipa_cmd *cmd, struct qeth_card *card)
-{
- int rc;
- int com;
- char * ipa_name;
-
- com = cmd->hdr.command;
- rc = cmd->hdr.return_code;
- ipa_name = qeth_get_ipa_cmd_name(com);
-
- PRINT_ERR("%s(x%X) for %s returned x%X \"%s\"\n", ipa_name, com,
- QETH_CARD_IFNAME(card), rc, qeth_get_ipa_msg(rc));
-}
-
-static struct qeth_ipa_cmd *
-qeth_check_ipa_data(struct qeth_card *card, struct qeth_cmd_buffer *iob)
-{
- struct qeth_ipa_cmd *cmd = NULL;
-
- 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)) {
- if (cmd->hdr.return_code)
- qeth_issue_ipa_msg(cmd, card);
- 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.\n",
- QETH_CARD_IFNAME(card),
- card->info.chpid);
- card->lan_online = 0;
- if (card->dev && netif_carrier_ok(card->dev))
- netif_carrier_off(card->dev);
- return NULL;
- case IPA_CMD_STARTLAN:
- PRINT_INFO("Link reestablished on %s "
- "(CHPID 0x%X). Scheduling "
- "IP address reset.\n",
- QETH_CARD_IFNAME(card),
- card->info.chpid);
- netif_carrier_on(card->dev);
- qeth_schedule_recovery(card);
- return NULL;
- case IPA_CMD_MODCCID:
- return cmd;
- case IPA_CMD_REGISTER_LOCAL_ADDR:
- QETH_DBF_TEXT(trace,3, "irla");
- break;
- case IPA_CMD_UNREGISTER_LOCAL_ADDR:
- QETH_DBF_TEXT(trace,3, "urla");
- 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;
- atomic_inc(&reply->received);
- 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;
- /*in case of OSN : check if cmd is set */
- if (card->info.type == QETH_CARD_TYPE_OSN &&
- cmd &&
- cmd->hdr.command != IPA_CMD_STARTLAN &&
- card->osn_info.assist_cb != NULL) {
- card->osn_info.assist_cb(card->dev, cmd);
- 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) {
- reply->offset = (__u16)((char*)cmd -
- (char *)iob->data);
- keep_reply = reply->callback(card,
- reply,
- (unsigned long)cmd);
- } else
- keep_reply = reply->callback(card,
- reply,
- (unsigned long)iob);
- }
- if (cmd)
- reply->rc = (u16) 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 {
- atomic_inc(&reply->received);
- 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 void
-qeth_prepare_control_data(struct qeth_card *card, int len,
- struct qeth_cmd_buffer *iob)
-{
- qeth_setup_ccw(&card->write,iob->data,len);
- iob->callback = qeth_release_buffer;
-
- 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);
- QETH_DBF_HEX(control, 2, iob->data, QETH_DBF_CONTROL_LEN);
-}
-
-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 = NULL;
- unsigned long timeout;
-
- QETH_DBF_TEXT(trace, 2, "sendctl");
-
- 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_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);
-
- while (atomic_cmpxchg(&card->write.irq_pending, 0, 1)) ;
- qeth_prepare_control_data(card, len, iob);
-
- if (IS_IPA(iob->data))
- timeout = jiffies + QETH_IPA_TIMEOUT;
- else
- timeout = jiffies + QETH_TIMEOUT;
-
- 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;
- }
- while (!atomic_read(&reply->received)) {
- if (time_after(jiffies, timeout)) {
- spin_lock_irqsave(&reply->card->lock, flags);
- list_del_init(&reply->list);
- spin_unlock_irqrestore(&reply->card->lock, flags);
- reply->rc = -ETIME;
- atomic_inc(&reply->received);
- wake_up(&reply->wait_q);
- }
- cpu_relax();
- };
- rc = reply->rc;
- qeth_put_reply(reply);
- return rc;
-}
-
-static int
-qeth_osn_send_control_data(struct qeth_card *card, int len,
- struct qeth_cmd_buffer *iob)
-{
- unsigned long flags;
- int rc = 0;
-
- QETH_DBF_TEXT(trace, 5, "osndctrd");
-
- wait_event(card->wait_q,
- atomic_cmpxchg(&card->write.irq_pending, 0, 1) == 0);
- qeth_prepare_control_data(card, len, iob);
- QETH_DBF_TEXT(trace, 6, "osnoirqp");
- 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_osn_send_control_data: "
- "ccw_device_start rc = %i\n", rc);
- QETH_DBF_TEXT_(trace, 2, " err%d", rc);
- qeth_release_buffer(iob->channel, iob);
- atomic_set(&card->write.irq_pending, 0);
- wake_up(&card->wait_q);
- }
- return rc;
-}
-
-static inline void
-qeth_prepare_ipa_cmd(struct qeth_card *card, struct qeth_cmd_buffer *iob,
- char prot_type)
-{
- memcpy(iob->data, IPA_PDU_HEADER, IPA_PDU_HEADER_SIZE);
- memcpy(QETH_IPA_CMD_PROT_TYPE(iob->data),&prot_type,1);
- memcpy(QETH_IPA_CMD_DEST_ADDR(iob->data),
- &card->token.ulp_connection_r, QETH_MPC_TOKEN_LENGTH);
-}
-
-static int
-qeth_osn_send_ipa_cmd(struct qeth_card *card, struct qeth_cmd_buffer *iob,
- int data_len)
-{
- u16 s1, s2;
-
- QETH_DBF_TEXT(trace,4,"osndipa");
-
- qeth_prepare_ipa_cmd(card, iob, QETH_PROT_OSN2);
- s1 = (u16)(IPA_PDU_HEADER_SIZE + data_len);
- s2 = (u16)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);
- return qeth_osn_send_control_data(card, s1, iob);
-}
-
-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)
-{
- int rc;
- char prot_type;
-
- QETH_DBF_TEXT(trace,4,"sendipa");
-
- if (card->options.layer2)
- if (card->info.type == QETH_CARD_TYPE_OSN)
- prot_type = QETH_PROT_OSN2;
- else
- prot_type = QETH_PROT_LAYER2;
- else
- prot_type = QETH_PROT_TCPIP;
- qeth_prepare_ipa_cmd(card,iob,prot_type);
- 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;
- char prot_type;
- 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;
- if (card->options.layer2)
- if (card->info.type == QETH_CARD_TYPE_OSN)
- prot_type = QETH_PROT_OSN2;
- else
- prot_type = QETH_PROT_LAYER2;
- else
- prot_type = QETH_PROT_TCPIP;
-
- memcpy(QETH_ULP_ENABLE_PROT_TYPE(iob->data),&prot_type,1);
- memcpy(QETH_ULP_ENABLE_DEST_ADDR(iob->data),
- &card->token.cm_connection_r, QETH_MPC_TOKEN_LENGTH);
- memcpy(QETH_ULP_ENABLE_FILTER_TOKEN(iob->data),
- &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 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;
- struct ccw_dev_id dev_id;
-
- 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);
-
- ccw_device_get_id(CARD_DDEV(card), &dev_id);
- memcpy(QETH_ULP_SETUP_CUA(iob->data), &dev_id.devno, 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_qdio_errors(struct qdio_buffer *buf, unsigned int qdio_error,
- unsigned int siga_error, const char *dbftext)
-{
- if (qdio_error || siga_error) {
- QETH_DBF_TEXT(trace, 2, dbftext);
- QETH_DBF_TEXT(qerr, 2, dbftext);
- QETH_DBF_TEXT_(qerr, 2, " F15=%02X",
- buf->element[15].flags & 0xff);
- QETH_DBF_TEXT_(qerr, 2, " F14=%02X",
- buf->element[14].flags & 0xff);
- QETH_DBF_TEXT_(qerr, 2, " qerr=%X", qdio_error);
- QETH_DBF_TEXT_(qerr, 2, " serr=%X", siga_error);
- return 1;
- }
- return 0;
-}
-
-static struct sk_buff *
-qeth_get_skb(unsigned int length, struct qeth_hdr *hdr)
-{
- struct sk_buff* skb;
- int add_len;
-
- add_len = 0;
- if (hdr->hdr.osn.id == QETH_HEADER_TYPE_OSN)
- add_len = sizeof(struct qeth_hdr);
-#ifdef CONFIG_QETH_VLAN
- else
- add_len = VLAN_HLEN;
-#endif
- skb = dev_alloc_skb(length + add_len);
- if (skb && add_len)
- skb_reserve(skb, add_len);
- return skb;
-}
-
-static inline int
-qeth_create_skb_frag(struct qdio_buffer_element *element,
- struct sk_buff **pskb,
- int offset, int *pfrag, int data_len)
-{
- struct page *page = virt_to_page(element->addr);
- if (*pfrag == 0) {
- /* the upper protocol layers assume that there is data in the
- * skb itself. Copy a small amount (64 bytes) to make them
- * happy. */
- *pskb = dev_alloc_skb(64 + QETH_FAKE_LL_LEN_ETH);
- if (!(*pskb))
- return -ENOMEM;
- skb_reserve(*pskb, QETH_FAKE_LL_LEN_ETH);
- if (data_len <= 64) {
- memcpy(skb_put(*pskb, data_len), element->addr + offset,
- data_len);
- } else {
- get_page(page);
- memcpy(skb_put(*pskb, 64), element->addr + offset, 64);
- skb_fill_page_desc(*pskb, *pfrag, page, offset + 64,
- data_len - 64);
- (*pskb)->data_len += data_len - 64;
- (*pskb)->len += data_len - 64;
- (*pskb)->truesize += data_len - 64;
- }
- } else {
- get_page(page);
- skb_fill_page_desc(*pskb, *pfrag, page, offset, data_len);
- (*pskb)->data_len += data_len;
- (*pskb)->len += data_len;
- (*pskb)->truesize += data_len;
- }
- (*pfrag)++;
- return 0;
-}
-
-static inline struct qeth_buffer_pool_entry *
-qeth_find_free_buffer_pool_entry(struct qeth_card *card)
-{
- struct list_head *plh;
- struct qeth_buffer_pool_entry *entry;
- int i, free;
- struct page *page;
-
- if (list_empty(&card->qdio.in_buf_pool.entry_list))
- return NULL;
-
- list_for_each(plh, &card->qdio.in_buf_pool.entry_list) {
- entry = list_entry(plh, struct qeth_buffer_pool_entry, list);
- free = 1;
- for (i = 0; i < QETH_MAX_BUFFER_ELEMENTS(card); ++i) {
- if (page_count(virt_to_page(entry->elements[i])) > 1) {
- free = 0;
- break;
- }
- }
- if (free) {
- list_del_init(&entry->list);
- return entry;
- }
- }
-
- /* no free buffer in pool so take first one and swap pages */
- entry = list_entry(card->qdio.in_buf_pool.entry_list.next,
- struct qeth_buffer_pool_entry, list);
- for (i = 0; i < QETH_MAX_BUFFER_ELEMENTS(card); ++i) {
- if (page_count(virt_to_page(entry->elements[i])) > 1) {
- page = alloc_page(GFP_ATOMIC|GFP_DMA);
- if (!page) {
- return NULL;
- } else {
- free_page((unsigned long)entry->elements[i]);
- entry->elements[i] = page_address(page);
- if (card->options.performance_stats)
- card->perf_stats.sg_alloc_page_rx++;
- }
- }
- }
- list_del_init(&entry->list);
- return entry;
-}
-
-static 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;
- int use_rx_sg = 0;
- int frag = 0;
-
- 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);
- if (card->options.layer2)
- if (card->info.type == QETH_CARD_TYPE_OSN)
- skb_len = (*hdr)->hdr.osn.pdu_length;
- else
- skb_len = (*hdr)->hdr.l2.pkt_length;
- else
- skb_len = (*hdr)->hdr.l3.length;
-
- if (!skb_len)
- return NULL;
- if ((skb_len >= card->options.rx_sg_cb) &&
- (!(card->info.type == QETH_CARD_TYPE_OSN)) &&
- (!atomic_read(&card->force_alloc_skb))) {
- use_rx_sg = 1;
- } else {
- if (card->options.fake_ll) {
- if (card->dev->type == ARPHRD_IEEE802_TR) {
- if (!(skb = qeth_get_skb(skb_len +
- QETH_FAKE_LL_LEN_TR, *hdr)))
- goto no_mem;
- skb_reserve(skb, QETH_FAKE_LL_LEN_TR);
- } else {
- if (!(skb = qeth_get_skb(skb_len +
- QETH_FAKE_LL_LEN_ETH, *hdr)))
- goto no_mem;
- skb_reserve(skb, QETH_FAKE_LL_LEN_ETH);
- }
- } else {
- skb = qeth_get_skb(skb_len, *hdr);
- if (!skb)
- goto no_mem;
- }
- }
-
- data_ptr = element->addr + offset;
- while (skb_len) {
- data_len = min(skb_len, (int)(element->length - offset));
- if (data_len) {
- if (use_rx_sg) {
- if (qeth_create_skb_frag(element, &skb, offset,
- &frag, data_len))
- goto no_mem;
- } else {
- 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_any(skb);
- card->stats.rx_errors++;
- return NULL;
- }
- element++;
- offset = 0;
- data_ptr = element->addr;
- } else {
- offset += data_len;
- }
- }
- *__element = element;
- *__offset = offset;
- if (use_rx_sg && card->options.performance_stats) {
- card->perf_stats.sg_skbs_rx++;
- card->perf_stats.sg_frags_rx += skb_shinfo(skb)->nr_frags;
- }
- return skb;
-no_mem:
- if (net_ratelimit()){
- PRINT_WARN("No memory for packet received on %s.\n",
- QETH_CARD_IFNAME(card));
- QETH_DBF_TEXT(trace,2,"noskbmem");
- QETH_DBF_TEXT_(trace,2,"%s",CARD_BUS_ID(card));
- }
- card->stats.rx_dropped++;
- return NULL;
-}
-
-static __be16
-qeth_type_trans(struct sk_buff *skb, struct net_device *dev)
-{
- struct qeth_card *card;
- struct ethhdr *eth;
-
- QETH_DBF_TEXT(trace,6,"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_reset_mac_header(skb);
- skb_pull(skb, ETH_HLEN );
- eth = eth_hdr(skb);
-
- 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 if (memcmp(eth->h_dest, dev->dev_addr, ETH_ALEN))
- skb->pkt_type = PACKET_OTHERHOST;
-
- if (ntohs(eth->h_proto) >= 1536)
- return eth->h_proto;
- if (*(unsigned short *) (skb->data) == 0xFFFF)
- return htons(ETH_P_802_3);
- return htons(ETH_P_802_2);
-}
-
-static void
-qeth_rebuild_skb_fake_ll_tr(struct qeth_card *card, struct sk_buff *skb,
- struct qeth_hdr *hdr)
-{
- struct trh_hdr *fake_hdr;
- struct trllc *fake_llc;
- struct iphdr *ip_hdr;
-
- QETH_DBF_TEXT(trace,5,"skbfktr");
- skb_set_mac_header(skb, (int)-QETH_FAKE_LL_LEN_TR);
- /* this is a fake ethernet header */
- fake_hdr = tr_hdr(skb);
-
- /* 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->daddr, card->dev, 0);
- break;
-#endif /* CONFIG_QETH_IPV6 */
- case __constant_htons(ETH_P_IP):
- ip_hdr = (struct iphdr *)skb->data;
- ip_tr_mc_map(ip_hdr->daddr, fake_hdr->daddr);
- break;
- default:
- memcpy(fake_hdr->daddr, card->dev->dev_addr, TR_ALEN);
- }
- break;
- case PACKET_BROADCAST:
- memset(fake_hdr->daddr, 0xff, TR_ALEN);
- break;
- default:
- memcpy(fake_hdr->daddr, card->dev->dev_addr, TR_ALEN);
- }
- /* the source MAC address */
- if (hdr->hdr.l3.ext_flags & QETH_HDR_EXT_SRC_MAC_ADDR)
- memcpy(fake_hdr->saddr, &hdr->hdr.l3.dest_addr[2], TR_ALEN);
- else
- memset(fake_hdr->saddr, 0, TR_ALEN);
- fake_hdr->rcf=0;
- fake_llc = (struct trllc*)&(fake_hdr->rcf);
- fake_llc->dsap = EXTENDED_SAP;
- fake_llc->ssap = EXTENDED_SAP;
- fake_llc->llc = UI_CMD;
- fake_llc->protid[0] = 0;
- fake_llc->protid[1] = 0;
- fake_llc->protid[2] = 0;
- fake_llc->ethertype = ETH_P_IP;
-}
-
-static void
-qeth_rebuild_skb_fake_ll_eth(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,"skbfketh");
- skb_set_mac_header(skb, -QETH_FAKE_LL_LEN_ETH);
- /* this is a fake ethernet header */
- fake_hdr = eth_hdr(skb);
-
- /* 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;
- 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->hdr.l3.ext_flags & QETH_HDR_EXT_SRC_MAC_ADDR)
- memcpy(fake_hdr->h_source, &hdr->hdr.l3.dest_addr[2], ETH_ALEN);
- else
- memset(fake_hdr->h_source, 0, ETH_ALEN);
- /* the protocol */
- fake_hdr->h_proto = skb->protocol;
-}
-
-static inline void
-qeth_rebuild_skb_fake_ll(struct qeth_card *card, struct sk_buff *skb,
- struct qeth_hdr *hdr)
-{
- if (card->dev->type == ARPHRD_IEEE802_TR)
- qeth_rebuild_skb_fake_ll_tr(card, skb, hdr);
- else
- qeth_rebuild_skb_fake_ll_eth(card, skb, hdr);
-}
-
-static inline void
-qeth_layer2_rebuild_skb(struct qeth_card *card, struct sk_buff *skb,
- struct qeth_hdr *hdr)
-{
- skb->pkt_type = PACKET_HOST;
- skb->protocol = qeth_type_trans(skb, skb->dev);
- if (card->options.checksum_type == NO_CHECKSUMMING)
- skb->ip_summed = CHECKSUM_UNNECESSARY;
- else
- skb->ip_summed = CHECKSUM_NONE;
- *((__u32 *)skb->cb) = ++card->seqno.pkt_seqno;
-}
-
-static __u16
-qeth_rebuild_skb(struct qeth_card *card, struct sk_buff *skb,
- struct qeth_hdr *hdr)
-{
- unsigned short vlan_id = 0;
-#ifdef CONFIG_QETH_IPV6
- if (hdr->hdr.l3.flags & QETH_HDR_PASSTHRU) {
- skb->pkt_type = PACKET_HOST;
- skb->protocol = qeth_type_trans(skb, card->dev);
- return 0;
- }
-#endif /* CONFIG_QETH_IPV6 */
- skb->protocol = htons((hdr->hdr.l3.flags & QETH_HDR_IPV6)? ETH_P_IPV6 :
- ETH_P_IP);
- switch (hdr->hdr.l3.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 (hdr->hdr.l3.ext_flags &
- (QETH_HDR_EXT_VLAN_FRAME | QETH_HDR_EXT_INCLUDE_VLAN_TAG)) {
- vlan_id = (hdr->hdr.l3.ext_flags & QETH_HDR_EXT_VLAN_FRAME)?
- hdr->hdr.l3.vlan_id : *((u16 *)&hdr->hdr.l3.dest_addr[12]);
- }
-
- if (card->options.fake_ll)
- qeth_rebuild_skb_fake_ll(card, skb, hdr);
- else
- skb_reset_mac_header(skb);
- skb->ip_summed = card->options.checksum_type;
- if (card->options.checksum_type == HW_CHECKSUMMING){
- if ( (hdr->hdr.l3.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;
- }
- return vlan_id;
-}
-
-static void
-qeth_process_inbound_buffer(struct qeth_card *card,
- struct qeth_qdio_buffer *buf, int index)
-{
- struct qdio_buffer_element *element;
- struct sk_buff *skb;
- struct qeth_hdr *hdr;
- int offset;
- int rxrc;
- __u16 vlan_tag = 0;
-
- /* get first element of current buffer */
- element = (struct qdio_buffer_element *)&buf->buffer->element[0];
- offset = 0;
- if (card->options.performance_stats)
- card->perf_stats.bufs_rec++;
- while((skb = qeth_get_next_skb(card, buf->buffer, &element,
- &offset, &hdr))) {
- skb->dev = card->dev;
- if (hdr->hdr.l2.id == QETH_HEADER_TYPE_LAYER2)
- qeth_layer2_rebuild_skb(card, skb, hdr);
- else if (hdr->hdr.l3.id == QETH_HEADER_TYPE_LAYER3)
- vlan_tag = qeth_rebuild_skb(card, skb, hdr);
- else if (hdr->hdr.osn.id == QETH_HEADER_TYPE_OSN) {
- skb_push(skb, sizeof(struct qeth_hdr));
- skb_copy_to_linear_data(skb, hdr,
- sizeof(struct qeth_hdr));
- } else { /* unknown header type */
- dev_kfree_skb_any(skb);
- QETH_DBF_TEXT(trace, 3, "inbunkno");
- QETH_DBF_HEX(control, 3, hdr, QETH_DBF_CONTROL_LEN);
- continue;
- }
- /* is device UP ? */
- if (!(card->dev->flags & IFF_UP)){
- dev_kfree_skb_any(skb);
- continue;
- }
- if (card->info.type == QETH_CARD_TYPE_OSN)
- rxrc = card->osn_info.data_cb(skb);
- else
-#ifdef CONFIG_QETH_VLAN
- if (vlan_tag)
- if (card->vlangrp)
- vlan_hwaccel_rx(skb, card->vlangrp, vlan_tag);
- else {
- dev_kfree_skb_any(skb);
- continue;
- }
- else
-#endif
- rxrc = netif_rx(skb);
- card->dev->last_rx = jiffies;
- card->stats.rx_packets++;
- card->stats.rx_bytes += skb->len;
- }
-}
-
-static int
-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_find_free_buffer_pool_entry(card);
- if (!pool_entry)
- return 1;
- /*
- * 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;
- return 0;
-}
-
-static void
-qeth_clear_output_buffer(struct qeth_qdio_out_q *queue,
- struct qeth_qdio_out_buffer *buf)
-{
- int i;
- struct sk_buff *skb;
-
- /* is PCI flag set on buffer? */
- if (buf->buffer->element[0].flags & 0x40)
- atomic_dec(&queue->set_pci_flags_count);
-
- while ((skb = skb_dequeue(&buf->skb_list))){
- atomic_dec(&skb->users);
- dev_kfree_skb_any(skb);
- }
- qeth_eddp_buf_release_contexts(buf);
- for(i = 0; i < QETH_MAX_BUFFER_ELEMENTS(queue->card); ++i){
- buf->buffer->element[i].length = 0;
- buf->buffer->element[i].addr = NULL;
- buf->buffer->element[i].flags = 0;
- }
- buf->next_element_to_fill = 0;
- atomic_set(&buf->state, QETH_QDIO_BUF_EMPTY);
-}
-
-static 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;
- int newcount = 0;
-
- 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) {
- if (qeth_init_input_buffer(card,
- &queue->bufs[i % QDIO_MAX_BUFFERS_PER_Q])) {
- break;
- } else {
- newcount++;
- }
- }
-
- if (newcount < count) {
- /* we are in memory shortage so we switch back to
- traditional skb allocation and drop packages */
- if (!atomic_read(&card->force_alloc_skb) &&
- net_ratelimit())
- PRINT_WARN("Switch to alloc skb\n");
- atomic_set(&card->force_alloc_skb, 3);
- count = newcount;
- } else {
- if ((atomic_read(&card->force_alloc_skb) == 1) &&
- net_ratelimit())
- PRINT_WARN("Switch to sg\n");
- atomic_add_unless(&card->force_alloc_skb, -1, 0);
- }
-
- /*
- * 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
- */
- if (card->options.performance_stats) {
- card->perf_stats.inbound_do_qdio_cnt++;
- card->perf_stats.inbound_do_qdio_start_time =
- qeth_get_micros();
- }
- rc = do_QDIO(CARD_DDEV(card),
- QDIO_FLAG_SYNC_INPUT | QDIO_FLAG_UNDER_INTERRUPT,
- 0, queue->next_buf_to_init, count, NULL);
- if (card->options.performance_stats)
- card->perf_stats.inbound_do_qdio_time +=
- qeth_get_micros() -
- card->perf_stats.inbound_do_qdio_start_time;
- 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_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 index;
- int i;
-
- QETH_DBF_TEXT(trace, 6, "qdinput");
- card = (struct qeth_card *) card_ptr;
- net_dev = card->dev;
- if (card->options.performance_stats) {
- card->perf_stats.inbound_cnt++;
- card->perf_stats.inbound_start_time = qeth_get_micros();
- }
- 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) {
- index = i % QDIO_MAX_BUFFERS_PER_Q;
- buffer = &card->qdio.in_q->bufs[index];
- if (!((status & QDIO_STATUS_LOOK_FOR_ERROR) &&
- qeth_check_qdio_errors(buffer->buffer,
- qdio_err, siga_err,"qinerr")))
- qeth_process_inbound_buffer(card, buffer, index);
- /* clear buffer and give back to hardware */
- qeth_put_buffer_pool_entry(card, buffer->pool_entry);
- qeth_queue_input_buffer(card, index);
- }
- if (card->options.performance_stats)
- card->perf_stats.inbound_time += qeth_get_micros() -
- card->perf_stats.inbound_start_time;
-}
-
-static int
-qeth_handle_send_error(struct qeth_card *card,
- struct qeth_qdio_out_buffer *buffer,
- unsigned int qdio_err, unsigned int siga_err)
-{
- int sbalf15 = buffer->buffer->element[15].flags & 0xff;
- int cc = siga_err & 3;
-
- QETH_DBF_TEXT(trace, 6, "hdsnderr");
- qeth_check_qdio_errors(buffer->buffer, qdio_err, siga_err, "qouterr");
- 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:
- default:
- QETH_DBF_TEXT(trace, 1, "SIGAcc3");
- QETH_DBF_TEXT_(trace,1,"%s",CARD_BUS_ID(card));
- return QETH_SEND_ERROR_KICK_IT;
- }
-}
-
-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;
- unsigned int qdio_flags;
-
- 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->card->info.type == QETH_CARD_TYPE_IQD)
- continue;
-
- 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 that 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;
- }
- }
- }
-
- queue->card->dev->trans_start = jiffies;
- if (queue->card->options.performance_stats) {
- queue->card->perf_stats.outbound_do_qdio_cnt++;
- queue->card->perf_stats.outbound_do_qdio_start_time =
- qeth_get_micros();
- }
- qdio_flags = QDIO_FLAG_SYNC_OUTPUT;
- if (under_int)
- qdio_flags |= QDIO_FLAG_UNDER_INTERRUPT;
- if (atomic_read(&queue->set_pci_flags_count))
- qdio_flags |= QDIO_FLAG_PCI_OUT;
- rc = do_QDIO(CARD_DDEV(queue->card), qdio_flags,
- queue->queue_no, index, count, NULL);
- if (queue->card->options.performance_stats)
- queue->card->perf_stats.outbound_do_qdio_time +=
- qeth_get_micros() -
- queue->card->perf_stats.outbound_do_qdio_start_time;
- if (rc){
- QETH_DBF_TEXT(trace, 2, "flushbuf");
- QETH_DBF_TEXT_(trace, 2, " err%d", rc);
- QETH_DBF_TEXT_(trace, 2, "%s", CARD_DDEV_ID(queue->card));
- queue->card->stats.tx_errors += count;
- /* this must not happen under normal circumstances. if it
- * happens something is really wrong -> recover */
- qeth_schedule_recovery(queue->card);
- return;
- }
- atomic_add(count, &queue->used_buffers);
- if (queue->card->options.performance_stats)
- queue->card->perf_stats.bufs_sent += count;
-}
-
-/*
- * Switched to packing state if the number of used buffers on a queue
- * reaches a certain limit.
- */
-static void
-qeth_switch_to_packing_if_needed(struct qeth_qdio_out_q *queue)
-{
- 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");
- if (queue->card->options.performance_stats)
- queue->card->perf_stats.sc_dp_p++;
- queue->do_pack = 1;
- }
- }
-}
-
-/*
- * Switches from packing to non-packing mode. If there is a packing
- * buffer on the queue this buffer will be prepared to be flushed.
- * In that case 1 is returned to inform the caller. If no buffer
- * has to be flushed, zero is returned.
- */
-static int
-qeth_switch_to_nonpacking_if_needed(struct qeth_qdio_out_q *queue)
-{
- struct qeth_qdio_out_buffer *buffer;
- int flush_count = 0;
-
- if (queue->do_pack) {
- if (atomic_read(&queue->used_buffers)
- <= QETH_LOW_WATERMARK_PACK) {
- /* switch PACKING -> non-PACKING */
- QETH_DBF_TEXT(trace, 6, "pack->np");
- if (queue->card->options.performance_stats)
- queue->card->perf_stats.sc_p_dp++;
- queue->do_pack = 0;
- /* flush packing buffers */
- buffer = &queue->bufs[queue->next_buf_to_fill];
- if ((atomic_read(&buffer->state) ==
- QETH_QDIO_BUF_EMPTY) &&
- (buffer->next_element_to_fill > 0)) {
- atomic_set(&buffer->state,QETH_QDIO_BUF_PRIMED);
- flush_count++;
- queue->next_buf_to_fill =
- (queue->next_buf_to_fill + 1) %
- QDIO_MAX_BUFFERS_PER_Q;
- }
- }
- }
- return flush_count;
-}
-
-/*
- * Called to flush a packing buffer if no more pci flags are on the queue.
- * Checks if there is a packing buffer and prepares it to be flushed.
- * In that case returns 1, otherwise zero.
- */
-static int
-qeth_flush_buffers_on_no_pci(struct qeth_qdio_out_q *queue)
-{
- struct qeth_qdio_out_buffer *buffer;
-
- buffer = &queue->bufs[queue->next_buf_to_fill];
- if((atomic_read(&buffer->state) == QETH_QDIO_BUF_EMPTY) &&
- (buffer->next_element_to_fill > 0)){
- /* it's a packing buffer */
- atomic_set(&buffer->state, QETH_QDIO_BUF_PRIMED);
- queue->next_buf_to_fill =
- (queue->next_buf_to_fill + 1) % QDIO_MAX_BUFFERS_PER_Q;
- return 1;
- }
- return 0;
-}
-
-static void
-qeth_check_outbound_queue(struct qeth_qdio_out_q *queue)
-{
- int index;
- int flush_cnt = 0;
- int q_was_packing = 0;
-
- /*
- * check if weed have to switch to non-packing mode or if
- * we have to get a pci flag out on the queue
- */
- if ((atomic_read(&queue->used_buffers) <= QETH_LOW_WATERMARK_PACK) ||
- !atomic_read(&queue->set_pci_flags_count)){
- if (atomic_xchg(&queue->state, QETH_OUT_Q_LOCKED_FLUSH) ==
- QETH_OUT_Q_UNLOCKED) {
- /*
- * If we get in here, there was no action in
- * do_send_packet. So, we check if there is a
- * packing buffer to be flushed here.
- */
- netif_stop_queue(queue->card->dev);
- index = queue->next_buf_to_fill;
- q_was_packing = queue->do_pack;
- flush_cnt += qeth_switch_to_nonpacking_if_needed(queue);
- if (!flush_cnt &&
- !atomic_read(&queue->set_pci_flags_count))
- flush_cnt +=
- qeth_flush_buffers_on_no_pci(queue);
- if (queue->card->options.performance_stats &&
- q_was_packing)
- queue->card->perf_stats.bufs_sent_pack +=
- flush_cnt;
- if (flush_cnt)
- qeth_flush_buffers(queue, 1, index, flush_cnt);
- atomic_set(&queue->state, QETH_OUT_Q_UNLOCKED);
- }
- }
-}
-
-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_TEXT(trace, 2, "achkcond");
- QETH_DBF_TEXT_(trace, 2, "%s", CARD_BUS_ID(card));
- QETH_DBF_TEXT_(trace, 2, "%08x", status);
- netif_stop_queue(card->dev);
- qeth_schedule_recovery(card);
- return;
- }
- }
- if (card->options.performance_stats) {
- card->perf_stats.outbound_handler_cnt++;
- card->perf_stats.outbound_handler_start_time =
- qeth_get_micros();
- }
- 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;
- }
- qeth_clear_output_buffer(queue, buffer);
- }
- atomic_sub(count, &queue->used_buffers);
- /* check if we need to do something on this outbound queue */
- if (card->info.type != QETH_CARD_TYPE_IQD)
- qeth_check_outbound_queue(queue);
-
- netif_wake_queue(queue->card->dev);
- if (card->options.performance_stats)
- card->perf_stats.outbound_handler_time += qeth_get_micros() -
- card->perf_stats.outbound_handler_start_time;
-}
-
-static void
-qeth_create_qib_param_field(struct qeth_card *card, char *param_field)
-{
-
- param_field[0] = _ascebc['P'];
- param_field[1] = _ascebc['C'];
- param_field[2] = _ascebc['I'];
- param_field[3] = _ascebc['T'];
- *((unsigned int *) (&param_field[4])) = QETH_PCI_THRESHOLD_A(card);
- *((unsigned int *) (&param_field[8])) = QETH_PCI_THRESHOLD_B(card);
- *((unsigned int *) (&param_field[12])) = QETH_PCI_TIMER_VALUE(card);
-}
-
-static void
-qeth_create_qib_param_field_blkt(struct qeth_card *card, char *param_field)
-{
- param_field[16] = _ascebc['B'];
- param_field[17] = _ascebc['L'];
- param_field[18] = _ascebc['K'];
- param_field[19] = _ascebc['T'];
- *((unsigned int *) (&param_field[20])) = card->info.blkt.time_total;
- *((unsigned int *) (&param_field[24])) = card->info.blkt.inter_packet;
- *((unsigned int *) (&param_field[28])) = card->info.blkt.inter_packet_jumbo;
-}
-
-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;
-
- QETH_DBF_TEXT(trace,5,"alocpool");
- 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|GFP_DMA);
- 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);
- }
- return 0;
-}
-
-int
-qeth_realloc_buffer_pool(struct qeth_card *card, int bufcnt)
-{
- QETH_DBF_TEXT(trace, 2, "realcbp");
-
- if ((card->state != CARD_STATE_DOWN) &&
- (card->state != CARD_STATE_RECOVER))
- return -EPERM;
-
- /* TODO: steel/add buffers from/to a running card's buffer pool (?) */
- qeth_clear_working_pool_list(card);
- qeth_free_buffer_pool(card);
- card->qdio.in_buf_pool.buf_count = bufcnt;
- card->qdio.init_pool.buf_count = bufcnt;
- return qeth_alloc_buffer_pool(card);
-}
-
-static int
-qeth_alloc_qdio_buffers(struct qeth_card *card)
-{
- int i, j;
-
- QETH_DBF_TEXT(setup, 2, "allcqdbf");
-
- if (atomic_cmpxchg(&card->qdio.state, QETH_QDIO_UNINITIALIZED,
- QETH_QDIO_ALLOCATED) != QETH_QDIO_UNINITIALIZED)
- return 0;
-
- card->qdio.in_q = kmalloc(sizeof(struct qeth_qdio_q),
- GFP_KERNEL|GFP_DMA);
- if (!card->qdio.in_q)
- goto out_nomem;
- 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))
- goto out_freeinq;
- /* outbound */
- card->qdio.out_qs =
- kmalloc(card->qdio.no_out_queues *
- sizeof(struct qeth_qdio_out_q *), GFP_KERNEL);
- if (!card->qdio.out_qs)
- goto out_freepool;
- for (i = 0; i < card->qdio.no_out_queues; ++i) {
- card->qdio.out_qs[i] = kmalloc(sizeof(struct qeth_qdio_out_q),
- GFP_KERNEL|GFP_DMA);
- if (!card->qdio.out_qs[i])
- goto out_freeoutq;
- 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 outbound 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);
- lockdep_set_class(
- &card->qdio.out_qs[i]->bufs[j].skb_list.lock,
- &qdio_out_skb_queue_key);
- INIT_LIST_HEAD(&card->qdio.out_qs[i]->bufs[j].ctx_list);
- }
- }
- return 0;
-
-out_freeoutq:
- while (i > 0)
- kfree(card->qdio.out_qs[--i]);
- kfree(card->qdio.out_qs);
- card->qdio.out_qs = NULL;
-out_freepool:
- qeth_free_buffer_pool(card);
-out_freeinq:
- kfree(card->qdio.in_q);
- card->qdio.in_q = NULL;
-out_nomem:
- atomic_set(&card->qdio.state, QETH_QDIO_UNINITIALIZED);
- return -ENOMEM;
-}
-
-static void
-qeth_free_qdio_buffers(struct qeth_card *card)
-{
- int i, j;
-
- QETH_DBF_TEXT(trace, 2, "freeqdbf");
- if (atomic_xchg(&card->qdio.state, QETH_QDIO_UNINITIALIZED) ==
- QETH_QDIO_UNINITIALIZED)
- return;
- kfree(card->qdio.in_q);
- card->qdio.in_q = NULL;
- /* inbound buffer pool */
- qeth_free_buffer_pool(card);
- /* free outbound qdio_qs */
- if (card->qdio.out_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->qdio.out_qs[i],
- &card->qdio.out_qs[i]->bufs[j]);
- kfree(card->qdio.out_qs[i]);
- }
- kfree(card->qdio.out_qs);
- card->qdio.out_qs = NULL;
- }
-}
-
-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 && card->qdio.out_qs[i]) {
- for (j = 0; j < QDIO_MAX_BUFFERS_PER_Q; ++j)
- qeth_clear_output_buffer(card->qdio.out_qs[i],
- &card->qdio.out_qs[i]->bufs[j]);
- }
-}
-
-static void
-qeth_init_qdio_info(struct qeth_card *card)
-{
- QETH_DBF_TEXT(setup, 4, "intqdinf");
- atomic_set(&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);
-}
-
-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));
- qeth_initialize_working_pool_list(card);
- /*give only as many buffers to hardware as we have buffer pool entries*/
- for (i = 0; i < card->qdio.in_buf_pool.buf_count - 1; ++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 - 1;
- rc = do_QDIO(CARD_DDEV(card), QDIO_FLAG_SYNC_INPUT, 0, 0,
- card->qdio.in_buf_pool.buf_count - 1, 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->qdio.out_qs[i],
- &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]->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);
- atomic_set(&card->qdio.out_qs[i]->state,
- QETH_OUT_Q_UNLOCKED);
- }
- 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 = 0;
-
- QETH_DBF_TEXT(setup, 2, "qdioest");
-
- qib_param_field = kzalloc(QDIO_MAX_BUFFERS_PER_Q * sizeof(char),
- GFP_KERNEL);
- if (!qib_param_field)
- return -ENOMEM;
-
- qeth_create_qib_param_field(card, qib_param_field);
- qeth_create_qib_param_field_blkt(card, qib_param_field);
-
- 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 (atomic_cmpxchg(&card->qdio.state, QETH_QDIO_ALLOCATED,
- QETH_QDIO_ESTABLISHED) == QETH_QDIO_ALLOCATED)
- if ((rc = qdio_initialize(&init_data)))
- atomic_set(&card->qdio.state, QETH_QDIO_ALLOCATED);
-
- 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 rc1 = 0, rc2=0, rc3 = 0;
-
- QETH_DBF_TEXT(trace,3,"haltchs");
- rc1 = qeth_halt_channel(&card->read);
- rc2 = qeth_halt_channel(&card->write);
- rc3 = qeth_halt_channel(&card->data);
- if (rc1)
- return rc1;
- if (rc2)
- return rc2;
- return rc3;
-}
-static int
-qeth_clear_channels(struct qeth_card *card)
-{
- int rc1 = 0, rc2=0, rc3 = 0;
-
- QETH_DBF_TEXT(trace,3,"clearchs");
- rc1 = qeth_clear_channel(&card->read);
- rc2 = qeth_clear_channel(&card->write);
- rc3 = qeth_clear_channel(&card->data);
- if (rc1)
- return rc1;
- if (rc2)
- return rc2;
- return rc3;
-}
-
-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");
- switch (atomic_cmpxchg(&card->qdio.state, QETH_QDIO_ESTABLISHED,
- QETH_QDIO_CLEANING)) {
- case QETH_QDIO_ESTABLISHED:
- if ((rc = qdio_cleanup(CARD_DDEV(card),
- (card->info.type == QETH_CARD_TYPE_IQD) ?
- QDIO_FLAG_CLEANUP_USING_HALT :
- QDIO_FLAG_CLEANUP_USING_CLEAR)))
- QETH_DBF_TEXT_(trace, 3, "1err%d", rc);
- atomic_set(&card->qdio.state, QETH_QDIO_ALLOCATED);
- break;
- case QETH_QDIO_CLEANING:
- return rc;
- default:
- break;
- }
- if ((rc = qeth_clear_halt_card(card, use_halt)))
- QETH_DBF_TEXT_(trace, 3, "2err%d", rc);
- 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);
- goto out_qdio;
- }
- if ((rc = qeth_cm_setup(card))){
- QETH_DBF_TEXT_(setup, 2, "3err%d", rc);
- goto out_qdio;
- }
- if ((rc = qeth_ulp_enable(card))){
- QETH_DBF_TEXT_(setup, 2, "4err%d", rc);
- goto out_qdio;
- }
- if ((rc = qeth_ulp_setup(card))){
- QETH_DBF_TEXT_(setup, 2, "5err%d", rc);
- goto out_qdio;
- }
- if ((rc = qeth_alloc_qdio_buffers(card))){
- QETH_DBF_TEXT_(setup, 2, "5err%d", rc);
- goto out_qdio;
- }
- 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_IQD);
- return rc;
-}
-
-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:
- dev = alloc_netdev(0, "hsi%d", ether_setup);
- break;
- case QETH_CARD_TYPE_OSN:
- dev = alloc_netdev(0, "osn%d", ether_setup);
- break;
- default:
- dev = alloc_etherdev(0);
- }
- return dev;
-}
-
-/*hard_header fake function; used in case fake_ll is set */
-static int
-qeth_fake_header(struct sk_buff *skb, struct net_device *dev,
- unsigned short type, const void *daddr, const void *saddr,
- unsigned len)
-{
- if(dev->type == ARPHRD_IEEE802_TR){
- struct trh_hdr *hdr;
- hdr = (struct trh_hdr *)skb_push(skb, QETH_FAKE_LL_LEN_TR);
- memcpy(hdr->saddr, dev->dev_addr, TR_ALEN);
- memcpy(hdr->daddr, "FAKELL", TR_ALEN);
- return QETH_FAKE_LL_LEN_TR;
-
- } else {
- struct ethhdr *hdr;
- hdr = (struct ethhdr *)skb_push(skb, QETH_FAKE_LL_LEN_ETH);
- memcpy(hdr->h_source, dev->dev_addr, ETH_ALEN);
- memcpy(hdr->h_dest, "FAKELL", ETH_ALEN);
- if (type != ETH_P_802_3)
- hdr->h_proto = htons(type);
- else
- hdr->h_proto = htons(len);
- return QETH_FAKE_LL_LEN_ETH;
-
- }
-}
-
-static const struct header_ops qeth_fake_ops = {
- .create = qeth_fake_header,
- .parse = qeth_hard_header_parse,
-};
-
-static 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 OK; otherwise ksoftirqd goes to 100% */
- return NETDEV_TX_OK;
- }
- if ((card->state != CARD_STATE_UP) || !card->lan_online) {
- card->stats.tx_dropped++;
- card->stats.tx_errors++;
- card->stats.tx_carrier_errors++;
- dev_kfree_skb_any(skb);
- /* return OK; otherwise ksoftirqd goes to 100% */
- return NETDEV_TX_OK;
- }
- if (card->options.performance_stats) {
- card->perf_stats.outbound_cnt++;
- card->perf_stats.outbound_start_time = qeth_get_micros();
- }
- netif_stop_queue(dev);
- if ((rc = qeth_send_packet(card, skb))) {
- if (rc == -EBUSY) {
- return NETDEV_TX_BUSY;
- } else {
- card->stats.tx_errors++;
- card->stats.tx_dropped++;
- dev_kfree_skb_any(skb);
- /*set to OK; otherwise ksoftirqd goes to 100% */
- rc = NETDEV_TX_OK;
- }
- }
- netif_wake_queue(dev);
- if (card->options.performance_stats)
- card->perf_stats.outbound_time += qeth_get_micros() -
- card->perf_stats.outbound_start_time;
- 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 (vlan_group_get_device(vg, i) == dev){
- rc = QETH_VLAN_CARD;
- break;
- }
- }
- if (rc && !(vlan_dev_info(dev)->real_dev->priv == (void *)card))
- return 0;
-
-#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)
- return -ENODEV;
-
- if ( (card->info.type != QETH_CARD_TYPE_OSN) &&
- (card->options.layer2) &&
- (!(card->info.mac_bits & QETH_LAYER2_MAC_REGISTERED))) {
- QETH_DBF_TEXT(trace,4,"nomacadr");
- return -EPERM;
- }
- card->data.state = CH_STATE_UP;
- card->state = CARD_STATE_UP;
- card->dev->flags |= IFF_UP;
- netif_start_queue(dev);
-
- if (!card->lan_online && netif_carrier_ok(dev))
- netif_carrier_off(dev);
- 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_tx_disable(dev);
- card->dev->flags &= ~IFF_UP;
- if (card->state == CARD_STATE_UP)
- card->state = CARD_STATE_SOFTSETUP;
- return 0;
-}
-
-static int
-qeth_get_cast_type(struct qeth_card *card, struct sk_buff *skb)
-{
- int cast_type = RTN_UNSPEC;
-
- if (card->info.type == QETH_CARD_TYPE_OSN)
- return cast_type;
-
- 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_network_header(skb)[24] == 0xff) ?
- RTN_MULTICAST : 0;
- else if (skb->protocol == ETH_P_IP)
- return ((skb_network_header(skb)[16] & 0xf0) == 0xe0) ?
- RTN_MULTICAST : 0;
- /* ... */
- if (!memcmp(skb->data, skb->dev->broadcast, 6))
- return RTN_BROADCAST;
- else {
- u16 hdr_mac;
-
- hdr_mac = *((u16 *)skb->data);
- /* tr multicast? */
- switch (card->info.link_type) {
- case QETH_LINK_TYPE_HSTR:
- case QETH_LINK_TYPE_LANE_TR:
- if ((hdr_mac == QETH_TR_MAC_NC) ||
- (hdr_mac == QETH_TR_MAC_C))
- return RTN_MULTICAST;
- break;
- /* 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 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)) {
- const u8 tos = ip_hdr(skb)->tos;
-
- if (card->qdio.do_prio_queueing==QETH_PRIO_Q_ING_TOS){
- if (tos & IP_TOS_NOTIMPORTANT)
- return 3;
- if (tos & IP_TOS_HIGHRELIABILITY)
- return 2;
- if (tos & IP_TOS_HIGHTHROUGHPUT)
- return 1;
- if (tos & IP_TOS_LOWDELAY)
- return 0;
- }
- if (card->qdio.do_prio_queueing==QETH_PRIO_Q_ING_PREC)
- return 3 - (tos >> 6);
- } else if (card->qdio.do_prio_queueing && (ipv == 6)) {
- /* TODO: IPv6!!! */
- }
- return card->qdio.default_out_queue;
- case 1: /* fallthrough for single-out-queue 1920-device */
- default:
- return card->qdio.default_out_queue;
- }
-}
-
-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 struct qeth_hdr *
-__qeth_prepare_skb(struct qeth_card *card, struct sk_buff *skb, int ipv)
-{
-#ifdef CONFIG_QETH_VLAN
- u16 *tag;
- if (card->vlangrp && vlan_tx_tag_present(skb) &&
- ((ipv == 6) || card->options.layer2) ) {
- /*
- * Move the mac addresses (6 bytes src, 6 bytes dest)
- * to the beginning of the new header. We are using three
- * memcpys instead of one memmove to save cycles.
- */
- skb_push(skb, VLAN_HLEN);
- skb_copy_to_linear_data(skb, skb->data + 4, 4);
- skb_copy_to_linear_data_offset(skb, 4, skb->data + 8, 4);
- skb_copy_to_linear_data_offset(skb, 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) = htons(vlan_tx_tag_get(skb));
- }
-#endif
- return ((struct qeth_hdr *)
- qeth_push_skb(card, skb, sizeof(struct qeth_hdr)));
-}
-
-static void
-__qeth_free_new_skb(struct sk_buff *orig_skb, struct sk_buff *new_skb)
-{
- if (orig_skb != new_skb)
- dev_kfree_skb_any(new_skb);
-}
-
-static struct sk_buff *
-qeth_prepare_skb(struct qeth_card *card, struct sk_buff *skb,
- struct qeth_hdr **hdr, int ipv)
-{
- struct sk_buff *new_skb, *new_skb2;
-
- QETH_DBF_TEXT(trace, 6, "prepskb");
- new_skb = skb;
- new_skb = qeth_pskb_unshare(skb, GFP_ATOMIC);
- if (!new_skb)
- return NULL;
- new_skb2 = qeth_realloc_headroom(card, new_skb,
- sizeof(struct qeth_hdr));
- if (!new_skb2) {
- __qeth_free_new_skb(skb, new_skb);
- return NULL;
- }
- if (new_skb != skb)
- __qeth_free_new_skb(new_skb2, new_skb);
- new_skb = new_skb2;
- *hdr = __qeth_prepare_skb(card, new_skb, ipv);
- if (*hdr == NULL) {
- __qeth_free_new_skb(skb, new_skb);
- return NULL;
- }
- return new_skb;
-}
-
-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 void
-qeth_layer2_get_packet_type(struct qeth_card *card, struct qeth_hdr *hdr,
- struct sk_buff *skb)
-{
- __u16 hdr_mac;
-
- if (!memcmp(skb->data+QETH_HEADER_SIZE,
- skb->dev->broadcast,6)) { /* broadcast? */
- *(__u32 *)hdr->hdr.l2.flags |=
- QETH_LAYER2_FLAG_BROADCAST << 8;
- return;
- }
- hdr_mac=*((__u16*)skb->data);
- /* tr multicast? */
- switch (card->info.link_type) {
- case QETH_LINK_TYPE_HSTR:
- case QETH_LINK_TYPE_LANE_TR:
- if ((hdr_mac == QETH_TR_MAC_NC) ||
- (hdr_mac == QETH_TR_MAC_C) )
- *(__u32 *)hdr->hdr.l2.flags |=
- QETH_LAYER2_FLAG_MULTICAST << 8;
- else
- *(__u32 *)hdr->hdr.l2.flags |=
- QETH_LAYER2_FLAG_UNICAST << 8;
- break;
- /* eth or so multicast? */
- default:
- if ( (hdr_mac==QETH_ETH_MAC_V4) ||
- (hdr_mac==QETH_ETH_MAC_V6) )
- *(__u32 *)hdr->hdr.l2.flags |=
- QETH_LAYER2_FLAG_MULTICAST << 8;
- else
- *(__u32 *)hdr->hdr.l2.flags |=
- QETH_LAYER2_FLAG_UNICAST << 8;
- }
-}
-
-static void
-qeth_layer2_fill_header(struct qeth_card *card, struct qeth_hdr *hdr,
- struct sk_buff *skb, int cast_type)
-{
- memset(hdr, 0, sizeof(struct qeth_hdr));
- hdr->hdr.l2.id = QETH_HEADER_TYPE_LAYER2;
-
- /* set byte 0 to "0x02" and byte 3 to casting flags */
- if (cast_type==RTN_MULTICAST)
- *(__u32 *)hdr->hdr.l2.flags |= QETH_LAYER2_FLAG_MULTICAST << 8;
- else if (cast_type==RTN_BROADCAST)
- *(__u32 *)hdr->hdr.l2.flags |= QETH_LAYER2_FLAG_BROADCAST << 8;
- else
- qeth_layer2_get_packet_type(card, hdr, skb);
-
- hdr->hdr.l2.pkt_length = skb->len-QETH_HEADER_SIZE;
-#ifdef CONFIG_QETH_VLAN
- /* VSWITCH relies on the VLAN
- * information to be present in
- * the QDIO header */
- if ((card->vlangrp != NULL) &&
- vlan_tx_tag_present(skb)) {
- *(__u32 *)hdr->hdr.l2.flags |= QETH_LAYER2_FLAG_VLAN << 8;
- hdr->hdr.l2.vlan_id = vlan_tx_tag_get(skb);
- }
-#endif
-}
-
-void
-qeth_fill_header(struct qeth_card *card, struct qeth_hdr *hdr,
- struct sk_buff *skb, int ipv, int cast_type)
-{
- QETH_DBF_TEXT(trace, 6, "fillhdr");
-
- memset(hdr, 0, sizeof(struct qeth_hdr));
- if (card->options.layer2) {
- qeth_layer2_fill_header(card, hdr, skb, cast_type);
- return;
- }
- hdr->hdr.l3.id = QETH_HEADER_TYPE_LAYER3;
- hdr->hdr.l3.ext_flags = 0;
-#ifdef CONFIG_QETH_VLAN
- /*
- * before we're going to overwrite this location with next hop ip.
- * v6 uses passthrough, v4 sets the tag in the QDIO header.
- */
- if (card->vlangrp && vlan_tx_tag_present(skb)) {
- hdr->hdr.l3.ext_flags = (ipv == 4) ?
- QETH_HDR_EXT_VLAN_FRAME :
- QETH_HDR_EXT_INCLUDE_VLAN_TAG;
- hdr->hdr.l3.vlan_id = vlan_tx_tag_get(skb);
- }
-#endif /* CONFIG_QETH_VLAN */
- hdr->hdr.l3.length = skb->len - sizeof(struct qeth_hdr);
- if (ipv == 4) { /* IPv4 */
- hdr->hdr.l3.flags = qeth_get_qeth_hdr_flags4(cast_type);
- memset(hdr->hdr.l3.dest_addr, 0, 12);
- if ((skb->dst) && (skb->dst->neighbour)) {
- *((u32 *) (&hdr->hdr.l3.dest_addr[12])) =
- *((u32 *) skb->dst->neighbour->primary_key);
- } else {
- /* fill in destination address used in ip header */
- *((u32 *)(&hdr->hdr.l3.dest_addr[12])) =
- ip_hdr(skb)->daddr;
- }
- } else if (ipv == 6) { /* IPv6 or passthru */
- hdr->hdr.l3.flags = qeth_get_qeth_hdr_flags6(cast_type);
- if ((skb->dst) && (skb->dst->neighbour)) {
- memcpy(hdr->hdr.l3.dest_addr,
- skb->dst->neighbour->primary_key, 16);
- } else {
- /* fill in destination address used in ip header */
- memcpy(hdr->hdr.l3.dest_addr,
- &ipv6_hdr(skb)->daddr, 16);
- }
- } else { /* passthrough */
- if((skb->dev->type == ARPHRD_IEEE802_TR) &&
- !memcmp(skb->data + sizeof(struct qeth_hdr) +
- sizeof(__u16), skb->dev->broadcast, 6)) {
- hdr->hdr.l3.flags = QETH_CAST_BROADCAST |
- QETH_HDR_PASSTHRU;
- } else if (!memcmp(skb->data + sizeof(struct qeth_hdr),
- skb->dev->broadcast, 6)) { /* broadcast? */
- hdr->hdr.l3.flags = QETH_CAST_BROADCAST |
- QETH_HDR_PASSTHRU;
- } else {
- hdr->hdr.l3.flags = (cast_type == RTN_MULTICAST) ?
- QETH_CAST_MULTICAST | QETH_HDR_PASSTHRU :
- QETH_CAST_UNICAST | QETH_HDR_PASSTHRU;
- }
- }
-}
-
-static void
-__qeth_fill_buffer(struct sk_buff *skb, struct qdio_buffer *buffer,
- int is_tso, int *next_element_to_fill)
-{
- int length = skb->len;
- int length_here;
- int element;
- char *data;
- int first_lap ;
-
- element = *next_element_to_fill;
- data = skb->data;
- first_lap = (is_tso == 0 ? 1 : 0);
-
- 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;
- }
- *next_element_to_fill = element;
-}
-
-static int
-qeth_fill_buffer(struct qeth_qdio_out_q *queue,
- struct qeth_qdio_out_buffer *buf,
- struct sk_buff *skb)
-{
- struct qdio_buffer *buffer;
- struct qeth_hdr_tso *hdr;
- int flush_cnt = 0, hdr_len, large_send = 0;
-
- QETH_DBF_TEXT(trace, 6, "qdfillbf");
-
- buffer = buf->buffer;
- atomic_inc(&skb->users);
- skb_queue_tail(&buf->skb_list, skb);
-
- hdr = (struct qeth_hdr_tso *) skb->data;
- /*check first on TSO ....*/
- if (hdr->hdr.hdr.l3.id == QETH_HEADER_TYPE_TSO) {
- int element = buf->next_element_to_fill;
-
- hdr_len = sizeof(struct qeth_hdr_tso) + hdr->ext.dg_hdr_len;
- /*fill first buffer entry only with header information */
- buffer->element[element].addr = skb->data;
- buffer->element[element].length = hdr_len;
- buffer->element[element].flags = SBAL_FLAGS_FIRST_FRAG;
- buf->next_element_to_fill++;
- skb->data += hdr_len;
- skb->len -= hdr_len;
- large_send = 1;
- }
- if (skb_shinfo(skb)->nr_frags == 0)
- __qeth_fill_buffer(skb, buffer, large_send,
- (int *)&buf->next_element_to_fill);
- else
- __qeth_fill_buffer_frag(skb, buffer, large_send,
- (int *)&buf->next_element_to_fill);
-
- if (!queue->do_pack) {
- QETH_DBF_TEXT(trace, 6, "fillbfnp");
- /* set state to PRIMED -> will be flushed */
- atomic_set(&buf->state, QETH_QDIO_BUF_PRIMED);
- flush_cnt = 1;
- } else {
- QETH_DBF_TEXT(trace, 6, "fillbfpa");
- if (queue->card->options.performance_stats)
- queue->card->perf_stats.skbs_sent_pack++;
- if (buf->next_element_to_fill >=
- QETH_MAX_BUFFER_ELEMENTS(queue->card)) {
- /*
- * packed buffer if full -> set state PRIMED
- * -> will be flushed
- */
- atomic_set(&buf->state, QETH_QDIO_BUF_PRIMED);
- flush_cnt = 1;
- }
- }
- return flush_cnt;
-}
-
-static int
-qeth_do_send_packet_fast(struct qeth_card *card, struct qeth_qdio_out_q *queue,
- struct sk_buff *skb, struct qeth_hdr *hdr,
- int elements_needed,
- struct qeth_eddp_context *ctx)
-{
- struct qeth_qdio_out_buffer *buffer;
- int buffers_needed = 0;
- int flush_cnt = 0;
- int index;
-
- QETH_DBF_TEXT(trace, 6, "dosndpfa");
-
- /* spin until we get the queue ... */
- while (atomic_cmpxchg(&queue->state, QETH_OUT_Q_UNLOCKED,
- QETH_OUT_Q_LOCKED) != QETH_OUT_Q_UNLOCKED);
- /* ... now we've got the queue */
- index = queue->next_buf_to_fill;
- buffer = &queue->bufs[queue->next_buf_to_fill];
- /*
- * check if buffer is empty to make sure that we do not 'overtake'
- * ourselves and try to fill a buffer that is already primed
- */
- if (atomic_read(&buffer->state) != QETH_QDIO_BUF_EMPTY)
- goto out;
- if (ctx == NULL)
- queue->next_buf_to_fill = (queue->next_buf_to_fill + 1) %
- QDIO_MAX_BUFFERS_PER_Q;
- else {
- buffers_needed = qeth_eddp_check_buffers_for_context(queue,ctx);
- if (buffers_needed < 0)
- goto out;
- queue->next_buf_to_fill =
- (queue->next_buf_to_fill + buffers_needed) %
- QDIO_MAX_BUFFERS_PER_Q;
- }
- atomic_set(&queue->state, QETH_OUT_Q_UNLOCKED);
- if (ctx == NULL) {
- qeth_fill_buffer(queue, buffer, skb);
- qeth_flush_buffers(queue, 0, index, 1);
- } else {
- flush_cnt = qeth_eddp_fill_buffer(queue, ctx, index);
- WARN_ON(buffers_needed != flush_cnt);
- qeth_flush_buffers(queue, 0, index, flush_cnt);
- }
- return 0;
-out:
- atomic_set(&queue->state, QETH_OUT_Q_UNLOCKED);
- return -EBUSY;
-}
-
-static int
-qeth_do_send_packet(struct qeth_card *card, struct qeth_qdio_out_q *queue,
- struct sk_buff *skb, struct qeth_hdr *hdr,
- int elements_needed, struct qeth_eddp_context *ctx)
-{
- struct qeth_qdio_out_buffer *buffer;
- int start_index;
- int flush_count = 0;
- int do_pack = 0;
- int tmp;
- int rc = 0;
-
- QETH_DBF_TEXT(trace, 6, "dosndpkt");
-
- /* spin until we get the queue ... */
- while (atomic_cmpxchg(&queue->state, QETH_OUT_Q_UNLOCKED,
- QETH_OUT_Q_LOCKED) != QETH_OUT_Q_UNLOCKED);
- start_index = queue->next_buf_to_fill;
- buffer = &queue->bufs[queue->next_buf_to_fill];
- /*
- * check if buffer is empty to make sure that we do not 'overtake'
- * ourselves and try to fill a buffer that is already primed
- */
- if (atomic_read(&buffer->state) != QETH_QDIO_BUF_EMPTY) {
- atomic_set(&queue->state, QETH_OUT_Q_UNLOCKED);
- return -EBUSY;
- }
- /* check if we need to switch packing state of this queue */
- qeth_switch_to_packing_if_needed(queue);
- if (queue->do_pack){
- do_pack = 1;
- if (ctx == NULL) {
- /* does packet fit in current buffer? */
- if((QETH_MAX_BUFFER_ELEMENTS(card) -
- buffer->next_element_to_fill) < elements_needed){
- /* ... no -> set state PRIMED */
- atomic_set(&buffer->state,QETH_QDIO_BUF_PRIMED);
- flush_count++;
- queue->next_buf_to_fill =
- (queue->next_buf_to_fill + 1) %
- QDIO_MAX_BUFFERS_PER_Q;
- buffer = &queue->bufs[queue->next_buf_to_fill];
- /* we did a step forward, so check buffer state
- * again */
- if (atomic_read(&buffer->state) !=
- QETH_QDIO_BUF_EMPTY){
- qeth_flush_buffers(queue, 0, start_index, flush_count);
- atomic_set(&queue->state, QETH_OUT_Q_UNLOCKED);
- return -EBUSY;
- }
- }
- } else {
- /* check if we have enough elements (including following
- * free buffers) to handle eddp context */
- if (qeth_eddp_check_buffers_for_context(queue,ctx) < 0){
- if (net_ratelimit())
- PRINT_WARN("eddp tx_dropped 1\n");
- rc = -EBUSY;
- goto out;
- }
- }
- }
- if (ctx == NULL)
- tmp = qeth_fill_buffer(queue, buffer, skb);
- else {
- tmp = qeth_eddp_fill_buffer(queue,ctx,queue->next_buf_to_fill);
- if (tmp < 0) {
- printk("eddp tx_dropped 2\n");
- rc = - EBUSY;
- goto out;
- }
- }
- queue->next_buf_to_fill = (queue->next_buf_to_fill + tmp) %
- QDIO_MAX_BUFFERS_PER_Q;
- flush_count += tmp;
-out:
- if (flush_count)
- qeth_flush_buffers(queue, 0, start_index, flush_count);
- else if (!atomic_read(&queue->set_pci_flags_count))
- atomic_xchg(&queue->state, QETH_OUT_Q_LOCKED_FLUSH);
- /*
- * queue->state will go from LOCKED -> UNLOCKED or from
- * LOCKED_FLUSH -> LOCKED if output_handler wanted to 'notify' us
- * (switch packing state or flush buffer to get another pci flag out).
- * In that case we will enter this loop
- */
- while (atomic_dec_return(&queue->state)){
- flush_count = 0;
- start_index = queue->next_buf_to_fill;
- /* check if we can go back to non-packing state */
- flush_count += qeth_switch_to_nonpacking_if_needed(queue);
- /*
- * check if we need to flush a packing buffer to get a pci
- * flag out on the queue
- */
- if (!flush_count && !atomic_read(&queue->set_pci_flags_count))
- flush_count += qeth_flush_buffers_on_no_pci(queue);
- if (flush_count)
- qeth_flush_buffers(queue, 0, start_index, flush_count);
- }
- /* at this point the queue is UNLOCKED again */
- if (queue->card->options.performance_stats && do_pack)
- queue->card->perf_stats.bufs_sent_pack += flush_count;
-
- return rc;
-}
-
-static int
-qeth_get_elements_no(struct qeth_card *card, void *hdr,
- struct sk_buff *skb, int elems)
-{
- int elements_needed = 0;
-
- if (skb_shinfo(skb)->nr_frags > 0)
- elements_needed = (skb_shinfo(skb)->nr_frags + 1);
- if (elements_needed == 0)
- elements_needed = 1 + (((((unsigned long) hdr) % PAGE_SIZE)
- + skb->len) >> PAGE_SHIFT);
- if ((elements_needed + elems) > QETH_MAX_BUFFER_ELEMENTS(card)){
- PRINT_ERR("Invalid size of IP packet "
- "(Number=%d / Length=%d). Discarded.\n",
- (elements_needed+elems), skb->len);
- return 0;
- }
- return elements_needed;
-}
-
-static void qeth_tx_csum(struct sk_buff *skb)
-{
- int tlen;
-
- if (skb->protocol == htons(ETH_P_IP)) {
- tlen = ntohs(ip_hdr(skb)->tot_len) - (ip_hdr(skb)->ihl << 2);
- switch (ip_hdr(skb)->protocol) {
- case IPPROTO_TCP:
- tcp_hdr(skb)->check = 0;
- tcp_hdr(skb)->check = csum_tcpudp_magic(
- ip_hdr(skb)->saddr, ip_hdr(skb)->daddr,
- tlen, ip_hdr(skb)->protocol,
- skb_checksum(skb, skb_transport_offset(skb),
- tlen, 0));
- break;
- case IPPROTO_UDP:
- udp_hdr(skb)->check = 0;
- udp_hdr(skb)->check = csum_tcpudp_magic(
- ip_hdr(skb)->saddr, ip_hdr(skb)->daddr,
- tlen, ip_hdr(skb)->protocol,
- skb_checksum(skb, skb_transport_offset(skb),
- tlen, 0));
- break;
- }
- } else if (skb->protocol == htons(ETH_P_IPV6)) {
- switch (ipv6_hdr(skb)->nexthdr) {
- case IPPROTO_TCP:
- tcp_hdr(skb)->check = 0;
- tcp_hdr(skb)->check = csum_ipv6_magic(
- &ipv6_hdr(skb)->saddr, &ipv6_hdr(skb)->daddr,
- ipv6_hdr(skb)->payload_len,
- ipv6_hdr(skb)->nexthdr,
- skb_checksum(skb, skb_transport_offset(skb),
- ipv6_hdr(skb)->payload_len, 0));
- break;
- case IPPROTO_UDP:
- udp_hdr(skb)->check = 0;
- udp_hdr(skb)->check = csum_ipv6_magic(
- &ipv6_hdr(skb)->saddr, &ipv6_hdr(skb)->daddr,
- ipv6_hdr(skb)->payload_len,
- ipv6_hdr(skb)->nexthdr,
- skb_checksum(skb, skb_transport_offset(skb),
- ipv6_hdr(skb)->payload_len, 0));
- break;
- }
- }
-}
-
-static int
-qeth_send_packet(struct qeth_card *card, struct sk_buff *skb)
-{
- int ipv = 0;
- int cast_type;
- struct qeth_qdio_out_q *queue;
- struct qeth_hdr *hdr = NULL;
- int elements_needed = 0;
- enum qeth_large_send_types large_send = QETH_LARGE_SEND_NO;
- struct qeth_eddp_context *ctx = NULL;
- int tx_bytes = skb->len;
- unsigned short nr_frags = skb_shinfo(skb)->nr_frags;
- unsigned short tso_size = skb_shinfo(skb)->gso_size;
- struct sk_buff *new_skb, *new_skb2;
- int rc;
-
- QETH_DBF_TEXT(trace, 6, "sendpkt");
-
- new_skb = skb;
- if ((card->info.type == QETH_CARD_TYPE_OSN) &&
- (skb->protocol == htons(ETH_P_IPV6)))
- return -EPERM;
- cast_type = qeth_get_cast_type(card, skb);
- if ((cast_type == RTN_BROADCAST) &&
- (card->info.broadcast_capable == 0))
- return -EPERM;
- queue = card->qdio.out_qs
- [qeth_get_priority_queue(card, skb, ipv, cast_type)];
- if (!card->options.layer2) {
- ipv = qeth_get_ip_version(skb);
- if ((card->dev->header_ops == &qeth_fake_ops) && ipv) {
- new_skb = qeth_pskb_unshare(skb, GFP_ATOMIC);
- if (!new_skb)
- return -ENOMEM;
- if(card->dev->type == ARPHRD_IEEE802_TR){
- skb_pull(new_skb, QETH_FAKE_LL_LEN_TR);
- } else {
- skb_pull(new_skb, QETH_FAKE_LL_LEN_ETH);
- }
- }
- }
- if (skb_is_gso(skb))
- large_send = card->options.large_send;
- /* check on OSN device*/
- if (card->info.type == QETH_CARD_TYPE_OSN)
- hdr = (struct qeth_hdr *)new_skb->data;
- /*are we able to do TSO ? */
- if ((large_send == QETH_LARGE_SEND_TSO) &&
- (cast_type == RTN_UNSPEC)) {
- rc = qeth_tso_prepare_packet(card, new_skb, ipv, cast_type);
- if (rc) {
- __qeth_free_new_skb(skb, new_skb);
- return rc;
- }
- elements_needed++;
- } else if (card->info.type != QETH_CARD_TYPE_OSN) {
- new_skb2 = qeth_prepare_skb(card, new_skb, &hdr, ipv);
- if (!new_skb2) {
- __qeth_free_new_skb(skb, new_skb);
- return -EINVAL;
- }
- if (new_skb != skb)
- __qeth_free_new_skb(new_skb2, new_skb);
- new_skb = new_skb2;
- qeth_fill_header(card, hdr, new_skb, ipv, cast_type);
- }
- if (large_send == QETH_LARGE_SEND_EDDP) {
- ctx = qeth_eddp_create_context(card, new_skb, hdr,
- skb->sk->sk_protocol);
- if (ctx == NULL) {
- __qeth_free_new_skb(skb, new_skb);
- PRINT_WARN("could not create eddp context\n");
- return -EINVAL;
- }
- } else {
- int elems = qeth_get_elements_no(card,(void*) hdr, new_skb,
- elements_needed);
- if (!elems) {
- __qeth_free_new_skb(skb, new_skb);
- return -EINVAL;
- }
- elements_needed += elems;
- }
-
- if ((large_send == QETH_LARGE_SEND_NO) &&
- (skb->ip_summed == CHECKSUM_PARTIAL))
- qeth_tx_csum(new_skb);
-
- if (card->info.type != QETH_CARD_TYPE_IQD)
- rc = qeth_do_send_packet(card, queue, new_skb, hdr,
- elements_needed, ctx);
- else {
- if ((!card->options.layer2) &&
- (ipv == 0)) {
- __qeth_free_new_skb(skb, new_skb);
- return -EPERM;
- }
- rc = qeth_do_send_packet_fast(card, queue, new_skb, hdr,
- elements_needed, ctx);
- }
- if (!rc) {
- card->stats.tx_packets++;
- card->stats.tx_bytes += tx_bytes;
- if (new_skb != skb)
- dev_kfree_skb_any(skb);
- if (card->options.performance_stats) {
- if (tso_size &&
- !(large_send == QETH_LARGE_SEND_NO)) {
- card->perf_stats.large_send_bytes += tx_bytes;
- card->perf_stats.large_send_cnt++;
- }
- if (nr_frags > 0) {
- card->perf_stats.sg_skbs_sent++;
- /* nr_frags + skb->data */
- card->perf_stats.sg_frags_sent +=
- nr_frags + 1;
- }
- }
- } else {
- card->stats.tx_dropped++;
- __qeth_free_new_skb(skb, new_skb);
- }
- if (ctx != NULL) {
- /* drop creator's reference */
- qeth_eddp_put_context(ctx);
- /* free skb; it's not referenced by a buffer */
- if (!rc)
- dev_kfree_skb_any(new_skb);
- }
- 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)&&
- (card->info.link_type != QETH_LINK_TYPE_OSN) &&
- (card->info.link_type != QETH_LINK_TYPE_10GBIT_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:
- break;
- }
- return rc;
-}
-
-
-static 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");
-
- /*
- * currently GuestLAN only supports the ARP assist function
- * IPA_CMD_ASS_ARP_QUERY_INFO, but not IPA_CMD_ASS_ARP_SET_NO_ENTRIES;
- * thus we say EOPNOTSUPP for this ARP function
- */
- if (card->info.guestlan)
- return -EOPNOTSUPP;
- if (!qeth_is_supported(card,IPA_ARP_PROCESSING)) {
- PRINT_WARN("ARP processing not supported "
- "on %s!\n", QETH_CARD_IFNAME(card));
- 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/%d)\n",
- QETH_CARD_IFNAME(card), qeth_arp_get_error_cause(&rc),
- tmp, tmp);
- }
- return rc;
-}
-
-static void
-qeth_copy_arp_entries_stripped(struct qeth_arp_query_info *qinfo,
- struct qeth_arp_query_data *qdata,
- int entry_size, int uentry_size)
-{
- char *entry_ptr;
- char *uentry_ptr;
- int i;
-
- entry_ptr = (char *)&qdata->data;
- uentry_ptr = (char *)(qinfo->udata + qinfo->udata_offset);
- for (i = 0; i < qdata->no_entries; ++i){
- /* strip off 32 bytes "media specific information" */
- memcpy(uentry_ptr, (entry_ptr + 32), entry_size - 32);
- entry_ptr += entry_size;
- uentry_ptr += uentry_size;
- }
-}
-
-static int
-qeth_arp_query_cb(struct qeth_card *card, struct qeth_reply *reply,
- unsigned long data)
-{
- struct qeth_ipa_cmd *cmd;
- struct qeth_arp_query_data *qdata;
- struct qeth_arp_query_info *qinfo;
- int entry_size;
- int uentry_size;
- int i;
-
- QETH_DBF_TEXT(trace,4,"arpquecb");
-
- qinfo = (struct qeth_arp_query_info *) reply->param;
- cmd = (struct qeth_ipa_cmd *) data;
- if (cmd->hdr.return_code) {
- QETH_DBF_TEXT_(trace,4,"qaer1%i", cmd->hdr.return_code);
- return 0;
- }
- if (cmd->data.setassparms.hdr.return_code) {
- cmd->hdr.return_code = cmd->data.setassparms.hdr.return_code;
- QETH_DBF_TEXT_(trace,4,"qaer2%i", cmd->hdr.return_code);
- return 0;
- }
- qdata = &cmd->data.setassparms.data.query_arp;
- switch(qdata->reply_bits){
- case 5:
- uentry_size = entry_size = sizeof(struct qeth_arp_qi_entry5);
- if (qinfo->mask_bits & QETH_QARP_STRIP_ENTRIES)
- uentry_size = sizeof(struct qeth_arp_qi_entry5_short);
- break;
- case 7:
- /* fall through to default */
- default:
- /* tr is the same as eth -> entry7 */
- uentry_size = entry_size = sizeof(struct qeth_arp_qi_entry7);
- if (qinfo->mask_bits & QETH_QARP_STRIP_ENTRIES)
- uentry_size = sizeof(struct qeth_arp_qi_entry7_short);
- break;
- }
- /* check if there is enough room in userspace */
- if ((qinfo->udata_len - qinfo->udata_offset) <
- qdata->no_entries * uentry_size){
- QETH_DBF_TEXT_(trace, 4, "qaer3%i", -ENOMEM);
- cmd->hdr.return_code = -ENOMEM;
- PRINT_WARN("query ARP user space buffer is too small for "
- "the returned number of ARP entries. "
- "Aborting query!\n");
- goto out_error;
- }
- QETH_DBF_TEXT_(trace, 4, "anore%i",
- cmd->data.setassparms.hdr.number_of_replies);
- QETH_DBF_TEXT_(trace, 4, "aseqn%i", cmd->data.setassparms.hdr.seq_no);
- QETH_DBF_TEXT_(trace, 4, "anoen%i", qdata->no_entries);
-
- if (qinfo->mask_bits & QETH_QARP_STRIP_ENTRIES) {
- /* strip off "media specific information" */
- qeth_copy_arp_entries_stripped(qinfo, qdata, entry_size,
- uentry_size);
- } else
- /*copy entries to user buffer*/
- memcpy(qinfo->udata + qinfo->udata_offset,
- (char *)&qdata->data, qdata->no_entries*uentry_size);
-
- qinfo->no_entries += qdata->no_entries;
- qinfo->udata_offset += (qdata->no_entries*uentry_size);
- /* check if all replies received ... */
- if (cmd->data.setassparms.hdr.seq_no <
- cmd->data.setassparms.hdr.number_of_replies)
- return 1;
- memcpy(qinfo->udata, &qinfo->no_entries, 4);
- /* keep STRIP_ENTRIES flag so the user program can distinguish
- * stripped entries from normal ones */
- if (qinfo->mask_bits & QETH_QARP_STRIP_ENTRIES)
- qdata->reply_bits |= QETH_QARP_STRIP_ENTRIES;
- 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 int
-qeth_send_ipa_arp_cmd(struct qeth_card *card, struct qeth_cmd_buffer *iob,
- int len, int (*reply_cb)(struct qeth_card *,
- struct qeth_reply *,
- unsigned long),
- void *reply_param)
-{
- QETH_DBF_TEXT(trace,4,"sendarp");
-
- 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);
- return qeth_send_control_data(card, IPA_PDU_HEADER_SIZE + len, iob,
- reply_cb, reply_param);
-}
-
-static int
-qeth_send_ipa_snmp_cmd(struct qeth_card *card, struct qeth_cmd_buffer *iob,
- int len, int (*reply_cb)(struct qeth_card *,
- struct qeth_reply *,
- unsigned long),
- void *reply_param)
-{
- u16 s1, s2;
-
- QETH_DBF_TEXT(trace,4,"sendsnmp");
-
- 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);
- /* adjust PDU length fields in IPA_PDU_HEADER */
- s1 = (u32) IPA_PDU_HEADER_SIZE + len;
- s2 = (u32) 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);
- return qeth_send_control_data(card, IPA_PDU_HEADER_SIZE + len, iob,
- reply_cb, reply_param);
-}
-
-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_arp_query(struct qeth_card *card, char __user *udata)
-{
- struct qeth_cmd_buffer *iob;
- struct qeth_arp_query_info qinfo = {0, };
- int tmp;
- int rc;
-
- QETH_DBF_TEXT(trace,3,"arpquery");
-
- if (!qeth_is_supported(card,/*IPA_QUERY_ARP_ADDR_INFO*/
- IPA_ARP_PROCESSING)) {
- PRINT_WARN("ARP processing not supported "
- "on %s!\n", QETH_CARD_IFNAME(card));
- return -EOPNOTSUPP;
- }
- /* get size of userspace buffer and mask_bits -> 6 bytes */
- if (copy_from_user(&qinfo, udata, 6))
- return -EFAULT;
- if (!(qinfo.udata = kzalloc(qinfo.udata_len, GFP_KERNEL)))
- return -ENOMEM;
- qinfo.udata_offset = QETH_QARP_ENTRIES_OFFSET;
- iob = qeth_get_setassparms_cmd(card, IPA_ARP_PROCESSING,
- IPA_CMD_ASS_ARP_QUERY_INFO,
- sizeof(int),QETH_PROT_IPV4);
-
- rc = qeth_send_ipa_arp_cmd(card, iob,
- QETH_SETASS_BASE_LEN+QETH_ARP_CMD_LEN,
- qeth_arp_query_cb, (void *)&qinfo);
- if (rc) {
- tmp = rc;
- PRINT_WARN("Error while querying ARP cache on %s: %s "
- "(0x%x/%d)\n",
- QETH_CARD_IFNAME(card), qeth_arp_get_error_cause(&rc),
- tmp, tmp);
- if (copy_to_user(udata, qinfo.udata, 4))
- rc = -EFAULT;
- } else {
- if (copy_to_user(udata, qinfo.udata, qinfo.udata_len))
- rc = -EFAULT;
- }
- kfree(qinfo.udata);
- return rc;
-}
-
-/**
- * SNMP command callback
- */
-static int
-qeth_snmp_command_cb(struct qeth_card *card, struct qeth_reply *reply,
- unsigned long sdata)
-{
- struct qeth_ipa_cmd *cmd;
- struct qeth_arp_query_info *qinfo;
- struct qeth_snmp_cmd *snmp;
- unsigned char *data;
- __u16 data_len;
-
- QETH_DBF_TEXT(trace,3,"snpcmdcb");
-
- cmd = (struct qeth_ipa_cmd *) sdata;
- data = (unsigned char *)((char *)cmd - reply->offset);
- qinfo = (struct qeth_arp_query_info *) reply->param;
- snmp = &cmd->data.setadapterparms.data.snmp;
-
- if (cmd->hdr.return_code) {
- QETH_DBF_TEXT_(trace,4,"scer1%i", cmd->hdr.return_code);
- return 0;
- }
- if (cmd->data.setadapterparms.hdr.return_code) {
- cmd->hdr.return_code = cmd->data.setadapterparms.hdr.return_code;
- QETH_DBF_TEXT_(trace,4,"scer2%i", cmd->hdr.return_code);
- return 0;
- }
- data_len = *((__u16*)QETH_IPA_PDU_LEN_PDU1(data));
- if (cmd->data.setadapterparms.hdr.seq_no == 1)
- data_len -= (__u16)((char *)&snmp->data - (char *)cmd);
- else
- data_len -= (__u16)((char*)&snmp->request - (char *)cmd);
-
- /* check if there is enough room in userspace */
- if ((qinfo->udata_len - qinfo->udata_offset) < data_len) {
- QETH_DBF_TEXT_(trace, 4, "scer3%i", -ENOMEM);
- cmd->hdr.return_code = -ENOMEM;
- return 0;
- }
- QETH_DBF_TEXT_(trace, 4, "snore%i",
- cmd->data.setadapterparms.hdr.used_total);
- QETH_DBF_TEXT_(trace, 4, "sseqn%i", cmd->data.setadapterparms.hdr.seq_no);
- /*copy entries to user buffer*/
- if (cmd->data.setadapterparms.hdr.seq_no == 1) {
- memcpy(qinfo->udata + qinfo->udata_offset,
- (char *)snmp,
- data_len + offsetof(struct qeth_snmp_cmd,data));
- qinfo->udata_offset += offsetof(struct qeth_snmp_cmd, data);
- } else {
- memcpy(qinfo->udata + qinfo->udata_offset,
- (char *)&snmp->request, data_len);
- }
- qinfo->udata_offset += data_len;
- /* check if all replies received ... */
- QETH_DBF_TEXT_(trace, 4, "srtot%i",
- cmd->data.setadapterparms.hdr.used_total);
- QETH_DBF_TEXT_(trace, 4, "srseq%i",
- cmd->data.setadapterparms.hdr.seq_no);
- if (cmd->data.setadapterparms.hdr.seq_no <
- cmd->data.setadapterparms.hdr.used_total)
- return 1;
- return 0;
-}
-
-static struct qeth_cmd_buffer *
-qeth_get_ipacmd_buffer(struct qeth_card *, enum qeth_ipa_cmds,
- enum qeth_prot_versions );
-
-static struct qeth_cmd_buffer *
-qeth_get_adapter_cmd(struct qeth_card *card, __u32 command, __u32 cmdlen)
-{
- 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.hdr.cmdlength = cmdlen;
- cmd->data.setadapterparms.hdr.command_code = command;
- cmd->data.setadapterparms.hdr.used_total = 1;
- cmd->data.setadapterparms.hdr.seq_no = 1;
-
- return iob;
-}
-
-/**
- * function to send SNMP commands to OSA-E card
- */
-static int
-qeth_snmp_command(struct qeth_card *card, char __user *udata)
-{
- struct qeth_cmd_buffer *iob;
- struct qeth_ipa_cmd *cmd;
- struct qeth_snmp_ureq *ureq;
- int req_len;
- struct qeth_arp_query_info qinfo = {0, };
- int rc = 0;
-
- QETH_DBF_TEXT(trace,3,"snmpcmd");
-
- if (card->info.guestlan)
- return -EOPNOTSUPP;
-
- if ((!qeth_adp_supported(card,IPA_SETADP_SET_SNMP_CONTROL)) &&
- (!card->options.layer2) ) {
- PRINT_WARN("SNMP Query MIBS not supported "
- "on %s!\n", QETH_CARD_IFNAME(card));
- return -EOPNOTSUPP;
- }
- /* skip 4 bytes (data_len struct member) to get req_len */
- if (copy_from_user(&req_len, udata + sizeof(int), sizeof(int)))
- return -EFAULT;
- ureq = kmalloc(req_len+sizeof(struct qeth_snmp_ureq_hdr), GFP_KERNEL);
- if (!ureq) {
- QETH_DBF_TEXT(trace, 2, "snmpnome");
- return -ENOMEM;
- }
- if (copy_from_user(ureq, udata,
- req_len+sizeof(struct qeth_snmp_ureq_hdr))){
- kfree(ureq);
- return -EFAULT;
- }
- qinfo.udata_len = ureq->hdr.data_len;
- if (!(qinfo.udata = kzalloc(qinfo.udata_len, GFP_KERNEL))){
- kfree(ureq);
- return -ENOMEM;
- }
- qinfo.udata_offset = sizeof(struct qeth_snmp_ureq_hdr);
-
- iob = qeth_get_adapter_cmd(card, IPA_SETADP_SET_SNMP_CONTROL,
- QETH_SNMP_SETADP_CMDLENGTH + req_len);
- cmd = (struct qeth_ipa_cmd *)(iob->data+IPA_PDU_HEADER_SIZE);
- memcpy(&cmd->data.setadapterparms.data.snmp, &ureq->cmd, req_len);
- rc = qeth_send_ipa_snmp_cmd(card, iob, QETH_SETADP_BASE_LEN + req_len,
- qeth_snmp_command_cb, (void *)&qinfo);
- if (rc)
- PRINT_WARN("SNMP command failed on %s: (0x%x)\n",
- QETH_CARD_IFNAME(card), rc);
- else {
- if (copy_to_user(udata, qinfo.udata, qinfo.udata_len))
- rc = -EFAULT;
- }
-
- kfree(ureq);
- kfree(qinfo.udata);
- return rc;
-}
-
-static int
-qeth_default_setassparms_cb(struct qeth_card *, struct qeth_reply *,
- unsigned long);
-
-static int
-qeth_default_setadapterparms_cb(struct qeth_card *card,
- struct qeth_reply *reply,
- unsigned long data);
-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");
-
- /*
- * currently GuestLAN only supports the ARP assist function
- * IPA_CMD_ASS_ARP_QUERY_INFO, but not IPA_CMD_ASS_ARP_ADD_ENTRY;
- * thus we say EOPNOTSUPP for this ARP function
- */
- if (card->info.guestlan)
- return -EOPNOTSUPP;
- if (!qeth_is_supported(card,IPA_ARP_PROCESSING)) {
- PRINT_WARN("ARP processing not supported "
- "on %s!\n", QETH_CARD_IFNAME(card));
- 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/%d)\n",
- buf, QETH_CARD_IFNAME(card),
- qeth_arp_get_error_cause(&rc), tmp, 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");
-
- /*
- * currently GuestLAN only supports the ARP assist function
- * IPA_CMD_ASS_ARP_QUERY_INFO, but not IPA_CMD_ASS_ARP_REMOVE_ENTRY;
- * thus we say EOPNOTSUPP for this ARP function
- */
- if (card->info.guestlan)
- return -EOPNOTSUPP;
- if (!qeth_is_supported(card,IPA_ARP_PROCESSING)) {
- PRINT_WARN("ARP processing not supported "
- "on %s!\n", QETH_CARD_IFNAME(card));
- 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/%d)\n",
- buf, QETH_CARD_IFNAME(card),
- qeth_arp_get_error_cause(&rc), tmp, tmp);
- }
- return rc;
-}
-
-static int
-qeth_arp_flush_cache(struct qeth_card *card)
-{
- int rc;
- int tmp;
-
- QETH_DBF_TEXT(trace,3,"arpflush");
-
- /*
- * currently GuestLAN only supports the ARP assist function
- * IPA_CMD_ASS_ARP_QUERY_INFO, but not IPA_CMD_ASS_ARP_FLUSH_CACHE;
- * thus we say EOPNOTSUPP for this ARP function
- */
- if (card->info.guestlan || (card->info.type == QETH_CARD_TYPE_IQD))
- return -EOPNOTSUPP;
- if (!qeth_is_supported(card,IPA_ARP_PROCESSING)) {
- PRINT_WARN("ARP processing not supported "
- "on %s!\n", QETH_CARD_IFNAME(card));
- 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/%d)\n",
- QETH_CARD_IFNAME(card), qeth_arp_get_error_cause(&rc),
- tmp, 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) &&
- (card->state != CARD_STATE_SOFTSETUP))
- return -ENODEV;
-
- if (card->info.type == QETH_CARD_TYPE_OSN)
- return -EPERM;
-
- switch (cmd){
- case SIOC_QETH_ARP_SET_NO_ENTRIES:
- if ( !capable(CAP_NET_ADMIN) ||
- (card->options.layer2) ) {
- rc = -EPERM;
- break;
- }
- rc = qeth_arp_set_no_entries(card, rq->ifr_ifru.ifru_ivalue);
- break;
- case SIOC_QETH_ARP_QUERY_INFO:
- if ( !capable(CAP_NET_ADMIN) ||
- (card->options.layer2) ) {
- rc = -EPERM;
- break;
- }
- rc = qeth_arp_query(card, rq->ifr_ifru.ifru_data);
- break;
- case SIOC_QETH_ARP_ADD_ENTRY:
- if ( !capable(CAP_NET_ADMIN) ||
- (card->options.layer2) ) {
- 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 SIOC_QETH_ARP_REMOVE_ENTRY:
- if ( !capable(CAP_NET_ADMIN) ||
- (card->options.layer2) ) {
- 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 SIOC_QETH_ARP_FLUSH_CACHE:
- if ( !capable(CAP_NET_ADMIN) ||
- (card->options.layer2) ) {
- rc = -EPERM;
- break;
- }
- rc = qeth_arp_flush_cache(card);
- break;
- case SIOC_QETH_ADP_SET_SNMP_CONTROL:
- rc = qeth_snmp_command(card, rq->ifr_ifru.ifru_data);
- break;
- case SIOC_QETH_GET_CARD_TYPE:
- if ((card->info.type == QETH_CARD_TYPE_OSAE) &&
- !card->info.guestlan)
- return 1;
- return 0;
- break;
- case SIOCGMIIPHY:
- mii_data = if_mii(rq);
- mii_data->phy_id = 0;
- break;
- case SIOCGMIIREG:
- mii_data = if_mii(rq);
- 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;
- default:
- rc = -EOPNOTSUPP;
- }
- if (rc)
- QETH_DBF_TEXT_(trace, 2, "ioce%d", rc);
- 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;
- unsigned long flags;
-
- QETH_DBF_TEXT(trace,4,"vlanreg");
-
- card = (struct qeth_card *) dev->priv;
- spin_lock_irqsave(&card->vlanlock, flags);
- card->vlangrp = grp;
- spin_unlock_irqrestore(&card->vlanlock, flags);
-}
-
-static void
-qeth_free_vlan_buffer(struct qeth_card *card, struct qeth_qdio_out_buffer *buf,
- unsigned short vid)
-{
- int i;
- struct sk_buff *skb;
- struct sk_buff_head tmp_list;
-
- skb_queue_head_init(&tmp_list);
- lockdep_set_class(&tmp_list.lock, &qdio_out_skb_queue_key);
- for(i = 0; i < QETH_MAX_BUFFER_ELEMENTS(card); ++i){
- while ((skb = skb_dequeue(&buf->skb_list))){
- if (vlan_tx_tag_present(skb) &&
- (vlan_tx_tag_get(skb) == vid)) {
- atomic_dec(&skb->users);
- dev_kfree_skb(skb);
- } else
- skb_queue_tail(&tmp_list, skb);
- }
- }
- while ((skb = skb_dequeue(&tmp_list)))
- skb_queue_tail(&buf->skb_list, skb);
-}
-
-static void
-qeth_free_vlan_skbs(struct qeth_card *card, unsigned short vid)
-{
- int i, j;
-
- QETH_DBF_TEXT(trace, 4, "frvlskbs");
- for (i = 0; i < card->qdio.no_out_queues; ++i){
- for (j = 0; j < QDIO_MAX_BUFFERS_PER_Q; ++j)
- qeth_free_vlan_buffer(card, &card->qdio.
- out_qs[i]->bufs[j], vid);
- }
-}
-
-static void
-qeth_free_vlan_addresses4(struct qeth_card *card, unsigned short vid)
-{
- struct in_device *in_dev;
- struct in_ifaddr *ifa;
- struct qeth_ipaddr *addr;
-
- QETH_DBF_TEXT(trace, 4, "frvaddr4");
-
- rcu_read_lock();
- in_dev = __in_dev_get_rcu(vlan_group_get_device(card->vlangrp, vid));
- if (!in_dev)
- goto out;
- for (ifa = in_dev->ifa_list; ifa; ifa = ifa->ifa_next) {
- addr = qeth_get_addr_buffer(QETH_PROT_IPV4);
- if (addr){
- addr->u.a4.addr = ifa->ifa_address;
- addr->u.a4.mask = ifa->ifa_mask;
- addr->type = QETH_IP_TYPE_NORMAL;
- if (!qeth_delete_ip(card, addr))
- kfree(addr);
- }
- }
-out:
- rcu_read_unlock();
-}
-
-static void
-qeth_free_vlan_addresses6(struct qeth_card *card, unsigned short vid)
-{
-#ifdef CONFIG_QETH_IPV6
- struct inet6_dev *in6_dev;
- struct inet6_ifaddr *ifa;
- struct qeth_ipaddr *addr;
-
- QETH_DBF_TEXT(trace, 4, "frvaddr6");
-
- in6_dev = in6_dev_get(vlan_group_get_device(card->vlangrp, vid));
- if (!in6_dev)
- return;
- for (ifa = in6_dev->addr_list; ifa; ifa = ifa->lst_next){
- addr = qeth_get_addr_buffer(QETH_PROT_IPV6);
- if (addr){
- memcpy(&addr->u.a6.addr, &ifa->addr,
- sizeof(struct in6_addr));
- addr->u.a6.pfxlen = ifa->prefix_len;
- addr->type = QETH_IP_TYPE_NORMAL;
- if (!qeth_delete_ip(card, addr))
- kfree(addr);
- }
- }
- in6_dev_put(in6_dev);
-#endif /* CONFIG_QETH_IPV6 */
-}
-
-static void
-qeth_free_vlan_addresses(struct qeth_card *card, unsigned short vid)
-{
- if (card->options.layer2 || !card->vlangrp)
- return;
- qeth_free_vlan_addresses4(card, vid);
- qeth_free_vlan_addresses6(card, vid);
-}
-
-static int
-qeth_layer2_send_setdelvlan_cb(struct qeth_card *card,
- struct qeth_reply *reply,
- unsigned long data)
-{
- struct qeth_ipa_cmd *cmd;
-
- QETH_DBF_TEXT(trace, 2, "L2sdvcb");
- cmd = (struct qeth_ipa_cmd *) data;
- if (cmd->hdr.return_code) {
- PRINT_ERR("Error in processing VLAN %i on %s: 0x%x. "
- "Continuing\n",cmd->data.setdelvlan.vlan_id,
- QETH_CARD_IFNAME(card), cmd->hdr.return_code);
- QETH_DBF_TEXT_(trace, 2, "L2VL%4x", cmd->hdr.command);
- QETH_DBF_TEXT_(trace, 2, "L2%s", CARD_BUS_ID(card));
- QETH_DBF_TEXT_(trace, 2, "err%d", cmd->hdr.return_code);
- }
- return 0;
-}
-
-static int
-qeth_layer2_send_setdelvlan(struct qeth_card *card, __u16 i,
- enum qeth_ipa_cmds ipacmd)
-{
- struct qeth_ipa_cmd *cmd;
- struct qeth_cmd_buffer *iob;
-
- QETH_DBF_TEXT_(trace, 4, "L2sdv%x",ipacmd);
- iob = qeth_get_ipacmd_buffer(card, ipacmd, QETH_PROT_IPV4);
- cmd = (struct qeth_ipa_cmd *)(iob->data+IPA_PDU_HEADER_SIZE);
- cmd->data.setdelvlan.vlan_id = i;
- return qeth_send_ipa_cmd(card, iob,
- qeth_layer2_send_setdelvlan_cb, NULL);
-}
-
-static void
-qeth_layer2_process_vlans(struct qeth_card *card, int clear)
-{
- unsigned short i;
-
- QETH_DBF_TEXT(trace, 3, "L2prcvln");
-
- if (!card->vlangrp)
- return;
- for (i = 0; i < VLAN_GROUP_ARRAY_LEN; i++) {
- if (vlan_group_get_device(card->vlangrp, i) == NULL)
- continue;
- if (clear)
- qeth_layer2_send_setdelvlan(card, i, IPA_CMD_DELVLAN);
- else
- qeth_layer2_send_setdelvlan(card, i, IPA_CMD_SETVLAN);
- }
-}
-
-/*add_vid is layer 2 used only ....*/
-static void
-qeth_vlan_rx_add_vid(struct net_device *dev, unsigned short vid)
-{
- struct qeth_card *card;
-
- QETH_DBF_TEXT_(trace, 4, "aid:%d", vid);
-
- card = (struct qeth_card *) dev->priv;
- if (!card->options.layer2)
- return;
- qeth_layer2_send_setdelvlan(card, vid, IPA_CMD_SETVLAN);
-}
-
-/*... kill_vid used for both modes*/
-static void
-qeth_vlan_rx_kill_vid(struct net_device *dev, unsigned short vid)
-{
- struct qeth_card *card;
- unsigned long flags;
-
- QETH_DBF_TEXT_(trace, 4, "kid:%d", vid);
-
- card = (struct qeth_card *) dev->priv;
- /* free all skbs for the vlan device */
- qeth_free_vlan_skbs(card, vid);
- spin_lock_irqsave(&card->vlanlock, flags);
- /* unregister IP addresses of vlan device */
- qeth_free_vlan_addresses(card, vid);
- vlan_group_set_device(card->vlangrp, vid, NULL);
- spin_unlock_irqrestore(&card->vlanlock, flags);
- if (card->options.layer2)
- qeth_layer2_send_setdelvlan(card, vid, IPA_CMD_DELVLAN);
- qeth_set_multicast_list(card->dev);
-}
-#endif
-/**
- * Examine hardware response to SET_PROMISC_MODE
- */
-static int
-qeth_setadp_promisc_mode_cb(struct qeth_card *card,
- struct qeth_reply *reply,
- unsigned long data)
-{
- struct qeth_ipa_cmd *cmd;
- struct qeth_ipacmd_setadpparms *setparms;
-
- QETH_DBF_TEXT(trace,4,"prmadpcb");
-
- cmd = (struct qeth_ipa_cmd *) data;
- setparms = &(cmd->data.setadapterparms);
-
- qeth_default_setadapterparms_cb(card, reply, (unsigned long)cmd);
- if (cmd->hdr.return_code) {
- QETH_DBF_TEXT_(trace,4,"prmrc%2.2x",cmd->hdr.return_code);
- setparms->data.mode = SET_PROMISC_MODE_OFF;
- }
- card->info.promisc_mode = setparms->data.mode;
- return 0;
-}
-/*
- * Set promiscuous mode (on or off) (SET_PROMISC_MODE command)
- */
-static void
-qeth_setadp_promisc_mode(struct qeth_card *card)
-{
- enum qeth_ipa_promisc_modes mode;
- struct net_device *dev = card->dev;
- struct qeth_cmd_buffer *iob;
- struct qeth_ipa_cmd *cmd;
-
- QETH_DBF_TEXT(trace, 4, "setprom");
-
- if (((dev->flags & IFF_PROMISC) &&
- (card->info.promisc_mode == SET_PROMISC_MODE_ON)) ||
- (!(dev->flags & IFF_PROMISC) &&
- (card->info.promisc_mode == SET_PROMISC_MODE_OFF)))
- return;
- mode = SET_PROMISC_MODE_OFF;
- if (dev->flags & IFF_PROMISC)
- mode = SET_PROMISC_MODE_ON;
- QETH_DBF_TEXT_(trace, 4, "mode:%x", mode);
-
- iob = qeth_get_adapter_cmd(card, IPA_SETADP_SET_PROMISC_MODE,
- sizeof(struct qeth_ipacmd_setadpparms));
- cmd = (struct qeth_ipa_cmd *)(iob->data + IPA_PDU_HEADER_SIZE);
- cmd->data.setadapterparms.data.mode = mode;
- qeth_send_ipa_cmd(card, iob, qeth_setadp_promisc_mode_cb, NULL);
-}
-
-/**
- * set multicast address on card
- */
-static void
-qeth_set_multicast_list(struct net_device *dev)
-{
- struct qeth_card *card = (struct qeth_card *) dev->priv;
-
- if (card->info.type == QETH_CARD_TYPE_OSN)
- return ;
-
- QETH_DBF_TEXT(trace, 3, "setmulti");
- qeth_delete_mc_addresses(card);
- if (card->options.layer2) {
- qeth_layer2_add_multicast(card);
- goto out;
- }
- qeth_add_multicast_ipv4(card);
-#ifdef CONFIG_QETH_IPV6
- qeth_add_multicast_ipv6(card);
-#endif
-out:
- qeth_set_ip_addr_list(card);
- if (!qeth_adp_supported(card, IPA_SETADP_SET_PROMISC_MODE))
- return;
- qeth_setadp_promisc_mode(card);
-}
-
-static int
-qeth_neigh_setup(struct net_device *dev, struct neigh_parms *np)
-{
- return 0;
-}
-
-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 = kzalloc(sizeof(struct qeth_ipaddr), GFP_ATOMIC);
- if (addr == NULL) {
- PRINT_WARN("Not enough memory to add address\n");
- return NULL;
- }
- addr->type = QETH_IP_TYPE_NORMAL;
- addr->proto = prot;
- return addr;
-}
-
-int
-qeth_osn_assist(struct net_device *dev,
- void *data,
- int data_len)
-{
- struct qeth_cmd_buffer *iob;
- struct qeth_card *card;
- int rc;
-
- QETH_DBF_TEXT(trace, 2, "osnsdmc");
- if (!dev)
- return -ENODEV;
- card = (struct qeth_card *)dev->priv;
- if (!card)
- return -ENODEV;
- if ((card->state != CARD_STATE_UP) &&
- (card->state != CARD_STATE_SOFTSETUP))
- return -ENODEV;
- iob = qeth_wait_for_buffer(&card->write);
- memcpy(iob->data+IPA_PDU_HEADER_SIZE, data, data_len);
- rc = qeth_osn_send_ipa_cmd(card, iob, data_len);
- return rc;
-}
-
-static struct net_device *
-qeth_netdev_by_devno(unsigned char *read_dev_no)
-{
- struct qeth_card *card;
- struct net_device *ndev;
- unsigned char *readno;
- __u16 temp_dev_no, card_dev_no;
- char *endp;
- unsigned long flags;
-
- ndev = NULL;
- memcpy(&temp_dev_no, read_dev_no, 2);
- read_lock_irqsave(&qeth_card_list.rwlock, flags);
- list_for_each_entry(card, &qeth_card_list.list, list) {
- readno = CARD_RDEV_ID(card);
- readno += (strlen(readno) - 4);
- card_dev_no = simple_strtoul(readno, &endp, 16);
- if (card_dev_no == temp_dev_no) {
- ndev = card->dev;
- break;
- }
- }
- read_unlock_irqrestore(&qeth_card_list.rwlock, flags);
- return ndev;
-}
-
-int
-qeth_osn_register(unsigned char *read_dev_no,
- struct net_device **dev,
- int (*assist_cb)(struct net_device *, void *),
- int (*data_cb)(struct sk_buff *))
-{
- struct qeth_card * card;
-
- QETH_DBF_TEXT(trace, 2, "osnreg");
- *dev = qeth_netdev_by_devno(read_dev_no);
- if (*dev == NULL)
- return -ENODEV;
- card = (struct qeth_card *)(*dev)->priv;
- if (!card)
- return -ENODEV;
- if ((assist_cb == NULL) || (data_cb == NULL))
- return -EINVAL;
- card->osn_info.assist_cb = assist_cb;
- card->osn_info.data_cb = data_cb;
- return 0;
-}
-
-void
-qeth_osn_deregister(struct net_device * dev)
-{
- struct qeth_card *card;
-
- QETH_DBF_TEXT(trace, 2, "osndereg");
- if (!dev)
- return;
- card = (struct qeth_card *)dev->priv;
- if (!card)
- return;
- card->osn_info.assist_cb = NULL;
- card->osn_info.data_cb = NULL;
- return;
-}
-
-static void
-qeth_delete_mc_addresses(struct qeth_card *card)
-{
- struct qeth_ipaddr *iptodo;
- unsigned long flags;
-
- QETH_DBF_TEXT(trace,4,"delmc");
- iptodo = qeth_get_addr_buffer(QETH_PROT_IPV4);
- if (!iptodo) {
- QETH_DBF_TEXT(trace, 2, "dmcnomem");
- return;
- }
- iptodo->type = QETH_IP_TYPE_DEL_ALL_MC;
- spin_lock_irqsave(&card->ip_lock, flags);
- if (!__qeth_insert_ip_todo(card, iptodo, 0))
- kfree(iptodo);
- spin_unlock_irqrestore(&card->ip_lock, flags);
-}
-
-static 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 ( ((card->options.layer2 == 0) &&
- (!qeth_is_supported(card,IPA_FULL_VLAN))) ||
- (card->vlangrp == NULL) )
- return ;
-
- vg = card->vlangrp;
- for (i = 0; i < VLAN_GROUP_ARRAY_LEN; i++) {
- struct net_device *netdev = vlan_group_get_device(vg, i);
- if (netdev == NULL ||
- !(netdev->flags & IFF_UP))
- continue;
- in_dev = in_dev_get(netdev);
- if (!in_dev)
- continue;
- read_lock(&in_dev->mc_list_lock);
- qeth_add_mc(card,in_dev);
- read_unlock(&in_dev->mc_list_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->mc_list_lock);
- qeth_add_mc(card, in4_dev);
- qeth_add_vlan_mc(card);
- read_unlock(&in4_dev->mc_list_lock);
- in_dev_put(in4_dev);
-}
-
-static void
-qeth_layer2_add_multicast(struct qeth_card *card)
-{
- struct qeth_ipaddr *ipm;
- struct dev_mc_list *dm;
-
- QETH_DBF_TEXT(trace,4,"L2addmc");
- for (dm = card->dev->mc_list; dm; dm = dm->next) {
- ipm = qeth_get_addr_buffer(QETH_PROT_IPV4);
- if (!ipm)
- continue;
- memcpy(ipm->mac,dm->dmi_addr,MAX_ADDR_LEN);
- ipm->is_multicast = 1;
- if (!qeth_add_ip(card, ipm))
- kfree(ipm);
- }
-}
-
-#ifdef CONFIG_QETH_IPV6
-static 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 ( ((card->options.layer2 == 0) &&
- (!qeth_is_supported(card,IPA_FULL_VLAN))) ||
- (card->vlangrp == NULL))
- return ;
-
- vg = card->vlangrp;
- for (i = 0; i < VLAN_GROUP_ARRAY_LEN; i++) {
- struct net_device *netdev = vlan_group_get_device(vg, i);
- if (netdev == NULL ||
- !(netdev->flags & IFF_UP))
- continue;
- in_dev = in6_dev_get(netdev);
- if (!in_dev)
- continue;
- read_lock_bh(&in_dev->lock);
- qeth_add_mc6(card,in_dev);
- read_unlock_bh(&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_bh(&in6_dev->lock);
- qeth_add_mc6(card, in6_dev);
- qeth_add_vlan_mc6(card);
- read_unlock_bh(&in6_dev->lock);
- in6_dev_put(in6_dev);
-}
-#endif /* CONFIG_QETH_IPV6 */
-
-static int
-qeth_layer2_send_setdelmac(struct qeth_card *card, __u8 *mac,
- enum qeth_ipa_cmds ipacmd,
- int (*reply_cb) (struct qeth_card *,
- struct qeth_reply*,
- unsigned long))
-{
- struct qeth_ipa_cmd *cmd;
- struct qeth_cmd_buffer *iob;
-
- QETH_DBF_TEXT(trace, 2, "L2sdmac");
- iob = qeth_get_ipacmd_buffer(card, ipacmd, QETH_PROT_IPV4);
- cmd = (struct qeth_ipa_cmd *)(iob->data+IPA_PDU_HEADER_SIZE);
- cmd->data.setdelmac.mac_length = OSA_ADDR_LEN;
- memcpy(&cmd->data.setdelmac.mac, mac, OSA_ADDR_LEN);
- return qeth_send_ipa_cmd(card, iob, reply_cb, NULL);
-}
-
-static int
-qeth_layer2_send_setgroupmac_cb(struct qeth_card *card,
- struct qeth_reply *reply,
- unsigned long data)
-{
- struct qeth_ipa_cmd *cmd;
- __u8 *mac;
-
- QETH_DBF_TEXT(trace, 2, "L2Sgmacb");
- cmd = (struct qeth_ipa_cmd *) data;
- mac = &cmd->data.setdelmac.mac[0];
- /* MAC already registered, needed in couple/uncouple case */
- if (cmd->hdr.return_code == 0x2005) {
- PRINT_WARN("Group MAC %02x:%02x:%02x:%02x:%02x:%02x " \
- "already existing on %s \n",
- mac[0], mac[1], mac[2], mac[3], mac[4], mac[5],
- QETH_CARD_IFNAME(card));
- cmd->hdr.return_code = 0;
- }
- if (cmd->hdr.return_code)
- PRINT_ERR("Could not set group MAC " \
- "%02x:%02x:%02x:%02x:%02x:%02x on %s: %x\n",
- mac[0], mac[1], mac[2], mac[3], mac[4], mac[5],
- QETH_CARD_IFNAME(card),cmd->hdr.return_code);
- return 0;
-}
-
-static int
-qeth_layer2_send_setgroupmac(struct qeth_card *card, __u8 *mac)
-{
- QETH_DBF_TEXT(trace, 2, "L2Sgmac");
- return qeth_layer2_send_setdelmac(card, mac, IPA_CMD_SETGMAC,
- qeth_layer2_send_setgroupmac_cb);
-}
-
-static int
-qeth_layer2_send_delgroupmac_cb(struct qeth_card *card,
- struct qeth_reply *reply,
- unsigned long data)
-{
- struct qeth_ipa_cmd *cmd;
- __u8 *mac;
-
- QETH_DBF_TEXT(trace, 2, "L2Dgmacb");
- cmd = (struct qeth_ipa_cmd *) data;
- mac = &cmd->data.setdelmac.mac[0];
- if (cmd->hdr.return_code)
- PRINT_ERR("Could not delete group MAC " \
- "%02x:%02x:%02x:%02x:%02x:%02x on %s: %x\n",
- mac[0], mac[1], mac[2], mac[3], mac[4], mac[5],
- QETH_CARD_IFNAME(card), cmd->hdr.return_code);
- return 0;
-}
-
-static int
-qeth_layer2_send_delgroupmac(struct qeth_card *card, __u8 *mac)
-{
- QETH_DBF_TEXT(trace, 2, "L2Dgmac");
- return qeth_layer2_send_setdelmac(card, mac, IPA_CMD_DELGMAC,
- qeth_layer2_send_delgroupmac_cb);
-}
-
-static int
-qeth_layer2_send_setmac_cb(struct qeth_card *card,
- struct qeth_reply *reply,
- unsigned long data)
-{
- struct qeth_ipa_cmd *cmd;
-
- QETH_DBF_TEXT(trace, 2, "L2Smaccb");
- cmd = (struct qeth_ipa_cmd *) data;
- if (cmd->hdr.return_code) {
- QETH_DBF_TEXT_(trace, 2, "L2er%x", cmd->hdr.return_code);
- card->info.mac_bits &= ~QETH_LAYER2_MAC_REGISTERED;
- cmd->hdr.return_code = -EIO;
- } else {
- card->info.mac_bits |= QETH_LAYER2_MAC_REGISTERED;
- memcpy(card->dev->dev_addr,cmd->data.setdelmac.mac,
- OSA_ADDR_LEN);
- PRINT_INFO("MAC address %2.2x:%2.2x:%2.2x:%2.2x:%2.2x:%2.2x "
- "successfully registered on device %s\n",
- card->dev->dev_addr[0], card->dev->dev_addr[1],
- card->dev->dev_addr[2], card->dev->dev_addr[3],
- card->dev->dev_addr[4], card->dev->dev_addr[5],
- card->dev->name);
- }
- return 0;
-}
-
-static int
-qeth_layer2_send_setmac(struct qeth_card *card, __u8 *mac)
-{
- QETH_DBF_TEXT(trace, 2, "L2Setmac");
- return qeth_layer2_send_setdelmac(card, mac, IPA_CMD_SETVMAC,
- qeth_layer2_send_setmac_cb);
-}
-
-static int
-qeth_layer2_send_delmac_cb(struct qeth_card *card,
- struct qeth_reply *reply,
- unsigned long data)
-{
- struct qeth_ipa_cmd *cmd;
-
- QETH_DBF_TEXT(trace, 2, "L2Dmaccb");
- cmd = (struct qeth_ipa_cmd *) data;
- if (cmd->hdr.return_code) {
- QETH_DBF_TEXT_(trace, 2, "err%d", cmd->hdr.return_code);
- cmd->hdr.return_code = -EIO;
- return 0;
- }
- card->info.mac_bits &= ~QETH_LAYER2_MAC_REGISTERED;
-
- return 0;
-}
-static int
-qeth_layer2_send_delmac(struct qeth_card *card, __u8 *mac)
-{
- QETH_DBF_TEXT(trace, 2, "L2Delmac");
- if (!(card->info.mac_bits & QETH_LAYER2_MAC_REGISTERED))
- return 0;
- return qeth_layer2_send_setdelmac(card, mac, IPA_CMD_DELVMAC,
- qeth_layer2_send_delmac_cb);
-}
-
-static int
-qeth_layer2_set_mac_address(struct net_device *dev, void *p)
-{
- struct sockaddr *addr = p;
- struct qeth_card *card;
- int rc = 0;
-
- QETH_DBF_TEXT(trace, 3, "setmac");
-
- if (qeth_verify_dev(dev) != QETH_REAL_CARD) {
- QETH_DBF_TEXT(trace, 3, "setmcINV");
- return -EOPNOTSUPP;
- }
- card = (struct qeth_card *) dev->priv;
-
- if (!card->options.layer2) {
- PRINT_WARN("Setting MAC address on %s is not supported "
- "in Layer 3 mode.\n", dev->name);
- QETH_DBF_TEXT(trace, 3, "setmcLY3");
- return -EOPNOTSUPP;
- }
- if (card->info.type == QETH_CARD_TYPE_OSN) {
- PRINT_WARN("Setting MAC address on %s is not supported.\n",
- dev->name);
- QETH_DBF_TEXT(trace, 3, "setmcOSN");
- return -EOPNOTSUPP;
- }
- QETH_DBF_TEXT_(trace, 3, "%s", CARD_BUS_ID(card));
- QETH_DBF_HEX(trace, 3, addr->sa_data, OSA_ADDR_LEN);
- rc = qeth_layer2_send_delmac(card, &card->dev->dev_addr[0]);
- if (!rc)
- rc = qeth_layer2_send_setmac(card, addr->sa_data);
- return rc;
-}
-
-static void
-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;
- if (card->options.layer2)
- cmd->hdr.prim_version_no = 2;
- else
- cmd->hdr.prim_version_no = 1;
- cmd->hdr.param_count = 1;
- cmd->hdr.prot_version = prot;
- cmd->hdr.ipa_supported = 0;
- 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 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_layer2_register_addr_entry(struct qeth_card *card,
- struct qeth_ipaddr *addr)
-{
- if (!addr->is_multicast)
- return 0;
- QETH_DBF_TEXT(trace, 2, "setgmac");
- QETH_DBF_HEX(trace,3,&addr->mac[0],OSA_ADDR_LEN);
- return qeth_layer2_send_setgroupmac(card, &addr->mac[0]);
-}
-
-static int
-qeth_layer2_deregister_addr_entry(struct qeth_card *card,
- struct qeth_ipaddr *addr)
-{
- if (!addr->is_multicast)
- return 0;
- QETH_DBF_TEXT(trace, 2, "delgmac");
- QETH_DBF_HEX(trace,3,&addr->mac[0],OSA_ADDR_LEN);
- return qeth_layer2_send_delgroupmac(card, &addr->mac[0]);
-}
-
-static int
-qeth_layer3_register_addr_entry(struct qeth_card *card,
- struct qeth_ipaddr *addr)
-{
- char buf[50];
- int rc;
- int cnt = 3;
-
- if (addr->proto == QETH_PROT_IPV4) {
- QETH_DBF_TEXT(trace, 2,"setaddr4");
- QETH_DBF_HEX(trace, 3, &addr->u.a4.addr, sizeof(int));
- } else if (addr->proto == QETH_PROT_IPV6) {
- QETH_DBF_TEXT(trace, 2, "setaddr6");
- QETH_DBF_HEX(trace,3,&addr->u.a6.addr,8);
- QETH_DBF_HEX(trace,3,((char *)&addr->u.a6.addr)+8,8);
- } else {
- QETH_DBF_TEXT(trace, 2, "setaddr?");
- QETH_DBF_HEX(trace, 3, 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");
- qeth_ipaddr_to_string(addr->proto, (u8 *)&addr->u, buf);
- PRINT_WARN("Could not register IP address %s (rc=0x%x/%d)\n",
- buf, rc, rc);
- }
- return rc;
-}
-
-static int
-qeth_layer3_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, 3, &addr->u.a4.addr, sizeof(int));
- } else if (addr->proto == QETH_PROT_IPV6) {
- QETH_DBF_TEXT(trace, 2, "deladdr6");
- QETH_DBF_HEX(trace,3,&addr->u.a6.addr,8);
- QETH_DBF_HEX(trace,3,((char *)&addr->u.a6.addr)+8,8);
- } else {
- QETH_DBF_TEXT(trace, 2, "deladdr?");
- QETH_DBF_HEX(trace, 3, 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_register_addr_entry(struct qeth_card *card, struct qeth_ipaddr *addr)
-{
- if (card->options.layer2)
- return qeth_layer2_register_addr_entry(card, addr);
-
- return qeth_layer3_register_addr_entry(card, addr);
-}
-
-static int
-qeth_deregister_addr_entry(struct qeth_card *card, struct qeth_ipaddr *addr)
-{
- if (card->options.layer2)
- return qeth_layer2_deregister_addr_entry(card, addr);
-
- return qeth_layer3_deregister_addr_entry(card, addr);
-}
-
-static u32
-qeth_ethtool_get_tx_csum(struct net_device *dev)
-{
- return (dev->features & NETIF_F_HW_CSUM) != 0;
-}
-
-static int
-qeth_ethtool_set_tx_csum(struct net_device *dev, u32 data)
-{
- if (data)
- dev->features |= NETIF_F_HW_CSUM;
- else
- dev->features &= ~NETIF_F_HW_CSUM;
-
- return 0;
-}
-
-static u32
-qeth_ethtool_get_rx_csum(struct net_device *dev)
-{
- struct qeth_card *card = (struct qeth_card *)dev->priv;
-
- return (card->options.checksum_type == HW_CHECKSUMMING);
-}
-
-static int
-qeth_ethtool_set_rx_csum(struct net_device *dev, u32 data)
-{
- struct qeth_card *card = (struct qeth_card *)dev->priv;
-
- if ((card->state != CARD_STATE_DOWN) &&
- (card->state != CARD_STATE_RECOVER))
- return -EPERM;
- if (data)
- card->options.checksum_type = HW_CHECKSUMMING;
- else
- card->options.checksum_type = SW_CHECKSUMMING;
- return 0;
-}
-
-static u32
-qeth_ethtool_get_sg(struct net_device *dev)
-{
- struct qeth_card *card = (struct qeth_card *)dev->priv;
-
- return ((card->options.large_send != QETH_LARGE_SEND_NO) &&
- (dev->features & NETIF_F_SG));
-}
-
-static int
-qeth_ethtool_set_sg(struct net_device *dev, u32 data)
-{
- struct qeth_card *card = (struct qeth_card *)dev->priv;
-
- if (data) {
- if (card->options.large_send != QETH_LARGE_SEND_NO)
- dev->features |= NETIF_F_SG;
- else {
- dev->features &= ~NETIF_F_SG;
- return -EINVAL;
- }
- } else
- dev->features &= ~NETIF_F_SG;
- return 0;
-}
-
-static u32
-qeth_ethtool_get_tso(struct net_device *dev)
-{
- struct qeth_card *card = (struct qeth_card *)dev->priv;
-
- return ((card->options.large_send != QETH_LARGE_SEND_NO) &&
- (dev->features & NETIF_F_TSO));
-}
-
-static int
-qeth_ethtool_set_tso(struct net_device *dev, u32 data)
-{
- struct qeth_card *card = (struct qeth_card *)dev->priv;
-
- if (data) {
- if (card->options.large_send != QETH_LARGE_SEND_NO)
- dev->features |= NETIF_F_TSO;
- else {
- dev->features &= ~NETIF_F_TSO;
- return -EINVAL;
- }
- } else
- dev->features &= ~NETIF_F_TSO;
- return 0;
-}
-
-static struct ethtool_ops qeth_ethtool_ops = {
- .get_tx_csum = qeth_ethtool_get_tx_csum,
- .set_tx_csum = qeth_ethtool_set_tx_csum,
- .get_rx_csum = qeth_ethtool_get_rx_csum,
- .set_rx_csum = qeth_ethtool_set_rx_csum,
- .get_sg = qeth_ethtool_get_sg,
- .set_sg = qeth_ethtool_set_sg,
- .get_tso = qeth_ethtool_get_tso,
- .set_tso = qeth_ethtool_set_tso,
-};
-
-static int
-qeth_hard_header_parse(const struct sk_buff *skb, unsigned char *haddr)
-{
- const struct qeth_card *card;
- const struct ethhdr *eth;
- struct net_device *dev = skb->dev;
-
- if (dev->type != ARPHRD_IEEE802_TR)
- return 0;
-
- card = qeth_get_card_from_dev(dev);
- if (card->options.layer2)
- goto haveheader;
-#ifdef CONFIG_QETH_IPV6
- /* cause of the manipulated arp constructor and the ARP
- flag for OSAE devices we have some nasty exceptions */
- if (card->info.type == QETH_CARD_TYPE_OSAE) {
- if (!card->options.fake_ll) {
- if ((skb->pkt_type==PACKET_OUTGOING) &&
- (skb->protocol==ETH_P_IPV6))
- goto haveheader;
- else
- return 0;
- } else {
- if ((skb->pkt_type==PACKET_OUTGOING) &&
- (skb->protocol==ETH_P_IP))
- return 0;
- else
- goto haveheader;
- }
- }
-#endif
- if (!card->options.fake_ll)
- return 0;
-haveheader:
- eth = eth_hdr(skb);
- memcpy(haddr, eth->h_source, ETH_ALEN);
- return ETH_ALEN;
-}
-
-static const struct header_ops qeth_null_ops = {
- .parse = qeth_hard_header_parse,
-};
-
-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;
- dev->vlan_rx_add_vid = qeth_vlan_rx_add_vid;
-#endif
- if (qeth_get_netdev_flags(card) & IFF_NOARP)
- dev->header_ops = &qeth_null_ops;
-
-#ifdef CONFIG_QETH_IPV6
- /*IPv6 address autoconfiguration stuff*/
- if (!(card->info.unique_id & UNIQUE_ID_NOT_BY_CARD))
- card->dev->dev_id = card->info.unique_id & 0xffff;
-#endif
- if (card->options.fake_ll &&
- (qeth_get_netdev_flags(card) & IFF_NOARP))
- dev->header_ops = &qeth_fake_ops;
-
- dev->set_mac_address = qeth_layer2_set_mac_address;
- dev->flags |= qeth_get_netdev_flags(card);
- if ((card->options.fake_broadcast) ||
- (card->info.broadcast_capable))
- dev->flags |= IFF_BROADCAST;
- dev->hard_header_len =
- qeth_get_hlen(card->info.link_type) + card->options.add_hhlen;
- dev->addr_len = OSA_ADDR_LEN;
- dev->mtu = card->info.initial_mtu;
- if (card->info.type != QETH_CARD_TYPE_OSN)
- SET_ETHTOOL_OPS(dev, &qeth_ethtool_ops);
- return 0;
-}
-
-static void
-qeth_init_func_level(struct qeth_card *card)
-{
- if (card->ipato.enabled) {
- if (card->info.type == QETH_CARD_TYPE_IQD)
- card->info.func_level =
- QETH_IDX_FUNC_LEVEL_IQD_ENA_IPAT;
- else
- card->info.func_level =
- QETH_IDX_FUNC_LEVEL_OSAE_ENA_IPAT;
- } else {
- if (card->info.type == QETH_CARD_TYPE_IQD)
- /*FIXME:why do we have same values for dis and ena for osae??? */
- card->info.func_level =
- QETH_IDX_FUNC_LEVEL_IQD_DIS_IPAT;
- else
- card->info.func_level =
- QETH_IDX_FUNC_LEVEL_OSAE_DIS_IPAT;
- }
-}
-
-/**
- * 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");
-
- atomic_set(&card->force_alloc_skb, 0);
-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_IQD);
- 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);
- qeth_init_func_level(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;
- }
- /*network device will be recovered*/
- if (card->dev) {
- card->dev->header_ops = card->orig_header_ops;
- if (card->options.fake_ll &&
- (qeth_get_netdev_flags(card) & IFF_NOARP))
- card->dev->header_ops = &qeth_fake_ops;
- return 0;
- }
- /* at first set_online allocate netdev */
- 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_IQD);
- rc = -ENODEV;
- QETH_DBF_TEXT_(setup, 2, "6err%d", rc);
- goto out;
- }
- card->dev->priv = card;
- card->orig_header_ops = card->dev->header_ops;
- card->dev->type = qeth_get_arphdr_type(card->info.type,
- card->info.link_type);
- card->dev->init = qeth_netdev_init;
- return 0;
-out:
- PRINT_ERR("Initialization in hardsetup failed! rc=%d\n", rc);
- return rc;
-}
-
-static int
-qeth_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.hdr.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,
- sizeof(struct qeth_ipacmd_setadpparms));
- 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;
- if (!card->options.layer2 ||
- !(card->info.mac_bits & QETH_LAYER2_MAC_READ)) {
- memcpy(card->dev->dev_addr,
- &cmd->data.setadapterparms.data.change_addr.addr,
- OSA_ADDR_LEN);
- card->info.mac_bits |= QETH_LAYER2_MAC_READ;
- }
- 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,
- sizeof(struct qeth_ipacmd_setadpparms));
- 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,
- sizeof(struct qeth_ipacmd_setadpparms));
- 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 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_layer2_initialize(struct qeth_card *card)
-{
- int rc = 0;
-
-
- QETH_DBF_TEXT(setup, 2, "doL2init");
- QETH_DBF_TEXT_(setup, 2, "doL2%s", CARD_BUS_ID(card));
-
- rc = qeth_query_setadapterparms(card);
- if (rc) {
- PRINT_WARN("could not query adapter parameters on device %s: "
- "x%x\n", CARD_BUS_ID(card), rc);
- }
-
- rc = qeth_setadpparms_change_macaddr(card);
- if (rc) {
- PRINT_WARN("couldn't get MAC address on "
- "device %s: x%x\n",
- CARD_BUS_ID(card), rc);
- QETH_DBF_TEXT_(setup, 2,"1err%d",rc);
- return rc;
- }
- QETH_DBF_HEX(setup,2, card->dev->dev_addr, OSA_ADDR_LEN);
-
- rc = qeth_layer2_send_setmac(card, &card->dev->dev_addr[0]);
- if (rc)
- QETH_DBF_TEXT_(setup, 2,"2err%d",rc);
- return 0;
-}
-
-
-static int
-qeth_send_startstoplan(struct qeth_card *card, enum qeth_ipa_cmds ipacmd,
- 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;
- /* Disable IPV6 support hard coded for Hipersockets */
- if(card->info.type == QETH_CARD_TYPE_IQD)
- card->options.ipa4.supported_funcs &= ~IPA_IPV6;
- } else {
-#ifdef CONFIG_QETH_IPV6
- card->options.ipa6.supported_funcs = cmd->hdr.ipa_supported;
- card->options.ipa6.enabled_funcs = cmd->hdr.ipa_enabled;
-#endif
- }
- QETH_DBF_TEXT(setup, 2, "suppenbl");
- QETH_DBF_TEXT_(setup, 2, "%x",cmd->hdr.ipa_supported);
- QETH_DBF_TEXT_(setup, 2, "%x",cmd->hdr.ipa_enabled);
- 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);
- if (card->options.layer2) {
- QETH_DBF_TEXT(setup, 2, "noprmly2");
- return -EPERM;
- }
-
- iob = qeth_get_ipacmd_buffer(card,IPA_CMD_QIPASSIST,prot);
- rc = qeth_send_ipa_cmd(card, iob, qeth_query_ipassists_cb, NULL);
- 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 /* (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 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", QETH_CARD_IFNAME(card));
- 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",
- QETH_CARD_IFNAME(card), 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("Hardware IP fragmentation not supported on %s\n",
- QETH_CARD_IFNAME(card));
- return -EOPNOTSUPP;
- }
-
- rc = qeth_send_simple_setassparms(card, IPA_IP_FRAGMENTATION,
- IPA_CMD_ASS_START, 0);
- if (rc) {
- PRINT_WARN("Could not start Hardware IP fragmentation "
- "assist on %s: 0x%x\n",
- QETH_CARD_IFNAME(card), rc);
- } else
- PRINT_INFO("Hardware 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", QETH_CARD_IFNAME(card));
- 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",
- QETH_CARD_IFNAME(card), 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", QETH_CARD_IFNAME(card));
- 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",
- QETH_CARD_IFNAME(card), rc);
- } else {
- PRINT_INFO("VLAN enabled \n");
- card->dev->features |=
- NETIF_F_HW_VLAN_FILTER |
- 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",
- QETH_CARD_IFNAME(card));
- 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",
- QETH_CARD_IFNAME(card), 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");
-
- rc = qeth_send_startlan(card, QETH_PROT_IPV6);
- if (rc) {
- PRINT_ERR("IPv6 startlan failed on %s\n",
- QETH_CARD_IFNAME(card));
- return rc;
- }
- rc = qeth_query_ipassists(card,QETH_PROT_IPV6);
- if (rc) {
- PRINT_ERR("IPv6 query ipassist failed on %s\n",
- QETH_CARD_IFNAME(card));
- 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",
- QETH_CARD_IFNAME(card), 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",
- QETH_CARD_IFNAME(card), 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",
- QETH_CARD_IFNAME(card), 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",
- QETH_CARD_IFNAME(card));
- 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");
- card->info.broadcast_capable = 0;
- if (!qeth_is_supported(card, IPA_FILTERING)) {
- PRINT_WARN("Broadcast not supported on %s\n",
- QETH_CARD_IFNAME(card));
- rc = -EOPNOTSUPP;
- goto out;
- }
- rc = qeth_send_simple_setassparms(card, IPA_FILTERING,
- IPA_CMD_ASS_START, 0);
- if (rc) {
- PRINT_WARN("Could not enable broadcasting filtering "
- "on %s: 0x%x\n",
- QETH_CARD_IFNAME(card), rc);
- goto out;
- }
-
- 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",
- QETH_CARD_IFNAME(card), rc);
- goto out;
- }
- card->info.broadcast_capable = QETH_BROADCAST_WITH_ECHO;
- PRINT_INFO("Broadcast enabled \n");
- rc = qeth_send_simple_setassparms(card, IPA_FILTERING,
- IPA_CMD_ASS_ENABLE, 1);
- if (rc) {
- PRINT_WARN("Could not set up broadcast echo filtering on "
- "%s: 0x%x\n", QETH_CARD_IFNAME(card), rc);
- goto out;
- }
- card->info.broadcast_capable = QETH_BROADCAST_WITHOUT_ECHO;
-out:
- if (card->info.broadcast_capable)
- card->dev->flags |= IFF_BROADCAST;
- else
- card->dev->flags &= ~IFF_BROADCAST;
- return rc;
-}
-
-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",
- QETH_CARD_IFNAME(card), 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",
- QETH_CARD_IFNAME(card), 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",
- QETH_CARD_IFNAME(card));
- return 0;
- }
- if (card->options.checksum_type == SW_CHECKSUMMING) {
- PRINT_WARN("Using SW checksumming on %s.\n",
- QETH_CARD_IFNAME(card));
- return 0;
- }
- if (!qeth_is_supported(card, IPA_INBOUND_CHECKSUM)) {
- PRINT_WARN("Inbound HW Checksumming not "
- "supported on %s,\ncontinuing "
- "using Inbound SW Checksumming\n",
- QETH_CARD_IFNAME(card));
- 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 int
-qeth_start_ipa_tso(struct qeth_card *card)
-{
- int rc;
-
- QETH_DBF_TEXT(trace,3,"sttso");
-
- if (!qeth_is_supported(card, IPA_OUTBOUND_TSO)) {
- PRINT_WARN("Outbound TSO not supported on %s\n",
- QETH_CARD_IFNAME(card));
- rc = -EOPNOTSUPP;
- } else {
- rc = qeth_send_simple_setassparms(card, IPA_OUTBOUND_TSO,
- IPA_CMD_ASS_START,0);
- if (rc)
- PRINT_WARN("Could not start outbound TSO "
- "assist on %s: rc=%i\n",
- QETH_CARD_IFNAME(card), rc);
- else
- PRINT_INFO("Outbound TSO enabled\n");
- }
- if (rc && (card->options.large_send == QETH_LARGE_SEND_TSO)){
- card->options.large_send = QETH_LARGE_SEND_NO;
- card->dev->features &= ~(NETIF_F_TSO | NETIF_F_SG |
- NETIF_F_HW_CSUM);
- }
- return rc;
-}
-
-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*/
- qeth_start_ipa_tso(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;
-
-}
-
-static void
-qeth_correct_routing_type(struct qeth_card *card, enum qeth_routing_types *type,
- enum qeth_prot_versions prot)
-{
- if (card->info.type == QETH_CARD_TYPE_IQD) {
- switch (*type) {
- case NO_ROUTER:
- case PRIMARY_CONNECTOR:
- case SECONDARY_CONNECTOR:
- case MULTICAST_ROUTER:
- return;
- default:
- goto out_inval;
- }
- } else {
- switch (*type) {
- case NO_ROUTER:
- case PRIMARY_ROUTER:
- case SECONDARY_ROUTER:
- return;
- case MULTICAST_ROUTER:
- if (qeth_is_ipafunc_supported(card, prot,
- IPA_OSA_MC_ROUTER))
- return;
- default:
- goto out_inval;
- }
- }
-out_inval:
- PRINT_WARN("Routing type '%s' not supported for interface %s.\n"
- "Router status set to 'no router'.\n",
- ((*type == PRIMARY_ROUTER)? "primary router" :
- (*type == SECONDARY_ROUTER)? "secondary router" :
- (*type == PRIMARY_CONNECTOR)? "primary connector" :
- (*type == SECONDARY_CONNECTOR)? "secondary connector" :
- (*type == MULTICAST_ROUTER)? "multicast router" :
- "unknown"),
- card->dev->name);
- *type = NO_ROUTER;
-}
-
-int
-qeth_setrouting_v4(struct qeth_card *card)
-{
- int rc;
-
- QETH_DBF_TEXT(trace,3,"setrtg4");
-
- qeth_correct_routing_type(card, &card->options.route4.type,
- QETH_PROT_IPV4);
-
- 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, QETH_CARD_IFNAME(card));
- }
- return rc;
-}
-
-int
-qeth_setrouting_v6(struct qeth_card *card)
-{
- int rc = 0;
-
- QETH_DBF_TEXT(trace,3,"setrtg6");
-#ifdef CONFIG_QETH_IPV6
-
- if (!qeth_is_supported(card, IPA_IPV6))
- return 0;
- qeth_correct_routing_type(card, &card->options.route6.type,
- QETH_PROT_IPV6);
-
- 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, QETH_CARD_IFNAME(card));
- }
-#endif
- return rc;
-}
-
-int
-qeth_set_large_send(struct qeth_card *card, enum qeth_large_send_types type)
-{
- int rc = 0;
-
- if (card->dev == NULL) {
- card->options.large_send = type;
- return 0;
- }
- if (card->state == CARD_STATE_UP)
- netif_tx_disable(card->dev);
- card->options.large_send = type;
- switch (card->options.large_send) {
- case QETH_LARGE_SEND_EDDP:
- card->dev->features |= NETIF_F_TSO | NETIF_F_SG |
- NETIF_F_HW_CSUM;
- break;
- case QETH_LARGE_SEND_TSO:
- if (qeth_is_supported(card, IPA_OUTBOUND_TSO)){
- card->dev->features |= NETIF_F_TSO | NETIF_F_SG |
- NETIF_F_HW_CSUM;
- } else {
- PRINT_WARN("TSO not supported on %s. "
- "large_send set to 'no'.\n",
- card->dev->name);
- card->dev->features &= ~(NETIF_F_TSO | NETIF_F_SG |
- NETIF_F_HW_CSUM);
- card->options.large_send = QETH_LARGE_SEND_NO;
- rc = -EOPNOTSUPP;
- }
- break;
- default: /* includes QETH_LARGE_SEND_NO */
- card->dev->features &= ~(NETIF_F_TSO | NETIF_F_SG |
- NETIF_F_HW_CSUM);
- break;
- }
- if (card->state == CARD_STATE_UP)
- netif_wake_queue(card->dev);
- 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! "
- "Waiting for STARTLAN from card.\n",
- CARD_BUS_ID(card));
- card->lan_online = 0;
- }
- return rc;
- } else
- card->lan_online = 1;
- if (card->info.type==QETH_CARD_TYPE_OSN)
- goto out;
- qeth_set_large_send(card, card->options.large_send);
- if (card->options.layer2) {
- card->dev->features |=
- NETIF_F_HW_VLAN_FILTER |
- NETIF_F_HW_VLAN_TX |
- NETIF_F_HW_VLAN_RX;
- card->dev->flags|=IFF_MULTICAST|IFF_BROADCAST;
- card->info.broadcast_capable=1;
- if ((rc = qeth_layer2_initialize(card))) {
- QETH_DBF_TEXT_(setup, 2, "L2err%d", rc);
- return rc;
- }
-#ifdef CONFIG_QETH_VLAN
- qeth_layer2_process_vlans(card, 0);
-#endif
- goto out;
- }
- if ((rc = qeth_setadapter_parms(card)))
- QETH_DBF_TEXT_(setup, 2, "2err%d", rc);
- if ((rc = qeth_start_ipassists(card)))
- QETH_DBF_TEXT_(setup, 2, "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);
-out:
- netif_tx_disable(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;
- 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);
- }
-
- while (!list_empty(&card->ip_list)) {
- addr = list_entry(card->ip_list.next,
- struct qeth_ipaddr, entry);
- list_del_init(&addr->entry);
- if (clean) {
- spin_unlock_irqrestore(&card->ip_lock, flags);
- qeth_deregister_addr_entry(card, addr);
- spin_lock_irqsave(&card->ip_lock, flags);
- }
- if (!recover || addr->is_multicast) {
- kfree(addr);
- continue;
- }
- list_add_tail(&addr->entry, card->ip_tbd_list);
- }
- 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 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 recovery_mode)
-{
- 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)) {
- if (recovery_mode &&
- card->info.type != QETH_CARD_TYPE_OSN) {
- qeth_stop(card->dev);
- } else {
- rtnl_lock();
- dev_close(card->dev);
- rtnl_unlock();
- }
- if (!card->use_hard_stop) {
- __u8 *mac = &card->dev->dev_addr[0];
- rc = qeth_layer2_send_delmac(card, mac);
- QETH_DBF_TEXT_(setup, 2, "Lerr%d", rc);
- if ((rc = qeth_send_stoplan(card)))
- QETH_DBF_TEXT_(setup, 2, "1err%d", rc);
- }
- card->state = CARD_STATE_SOFTSETUP;
- }
- if (card->state == CARD_STATE_SOFTSETUP) {
-#ifdef CONFIG_QETH_VLAN
- if (card->options.layer2)
- qeth_layer2_process_vlans(card, 1);
-#endif
- qeth_clear_ip_list(card, !card->use_hard_stop, 1);
- qeth_clear_ipacmd_list(card);
- card->state = CARD_STATE_HARDSETUP;
- }
- if (card->state == CARD_STATE_HARDSETUP) {
- if ((!card->use_hard_stop) &&
- (!card->options.layer2))
- 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:
- if (card->info.guestlan) {
- 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)
-{
- 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);
- return register_netdev(card->dev);
-}
-
-static void
-qeth_start_again(struct qeth_card *card, int recovery_mode)
-{
- QETH_DBF_TEXT(setup ,2, "startag");
-
- if (recovery_mode &&
- card->info.type != QETH_CARD_TYPE_OSN) {
- qeth_open(card->dev);
- } else {
- rtnl_lock();
- dev_open(card->dev);
- rtnl_unlock();
- }
- /* this also sets saved unicast addresses */
- qeth_set_multicast_list(card->dev);
-}
-
-
-/* Layer 2 specific stuff */
-#define IGNORE_PARAM_EQ(option,value,reset_value,msg) \
- if (card->options.option == value) { \
- PRINT_ERR("%s not supported with layer 2 " \
- "functionality, ignoring option on read" \
- "channel device %s .\n",msg,CARD_RDEV_ID(card)); \
- card->options.option = reset_value; \
- }
-#define IGNORE_PARAM_NEQ(option,value,reset_value,msg) \
- if (card->options.option != value) { \
- PRINT_ERR("%s not supported with layer 2 " \
- "functionality, ignoring option on read" \
- "channel device %s .\n",msg,CARD_RDEV_ID(card)); \
- card->options.option = reset_value; \
- }
-
-
-static void qeth_make_parameters_consistent(struct qeth_card *card)
-{
-
- if (card->options.layer2 == 0)
- return;
- if (card->info.type == QETH_CARD_TYPE_OSN)
- return;
- if (card->info.type == QETH_CARD_TYPE_IQD) {
- PRINT_ERR("Device %s does not support layer 2 functionality." \
- " Ignoring layer2 option.\n",CARD_BUS_ID(card));
- card->options.layer2 = 0;
- return;
- }
- IGNORE_PARAM_NEQ(route4.type, NO_ROUTER, NO_ROUTER,
- "Routing options are");
-#ifdef CONFIG_QETH_IPV6
- IGNORE_PARAM_NEQ(route6.type, NO_ROUTER, NO_ROUTER,
- "Routing options are");
-#endif
- IGNORE_PARAM_EQ(checksum_type, HW_CHECKSUMMING,
- QETH_CHECKSUM_DEFAULT,
- "Checksumming options are");
- IGNORE_PARAM_NEQ(broadcast_mode, QETH_TR_BROADCAST_ALLRINGS,
- QETH_TR_BROADCAST_ALLRINGS,
- "Broadcast mode options are");
- IGNORE_PARAM_NEQ(macaddr_mode, QETH_TR_MACADDR_NONCANONICAL,
- QETH_TR_MACADDR_NONCANONICAL,
- "Canonical MAC addr options are");
- IGNORE_PARAM_NEQ(fake_broadcast, 0, 0,
- "Broadcast faking options are");
- IGNORE_PARAM_NEQ(add_hhlen, DEFAULT_ADD_HHLEN,
- DEFAULT_ADD_HHLEN,"Option add_hhlen is");
- IGNORE_PARAM_NEQ(fake_ll, 0, 0,"Option fake_ll is");
-}
-
-
-static int
-__qeth_set_online(struct ccwgroup_device *gdev, int recovery_mode)
-{
- 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 ((rc = ccw_device_set_online(CARD_RDEV(card))) ||
- (rc = ccw_device_set_online(CARD_WDEV(card))) ||
- (rc = ccw_device_set_online(CARD_DDEV(card)))){
- QETH_DBF_TEXT_(setup, 2, "1err%d", rc);
- return -EIO;
- }
-
- qeth_make_parameters_consistent(card);
-
- if ((rc = qeth_hardsetup_card(card))){
- QETH_DBF_TEXT_(setup, 2, "2err%d", rc);
- goto out_remove;
- }
- card->state = CARD_STATE_HARDSETUP;
-
- if (!(rc = qeth_query_ipassists(card,QETH_PROT_IPV4)))
- rc = qeth_get_unique_id(card);
-
- if (rc && card->options.layer2 == 0) {
- QETH_DBF_TEXT_(setup, 2, "3err%d", rc);
- goto out_remove;
- }
- qeth_print_status_message(card);
- if ((rc = qeth_register_netdev(card))){
- QETH_DBF_TEXT_(setup, 2, "4err%d", rc);
- goto out_remove;
- }
- if ((rc = qeth_softsetup_card(card))){
- QETH_DBF_TEXT_(setup, 2, "5err%d", rc);
- goto out_remove;
- }
-
- if ((rc = qeth_init_qdio_queues(card))){
- QETH_DBF_TEXT_(setup, 2, "6err%d", rc);
- goto out_remove;
- }
- card->state = CARD_STATE_SOFTSETUP;
- netif_carrier_on(card->dev);
-
- qeth_set_allowed_threads(card, 0xffffffff, 0);
- if (recover_flag == CARD_STATE_RECOVER)
- qeth_start_again(card, recovery_mode);
- qeth_notify_processes();
- return 0;
-out_remove:
- card->use_hard_stop = 1;
- qeth_stop_card(card, 0);
- 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 int
-qeth_set_online(struct ccwgroup_device *gdev)
-{
- return __qeth_set_online(gdev, 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},
- {CCW_DEVICE(0x1731, 0x06), .driver_info = QETH_CARD_TYPE_OSN},
- {},
-};
-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_PAGES,
- QETH_DBF_SETUP_NR_AREAS,
- QETH_DBF_SETUP_LEN);
- qeth_dbf_misc = debug_register(QETH_DBF_MISC_NAME,
- QETH_DBF_MISC_PAGES,
- QETH_DBF_MISC_NR_AREAS,
- QETH_DBF_MISC_LEN);
- qeth_dbf_data = debug_register(QETH_DBF_DATA_NAME,
- QETH_DBF_DATA_PAGES,
- QETH_DBF_DATA_NR_AREAS,
- QETH_DBF_DATA_LEN);
- qeth_dbf_control = debug_register(QETH_DBF_CONTROL_NAME,
- QETH_DBF_CONTROL_PAGES,
- QETH_DBF_CONTROL_NR_AREAS,
- QETH_DBF_CONTROL_LEN);
- qeth_dbf_sense = debug_register(QETH_DBF_SENSE_NAME,
- QETH_DBF_SENSE_PAGES,
- QETH_DBF_SENSE_NR_AREAS,
- QETH_DBF_SENSE_LEN);
- qeth_dbf_qerr = debug_register(QETH_DBF_QERR_NAME,
- QETH_DBF_QERR_PAGES,
- QETH_DBF_QERR_NR_AREAS,
- QETH_DBF_QERR_LEN);
- qeth_dbf_trace = debug_register(QETH_DBF_TRACE_NAME,
- QETH_DBF_TRACE_PAGES,
- 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,
- .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;
- struct neigh_parms *parms;
- struct qeth_card *card;
-
- card = qeth_get_card_from_dev(dev);
- if (card == NULL)
- goto out;
- if((card->options.layer2) ||
- (card->dev->header_ops == &qeth_fake_ops))
- goto out;
-
- rcu_read_lock();
- in_dev = __in_dev_get_rcu(dev);
- if (in_dev == NULL) {
- rcu_read_unlock();
- return -EINVAL;
- }
-
- parms = in_dev->arp_parms;
- __neigh_parms_put(neigh->parms);
- neigh->parms = neigh_parms_clone(parms);
- rcu_read_unlock();
-
- neigh->type = inet_addr_type(&init_net, *(__be32 *) neigh->primary_key);
- neigh->nud_state = NUD_NOARP;
- neigh->ops = arp_direct_ops;
- neigh->output = neigh->ops->queue_xmit;
- return 0;
-out:
- return qeth_old_arp_constructor(neigh);
-}
-#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 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_ip_addr_list(card);
- 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_ip_addr_list(card);
-}
-
-/*
- * 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_ip_addr_list(card);
- 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_ip_addr_list(card);
-}
-
-/**
- * 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;
- if (card->options.layer2)
- 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;
- } else
- goto out;
-
- switch(event) {
- case NETDEV_UP:
- if (!qeth_add_ip(card, addr))
- kfree(addr);
- break;
- case NETDEV_DOWN:
- if (!qeth_delete_ip(card, addr))
- kfree(addr);
- break;
- default:
- break;
- }
- qeth_set_ip_addr_list(card);
-out:
- return NOTIFY_DONE;
-}
-
-static struct notifier_block qeth_ip_notifier = {
- qeth_ip_event,
- NULL,
-};
-
-#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;
- } else
- goto out;
-
- switch(event) {
- case NETDEV_UP:
- if (!qeth_add_ip(card, addr))
- kfree(addr);
- break;
- case NETDEV_DOWN:
- if (!qeth_delete_ip(card, addr))
- kfree(addr);
- break;
- default:
- break;
- }
- qeth_set_ip_addr_list(card);
-out:
- return NOTIFY_DONE;
-}
-
-static struct notifier_block qeth_ip6_notifier = {
- qeth_ip6_event,
- NULL,
-};
-#endif
-
-static int
-__qeth_reboot_event_card(struct device *dev, void *data)
-{
- struct qeth_card *card;
-
- card = (struct qeth_card *) dev->driver_data;
- qeth_clear_ip_list(card, 0, 0);
- qeth_qdio_clear_card(card, 0);
- qeth_clear_qdio_buffers(card);
- return 0;
-}
-
-static int
-qeth_reboot_event(struct notifier_block *this, unsigned long event, void *ptr)
-{
- int ret;
-
- ret = driver_for_each_device(&qeth_ccwgroup_driver.driver, NULL, NULL,
- __qeth_reboot_event_card);
- return ret ? NOTIFY_BAD : NOTIFY_DONE;
-}
-
-
-static struct notifier_block qeth_reboot_notifier = {
- qeth_reboot_event,
- NULL,
-};
-
-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_bh(&arp_tbl.lock);
- arp_tbl.constructor = qeth_arp_constructor;
- write_unlock_bh(&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_bh(&arp_tbl.lock);
- arp_tbl.constructor = qeth_old_arp_constructor;
- write_unlock_bh(&arp_tbl.lock);
- kfree(arp_direct_ops);
-}
-#endif /* CONFIG_QETH_IPV6 */
-
-static void
-qeth_sysfs_unregister(void)
-{
- s390_root_dev_unregister(qeth_root_dev);
- qeth_remove_driver_attributes();
- ccw_driver_unregister(&qeth_ccw_driver);
- ccwgroup_driver_unregister(&qeth_ccwgroup_driver);
-}
-
-/**
- * register qeth at sysfs
- */
-static int
-qeth_sysfs_register(void)
-{
- int rc;
-
- rc = ccwgroup_driver_register(&qeth_ccwgroup_driver);
- if (rc)
- goto out;
-
- rc = ccw_driver_register(&qeth_ccw_driver);
- if (rc)
- goto out_ccw_driver;
-
- rc = qeth_create_driver_attributes();
- if (rc)
- goto out_qeth_attr;
-
- qeth_root_dev = s390_root_dev_register("qeth");
- rc = IS_ERR(qeth_root_dev) ? PTR_ERR(qeth_root_dev) : 0;
- if (!rc)
- goto out;
-
- qeth_remove_driver_attributes();
-out_qeth_attr:
- ccw_driver_unregister(&qeth_ccw_driver);
-out_ccw_driver:
- ccwgroup_driver_unregister(&qeth_ccwgroup_driver);
-out:
- return rc;
-}
-
-/***
- * init function
- */
-static int __init
-qeth_init(void)
-{
- int rc;
-
- PRINT_INFO("loading %s\n", version);
-
- INIT_LIST_HEAD(&qeth_card_list.list);
- INIT_LIST_HEAD(&qeth_notify_list);
- spin_lock_init(&qeth_notify_lock);
- rwlock_init(&qeth_card_list.rwlock);
-
- rc = qeth_register_dbf_views();
- if (rc)
- goto out_err;
-
- rc = qeth_sysfs_register();
- if (rc)
- goto out_dbf;
-
-#ifdef CONFIG_QETH_IPV6
- rc = qeth_ipv6_init();
- if (rc) {
- PRINT_ERR("Out of memory during ipv6 init code = %d\n", rc);
- goto out_sysfs;
- }
-#endif /* QETH_IPV6 */
- rc = qeth_register_notifiers();
- if (rc)
- goto out_ipv6;
- rc = qeth_create_procfs_entries();
- if (rc)
- goto out_notifiers;
-
- return rc;
-
-out_notifiers:
- qeth_unregister_notifiers();
-out_ipv6:
-#ifdef CONFIG_QETH_IPV6
- qeth_ipv6_uninit();
-out_sysfs:
-#endif /* QETH_IPV6 */
- qeth_sysfs_unregister();
-out_dbf:
- qeth_unregister_dbf_views();
-out_err:
- PRINT_ERR("Initialization failed with code %d\n", rc);
- 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_osn_register);
-EXPORT_SYMBOL(qeth_osn_deregister);
-EXPORT_SYMBOL(qeth_osn_assist);
-module_init(qeth_init);
-module_exit(qeth_exit);
-MODULE_AUTHOR("Frank Pavlic <fpavlic@de.ibm.com>");
-MODULE_DESCRIPTION("Linux on zSeries OSA Express and HiperSockets support\n" \
- "Copyright 2000,2003 IBM Corporation\n");
-
-MODULE_LICENSE("GPL");
diff --git a/drivers/s390/net/qeth_mpc.c b/drivers/s390/net/qeth_mpc.c
deleted file mode 100644
index f29a4bc4f6f..00000000000
--- a/drivers/s390/net/qeth_mpc.c
+++ /dev/null
@@ -1,269 +0,0 @@
-/*
- * linux/drivers/s390/net/qeth_mpc.c
- *
- * Linux on zSeries OSA Express and HiperSockets support
- *
- * Copyright 2000,2003 IBM Corporation
- * Author(s): Frank Pavlic <fpavlic@de.ibm.com>
- * Thomas Spatzier <tspat@de.ibm.com>
- *
- */
-#include <asm/cio.h>
-#include "qeth_mpc.h"
-
-unsigned char IDX_ACTIVATE_READ[]={
- 0x00,0x00,0x80,0x00, 0x00,0x00,0x00,0x00,
- 0x19,0x01,0x01,0x80, 0x00,0x00,0x00,0x00,
- 0x00,0x00,0x00,0x00, 0x00,0x00,0xc8,0xc1,
- 0xd3,0xd3,0xd6,0xd3, 0xc5,0x40,0x00,0x00,
- 0x00,0x00
-};
-
-unsigned char IDX_ACTIVATE_WRITE[]={
- 0x00,0x00,0x80,0x00, 0x00,0x00,0x00,0x00,
- 0x15,0x01,0x01,0x80, 0x00,0x00,0x00,0x00,
- 0xff,0xff,0x00,0x00, 0x00,0x00,0xc8,0xc1,
- 0xd3,0xd3,0xd6,0xd3, 0xc5,0x40,0x00,0x00,
- 0x00,0x00
-};
-
-unsigned char CM_ENABLE[]={
- 0x00,0xe0,0x00,0x00, 0x00,0x00,0x00,0x01,
- 0x00,0x00,0x00,0x14, 0x00,0x00,0x00,0x63,
- 0x10,0x00,0x00,0x01,
- 0x00,0x00,0x00,0x00,
- 0x81,0x7e,0x00,0x01, 0x00,0x00,0x00,0x00,
- 0x00,0x00,0x00,0x00, 0x00,0x24,0x00,0x23,
- 0x00,0x00,0x23,0x05, 0x00,0x00,0x00,0x00,
- 0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,
- 0x01,0x00,0x00,0x23, 0x00,0x00,0x00,0x40,
- 0x00,0x0c,0x41,0x02, 0x00,0x17,0x00,0x00,
- 0x00,0x00,0x00,0x00,
- 0x00,0x0b,0x04,0x01,
- 0x7e,0x04,0x05,0x00, 0x01,0x01,0x0f,
- 0x00,
- 0x0c,0x04,0x02,0xff, 0xff,0xff,0xff,0xff,
- 0xff,0xff,0xff
-};
-
-unsigned char CM_SETUP[]={
- 0x00,0xe0,0x00,0x00, 0x00,0x00,0x00,0x02,
- 0x00,0x00,0x00,0x14, 0x00,0x00,0x00,0x64,
- 0x10,0x00,0x00,0x01,
- 0x00,0x00,0x00,0x00,
- 0x81,0x7e,0x00,0x01, 0x00,0x00,0x00,0x00,
- 0x00,0x00,0x00,0x00, 0x00,0x24,0x00,0x24,
- 0x00,0x00,0x24,0x05, 0x00,0x00,0x00,0x00,
- 0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,
- 0x01,0x00,0x00,0x24, 0x00,0x00,0x00,0x40,
- 0x00,0x0c,0x41,0x04, 0x00,0x18,0x00,0x00,
- 0x00,0x00,0x00,0x00,
- 0x00,0x09,0x04,0x04,
- 0x05,0x00,0x01,0x01, 0x11,
- 0x00,0x09,0x04,
- 0x05,0x05,0x00,0x00, 0x00,0x00,
- 0x00,0x06,
- 0x04,0x06,0xc8,0x00
-};
-
-unsigned char ULP_ENABLE[]={
- 0x00,0xe0,0x00,0x00, 0x00,0x00,0x00,0x03,
- 0x00,0x00,0x00,0x14, 0x00,0x00,0x00,0x6b,
- 0x10,0x00,0x00,0x01,
- 0x00,0x00,0x00,0x00,
- 0x41,0x7e,0x00,0x01, 0x00,0x00,0x00,0x01,
- 0x00,0x00,0x00,0x00, 0x00,0x24,0x00,0x2b,
- 0x00,0x00,0x2b,0x05, 0x20,0x01,0x00,0x00,
- 0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,
- 0x01,0x00,0x00,0x2b, 0x00,0x00,0x00,0x40,
- 0x00,0x0c,0x41,0x02, 0x00,0x1f,0x00,0x00,
- 0x00,0x00,0x00,0x00,
- 0x00,0x0b,0x04,0x01,
- 0x03,0x04,0x05,0x00, 0x01,0x01,0x12,
- 0x00,
- 0x14,0x04,0x0a,0x00, 0x20,0x00,0x00,0xff,
- 0xff,0x00,0x08,0xc8, 0xe8,0xc4,0xf1,0xc7,
- 0xf1,0x00,0x00
-};
-
-unsigned char ULP_SETUP[]={
- 0x00,0xe0,0x00,0x00, 0x00,0x00,0x00,0x04,
- 0x00,0x00,0x00,0x14, 0x00,0x00,0x00,0x6c,
- 0x10,0x00,0x00,0x01,
- 0x00,0x00,0x00,0x00,
- 0x41,0x7e,0x00,0x01, 0x00,0x00,0x00,0x02,
- 0x00,0x00,0x00,0x01, 0x00,0x24,0x00,0x2c,
- 0x00,0x00,0x2c,0x05, 0x20,0x01,0x00,0x00,
- 0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,
- 0x01,0x00,0x00,0x2c, 0x00,0x00,0x00,0x40,
- 0x00,0x0c,0x41,0x04, 0x00,0x20,0x00,0x00,
- 0x00,0x00,0x00,0x00,
- 0x00,0x09,0x04,0x04,
- 0x05,0x00,0x01,0x01, 0x14,
- 0x00,0x09,0x04,
- 0x05,0x05,0x30,0x01, 0x00,0x00,
- 0x00,0x06,
- 0x04,0x06,0x40,0x00,
- 0x00,0x08,0x04,0x0b,
- 0x00,0x00,0x00,0x00
-};
-
-unsigned char DM_ACT[]={
- 0x00,0xe0,0x00,0x00, 0x00,0x00,0x00,0x05,
- 0x00,0x00,0x00,0x14, 0x00,0x00,0x00,0x55,
- 0x10,0x00,0x00,0x01,
- 0x00,0x00,0x00,0x00,
- 0x41,0x7e,0x00,0x01, 0x00,0x00,0x00,0x03,
- 0x00,0x00,0x00,0x02, 0x00,0x24,0x00,0x15,
- 0x00,0x00,0x2c,0x05, 0x20,0x01,0x00,0x00,
- 0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,
- 0x01,0x00,0x00,0x15, 0x00,0x00,0x00,0x40,
- 0x00,0x0c,0x43,0x60, 0x00,0x09,0x00,0x00,
- 0x00,0x00,0x00,0x00,
- 0x00,0x09,0x04,0x04,
- 0x05,0x40,0x01,0x01, 0x00
-};
-
-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 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,
- sizeof(struct qeth_ipa_cmd)/256,
- sizeof(struct qeth_ipa_cmd)%256,
- 0x00,
- sizeof(struct qeth_ipa_cmd)/256,
- sizeof(struct qeth_ipa_cmd)%256,
- 0x05,
- 0x77,0x77,0x77,0x77,
- 0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,
- 0x01,0x00,
- sizeof(struct qeth_ipa_cmd)/256,
- sizeof(struct qeth_ipa_cmd)%256,
- 0x00,0x00,0x00,0x40,
-};
-
-unsigned char WRITE_CCW[]={
- 0x01,CCW_FLAG_SLI,0,0,
- 0,0,0,0
-};
-
-unsigned char READ_CCW[]={
- 0x02,CCW_FLAG_SLI,0,0,
- 0,0,0,0
-};
-
-
-struct ipa_rc_msg {
- enum qeth_ipa_return_codes rc;
- char *msg;
-};
-
-static struct ipa_rc_msg qeth_ipa_rc_msg[] = {
- {IPA_RC_SUCCESS, "success"},
- {IPA_RC_NOTSUPP, "Command not supported"},
- {IPA_RC_IP_TABLE_FULL, "Add Addr IP Table Full - ipv6"},
- {IPA_RC_UNKNOWN_ERROR, "IPA command failed - reason unknown"},
- {IPA_RC_UNSUPPORTED_COMMAND, "Command not supported"},
- {IPA_RC_DUP_IPV6_REMOTE,"ipv6 address already registered remote"},
- {IPA_RC_DUP_IPV6_HOME, "ipv6 address already registered"},
- {IPA_RC_UNREGISTERED_ADDR, "Address not registered"},
- {IPA_RC_NO_ID_AVAILABLE, "No identifiers available"},
- {IPA_RC_ID_NOT_FOUND, "Identifier not found"},
- {IPA_RC_INVALID_IP_VERSION, "IP version incorrect"},
- {IPA_RC_LAN_FRAME_MISMATCH, "LAN and frame mismatch"},
- {IPA_RC_L2_UNSUPPORTED_CMD, "Unsupported layer 2 command"},
- {IPA_RC_L2_DUP_MAC, "Duplicate MAC address"},
- {IPA_RC_L2_ADDR_TABLE_FULL, "Layer2 address table full"},
- {IPA_RC_L2_DUP_LAYER3_MAC, "Duplicate with layer 3 MAC"},
- {IPA_RC_L2_GMAC_NOT_FOUND, "GMAC not found"},
- {IPA_RC_L2_MAC_NOT_FOUND, "L2 mac address not found"},
- {IPA_RC_L2_INVALID_VLAN_ID, "L2 invalid vlan id"},
- {IPA_RC_L2_DUP_VLAN_ID, "L2 duplicate vlan id"},
- {IPA_RC_L2_VLAN_ID_NOT_FOUND, "L2 vlan id not found"},
- {IPA_RC_DATA_MISMATCH, "Data field mismatch (v4/v6 mixed)"},
- {IPA_RC_INVALID_MTU_SIZE, "Invalid MTU size"},
- {IPA_RC_INVALID_LANTYPE, "Invalid LAN type"},
- {IPA_RC_INVALID_LANNUM, "Invalid LAN num"},
- {IPA_RC_DUPLICATE_IP_ADDRESS, "Address already registered"},
- {IPA_RC_IP_ADDR_TABLE_FULL, "IP address table full"},
- {IPA_RC_LAN_PORT_STATE_ERROR, "LAN port state error"},
- {IPA_RC_SETIP_NO_STARTLAN, "Setip no startlan received"},
- {IPA_RC_SETIP_ALREADY_RECEIVED, "Setip already received"},
- {IPA_RC_IP_ADDR_ALREADY_USED, "IP address already in use on LAN"},
- {IPA_RC_MULTICAST_FULL, "No task available, multicast full"},
- {IPA_RC_SETIP_INVALID_VERSION, "SETIP invalid IP version"},
- {IPA_RC_UNSUPPORTED_SUBCMD, "Unsupported assist subcommand"},
- {IPA_RC_ARP_ASSIST_NO_ENABLE, "Only partial success, no enable"},
- {IPA_RC_PRIMARY_ALREADY_DEFINED,"Primary already defined"},
- {IPA_RC_SECOND_ALREADY_DEFINED, "Secondary already defined"},
- {IPA_RC_INVALID_SETRTG_INDICATOR,"Invalid SETRTG indicator"},
- {IPA_RC_MC_ADDR_ALREADY_DEFINED,"Multicast address already defined"},
- {IPA_RC_LAN_OFFLINE, "STRTLAN_LAN_DISABLED - LAN offline"},
- {IPA_RC_INVALID_IP_VERSION2, "Invalid IP version"},
- {IPA_RC_FFFF, "Unknown Error"}
-};
-
-
-
-char *
-qeth_get_ipa_msg(enum qeth_ipa_return_codes rc)
-{
- int x = 0;
- qeth_ipa_rc_msg[sizeof(qeth_ipa_rc_msg) /
- sizeof(struct ipa_rc_msg) - 1].rc = rc;
- while(qeth_ipa_rc_msg[x].rc != rc)
- x++;
- return qeth_ipa_rc_msg[x].msg;
-}
-
-
-struct ipa_cmd_names {
- enum qeth_ipa_cmds cmd;
- char *name;
-};
-
-static struct ipa_cmd_names qeth_ipa_cmd_names[] = {
- {IPA_CMD_STARTLAN, "startlan"},
- {IPA_CMD_STOPLAN, "stoplan"},
- {IPA_CMD_SETVMAC, "setvmac"},
- {IPA_CMD_DELVMAC, "delvmca"},
- {IPA_CMD_SETGMAC, "setgmac"},
- {IPA_CMD_DELGMAC, "delgmac"},
- {IPA_CMD_SETVLAN, "setvlan"},
- {IPA_CMD_DELVLAN, "delvlan"},
- {IPA_CMD_SETCCID, "setccid"},
- {IPA_CMD_DELCCID, "delccid"},
- {IPA_CMD_MODCCID, "setip"},
- {IPA_CMD_SETIP, "setip"},
- {IPA_CMD_QIPASSIST, "qipassist"},
- {IPA_CMD_SETASSPARMS, "setassparms"},
- {IPA_CMD_SETIPM, "setipm"},
- {IPA_CMD_DELIPM, "delipm"},
- {IPA_CMD_SETRTG, "setrtg"},
- {IPA_CMD_DELIP, "delip"},
- {IPA_CMD_SETADAPTERPARMS, "setadapterparms"},
- {IPA_CMD_SET_DIAG_ASS, "set_diag_ass"},
- {IPA_CMD_CREATE_ADDR, "create_addr"},
- {IPA_CMD_DESTROY_ADDR, "destroy_addr"},
- {IPA_CMD_REGISTER_LOCAL_ADDR, "register_local_addr"},
- {IPA_CMD_UNREGISTER_LOCAL_ADDR, "unregister_local_addr"},
- {IPA_CMD_UNKNOWN, "unknown"},
-};
-
-char *
-qeth_get_ipa_cmd_name(enum qeth_ipa_cmds cmd)
-{
- int x = 0;
- qeth_ipa_cmd_names[
- sizeof(qeth_ipa_cmd_names)/
- sizeof(struct ipa_cmd_names)-1].cmd = cmd;
- while(qeth_ipa_cmd_names[x].cmd != cmd)
- x++;
- return qeth_ipa_cmd_names[x].name;
-}
-
-
diff --git a/drivers/s390/net/qeth_proc.c b/drivers/s390/net/qeth_proc.c
deleted file mode 100644
index 46ecd03a597..00000000000
--- a/drivers/s390/net/qeth_proc.c
+++ /dev/null
@@ -1,316 +0,0 @@
-/*
- *
- * linux/drivers/s390/net/qeth_fs.c
- *
- * Linux on zSeries OSA Express and HiperSockets support
- * This file contains code related to procfs.
- *
- * Copyright 2000,2003 IBM Corporation
- *
- * Author(s): Thomas Spatzier <tspat@de.ibm.com>
- *
- */
-#include <linux/module.h>
-#include <linux/init.h>
-#include <linux/proc_fs.h>
-#include <linux/seq_file.h>
-#include <linux/list.h>
-#include <linux/rwsem.h>
-
-#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 int
-qeth_procfile_seq_match(struct device *dev, void *data)
-{
- return(dev ? 1 : 0);
-}
-
-static void *
-qeth_procfile_seq_start(struct seq_file *s, loff_t *offset)
-{
- struct device *dev = NULL;
- loff_t nr = 0;
-
- if (*offset == 0)
- return SEQ_START_TOKEN;
- while (1) {
- dev = driver_find_device(&qeth_ccwgroup_driver.driver, dev,
- NULL, qeth_procfile_seq_match);
- if (++nr == *offset)
- break;
- put_device(dev);
- }
- return dev;
-}
-
-static void
-qeth_procfile_seq_stop(struct seq_file *s, void* it)
-{
-}
-
-static void *
-qeth_procfile_seq_next(struct seq_file *s, void *it, loff_t *offset)
-{
- struct device *prev, *next;
-
- if (it == SEQ_START_TOKEN)
- prev = NULL;
- else
- prev = (struct device *) it;
- next = driver_find_device(&qeth_ccwgroup_driver.driver,
- prev, NULL, qeth_procfile_seq_match);
- (*offset)++;
- return (void *) next;
-}
-
-static inline const char *
-qeth_get_router_str(struct qeth_card *card, int ipv)
-{
- enum qeth_routing_types routing_type = NO_ROUTER;
-
- 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 */
- }
-
- switch (routing_type){
- case PRIMARY_ROUTER:
- return "pri";
- case SECONDARY_ROUTER:
- return "sec";
- case MULTICAST_ROUTER:
- if (card->info.broadcast_capable == QETH_BROADCAST_WITHOUT_ECHO)
- return "mc+";
- return "mc";
- case PRIMARY_CONNECTOR:
- if (card->info.broadcast_capable == QETH_BROADCAST_WITHOUT_ECHO)
- return "p+c";
- return "p.c";
- case SECONDARY_CONNECTOR:
- if (card->info.broadcast_capable == QETH_BROADCAST_WITHOUT_ECHO)
- return "s+c";
- return "s.c";
- default: /* NO_ROUTER */
- return "no";
- }
-}
-
-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 = (struct device *) it;
- 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,
- QETH_CARD_IFNAME(card),
- 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");
- put_device(device);
- }
- return 0;
-}
-
-static const 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 const 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;
-
-static int
-qeth_perf_procfile_seq_show(struct seq_file *s, void *it)
-{
- struct device *device;
- struct qeth_card *card;
-
-
- if (it == SEQ_START_TOKEN)
- return 0;
-
- device = (struct device *) it;
- 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),
- QETH_CARD_IFNAME(card)
- );
- if (!card->options.performance_stats)
- seq_printf(s, "Performance statistics are deactivated.\n");
- seq_printf(s, " Skb's/buffers received : %lu/%u\n"
- " Skb's/buffers sent : %lu/%u\n\n",
- card->stats.rx_packets -
- card->perf_stats.initial_rx_packets,
- card->perf_stats.bufs_rec,
- card->stats.tx_packets -
- card->perf_stats.initial_tx_packets,
- card->perf_stats.bufs_sent
- );
- seq_printf(s, " Skb's/buffers sent without packing : %lu/%u\n"
- " Skb's/buffers sent with packing : %u/%u\n\n",
- card->stats.tx_packets - card->perf_stats.initial_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, " Skbs sent in SG mode : %u\n"
- " Skb fragments sent in SG mode : %u\n\n",
- card->perf_stats.sg_skbs_sent,
- card->perf_stats.sg_frags_sent);
- seq_printf(s, " Skbs received in SG mode : %u\n"
- " Skb fragments received in SG mode : %u\n"
- " Page allocations for rx SG mode : %u\n\n",
- card->perf_stats.sg_skbs_rx,
- card->perf_stats.sg_frags_rx,
- card->perf_stats.sg_alloc_page_rx);
- seq_printf(s, " large_send tx (in Kbytes) : %u\n"
- " large_send count : %u\n\n",
- card->perf_stats.large_send_bytes >> 10,
- card->perf_stats.large_send_cnt);
- seq_printf(s, " Packing state changes no pkg.->packing : %u/%u\n"
- " Watermarks L/H : %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,
- QETH_LOW_WATERMARK_PACK, QETH_HIGH_WATERMARK_PACK,
- 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 handler time (in us) : %u\n"
- " Inbound handler count : %u\n"
- " Inbound do_QDIO time (in us) : %u\n"
- " Inbound do_QDIO count : %u\n\n"
- " Outbound handler time (in us) : %u\n"
- " Outbound handler count : %u\n\n"
- " Outbound time (in us, incl QDIO) : %u\n"
- " Outbound count : %u\n"
- " Outbound do_QDIO time (in us) : %u\n"
- " Outbound do_QDIO count : %u\n\n",
- card->perf_stats.inbound_time,
- card->perf_stats.inbound_cnt,
- card->perf_stats.inbound_do_qdio_time,
- card->perf_stats.inbound_do_qdio_cnt,
- card->perf_stats.outbound_handler_time,
- card->perf_stats.outbound_handler_cnt,
- card->perf_stats.outbound_time,
- card->perf_stats.outbound_cnt,
- card->perf_stats.outbound_do_qdio_time,
- card->perf_stats.outbound_do_qdio_cnt
- );
- put_device(device);
- return 0;
-}
-
-static const struct seq_operations qeth_perf_procfile_seq_ops = {
- .start = qeth_procfile_seq_start,
- .stop = qeth_procfile_seq_stop,
- .next = qeth_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 const struct file_operations qeth_perf_procfile_fops = {
- .owner = THIS_MODULE,
- .open = qeth_perf_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;
-
- 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;
-
- if (qeth_procfile &&
- qeth_perf_procfile)
- 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);
-}
-
diff --git a/drivers/s390/net/qeth_sys.c b/drivers/s390/net/qeth_sys.c
deleted file mode 100644
index 2cc3f3a0e39..00000000000
--- a/drivers/s390/net/qeth_sys.c
+++ /dev/null
@@ -1,1858 +0,0 @@
-/*
- *
- * linux/drivers/s390/net/qeth_sys.c
- *
- * Linux on zSeries OSA Express and HiperSockets support
- * This file contains code related to sysfs.
- *
- * Copyright 2000,2003 IBM Corporation
- *
- * Author(s): Thomas Spatzier <tspat@de.ibm.com>
- * Frank Pavlic <fpavlic@de.ibm.com>
- *
- */
-#include <linux/list.h>
-#include <linux/rwsem.h>
-
-#include <asm/ebcdic.h>
-
-#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, struct device_attribute *attr, 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:
- if (card->lan_online)
- return sprintf(buf, "UP (LAN ONLINE)\n");
- else
- return sprintf(buf, "UP (LAN OFFLINE)\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, struct device_attribute *attr, 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, struct device_attribute *attr, char *buf)
-{
- struct qeth_card *card = dev->driver_data;
- if (!card)
- return -EINVAL;
- return sprintf(buf, "%s\n", QETH_CARD_IFNAME(card));
-}
-
-static DEVICE_ATTR(if_name, 0444, qeth_dev_if_name_show, NULL);
-
-static ssize_t
-qeth_dev_card_type_show(struct device *dev, struct device_attribute *attr, 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, struct device_attribute *attr, 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, struct device_attribute *attr, 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 > 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, struct device_attribute *attr, 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, struct device_attribute *attr, 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) == 0))
- 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, struct device_attribute *attr, 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, struct device_attribute *attr, 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, struct device_attribute *attr, 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, struct device_attribute *attr, 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;
-
- /* check if 1920 devices are supported ,
- * if though we have to permit priority queueing
- */
- if (card->qdio.no_out_queues == 1) {
- PRINT_WARN("Priority queueing disabled due "
- "to hardware limitations!\n");
- card->qdio.do_prio_queueing = QETH_PRIOQ_DEFAULT;
- 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, struct device_attribute *attr, 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, struct device_attribute *attr, const char *buf, size_t count)
-{
- struct qeth_card *card = dev->driver_data;
- char *tmp;
- int cnt, old_cnt;
- int rc;
-
- if (!card)
- return -EINVAL;
-
- if ((card->state != CARD_STATE_DOWN) &&
- (card->state != CARD_STATE_RECOVER))
- return -EPERM;
-
- old_cnt = card->qdio.in_buf_pool.buf_count;
- cnt = simple_strtoul(buf, &tmp, 10);
- cnt = (cnt < QETH_IN_BUF_COUNT_MIN) ? QETH_IN_BUF_COUNT_MIN :
- ((cnt > QETH_IN_BUF_COUNT_MAX) ? QETH_IN_BUF_COUNT_MAX : cnt);
- if (old_cnt != cnt) {
- if ((rc = qeth_realloc_buffer_pool(card, cnt)))
- PRINT_WARN("Error (%d) while setting "
- "buffer count.\n", rc);
- }
- return count;
-}
-
-static DEVICE_ATTR(buffer_count, 0644, qeth_dev_bufcnt_show,
- qeth_dev_bufcnt_store);
-
-static ssize_t
-qeth_dev_route_show(struct qeth_card *card, 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:
- if (card->info.broadcast_capable == QETH_BROADCAST_WITHOUT_ECHO)
- return sprintf(buf, "%s\n", "multicast router+");
- else
- return sprintf(buf, "%s\n", "multicast router");
- case PRIMARY_CONNECTOR:
- if (card->info.broadcast_capable == QETH_BROADCAST_WITHOUT_ECHO)
- return sprintf(buf, "%s\n", "primary connector+");
- else
- return sprintf(buf, "%s\n", "primary connector");
- case SECONDARY_CONNECTOR:
- if (card->info.broadcast_capable == QETH_BROADCAST_WITHOUT_ECHO)
- return sprintf(buf, "%s\n", "secondary connector+");
- else
- return sprintf(buf, "%s\n", "secondary connector");
- default:
- return sprintf(buf, "%s\n", "no");
- }
-}
-
-static ssize_t
-qeth_dev_route4_show(struct device *dev, struct device_attribute *attr, char *buf)
-{
- struct qeth_card *card = dev->driver_data;
-
- if (!card)
- return -EINVAL;
-
- return qeth_dev_route_show(card, &card->options.route4, buf);
-}
-
-static 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;
- } else if (!strcmp(tmp, "primary_connector")) {
- route->type = PRIMARY_CONNECTOR;
- } else if (!strcmp(tmp, "secondary_connector")) {
- route->type = SECONDARY_CONNECTOR;
- } 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")) {
- route->type = MULTICAST_ROUTER;
- } else {
- PRINT_WARN("Invalid routing type '%s'.\n", tmp);
- return -EINVAL;
- }
- if (((card->state == CARD_STATE_SOFTSETUP) ||
- (card->state == CARD_STATE_UP)) &&
- (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;
-}
-
-static ssize_t
-qeth_dev_route4_store(struct device *dev, struct device_attribute *attr, 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, struct device_attribute *attr, 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, &card->options.route6, buf);
-}
-
-static ssize_t
-qeth_dev_route6_store(struct device *dev, struct device_attribute *attr, 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",
- QETH_CARD_IFNAME(card));
- 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, struct device_attribute *attr, 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, struct device_attribute *attr, 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, 10);
- 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, struct device_attribute *attr, 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, struct device_attribute *attr, 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)) {
- PRINT_WARN("fake_ll: write 0 or 1 to this file!\n");
- return -EINVAL;
- }
- card->options.fake_ll = i;
- 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, struct device_attribute *attr, 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, struct device_attribute *attr, 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, struct device_attribute *attr, 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)
- 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);
-
-static ssize_t
-qeth_dev_broadcast_mode_show(struct device *dev, struct device_attribute *attr, 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");
-}
-
-static ssize_t
-qeth_dev_broadcast_mode_store(struct device *dev, struct device_attribute *attr, 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);
-
-static ssize_t
-qeth_dev_canonical_macaddr_show(struct device *dev, struct device_attribute *attr, 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);
-}
-
-static ssize_t
-qeth_dev_canonical_macaddr_store(struct device *dev, struct device_attribute *attr, 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 ssize_t
-qeth_dev_layer2_show(struct device *dev, struct device_attribute *attr, char *buf)
-{
- struct qeth_card *card = dev->driver_data;
-
- if (!card)
- return -EINVAL;
-
- return sprintf(buf, "%i\n", card->options.layer2 ? 1:0);
-}
-
-static ssize_t
-qeth_dev_layer2_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
-{
- struct qeth_card *card = dev->driver_data;
- char *tmp;
- int i;
-
- if (!card)
- return -EINVAL;
- if (card->info.type == QETH_CARD_TYPE_IQD) {
- PRINT_WARN("Layer2 on Hipersockets is not supported! \n");
- return -EPERM;
- }
-
- 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.layer2 = i;
- else {
- PRINT_WARN("layer2: write 0 or 1 to this file!\n");
- return -EINVAL;
- }
- return count;
-}
-
-static DEVICE_ATTR(layer2, 0644, qeth_dev_layer2_show,
- qeth_dev_layer2_store);
-
-static ssize_t
-qeth_dev_performance_stats_show(struct device *dev, struct device_attribute *attr, char *buf)
-{
- struct qeth_card *card = dev->driver_data;
-
- if (!card)
- return -EINVAL;
-
- return sprintf(buf, "%i\n", card->options.performance_stats ? 1:0);
-}
-
-static ssize_t
-qeth_dev_performance_stats_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
-{
- struct qeth_card *card = dev->driver_data;
- char *tmp;
- int i;
-
- if (!card)
- return -EINVAL;
-
- i = simple_strtoul(buf, &tmp, 16);
- if ((i == 0) || (i == 1)) {
- if (i == card->options.performance_stats)
- return count;
- card->options.performance_stats = i;
- if (i == 0)
- memset(&card->perf_stats, 0,
- sizeof(struct qeth_perf_stats));
- card->perf_stats.initial_rx_packets = card->stats.rx_packets;
- card->perf_stats.initial_tx_packets = card->stats.tx_packets;
- } else {
- PRINT_WARN("performance_stats: write 0 or 1 to this file!\n");
- return -EINVAL;
- }
- return count;
-}
-
-static DEVICE_ATTR(performance_stats, 0644, qeth_dev_performance_stats_show,
- qeth_dev_performance_stats_store);
-
-static ssize_t
-qeth_dev_large_send_show(struct device *dev, struct device_attribute *attr, char *buf)
-{
- struct qeth_card *card = dev->driver_data;
-
- if (!card)
- return -EINVAL;
-
- switch (card->options.large_send) {
- case QETH_LARGE_SEND_NO:
- return sprintf(buf, "%s\n", "no");
- case QETH_LARGE_SEND_EDDP:
- return sprintf(buf, "%s\n", "EDDP");
- case QETH_LARGE_SEND_TSO:
- return sprintf(buf, "%s\n", "TSO");
- default:
- return sprintf(buf, "%s\n", "N/A");
- }
-}
-
-static ssize_t
-qeth_dev_large_send_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
-{
- struct qeth_card *card = dev->driver_data;
- enum qeth_large_send_types type;
- int rc = 0;
- char *tmp;
-
- if (!card)
- return -EINVAL;
- tmp = strsep((char **) &buf, "\n");
- if (!strcmp(tmp, "no")){
- type = QETH_LARGE_SEND_NO;
- } else if (!strcmp(tmp, "EDDP")) {
- type = QETH_LARGE_SEND_EDDP;
- } else if (!strcmp(tmp, "TSO")) {
- type = QETH_LARGE_SEND_TSO;
- } else {
- PRINT_WARN("large_send: invalid mode %s!\n", tmp);
- return -EINVAL;
- }
- if (card->options.large_send == type)
- return count;
- if ((rc = qeth_set_large_send(card, type)))
- return rc;
- return count;
-}
-
-static DEVICE_ATTR(large_send, 0644, qeth_dev_large_send_show,
- qeth_dev_large_send_store);
-
-static ssize_t
-qeth_dev_blkt_show(char *buf, struct qeth_card *card, int value )
-{
-
- if (!card)
- return -EINVAL;
-
- return sprintf(buf, "%i\n", value);
-}
-
-static ssize_t
-qeth_dev_blkt_store(struct qeth_card *card, const char *buf, size_t count,
- int *value, int max_value)
-{
- 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, 10);
- if (i <= max_value) {
- *value = i;
- } else {
- PRINT_WARN("blkt total time: write values between"
- " 0 and %d to this file!\n", max_value);
- return -EINVAL;
- }
- return count;
-}
-
-static ssize_t
-qeth_dev_blkt_total_show(struct device *dev, struct device_attribute *attr, char *buf)
-{
- struct qeth_card *card = dev->driver_data;
-
- return qeth_dev_blkt_show(buf, card, card->info.blkt.time_total);
-}
-
-
-static ssize_t
-qeth_dev_blkt_total_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
-{
- struct qeth_card *card = dev->driver_data;
-
- return qeth_dev_blkt_store(card, buf, count,
- &card->info.blkt.time_total,1000);
-}
-
-
-
-static DEVICE_ATTR(total, 0644, qeth_dev_blkt_total_show,
- qeth_dev_blkt_total_store);
-
-static ssize_t
-qeth_dev_blkt_inter_show(struct device *dev, struct device_attribute *attr, char *buf)
-{
- struct qeth_card *card = dev->driver_data;
-
- return qeth_dev_blkt_show(buf, card, card->info.blkt.inter_packet);
-}
-
-
-static ssize_t
-qeth_dev_blkt_inter_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
-{
- struct qeth_card *card = dev->driver_data;
-
- return qeth_dev_blkt_store(card, buf, count,
- &card->info.blkt.inter_packet,100);
-}
-
-static DEVICE_ATTR(inter, 0644, qeth_dev_blkt_inter_show,
- qeth_dev_blkt_inter_store);
-
-static ssize_t
-qeth_dev_blkt_inter_jumbo_show(struct device *dev, struct device_attribute *attr, char *buf)
-{
- struct qeth_card *card = dev->driver_data;
-
- return qeth_dev_blkt_show(buf, card,
- card->info.blkt.inter_packet_jumbo);
-}
-
-
-static ssize_t
-qeth_dev_blkt_inter_jumbo_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
-{
- struct qeth_card *card = dev->driver_data;
-
- return qeth_dev_blkt_store(card, buf, count,
- &card->info.blkt.inter_packet_jumbo,100);
-}
-
-static DEVICE_ATTR(inter_jumbo, 0644, qeth_dev_blkt_inter_jumbo_show,
- qeth_dev_blkt_inter_jumbo_store);
-
-static struct device_attribute * qeth_blkt_device_attrs[] = {
- &dev_attr_total,
- &dev_attr_inter,
- &dev_attr_inter_jumbo,
- NULL,
-};
-
-static struct attribute_group qeth_device_blkt_group = {
- .name = "blkt",
- .attrs = (struct attribute **)qeth_blkt_device_attrs,
-};
-
-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,
- &dev_attr_layer2,
- &dev_attr_large_send,
- &dev_attr_performance_stats,
- NULL,
-};
-
-static struct attribute_group qeth_device_attr_group = {
- .attrs = (struct attribute **)qeth_device_attrs,
-};
-
-static struct device_attribute * qeth_osn_device_attrs[] = {
- &dev_attr_state,
- &dev_attr_chpid,
- &dev_attr_if_name,
- &dev_attr_card_type,
- &dev_attr_buffer_count,
- &dev_attr_recover,
- NULL,
-};
-
-static struct attribute_group qeth_osn_device_attr_group = {
- .attrs = (struct attribute **)qeth_osn_device_attrs,
-};
-
-#define QETH_DEVICE_ATTR(_id,_name,_mode,_show,_store) \
-struct device_attribute dev_attr_##_id = { \
- .attr = {.name=__stringify(_name), .mode=_mode, },\
- .show = _show, \
- .store = _store, \
-};
-
-static int
-qeth_check_layer2(struct qeth_card *card)
-{
- if (card->options.layer2)
- return -EPERM;
- return 0;
-}
-
-
-static ssize_t
-qeth_dev_ipato_enable_show(struct device *dev, struct device_attribute *attr, char *buf)
-{
- struct qeth_card *card = dev->driver_data;
-
- if (!card)
- return -EINVAL;
-
- if (qeth_check_layer2(card))
- return -EPERM;
- return sprintf(buf, "%i\n", card->ipato.enabled? 1:0);
-}
-
-static ssize_t
-qeth_dev_ipato_enable_store(struct device *dev, struct device_attribute *attr, 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 (qeth_check_layer2(card))
- return -EPERM;
-
- 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, struct device_attribute *attr, char *buf)
-{
- struct qeth_card *card = dev->driver_data;
-
- if (!card)
- return -EINVAL;
-
- if (qeth_check_layer2(card))
- return -EPERM;
-
- return sprintf(buf, "%i\n", card->ipato.invert4? 1:0);
-}
-
-static ssize_t
-qeth_dev_ipato_invert4_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
-{
- struct qeth_card *card = dev->driver_data;
- char *tmp;
-
- if (!card)
- return -EINVAL;
-
- if (qeth_check_layer2(card))
- return -EPERM;
-
- 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 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[40];
- int entry_len; /* length of 1 entry string, differs between v4 and v6 */
- int i = 0;
-
- if (qeth_check_layer2(card))
- return -EPERM;
-
- entry_len = (proto == QETH_PROT_IPV4)? 12 : 40;
- /* add strlen for "/<mask>\n" */
- entry_len += (proto == QETH_PROT_IPV4)? 5 : 6;
- spin_lock_irqsave(&card->ip_lock, flags);
- list_for_each_entry(ipatoe, &card->ipato.entries, entry){
- if (ipatoe->proto != proto)
- continue;
- /* String must not be longer than PAGE_SIZE. So we check if
- * string length gets near PAGE_SIZE. Then we can savely display
- * the next IPv6 address (worst case, compared to IPv4) */
- if ((PAGE_SIZE - i) <= entry_len)
- break;
- qeth_ipaddr_to_string(proto, ipatoe->addr, addr_str);
- i += snprintf(buf + i, PAGE_SIZE - i,
- "%s/%i\n", addr_str, ipatoe->mask_bits);
- }
- spin_unlock_irqrestore(&card->ip_lock, flags);
- i += snprintf(buf + i, PAGE_SIZE - i, "\n");
-
- return i;
-}
-
-static ssize_t
-qeth_dev_ipato_add4_show(struct device *dev, struct device_attribute *attr, 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 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[40] = {0, };
-
- start = buf;
- /* get address string */
- end = strchr(start, '/');
- if (!end || (end - start >= 40)){
- PRINT_WARN("Invalid format for ipato_addx/delx. "
- "Use <ip addr>/<mask bits>\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);
- if (!strlen(start) ||
- (tmp == start) ||
- (*mask_bits > ((proto == QETH_PROT_IPV4) ? 32 : 128))) {
- PRINT_WARN("Invalid mask bits for ipato_addx/delx !\n");
- return -EINVAL;
- }
- return 0;
-}
-
-static 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 (qeth_check_layer2(card))
- return -EPERM;
- if ((rc = qeth_parse_ipatoe(buf, proto, addr, &mask_bits)))
- return rc;
-
- if (!(ipatoe = kzalloc(sizeof(struct qeth_ipato_entry), GFP_KERNEL))){
- PRINT_WARN("No memory to allocate ipato entry\n");
- return -ENOMEM;
- }
- 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, struct device_attribute *attr, 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 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 (qeth_check_layer2(card))
- return -EPERM;
- 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, struct device_attribute *attr, 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, struct device_attribute *attr, char *buf)
-{
- struct qeth_card *card = dev->driver_data;
-
- if (!card)
- return -EINVAL;
-
- if (qeth_check_layer2(card))
- return -EPERM;
-
- return sprintf(buf, "%i\n", card->ipato.invert6? 1:0);
-}
-
-static ssize_t
-qeth_dev_ipato_invert6_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
-{
- struct qeth_card *card = dev->driver_data;
- char *tmp;
-
- if (!card)
- return -EINVAL;
-
- if (qeth_check_layer2(card))
- return -EPERM;
-
- 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, struct device_attribute *attr, 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, struct device_attribute *attr, 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, struct device_attribute *attr, 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 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[40];
- int entry_len; /* length of 1 entry string, differs between v4 and v6 */
- unsigned long flags;
- int i = 0;
-
- if (qeth_check_layer2(card))
- return -EPERM;
-
- entry_len = (proto == QETH_PROT_IPV4)? 12 : 40;
- entry_len += 2; /* \n + terminator */
- 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;
- /* String must not be longer than PAGE_SIZE. So we check if
- * string length gets near PAGE_SIZE. Then we can savely display
- * the next IPv6 address (worst case, compared to IPv4) */
- if ((PAGE_SIZE - i) <= entry_len)
- break;
- qeth_ipaddr_to_string(proto, (const u8 *)&ipaddr->u, addr_str);
- i += snprintf(buf + i, PAGE_SIZE - i, "%s\n", addr_str);
- }
- spin_unlock_irqrestore(&card->ip_lock, flags);
- i += snprintf(buf + i, PAGE_SIZE - i, "\n");
-
- return i;
-}
-
-static ssize_t
-qeth_dev_vipa_add4_show(struct device *dev, struct device_attribute *attr, 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 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 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 (qeth_check_layer2(card))
- return -EPERM;
- 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, struct device_attribute *attr, 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 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 (qeth_check_layer2(card))
- return -EPERM;
- 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, struct device_attribute *attr, 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, struct device_attribute *attr, 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, struct device_attribute *attr, 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, struct device_attribute *attr, const char *buf, size_t count)
-{
- struct qeth_card *card = dev->driver_data;
-
- if (!card)
- return -EINVAL;
-
- if (qeth_check_layer2(card))
- return -EPERM;
-
- 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 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[40];
- int entry_len; /* length of 1 entry string, differs between v4 and v6 */
- unsigned long flags;
- int i = 0;
-
- if (qeth_check_layer2(card))
- return -EPERM;
-
- entry_len = (proto == QETH_PROT_IPV4)? 12 : 40;
- entry_len += 2; /* \n + terminator */
- 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;
- /* String must not be longer than PAGE_SIZE. So we check if
- * string length gets near PAGE_SIZE. Then we can savely display
- * the next IPv6 address (worst case, compared to IPv4) */
- if ((PAGE_SIZE - i) <= entry_len)
- break;
- qeth_ipaddr_to_string(proto, (const u8 *)&ipaddr->u, addr_str);
- i += snprintf(buf + i, PAGE_SIZE - i, "%s\n", addr_str);
- }
- spin_unlock_irqrestore(&card->ip_lock, flags);
- i += snprintf(buf + i, PAGE_SIZE - i, "\n");
-
- return i;
-}
-
-static ssize_t
-qeth_dev_rxip_add4_show(struct device *dev, struct device_attribute *attr, 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 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 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 (qeth_check_layer2(card))
- return -EPERM;
- 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, struct device_attribute *attr, 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 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 (qeth_check_layer2(card))
- return -EPERM;
- 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, struct device_attribute *attr, 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, struct device_attribute *attr, 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, struct device_attribute *attr, 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, struct device_attribute *attr, 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;
- struct qeth_card *card = dev->driver_data;
-
- if (card->info.type == QETH_CARD_TYPE_OSN)
- return sysfs_create_group(&dev->kobj,
- &qeth_osn_device_attr_group);
-
- 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;
- }
- if ((ret = sysfs_create_group(&dev->kobj, &qeth_device_blkt_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);
- sysfs_remove_group(&dev->kobj, &qeth_device_rxip_group);
- return ret;
- }
- return 0;
-}
-
-void
-qeth_remove_device_attributes(struct device *dev)
-{
- struct qeth_card *card = dev->driver_data;
-
- if (card->info.type == QETH_CARD_TYPE_OSN) {
- sysfs_remove_group(&dev->kobj, &qeth_osn_device_attr_group);
- return;
- }
- 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);
- sysfs_remove_group(&dev->kobj, &qeth_device_blkt_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, NULL, qeth_driver_group_store);
-
-static ssize_t
-qeth_driver_notifier_register_store(struct device_driver *ddrv, const char *buf,
- size_t count)
-{
- int rc;
- int signum;
- char *tmp, *tmp2;
-
- tmp = strsep((char **) &buf, "\n");
- if (!strncmp(tmp, "unregister", 10)){
- if ((rc = qeth_notifier_unregister(current)))
- return rc;
- return count;
- }
-
- signum = simple_strtoul(tmp, &tmp2, 10);
- if ((signum < 0) || (signum > 32)){
- PRINT_WARN("Signal number %d is out of range\n", signum);
- return -EINVAL;
- }
- if ((rc = qeth_notifier_register(current, signum)))
- return rc;
-
- return count;
-}
-
-static DRIVER_ATTR(notifier_register, 0200, NULL,
- qeth_driver_notifier_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_notifier_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_notifier_register);
-}
diff --git a/drivers/s390/net/qeth_tso.h b/drivers/s390/net/qeth_tso.h
deleted file mode 100644
index c20e923cf9a..00000000000
--- a/drivers/s390/net/qeth_tso.h
+++ /dev/null
@@ -1,148 +0,0 @@
-/*
- * linux/drivers/s390/net/qeth_tso.h
- *
- * Header file for qeth TCP Segmentation Offload support.
- *
- * Copyright 2004 IBM Corporation
- *
- * Author(s): Frank Pavlic <fpavlic@de.ibm.com>
- *
- */
-#ifndef __QETH_TSO_H__
-#define __QETH_TSO_H__
-
-#include <linux/skbuff.h>
-#include <linux/tcp.h>
-#include <linux/ip.h>
-#include <linux/ipv6.h>
-#include <net/ip6_checksum.h>
-#include "qeth.h"
-#include "qeth_mpc.h"
-
-
-static inline struct qeth_hdr_tso *
-qeth_tso_prepare_skb(struct qeth_card *card, struct sk_buff **skb)
-{
- QETH_DBF_TEXT(trace, 5, "tsoprsk");
- return qeth_push_skb(card, *skb, sizeof(struct qeth_hdr_tso));
-}
-
-/**
- * fill header for a TSO packet
- */
-static inline void
-qeth_tso_fill_header(struct qeth_card *card, struct sk_buff *skb)
-{
- struct qeth_hdr_tso *hdr;
- struct tcphdr *tcph;
- struct iphdr *iph;
-
- QETH_DBF_TEXT(trace, 5, "tsofhdr");
-
- hdr = (struct qeth_hdr_tso *) skb->data;
- iph = ip_hdr(skb);
- tcph = tcp_hdr(skb);
- /*fix header to TSO values ...*/
- hdr->hdr.hdr.l3.id = QETH_HEADER_TYPE_TSO;
- /*set values which are fix for the first approach ...*/
- hdr->ext.hdr_tot_len = (__u16) sizeof(struct qeth_hdr_ext_tso);
- hdr->ext.imb_hdr_no = 1;
- hdr->ext.hdr_type = 1;
- hdr->ext.hdr_version = 1;
- hdr->ext.hdr_len = 28;
- /*insert non-fix values */
- hdr->ext.mss = skb_shinfo(skb)->gso_size;
- hdr->ext.dg_hdr_len = (__u16)(iph->ihl*4 + tcph->doff*4);
- hdr->ext.payload_len = (__u16)(skb->len - hdr->ext.dg_hdr_len -
- sizeof(struct qeth_hdr_tso));
-}
-
-/**
- * change some header values as requested by hardware
- */
-static inline void
-qeth_tso_set_tcpip_header(struct qeth_card *card, struct sk_buff *skb)
-{
- struct iphdr *iph = ip_hdr(skb);
- struct ipv6hdr *ip6h = ipv6_hdr(skb);
- struct tcphdr *tcph = tcp_hdr(skb);
-
- tcph->check = 0;
- if (skb->protocol == ETH_P_IPV6) {
- ip6h->payload_len = 0;
- tcph->check = ~csum_ipv6_magic(&ip6h->saddr, &ip6h->daddr,
- 0, IPPROTO_TCP, 0);
- return;
- }
- /*OSA want us to set these values ...*/
- tcph->check = ~csum_tcpudp_magic(iph->saddr, iph->daddr,
- 0, IPPROTO_TCP, 0);
- iph->tot_len = 0;
- iph->check = 0;
-}
-
-static inline int
-qeth_tso_prepare_packet(struct qeth_card *card, struct sk_buff *skb,
- int ipv, int cast_type)
-{
- struct qeth_hdr_tso *hdr;
-
- QETH_DBF_TEXT(trace, 5, "tsoprep");
-
- hdr = (struct qeth_hdr_tso *) qeth_tso_prepare_skb(card, &skb);
- if (hdr == NULL) {
- QETH_DBF_TEXT(trace, 4, "tsoperr");
- return -ENOMEM;
- }
- memset(hdr, 0, sizeof(struct qeth_hdr_tso));
- /*fill first 32 bytes of qdio header as used
- *FIXME: TSO has two struct members
- * with different names but same size
- * */
- qeth_fill_header(card, &hdr->hdr, skb, ipv, cast_type);
- qeth_tso_fill_header(card, skb);
- qeth_tso_set_tcpip_header(card, skb);
- return 0;
-}
-
-static inline void
-__qeth_fill_buffer_frag(struct sk_buff *skb, struct qdio_buffer *buffer,
- int is_tso, int *next_element_to_fill)
-{
- struct skb_frag_struct *frag;
- int fragno;
- unsigned long addr;
- int element, cnt, dlen;
-
- fragno = skb_shinfo(skb)->nr_frags;
- element = *next_element_to_fill;
- dlen = 0;
-
- if (is_tso)
- buffer->element[element].flags =
- SBAL_FLAGS_MIDDLE_FRAG;
- else
- buffer->element[element].flags =
- SBAL_FLAGS_FIRST_FRAG;
- if ( (dlen = (skb->len - skb->data_len)) ) {
- buffer->element[element].addr = skb->data;
- buffer->element[element].length = dlen;
- element++;
- }
- for (cnt = 0; cnt < fragno; cnt++) {
- frag = &skb_shinfo(skb)->frags[cnt];
- addr = (page_to_pfn(frag->page) << PAGE_SHIFT) +
- frag->page_offset;
- buffer->element[element].addr = (char *)addr;
- buffer->element[element].length = frag->size;
- if (cnt < (fragno - 1))
- buffer->element[element].flags =
- SBAL_FLAGS_MIDDLE_FRAG;
- else
- buffer->element[element].flags =
- SBAL_FLAGS_LAST_FRAG;
- element++;
- }
- *next_element_to_fill = element;
-}
-#endif /* __QETH_TSO_H__ */
diff --git a/drivers/s390/net/smsgiucv.c b/drivers/s390/net/smsgiucv.c
index 8735a415a11..d8f990b6b33 100644
--- a/drivers/s390/net/smsgiucv.c
+++ b/drivers/s390/net/smsgiucv.c
@@ -1,7 +1,8 @@
/*
* IUCV special message driver
*
- * Copyright 2003 IBM Deutschland Entwicklung GmbH, IBM Corporation
+ * Copyright IBM Corp. 2003, 2009
+ *
* Author(s): Martin Schwidefsky (schwidefsky@de.ibm.com)
*
* This program is free software; you can redistribute it and/or modify
@@ -23,6 +24,7 @@
#include <linux/init.h>
#include <linux/errno.h>
#include <linux/device.h>
+#include <linux/slab.h>
#include <net/iucv/iucv.h>
#include <asm/cpcmd.h>
#include <asm/ebcdic.h>
@@ -30,9 +32,9 @@
struct smsg_callback {
struct list_head list;
- char *prefix;
+ const char *prefix;
int len;
- void (*callback)(char *from, char *str);
+ void (*callback)(const char *from, char *str);
};
MODULE_AUTHOR
@@ -40,9 +42,12 @@ MODULE_AUTHOR
MODULE_DESCRIPTION ("Linux for S/390 IUCV special message driver");
static struct iucv_path *smsg_path;
+/* dummy device used as trigger for PM functions */
+static struct device *smsg_dev;
static DEFINE_SPINLOCK(smsg_list_lock);
static LIST_HEAD(smsg_list);
+static int iucv_path_connected;
static int smsg_path_pending(struct iucv_path *, u8 ipvmid[8], u8 ipuser[16]);
static void smsg_message_pending(struct iucv_path *, struct iucv_message *);
@@ -55,7 +60,7 @@ static struct iucv_handler smsg_handler = {
static int smsg_path_pending(struct iucv_path *path, u8 ipvmid[8],
u8 ipuser[16])
{
- if (strncmp(ipvmid, "*MSG ", sizeof(ipvmid)) != 0)
+ if (strncmp(ipvmid, "*MSG ", 8) != 0)
return -EINVAL;
/* Path pending from *MSG. */
return iucv_path_accept(path, &smsg_handler, "SMSGIUCV ", NULL);
@@ -97,8 +102,8 @@ static void smsg_message_pending(struct iucv_path *path,
kfree(buffer);
}
-int smsg_register_callback(char *prefix,
- void (*callback)(char *from, char *str))
+int smsg_register_callback(const char *prefix,
+ void (*callback)(const char *from, char *str))
{
struct smsg_callback *cb;
@@ -114,8 +119,9 @@ int smsg_register_callback(char *prefix,
return 0;
}
-void smsg_unregister_callback(char *prefix,
- void (*callback)(char *from, char *str))
+void smsg_unregister_callback(const char *prefix,
+ void (*callback)(const char *from,
+ char *str))
{
struct smsg_callback *cb, *tmp;
@@ -132,14 +138,60 @@ void smsg_unregister_callback(char *prefix,
kfree(cb);
}
+static int smsg_pm_freeze(struct device *dev)
+{
+#ifdef CONFIG_PM_DEBUG
+ printk(KERN_WARNING "smsg_pm_freeze\n");
+#endif
+ if (smsg_path && iucv_path_connected) {
+ iucv_path_sever(smsg_path, NULL);
+ iucv_path_connected = 0;
+ }
+ return 0;
+}
+
+static int smsg_pm_restore_thaw(struct device *dev)
+{
+ int rc;
+
+#ifdef CONFIG_PM_DEBUG
+ printk(KERN_WARNING "smsg_pm_restore_thaw\n");
+#endif
+ if (smsg_path && !iucv_path_connected) {
+ memset(smsg_path, 0, sizeof(*smsg_path));
+ smsg_path->msglim = 255;
+ smsg_path->flags = 0;
+ rc = iucv_path_connect(smsg_path, &smsg_handler, "*MSG ",
+ NULL, NULL, NULL);
+#ifdef CONFIG_PM_DEBUG
+ if (rc)
+ printk(KERN_ERR
+ "iucv_path_connect returned with rc %i\n", rc);
+#endif
+ if (!rc)
+ iucv_path_connected = 1;
+ cpcmd("SET SMSG IUCV", NULL, 0, NULL);
+ }
+ return 0;
+}
+
+static const struct dev_pm_ops smsg_pm_ops = {
+ .freeze = smsg_pm_freeze,
+ .thaw = smsg_pm_restore_thaw,
+ .restore = smsg_pm_restore_thaw,
+};
+
static struct device_driver smsg_driver = {
- .name = "SMSGIUCV",
+ .owner = THIS_MODULE,
+ .name = SMSGIUCV_DRV_NAME,
.bus = &iucv_bus,
+ .pm = &smsg_pm_ops,
};
static void __exit smsg_exit(void)
{
cpcmd("SET SMSG IUCV", NULL, 0, NULL);
+ device_unregister(smsg_dev);
iucv_unregister(&smsg_handler, 1);
driver_unregister(&smsg_driver);
}
@@ -156,11 +208,8 @@ static int __init smsg_init(void)
if (rc != 0)
goto out;
rc = iucv_register(&smsg_handler, 1);
- if (rc) {
- printk(KERN_ERR "SMSGIUCV: failed to register to iucv");
- rc = -EIO; /* better errno ? */
+ if (rc)
goto out_driver;
- }
smsg_path = iucv_path_alloc(255, 0, GFP_KERNEL);
if (!smsg_path) {
rc = -ENOMEM;
@@ -168,16 +217,32 @@ static int __init smsg_init(void)
}
rc = iucv_path_connect(smsg_path, &smsg_handler, "*MSG ",
NULL, NULL, NULL);
- if (rc) {
- printk(KERN_ERR "SMSGIUCV: failed to connect to *MSG");
- rc = -EIO; /* better errno ? */
- goto out_free;
+ if (rc)
+ goto out_free_path;
+ else
+ iucv_path_connected = 1;
+ smsg_dev = kzalloc(sizeof(struct device), GFP_KERNEL);
+ if (!smsg_dev) {
+ rc = -ENOMEM;
+ goto out_free_path;
}
+ dev_set_name(smsg_dev, "smsg_iucv");
+ smsg_dev->bus = &iucv_bus;
+ smsg_dev->parent = iucv_root;
+ smsg_dev->release = (void (*)(struct device *))kfree;
+ smsg_dev->driver = &smsg_driver;
+ rc = device_register(smsg_dev);
+ if (rc)
+ goto out_put;
+
cpcmd("SET SMSG IUCV", NULL, 0, NULL);
return 0;
-out_free:
+out_put:
+ put_device(smsg_dev);
+out_free_path:
iucv_path_free(smsg_path);
+ smsg_path = NULL;
out_register:
iucv_unregister(&smsg_handler, 1);
out_driver:
diff --git a/drivers/s390/net/smsgiucv.h b/drivers/s390/net/smsgiucv.h
index 67f5d4f8378..45bc925928c 100644
--- a/drivers/s390/net/smsgiucv.h
+++ b/drivers/s390/net/smsgiucv.h
@@ -1,10 +1,14 @@
/*
* IUCV special message driver
*
- * Copyright (C) 2003 IBM Deutschland Entwicklung GmbH, IBM Corporation
+ * Copyright IBM Corp. 2003
* Author(s): Martin Schwidefsky (schwidefsky@de.ibm.com)
*/
-int smsg_register_callback(char *, void (*)(char *, char *));
-void smsg_unregister_callback(char *, void (*)(char *, char *));
+#define SMSGIUCV_DRV_NAME "SMSGIUCV"
+
+int smsg_register_callback(const char *,
+ void (*)(const char *, char *));
+void smsg_unregister_callback(const char *,
+ void (*)(const char *, char *));
diff --git a/drivers/s390/net/smsgiucv_app.c b/drivers/s390/net/smsgiucv_app.c
new file mode 100644
index 00000000000..32515a201bb
--- /dev/null
+++ b/drivers/s390/net/smsgiucv_app.c
@@ -0,0 +1,218 @@
+/*
+ * Deliver z/VM CP special messages (SMSG) as uevents.
+ *
+ * The driver registers for z/VM CP special messages with the
+ * "APP" prefix. Incoming messages are delivered to user space
+ * as uevents.
+ *
+ * Copyright IBM Corp. 2010
+ * Author(s): Hendrik Brueckner <brueckner@linux.vnet.ibm.com>
+ *
+ */
+#define KMSG_COMPONENT "smsgiucv_app"
+#define pr_fmt(fmt) KMSG_COMPONENT ": " fmt
+
+#include <linux/ctype.h>
+#include <linux/err.h>
+#include <linux/device.h>
+#include <linux/list.h>
+#include <linux/kobject.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/spinlock.h>
+#include <linux/workqueue.h>
+#include <net/iucv/iucv.h>
+#include "smsgiucv.h"
+
+/* prefix used for SMSG registration */
+#define SMSG_PREFIX "APP"
+
+/* SMSG related uevent environment variables */
+#define ENV_SENDER_STR "SMSG_SENDER="
+#define ENV_SENDER_LEN (strlen(ENV_SENDER_STR) + 8 + 1)
+#define ENV_PREFIX_STR "SMSG_ID="
+#define ENV_PREFIX_LEN (strlen(ENV_PREFIX_STR) + \
+ strlen(SMSG_PREFIX) + 1)
+#define ENV_TEXT_STR "SMSG_TEXT="
+#define ENV_TEXT_LEN(msg) (strlen(ENV_TEXT_STR) + strlen((msg)) + 1)
+
+/* z/VM user ID which is permitted to send SMSGs
+ * If the value is undefined or empty (""), special messages are
+ * accepted from any z/VM user ID. */
+static char *sender;
+module_param(sender, charp, 0400);
+MODULE_PARM_DESC(sender, "z/VM user ID from which CP SMSGs are accepted");
+
+/* SMSG device representation */
+static struct device *smsg_app_dev;
+
+/* list element for queuing received messages for delivery */
+struct smsg_app_event {
+ struct list_head list;
+ char *buf;
+ char *envp[4];
+};
+
+/* queue for outgoing uevents */
+static LIST_HEAD(smsg_event_queue);
+static DEFINE_SPINLOCK(smsg_event_queue_lock);
+
+static void smsg_app_event_free(struct smsg_app_event *ev)
+{
+ kfree(ev->buf);
+ kfree(ev);
+}
+
+static struct smsg_app_event *smsg_app_event_alloc(const char *from,
+ const char *msg)
+{
+ struct smsg_app_event *ev;
+
+ ev = kzalloc(sizeof(*ev), GFP_ATOMIC);
+ if (!ev)
+ return NULL;
+
+ ev->buf = kzalloc(ENV_SENDER_LEN + ENV_PREFIX_LEN +
+ ENV_TEXT_LEN(msg), GFP_ATOMIC);
+ if (!ev->buf) {
+ kfree(ev);
+ return NULL;
+ }
+
+ /* setting up environment pointers into buf */
+ ev->envp[0] = ev->buf;
+ ev->envp[1] = ev->envp[0] + ENV_SENDER_LEN;
+ ev->envp[2] = ev->envp[1] + ENV_PREFIX_LEN;
+ ev->envp[3] = NULL;
+
+ /* setting up environment: sender, prefix name, and message text */
+ snprintf(ev->envp[0], ENV_SENDER_LEN, ENV_SENDER_STR "%s", from);
+ snprintf(ev->envp[1], ENV_PREFIX_LEN, ENV_PREFIX_STR "%s", SMSG_PREFIX);
+ snprintf(ev->envp[2], ENV_TEXT_LEN(msg), ENV_TEXT_STR "%s", msg);
+
+ return ev;
+}
+
+static void smsg_event_work_fn(struct work_struct *work)
+{
+ LIST_HEAD(event_queue);
+ struct smsg_app_event *p, *n;
+ struct device *dev;
+
+ dev = get_device(smsg_app_dev);
+ if (!dev)
+ return;
+
+ spin_lock_bh(&smsg_event_queue_lock);
+ list_splice_init(&smsg_event_queue, &event_queue);
+ spin_unlock_bh(&smsg_event_queue_lock);
+
+ list_for_each_entry_safe(p, n, &event_queue, list) {
+ list_del(&p->list);
+ kobject_uevent_env(&dev->kobj, KOBJ_CHANGE, p->envp);
+ smsg_app_event_free(p);
+ }
+
+ put_device(dev);
+}
+static DECLARE_WORK(smsg_event_work, smsg_event_work_fn);
+
+static void smsg_app_callback(const char *from, char *msg)
+{
+ struct smsg_app_event *se;
+
+ /* check if the originating z/VM user ID matches
+ * the configured sender. */
+ if (sender && strlen(sender) > 0 && strcmp(from, sender) != 0)
+ return;
+
+ /* get start of message text (skip prefix and leading blanks) */
+ msg += strlen(SMSG_PREFIX);
+ while (*msg && isspace(*msg))
+ msg++;
+ if (*msg == '\0')
+ return;
+
+ /* allocate event list element and its environment */
+ se = smsg_app_event_alloc(from, msg);
+ if (!se)
+ return;
+
+ /* queue event and schedule work function */
+ spin_lock(&smsg_event_queue_lock);
+ list_add_tail(&se->list, &smsg_event_queue);
+ spin_unlock(&smsg_event_queue_lock);
+
+ schedule_work(&smsg_event_work);
+ return;
+}
+
+static int __init smsgiucv_app_init(void)
+{
+ struct device_driver *smsgiucv_drv;
+ int rc;
+
+ if (!MACHINE_IS_VM)
+ return -ENODEV;
+
+ smsg_app_dev = kzalloc(sizeof(*smsg_app_dev), GFP_KERNEL);
+ if (!smsg_app_dev)
+ return -ENOMEM;
+
+ smsgiucv_drv = driver_find(SMSGIUCV_DRV_NAME, &iucv_bus);
+ if (!smsgiucv_drv) {
+ kfree(smsg_app_dev);
+ return -ENODEV;
+ }
+
+ rc = dev_set_name(smsg_app_dev, KMSG_COMPONENT);
+ if (rc) {
+ kfree(smsg_app_dev);
+ goto fail;
+ }
+ smsg_app_dev->bus = &iucv_bus;
+ smsg_app_dev->parent = iucv_root;
+ smsg_app_dev->release = (void (*)(struct device *)) kfree;
+ smsg_app_dev->driver = smsgiucv_drv;
+ rc = device_register(smsg_app_dev);
+ if (rc) {
+ put_device(smsg_app_dev);
+ goto fail;
+ }
+
+ /* convert sender to uppercase characters */
+ if (sender) {
+ int len = strlen(sender);
+ while (len--)
+ sender[len] = toupper(sender[len]);
+ }
+
+ /* register with the smsgiucv device driver */
+ rc = smsg_register_callback(SMSG_PREFIX, smsg_app_callback);
+ if (rc) {
+ device_unregister(smsg_app_dev);
+ goto fail;
+ }
+
+ rc = 0;
+fail:
+ return rc;
+}
+module_init(smsgiucv_app_init);
+
+static void __exit smsgiucv_app_exit(void)
+{
+ /* unregister callback */
+ smsg_unregister_callback(SMSG_PREFIX, smsg_app_callback);
+
+ /* cancel pending work and flush any queued event work */
+ cancel_work_sync(&smsg_event_work);
+ smsg_event_work_fn(&smsg_event_work);
+
+ device_unregister(smsg_app_dev);
+}
+module_exit(smsgiucv_app_exit);
+
+MODULE_LICENSE("GPL v2");
+MODULE_DESCRIPTION("Deliver z/VM CP SMSG as uevents");
+MODULE_AUTHOR("Hendrik Brueckner <brueckner@linux.vnet.ibm.com>");