aboutsummaryrefslogtreecommitdiff
path: root/drivers/s390/net/claw.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/s390/net/claw.c')
-rw-r--r--drivers/s390/net/claw.c4447
1 files changed, 4447 insertions, 0 deletions
diff --git a/drivers/s390/net/claw.c b/drivers/s390/net/claw.c
new file mode 100644
index 00000000000..06804d39a9c
--- /dev/null
+++ b/drivers/s390/net/claw.c
@@ -0,0 +1,4447 @@
+/*
+ * drivers/s390/net/claw.c
+ * ESCON CLAW network driver
+ *
+ * $Revision: 1.35 $ $Date: 2005/03/24 12:25:38 $
+ *
+ * Linux fo zSeries version
+ * Copyright (C) 2002,2005 IBM Corporation
+ * Author(s) Original code written by:
+ * Kazuo Iimura (iimura@jp.ibm.com)
+ * Rewritten by
+ * Andy Richter (richtera@us.ibm.com)
+ * Marc Price (mwprice@us.ibm.com)
+ *
+ * sysfs parms:
+ * group x.x.rrrr,x.x.wwww
+ * read_buffer nnnnnnn
+ * write_buffer nnnnnn
+ * host_name aaaaaaaa
+ * adapter_name aaaaaaaa
+ * api_type aaaaaaaa
+ *
+ * eg.
+ * group 0.0.0200 0.0.0201
+ * read_buffer 25
+ * write_buffer 20
+ * host_name LINUX390
+ * adapter_name RS6K
+ * api_type TCPIP
+ *
+ * where
+ *
+ * The device id is decided by the order entries
+ * are added to the group the first is claw0 the second claw1
+ * up to CLAW_MAX_DEV
+ *
+ * rrrr - the first of 2 consecutive device addresses used for the
+ * CLAW protocol.
+ * The specified address is always used as the input (Read)
+ * channel and the next address is used as the output channel.
+ *
+ * wwww - the second of 2 consecutive device addresses used for
+ * the CLAW protocol.
+ * The specified address is always used as the output
+ * channel and the previous address is used as the input channel.
+ *
+ * read_buffer - specifies number of input buffers to allocate.
+ * write_buffer - specifies number of output buffers to allocate.
+ * host_name - host name
+ * adaptor_name - adaptor name
+ * api_type - API type TCPIP or API will be sent and expected
+ * as ws_name
+ *
+ * Note the following requirements:
+ * 1) host_name must match the configured adapter_name on the remote side
+ * 2) adaptor_name must match the configured host name on the remote side
+ *
+ * Change History
+ * 1.00 Initial release shipped
+ * 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
+ */
+#include <asm/bitops.h>
+#include <asm/ccwdev.h>
+#include <asm/ccwgroup.h>
+#include <asm/debug.h>
+#include <asm/idals.h>
+#include <asm/io.h>
+
+#include <linux/ctype.h>
+#include <linux/delay.h>
+#include <linux/errno.h>
+#include <linux/if_arp.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/ip.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/proc_fs.h>
+#include <linux/sched.h>
+#include <linux/signal.h>
+#include <linux/skbuff.h>
+#include <linux/slab.h>
+#include <linux/string.h>
+#include <linux/tcp.h>
+#include <linux/timer.h>
+#include <linux/types.h>
+#include <linux/version.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
+*/
+
+/* following enables tracing */
+//#define DEBUGMSG
+//#define IOTRACE
+//#define FUNCTRACE
+
+#ifdef DEBUGMSG
+#define DEBUG
+#endif
+
+#ifdef IOTRACE
+#define DEBUG
+#endif
+
+#ifdef FUNCTRACE
+#define DEBUG
+#endif
+
+ char debug_buffer[255];
+/**
+ * Debug Facility Stuff
+ */
+static debug_info_t *claw_dbf_setup;
+static debug_info_t *claw_dbf_trace;
+
+/**
+ * CLAW Debug Facility functions
+ */
+static void
+claw_unregister_debug_facility(void)
+{
+ if (claw_dbf_setup)
+ debug_unregister(claw_dbf_setup);
+ if (claw_dbf_trace)
+ debug_unregister(claw_dbf_trace);
+}
+
+static int
+claw_register_debug_facility(void)
+{
+ claw_dbf_setup = debug_register("claw_setup", 1, 1, 8);
+ claw_dbf_trace = debug_register("claw_trace", 1, 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;
+ }
+ debug_register_view(claw_dbf_setup, &debug_hex_ascii_view);
+ debug_set_level(claw_dbf_setup, 2);
+ debug_register_view(claw_dbf_trace, &debug_hex_ascii_view);
+ debug_set_level(claw_dbf_trace, 2);
+ return 0;
+}
+
+static inline void
+claw_set_busy(struct net_device *dev)
+{
+ ((struct claw_privbk *) dev->priv)->tbusy=1;
+ eieio();
+}
+
+static inline void
+claw_clear_busy(struct net_device *dev)
+{
+ clear_bit(0, &(((struct claw_privbk *) dev->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;
+}
+
+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));
+}
+
+static inline void
+claw_clearbit_busy(int nr,struct net_device *dev)
+{
+ clear_bit(nr,(void *)&(((struct claw_privbk *)dev->priv)->tbusy));
+ netif_wake_queue(dev);
+}
+
+static inline int
+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));
+}
+
+
+/* Functions for the DEV methods */
+
+static int claw_probe(struct ccwgroup_device *cgdev);
+static void claw_remove_device(struct ccwgroup_device *cgdev);
+static void claw_purge_skb_queue(struct sk_buff_head *q);
+static int claw_new_device(struct ccwgroup_device *cgdev);
+static int claw_shutdown_device(struct ccwgroup_device *cgdev);
+static int claw_tx(struct sk_buff *skb, struct net_device *dev);
+static int claw_change_mtu( struct net_device *dev, int new_mtu);
+static int claw_open(struct net_device *dev);
+static void claw_irq_handler(struct ccw_device *cdev,
+ unsigned long intparm, struct irb *irb);
+static void claw_irq_tasklet ( unsigned long data );
+static int claw_release(struct net_device *dev);
+static void claw_write_retry ( struct chbk * p_ch );
+static void claw_write_next ( struct chbk * p_ch );
+static void claw_timer ( struct chbk * p_ch );
+
+/* Functions */
+static int add_claw_reads(struct net_device *dev,
+ struct ccwbk* p_first, struct ccwbk* p_last);
+static void inline ccw_check_return_code (struct ccw_device *cdev,
+ int return_code);
+static void inline ccw_check_unit_check (struct chbk * p_ch,
+ unsigned char sense );
+static int find_link(struct net_device *dev, char *host_name, char *ws_name );
+static int claw_hw_tx(struct sk_buff *skb, struct net_device *dev, long linkid);
+static int init_ccw_bk(struct net_device *dev);
+static void probe_error( struct ccwgroup_device *cgdev);
+static struct net_device_stats *claw_stats(struct net_device *dev);
+static int inline 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, char *buf);
+static ssize_t claw_hname_write(struct device *dev,
+ const char *buf, size_t count);
+static ssize_t claw_adname_show(struct device *dev, char *buf);
+static ssize_t claw_adname_write(struct device *dev,
+ const char *buf, size_t count);
+static ssize_t claw_apname_show(struct device *dev, char *buf);
+static ssize_t claw_apname_write(struct device *dev,
+ const char *buf, size_t count);
+static ssize_t claw_wbuff_show(struct device *dev, char *buf);
+static ssize_t claw_wbuff_write(struct device *dev,
+ const char *buf, size_t count);
+static ssize_t claw_rbuff_show(struct device *dev, char *buf);
+static ssize_t claw_rbuff_write(struct device *dev,
+ 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);
+static int claw_send_control(struct net_device *dev, __u8 type, __u8 link,
+ __u8 correlator, __u8 rc , char *local_name, char *remote_name);
+static int claw_snd_conn_req(struct net_device *dev, __u8 link);
+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 );
+
+/* Functions for unpack reads */
+static void unpack_read (struct net_device *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,
+};
+
+/*
+*
+* Key functions
+*/
+
+/*----------------------------------------------------------------*
+ * claw_probe *
+ * this function is called for each CLAW device. *
+ *----------------------------------------------------------------*/
+static int
+claw_probe(struct ccwgroup_device *cgdev)
+{
+ int rc;
+ struct claw_privbk *privptr=NULL;
+
+#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 = kmalloc(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;
+ }
+ memset(privptr,0x00,sizeof(struct claw_privbk));
+ privptr->p_mtc_envelope= kmalloc( MAX_ENVELOPE_SIZE, GFP_KERNEL);
+ privptr->p_env = kmalloc(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;
+ }
+ memset(privptr->p_mtc_envelope, 0x00, MAX_ENVELOPE_SIZE);
+ memset(privptr->p_env, 0x00, sizeof(struct claw_env));
+ 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");
+
+ return 0;
+} /* end of claw_probe */
+
+/*-------------------------------------------------------------------*
+ * claw_tx *
+ *-------------------------------------------------------------------*/
+
+static int
+claw_tx(struct sk_buff *skb, struct net_device *dev)
+{
+ int rc;
+ struct claw_privbk *privptr=dev->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
+ 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);
+ return rc;
+} /* end of claw_tx */
+
+/*------------------------------------------------------------------*
+ * pack the collect queue into an skb and return it *
+ * If not packing just return the top skb from the queue *
+ *------------------------------------------------------------------*/
+
+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 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");
+ if (skb_queue_len(&p_ch->collect_queue) > 0) {
+ /* some data */
+ held_skb = skb_dequeue(&p_ch->collect_queue);
+ if (p_env->packing != DO_PACKED)
+ return held_skb;
+ if (held_skb)
+ atomic_dec(&held_skb->users);
+ else
+ return NULL;
+ /* get a new SKB we will pack at least one */
+ new_skb = dev_alloc_skb(p_env->write_size);
+ if (new_skb == NULL) {
+ atomic_inc(&held_skb->users);
+ skb_queue_head(&p_ch->collect_queue,held_skb);
+ return NULL;
+ }
+ /* we have packed packet and a place to put it */
+ pk_ind = 1;
+ so_far = 0;
+ new_skb->cb[1] = 'P'; /* every skb on queue has pack header */
+ while ((pk_ind) && (held_skb != NULL)) {
+ if (held_skb->len+so_far <= p_env->write_size-8) {
+ memcpy(skb_put(new_skb,held_skb->len),
+ held_skb->data,held_skb->len);
+ privptr->stats.tx_packets++;
+ so_far += held_skb->len;
+ pkt_cnt++;
+ dev_kfree_skb_irq(held_skb);
+ held_skb = skb_dequeue(&p_ch->collect_queue);
+ if (held_skb)
+ atomic_dec(&held_skb->users);
+ } else {
+ pk_ind = 0;
+ atomic_inc(&held_skb->users);
+ 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");
+ return new_skb;
+}
+
+/*-------------------------------------------------------------------*
+ * claw_change_mtu *
+ * *
+ *-------------------------------------------------------------------*/
+
+static int
+claw_change_mtu(struct net_device *dev, int new_mtu)
+{
+ struct claw_privbk *privptr=dev->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");
+ 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 */
+
+
+/*-------------------------------------------------------------------*
+ * claw_open *
+ * *
+ *-------------------------------------------------------------------*/
+static int
+claw_open(struct net_device *dev)
+{
+
+ int rc;
+ int i;
+ unsigned long saveflags=0;
+ unsigned long parm;
+ struct claw_privbk *privptr;
+ DECLARE_WAITQUEUE(wait, current);
+ 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;
+ /* 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");
+ return -ENOMEM;
+ }
+ }
+ privptr->system_validate_comp=0;
+ privptr->release_pend=0;
+ if(strncmp(privptr->p_env->api_type,WS_APPL_NAME_PACKED,6) == 0) {
+ privptr->p_env->read_size=DEF_PACK_BUFSIZE;
+ privptr->p_env->write_size=DEF_PACK_BUFSIZE;
+ privptr->p_env->packing=PACKING_ASK;
+ } else {
+ privptr->p_env->packing=0;
+ privptr->p_env->read_size=CLAW_FRAME_SIZE;
+ 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]);
+ for ( i = 0; i < 2; 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)
+ skb_queue_head_init(
+ &privptr->channel[WRITE].collect_queue);
+ privptr->channel[i].flag_a = 0;
+ privptr->channel[i].IO_active = 0;
+ privptr->channel[i].flag &= ~CLAW_TIMER;
+ init_timer(&timer);
+ timer.function = (void *)claw_timer;
+ timer.data = (unsigned long)(&privptr->channel[i]);
+ timer.expires = jiffies + 15*HZ;
+ add_timer(&timer);
+ spin_lock_irqsave(get_ccwdev_lock(
+ privptr->channel[i].cdev), saveflags);
+ parm = (unsigned long) &privptr->channel[i];
+ privptr->channel[i].claw_state = CLAW_START_HALT_IO;
+ rc = 0;
+ add_wait_queue(&privptr->channel[i].wait, &wait);
+ rc = ccw_device_halt(
+ (struct ccw_device *)privptr->channel[i].cdev,parm);
+ set_current_state(TASK_INTERRUPTIBLE);
+ spin_unlock_irqrestore(
+ get_ccwdev_lock(privptr->channel[i].cdev), saveflags);
+ schedule();
+ set_current_state(TASK_RUNNING);
+ remove_wait_queue(&privptr->channel[i].wait, &wait);
+ if(rc != 0)
+ ccw_check_return_code(privptr->channel[i].cdev, rc);
+ if((privptr->channel[i].flag & CLAW_TIMER) == 0x00)
+ del_timer(&timer);
+ }
+ if ((((privptr->channel[READ].last_dstat |
+ privptr->channel[WRITE].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");
+
+ for ( i = 0; i < 2; i++) {
+ spin_lock_irqsave(
+ get_ccwdev_lock(privptr->channel[i].cdev),
+ saveflags);
+ parm = (unsigned long) &privptr->channel[i];
+ privptr->channel[i].claw_state = CLAW_STOP;
+ rc = ccw_device_halt(
+ (struct ccw_device *)&privptr->channel[i].cdev,
+ parm);
+ spin_unlock_irqrestore(
+ get_ccwdev_lock(privptr->channel[i].cdev),
+ saveflags);
+ if (rc != 0) {
+ ccw_check_return_code(
+ privptr->channel[i].cdev, rc);
+ }
+ }
+ free_pages((unsigned long)privptr->p_buff_ccw,
+ (int)pages_to_order_of_mag(privptr->p_buff_ccw_num));
+ if (privptr->p_env->read_size < PAGE_SIZE) {
+ free_pages((unsigned long)privptr->p_buff_read,
+ (int)pages_to_order_of_mag(
+ privptr->p_buff_read_num));
+ }
+ else {
+ p_buf=privptr->p_read_active_first;
+ while (p_buf!=NULL) {
+ free_pages((unsigned long)p_buf->p_buffer,
+ (int)pages_to_order_of_mag(
+ privptr->p_buff_pages_perread ));
+ p_buf=p_buf->next;
+ }
+ }
+ 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));
+ }
+ else {
+ p_buf=privptr->p_write_active_first;
+ while (p_buf!=NULL) {
+ free_pages((unsigned long)p_buf->p_buffer,
+ (int)pages_to_order_of_mag(
+ privptr->p_buff_pages_perwrite ));
+ p_buf=p_buf->next;
+ }
+ }
+ privptr->buffs_alloc = 0;
+ privptr->channel[READ].flag= 0x00;
+ privptr->channel[WRITE].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");
+ 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");
+ return 0;
+} /* end of claw_open */
+
+/*-------------------------------------------------------------------*
+* *
+* claw_irq_handler *
+* *
+*--------------------------------------------------------------------*/
+static void
+claw_irq_handler(struct ccw_device *cdev,
+ unsigned long intparm, struct irb *irb)
+{
+ struct chbk *p_ch = NULL;
+ struct claw_privbk *privptr = NULL;
+ struct net_device *dev = NULL;
+ 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");
+ /* 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");
+ 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];
+ else {
+ printk(KERN_WARNING "claw: Can't determine channel for "
+ "interrupt, device %s\n", cdev->dev.bus_id);
+ CLAW_DBF_TEXT(2,trace,"badchan");
+ return;
+ }
+ 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 -"
+ " 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");
+ /* return; */
+ }
+
+ /* Check the reason-code of a unit check */
+ if (irb->scsw.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;
+
+ 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)*/
+ }
+ 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",
+ 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;
+ }
+
+} /* end of claw_irq_handler */
+
+
+/*-------------------------------------------------------------------*
+* claw_irq_tasklet *
+* *
+*--------------------------------------------------------------------*/
+static void