diff options
Diffstat (limited to 'drivers/s390/net/claw.c')
-rw-r--r-- | drivers/s390/net/claw.c | 4447 |
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 |