OpenCores
URL https://opencores.org/ocsvn/or1k/or1k/trunk

Subversion Repositories or1k

[/] [or1k/] [trunk/] [linux/] [linux-2.4/] [drivers/] [s390/] [net/] [c7000.c] - Rev 1275

Go to most recent revision | Compare with Previous | Blame | View Log

/*
	Cisco/7000 driver -- Copyright (C) 2000 UTS Global LLC.
	Author: Bob Scardapane (UTS Global LLC).
	Version: 3.
 
	To use this driver, run the LINUX command:
 
	insmod c7000 base0=0xYYYY lhost0=s1 uhost0=s2 lappl0=s3 uappl0=s4 dbg=x
 
	base0=0xYYYY defines the base unit address of the interface.
	lhost0=s1 defines the local host name.
	uhost0=s2 defines the unit host name.
	lappl0=s3 defines the local application name.
	uappl0=s4 defines the unit application name.
	dbg=x defines the message level.  Higher values will result in
	additional diagnostic messages.
 
	Additional interfaces are defined on insmod by using the variable
	name groups "base1,lhost1,lappl1,uhost1,uappl1", etc... up to three
	additional groups.
 
	In addition, the module will automatically detect the unit base
	addresses by scanning all active irq's for a control unit type
	of 0x3088 and a model of 0x61 (CLAW mode). The noauto parameter
	can be used to suppress automatic detection.
 
	The values of lhostx, lapplx, uhostx and uapplx default to:
 
	lapplx - TCPIP
	lhostx - UTS
	uapplx - TCPIP
	uhostx - C7011
 
	Note that the values passed in the insmod command will always
	override the automatic detection of the unit base addreeses and
	the default values of lapplx, lhostx, uapplx and uhostx.
 
	The parameter noauto can be used to disable automatic detection of
	devices:
 
	noauto=1 (disable automatic detection)
	noauto=0 (Enable automatic detectio.  This is the default value.)
 
	The values in base0 - base3 will be copied to the bases array when
	the module is loaded. 
 
	To configure the interface(s), run the LINUX command(s):
 
	ifconfig ci0 ...
	ifconfig ci1 ...
	ifconfig ci2 ...
	ifconfig ci3 ...
 
	There is one device structure for each controller in the c7000_devices
	array.  The base address of each controller is in the bases array.
	These arrays parallel each other.  There is also one c7000_controller
	structure for each controller.  This structure is pointed to by field
	priv in the individual device structure. 
 
	In each c7000_controller, there are embedded c7000_unit structures.
	There is one c7000_unit structure per device number that makes up
	a controller (currently 2 units per controller).
*/
 
#include <linux/config.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/sched.h>
#include <linux/signal.h>
#include <linux/string.h>
#include <linux/netdevice.h>
#include <linux/etherdevice.h>
#include <linux/if_arp.h>
#include <linux/tcp.h>
#include <linux/skbuff.h>
#include <asm/io.h>
#include <asm/bitops.h>
#include <asm/irq.h>
 
/*
	Global defines
*/
 
/*
	Maximum number of controllers.
*/
 
#define	MAX_C7000	4
 
/*
	Number of units per controller.
*/
 
#define	NUNITS		2
 
/*
	Define indexes of read and write units in the cunits array.
*/
 
#define	C7000_RD	0
#define	C7000_WR	1
 
/*
	Number of device buffers.
*/
 
#define	C7000_MAXBUF	40
 
/*
	Transmission queue length.
*/
 
#define	C7000_TXQUEUE_LEN	100
/*
	Size of the IP packet data.
*/
 
#define	C7000_DATAL	4096
 
/*
	Size of the read header data.
*/
 
#define	C7000_READHDRL	4
 
/*
	Size of read flag byte.
*/
 
#define	C7000_READFFL	1
 
/*
	Size of a device buffer.  This is how it is arranged in memory:
	4096 (IP packet data) + 4 (read header) + 1 (read flag) = 4101.
*/
 
#define	C7000_BUFSIZE	C7000_DATAL + C7000_READHDRL + C7000_READFFL
 
/*
	Size of sigsmod data.
*/
 
#define	C7000_SIGSMODL	1
 
/*
	Flag value that indicates a read was completed in the flag
	field of a c7000_rd_header.
*/
 
#define	FLAG_FF		0xff
/*
	Size of C7000 sense id data.
*/
 
#define SIDL		32
 
/*
	Maximum number of read and write retries.
*/
 
#define	C7000_MAX_RETRIES	3
 
/*
	Define sense byte0 value for a box reset.
*/
 
#define	C7000_BOX_RESET		0x41
 
/*
	CCW commands.
*/
 
#define	C7000_WRITE_CCW		0x01	/* normal write */
#define	C7000_READ_CCW		0x02	/* normal read */
#define	C7000_NOOP_CCW		0x03	/* no operation */
#define	C7000_SIGSMOD_CCW	0x05	/* signal status modifier */
#define	C7000_TIC_CCW		0x08	/* transfer in channel */
#define	C7000_READHDR_CCW	0x12	/* read header  */
#define	C7000_READFF_CCW	0x22	/* read FF flag */
#define	C7000_SID_CCW		0xe4	/* sense identification */
 
/*
	Control commands.
*/
 
#define	C7000_SYS_VALIDATE		1
#define C7000_SYS_VALIDATE_RESP		2
#define	C7000_CONN_REQ			33
#define C7000_CONN_RESP			34
#define C7000_CONN_CONFRM		35
#define C7000_DISCONN			36
#define C7000_BOXERROR			65
 
/*
	State machine values.
*/
 
#define	C7000_INIT	1
#define C7000_HALT	2
#define C7000_SID	3
#define C7000_SYSVAL	4
#define	C7000_CONNECT	5
#define C7000_READY	6
#define C7000_READ	7
#define C7000_WRITE	8
#define	C7000_DISC	9
#define C7000_STOP	10
#define	C7000_STOPPED	11
#define C7000_ERROR	12
 
/*
	The lower subchannel is used for read operations and the one that is
	one higher is used for write operations.
 
	Both subchannels are initially in state C7000_INIT.  A transition to
	state C7000_HALT occurs when halt_IO is issued on each.  When the
	halts completes a transition to state C7000_SID occurs and a channel
	program is issued to do a sense identification on both subchannels.
 
	When the sense identification completes, the state C7000_SYSVAL is
	entered on the read subchannel.  A read channel program is issued.
 
	When the sense identification completes, the write subchannel enters
	state C7000_SYSVAL and a system validation request is written.  The
	read subchannel is also put into this state.
 
	When both the system validation response is read and an inbound system
	validation request is read, the inbound system validation request is
	responded to and both subchannels enter the C7000_CONNECT state.
 
	A read channel program is posted to look for the inbound connect
	request.  When that is received a connection confirmation is written.
	The state of both subchannels is then changed to C7000_READY.  A
	read channel program is then posted and the state is changed to
	C7000_READ.  When a read completes, the packet is sent to the higher
	layers and the read channel program is restarted.
 
	When there is a packet to be written, state C7000_WRITE is entered
	and a channel program is issued to write the data.  The subchannel
	is in state C7000_READY when there is nothing to be written.
 
	When the stop method is executed, a disconnect message is sent and
	the state is changed to C7000_DISC in both subchannels.  A halt_IO
	will be issued to both subchannels and state C7000_STOP will be entered.
	When the halt IO completes, state C7000_STOPPED will be set. 
 
	State C7000_ERROR is set when an error occurs in the interrupt
	routine.  Recycle the interface (ifconfig down / ifconfig up)
	to reset this state.
*/
 
/*
	Results from c7000_check_csw.
*/
 
enum	c7000_rupt {
	C7000_NORMAL,
	C7000_CHANERR,
	C7000_UCK,
	C7000_UCK_RESET,
	C7000_UE,
	C7000_ATTN,
	C7000_BUSY,
	C7000_OTHER
};
 
/*
	Bits set in device structure tbusy field.
*/
 
#define	TB_TX		0	/* sk buffer handling in progress */
#define	TB_STOP		1	/* network device stop in progress */
#define	TB_RETRY	2	/* retry in progress */
#define	TB_NOBUFFER	3	/* no buffer on free queue */
 
/*
	Bit in c7000_unit.flag_a that indicates the bh routine is busy.
*/
 
#define	C7000_BH_ACTIVE	0
 
#define CPrintk(level, args...) \
	if (level <= dbg) \
		printk(args)
 
/*
	Maximum length of a system validation string.
*/
 
#define	NAMLEN	8
 
#define	Err_Conn_Confirm	1
#define	Err_Names_not_Matched	166
#define	Err_C7000_NOT_READY	167
#define	Err_Duplicate		170
#define	Err_Closing		171
#define	Err_No_Such_App		172
#define	Err_Host_Not_Ready	173
#define	Err_CLOSING		174
#define	Err_Dup_Link		175
#define	Err_Wrong_Version	179
#define	Err_Wrong_Frame_Size	180
 
/*
	Define a macro to extract the logical link identifier from
	the c7000 read header command field.
*/
 
#define	C7000_LINKID(cmd)	((unsigned char)cmd >> 3)
 
/*
	Define the control unit type for a Cisco 7000.
*/
 
#define	C7000_CU_TYPE		0x3088
 
/*
	Define the control unit model for a Cisco 7000.
*/
 
#define	C7000_CU_MODEL		0x61
 
/*
	Define the default system validate parameters (lapplx,
	lhostx, uapplx, uhostx).
*/
 
#define	C7000_DFLT_LAPPL	"TCPIP"
#define	C7000_DFLT_LHOST	"UTS"
#define	C7000_DFLT_UAPPL	"TCPIP"
#define	C7000_DFLT_UHOST	"C7011"
 
/*
	Global variables.
*/
 
/*
	Base device addresses of the controllers.
*/
 
static int	base0 = -1;
static int	base1 = -1;
static int	base2 = -1;
static int	base3 = -1;
 
static int	bases[MAX_C7000];
 
/*
	Local application names.
*/
 
static char	*lappl0;
static char	*lappl1;
static char	*lappl2;
static char	*lappl3;
 
/*
	Local host names.
*/
 
static char	*lhost0;
static char	*lhost1;
static char	*lhost2;
static char	*lhost3;
 
/*
	Unit application names.
*/
 
static char	*uappl0;
static char	*uappl1;
static char	*uappl2;
static char	*uappl3;
 
/*
	Unit hosts names.
*/
 
static char	*uhost0;
static char	*uhost1;
static char	*uhost2;
static char	*uhost3;
 
/*
	Debugging level (higher numbers emit lower priority messages).
*/
 
static unsigned int	dbg = 0;
 
/*
	Parameter that controls auto detection.
*/
 
static int	noauto = 0;
 
/*
	Interface names.
*/
 
static char	ifnames[MAX_C7000][8] = {"ci0", "ci1", "ci2", "ci3"};
 
/*
	One device structure per controller.
*/
 
/* RBH Try out the new code for 2.4.0 */
#define NEWSTUFF
 
#ifdef NEWSTUFF
#define STRUCT_NET_DEVICE struct net_device
#else
#define STRUCT_NET_DEVICE struct device
#endif
 
STRUCT_NET_DEVICE	c7000_devices[MAX_C7000];
 
/*
	Scratch variable filled in with controller name.
*/
 
static char	*controller;
 
/*
	Identify parameters that can be passed on the LINUX insmod command.
*/
 
MODULE_AUTHOR("Robert Scardapane (UTS Global)");
MODULE_DESCRIPTION("Network module for Cisco 7000 box.");
 
MODULE_PARM(base0, "1i");
MODULE_PARM_DESC(base0, "Base unit address for 1st C7000 box.");
MODULE_PARM(base1, "1i");
MODULE_PARM_DESC(base1, "Base unit address for 2nd C7000 box.");
MODULE_PARM(base2, "1i");
MODULE_PARM_DESC(base2, "Base unit address for 3rd C7000 box.");
MODULE_PARM(base3, "1i");
MODULE_PARM_DESC(base3, "Base unit address for 4th C7000 box.");
 
MODULE_PARM(lappl0, "s");
MODULE_PARM_DESC(lappl0, "Application name for 1st C7000 box.");
MODULE_PARM(lappl1, "s");
MODULE_PARM_DESC(lappl1, "Application name for 2nd C7000 box.");
MODULE_PARM(lappl2, "s");
MODULE_PARM_DESC(lappl2, "Application name for 3rd C7000 box.");
MODULE_PARM(lappl3, "s");
MODULE_PARM_DESC(lappl3, "Application name for 4th C7000 box.");
 
MODULE_PARM(lhost0, "s");
MODULE_PARM_DESC(lhost0, "Host name for 1st C7000 box.");
MODULE_PARM(lhost1, "s");
MODULE_PARM_DESC(lhost1, "Host name for 2nd C7000 box.");
MODULE_PARM(lhost2, "s");
MODULE_PARM_DESC(lhost2, "Host name for 3rd C7000 box.");
MODULE_PARM(lhost3, "s");
MODULE_PARM_DESC(lhost3, "Host name for 4th C7000 box.");
 
MODULE_PARM(uhost0, "s");
MODULE_PARM_DESC(uhost0, "Unit name for 1st C7000 box.");
MODULE_PARM(uhost1, "s");
MODULE_PARM_DESC(uhost1, "Unit name for 2nd C7000 box.");
MODULE_PARM(uhost2, "s");
MODULE_PARM_DESC(uhost2, "Unit name for 3rd C7000 box.");
MODULE_PARM(uhost3, "s");
MODULE_PARM_DESC(uhost3, "Unit name for 4th C7000 box.");
 
MODULE_PARM(uappl0, "s");
MODULE_PARM_DESC(uappl0, "Unit application name for 1st C7000 box.");
MODULE_PARM(uappl1, "s");
MODULE_PARM_DESC(uappl1, "Unit application name for 2nd C7000 box.");
MODULE_PARM(uappl2, "s");
MODULE_PARM_DESC(uappl2, "Unit application name for 3rd C7000 box.");
MODULE_PARM(uappl3, "s");
MODULE_PARM_DESC(uappl3, "Unit application name for 4th C7000 box.");
 
MODULE_PARM(dbg, "1i");
MODULE_PARM_DESC(dbg, "Message level for debugging.");
 
MODULE_PARM(noauto, "1i");
MODULE_PARM_DESC(noauto, "Control automatic detection of unit base addresses.");
 
/*
	Structure used to manage unit buffers.
*/
 
struct	c7000_buffer {
	ccw1_t			ccws[7];	/* channel program */
	struct	c7000_buffer	*next;		/* list pointer */
	char			*data;		/* pointer to actual data */
	int			len;		/* length of the data */
};
 
/*
	C7000 Control Block.
 */
 
struct	c7000_control_blk {
	unsigned char	cmd;
	unsigned char	ver;
	unsigned char	link_id;
	unsigned char	correlator;
	unsigned char	ret_code;
	unsigned char	resvd1[3];
	unsigned char	unitname[NAMLEN];
	unsigned char	hostname[NAMLEN];
	unsigned short	rdsize;		/* read frame size   */
	unsigned short	wrtsize;	/* write frame size  */
	unsigned char	resvd2[4];
};
 
/*
	Per unit structure contained within the c7000_controller structure.
*/
 
struct	c7000_unit {
	ccw1_t				ccws[5];	/* control ccws */
	int				devno;		/* device number */
	int				irq;		/* subchannel number */
	int				IO_active;	/* IO activity flag */
	int				state;		/* fsm state */
	int				retries;	/* retry counter */
	unsigned long			flag_a;		/* bh activity flag */
	devstat_t			devstat;	/* device status */
#ifdef NEWSTUFF
	wait_queue_head_t		wait;		/* sleep q head */
#else
	struct wait_queue		*wait;		/* sleep q pointer */
#endif
	struct c7000_controller		*cntlp;		/* controller pointer */
	struct c7000_buffer		*free;		/* free buffer anchor */
	struct c7000_buffer		*proc_head;	/* proc head */
	struct c7000_buffer		*proc_tail;	/* proc tail */
	struct c7000_buffer		*bh_head;	/* bh head */
	struct c7000_buffer		*bh_tail;	/* bh tail */
	struct tq_struct		tq;		/* bh scheduling */
	char				senseid[SIDL];	/* sense id data */
	struct c7000_control_blk	control_blk;	/* control block */
	unsigned char			sigsmod;	/* sigsmod flag */
	unsigned char			readhdr[4];	/* read header */
	unsigned char			readff;		/* readff flag */
};
 
/*
	Private structure pointed to by dev->priv.
*/
 
struct c7000_controller {
	struct	net_device_stats	stats;		/* statistics */
	STRUCT_NET_DEVICE		*dev;		/* -> device struct */
	unsigned int			base_addr;	/* base address */
	char				lappl[NAMLEN];	/* local appl */
	char				lhost[NAMLEN];	/* local host */
	char				uappl[NAMLEN];	/* unit appl */
	char				uhost[NAMLEN];	/* unit host */
	unsigned char			version;	/* version = 2 */
	unsigned char			linkid;		/* link id */
	struct	c7000_unit		cunits[NUNITS];	/* embedded units */
#ifdef NEWSTUFF
	int				tbusy;
#endif
};
 
/*
	This is the structure returned by the C7000_READHDR_CCW.
*/
 
struct	c7000_rd_header {
	unsigned short	len;	/* packet length */
	unsigned char	cmd;	/* command code */
	unsigned char	flag;	/* flag */
};
 
/*
	Set the device structure transmission busy flag.
*/
 
#ifdef NEWSTUFF
#define c7000_set_busy(dev) netif_stop_queue(dev)
#else
static __inline__ void
c7000_set_busy(STRUCT_NET_DEVICE *dev)
{
	dev->tbusy = 1;
	eieio();
	return;
}
#endif
 
/*
	Clear the device structure transmission busy flag.
*/
 
#ifdef NEWSTUFF
#define c7000_clear_busy(dev) netif_wake_queue(dev)
#else
static __inline__ void
c7000_clear_busy(STRUCT_NET_DEVICE *dev)
{
	dev->tbusy = 0;
	eieio();
	return;
}
#endif
 
/*
	Extract the device structure transmission busy flag.
*/
 
#ifdef NEWSTUFF
#define c7000_check_busy(dev) netif_queue_stopped(dev)
#else
static __inline__ int
c7000_check_busy(STRUCT_NET_DEVICE *dev)
{
	eieio();
	return(dev->tbusy);
}
#endif
 
/*
	Set a bit in the device structure transmission busy flag.
*/
 
static __inline__ void
c7000_setbit_busy(int nr, STRUCT_NET_DEVICE *dev)
{
#ifdef NEWSTUFF
	netif_stop_queue(dev);
	test_and_set_bit(nr, &((struct c7000_controller *)dev->priv)->tbusy);
#else
	set_bit(nr, (void *)&dev->tbusy);
#endif
	return;
}
 
/*
	Clear a bit in the device structure transmission busy flag.
*/
 
static __inline__ void
c7000_clearbit_busy(int nr, STRUCT_NET_DEVICE *dev)
{
#ifdef NEWSTUFF
	clear_bit(nr, &((struct c7000_controller *)dev->priv)->tbusy);
	netif_wake_queue(dev);
#else
	clear_bit(nr, (void *)&dev->tbusy);
#endif
	return;
}
 
/*
	Test and set a bit in the device structure transmission busy flag.
*/
 
static __inline__ int
c7000_ts_busy(int nr, STRUCT_NET_DEVICE *dev)
{
#ifdef NEWSTUFF
	netif_stop_queue(dev);
	return test_and_set_bit(nr, &((struct c7000_controller *)dev->priv)->tbusy);
#else
	return(test_and_set_bit(nr, (void *)&dev->tbusy));
#endif
}
 
/*
	Set the C7000 controller in the error state.
*/
 
static void
c7000_error(struct c7000_controller *ccp)
{
	int			i;
	struct	c7000_unit	*cup;
	STRUCT_NET_DEVICE	*dev = ccp->dev;
 
	for (i = 0; i < NUNITS; i++) {
		cup = &ccp->cunits[i];
		cup->state = C7000_ERROR;
	}
 
	if (dev != NULL)
#ifdef NEWSTUFF
		/* RBH XXX Should we be doing this? */
		dev->state &= ~__LINK_STATE_START;
#else
		dev->flags &= ~IFF_RUNNING;
#endif
 
	CPrintk(0, "c7000: c7000_error: base unit 0x%x is down\n", ccp->base_addr);
	return;
}
 
/*
	Based on the SENSE ID information, fill in the
	controller name.  Note that this is the SENSE ID
	information saved by LINUX/390 at boot time.
*/
 
static int
c7000_check_type(senseid_t *id)
{
 
	switch (id->cu_type) {
 
		case C7000_CU_TYPE:
 
			if (id->cu_model == C7000_CU_MODEL) {
				controller = "C7000  ";
 				return(0);
			}
 
			break;
 
                default:
			break;
	}
 
	return(-1);
}
 
/*
	Check the device information for the controller.
*/
 
static int
c7000_check_devices(int devno)
{
	int		i;
	s390_dev_info_t	temp;
 
	/*
		Get the SENSE ID information for each device.
	*/
 
	for (i = devno; i < (devno + NUNITS); i++) {
 
		if (get_dev_info_by_devno(devno, &temp) != 0)
			return(-1);
 
		if (c7000_check_type(&temp.sid_data) == -1)
			return(-1);
	}
 
	CPrintk(1, "c7000: c7000_check_devices: device type is %s\n", controller);
	return(0);
}
 
/*
	Issue a halt I/O to device pointed to by cup.
*/
 
static int
c7000_haltio(struct c7000_unit *cup)
{
	__u32			parm;
	__u8			flags = 0x00;
	__u32			saveflags;
	DECLARE_WAITQUEUE(wait, current);
	int			rc;
 
	s390irq_spin_lock_irqsave(cup->irq, saveflags);
	parm = (unsigned long)cup;
 
	if ((rc = halt_IO(cup->irq, parm, flags)) != 0) {
		s390irq_spin_unlock_irqrestore(cup->irq, saveflags);
		return(rc);
	}
 
	/*
		Wait for the halt I/O to finish.
	*/
 
	add_wait_queue(&cup->wait, &wait);
	current->state = TASK_UNINTERRUPTIBLE;
	s390irq_spin_unlock_irqrestore(cup->irq, saveflags);
	schedule();
	remove_wait_queue(&cup->wait, &wait);
	return(0);
}
 
/*
	Issue a start I/O to device pointed to by cup.
*/
 
static int
c7000_doio(struct c7000_unit *cup)
{
	__u32			parm;
	__u8			flags = 0x00;
	__u32			saveflags;
	DECLARE_WAITQUEUE(wait, current);
	int			rc;
 
	/*
		Do no further I/O while the device is in the ERROR, STOP
		or STOPPED state.
	*/
 
	if (cup->state == C7000_ERROR || cup->state == C7000_STOP || cup->state == C7000_STOPPED)
		return(-1);
 
	s390irq_spin_lock_irqsave(cup->irq, saveflags);
	parm = (unsigned long)cup;
 
	if ((rc = do_IO(cup->irq, &cup->ccws[0], parm, 0xff, flags)) != 0) {
		s390irq_spin_unlock_irqrestore(cup->irq, saveflags);
		return(rc);
	}
 
	/*
		Wait for the I/O to complete.
	*/
 
	add_wait_queue(&cup->wait, &wait);
	current->state = TASK_UNINTERRUPTIBLE;
	s390irq_spin_unlock_irqrestore(cup->irq, saveflags);
	schedule();
	remove_wait_queue(&cup->wait, &wait);
 
	/*
		Interrupt handling may have marked the device in ERROR.
	*/
 
	if (cup->state == C7000_ERROR)
		return(-1);
 
	return(0);
}
 
/*
	Build a channel program to do a sense id channel program.
*/
 
static void
c7000_bld_senseid_chpgm(struct c7000_unit *cup)
{
	ccw1_t	*ccwp;
 
	ccwp = &cup->ccws[0];
	ccwp->cmd_code = C7000_SID_CCW;
	ccwp->flags = (CCW_FLAG_SLI | CCW_FLAG_CC);
	ccwp->cda = (__u32)virt_to_phys(&cup->senseid);
	ccwp->count = SIDL;
	ccwp++;
	ccwp->cmd_code = C7000_NOOP_CCW;
	ccwp->flags = CCW_FLAG_SLI;
	ccwp->cda = (__u32)NULL;
	ccwp->count = 1;
	return;
}
 
/*
	Build a channel program to write a control message.
*/
 
static void
c7000_bld_wrtctl_chpgm(struct c7000_unit *cup)
{
	ccw1_t	*ccwp;
 
	ccwp = &cup->ccws[0];
	ccwp->cmd_code = C7000_WRITE_CCW;
	ccwp->flags = (CCW_FLAG_SLI | CCW_FLAG_CC);
	ccwp->cda = (__u32)virt_to_phys(&cup->control_blk);
	ccwp->count = sizeof(struct c7000_control_blk);
	ccwp++;
	ccwp->cmd_code = C7000_READFF_CCW;
	ccwp->flags = (CCW_FLAG_SLI | CCW_FLAG_CC);
	ccwp->cda = (__u32)virt_to_phys(&cup->readff);
	ccwp->count = C7000_READFFL;
	ccwp++;
	ccwp->cmd_code = C7000_TIC_CCW;
	ccwp->flags = 0;
	ccwp->cda = (__u32)virt_to_phys(ccwp + 1);
	ccwp->count = 0;
	ccwp++;
	ccwp->cmd_code = C7000_NOOP_CCW;
	ccwp->flags = CCW_FLAG_SLI;
	ccwp->cda = (__u32)NULL;
	ccwp->count = 1;
	return;
}
 
/*
	Build a write channel program to write the indicated buffer.
*/
 
static void
c7000_bld_wrt_chpgm(struct c7000_unit *cup, struct c7000_buffer *buf)
{
	ccw1_t				*ccwp;
	struct	c7000_controller	*ccp = cup->cntlp;
 
	ccwp = &buf->ccws[0];
	ccwp->cmd_code = C7000_WRITE_CCW | (ccp->linkid << 3);
	ccwp->flags = (CCW_FLAG_SLI | CCW_FLAG_CC);
	ccwp->cda = (__u32)virt_to_phys(buf->data);
	ccwp->count = buf->len;
	ccwp++;
	ccwp->cmd_code = C7000_READFF_CCW;
	ccwp->flags = (CCW_FLAG_SLI | CCW_FLAG_CC);
	ccwp->cda = (__u32)virt_to_phys(buf->data + C7000_DATAL + C7000_READHDRL);
	ccwp->count = C7000_READFFL;
	ccwp++;
	ccwp->cmd_code = C7000_TIC_CCW;
	ccwp->flags = 0;
	ccwp->cda = (__u32)virt_to_phys(ccwp + 1);
	ccwp->count = 0;
	ccwp++;
	ccwp->cmd_code = C7000_NOOP_CCW;
	ccwp->flags = (CCW_FLAG_SLI);
	ccwp->cda = (__u32)NULL;
	ccwp->count = 1;
	return;
}
 
/*
	Build a channel program to read a control message.
*/
 
static void
c7000_bld_readctl_chpgm(struct c7000_unit *cup)
{
	ccw1_t	*ccwp;
 
	ccwp = &cup->ccws[0];
	ccwp->cmd_code = C7000_READ_CCW;
	ccwp->flags = (CCW_FLAG_SLI | CCW_FLAG_CC);
	ccwp->cda = (__u32)virt_to_phys(&cup->control_blk);
	ccwp->count = sizeof(struct c7000_control_blk);
	ccwp++;
	ccwp->cmd_code = C7000_READHDR_CCW;
	ccwp->flags = (CCW_FLAG_SLI | CCW_FLAG_CC);
	ccwp->cda = (__u32)virt_to_phys(&cup->readhdr);
	ccwp->count = C7000_READHDRL;
	ccwp++;
	ccwp->cmd_code = C7000_SIGSMOD_CCW;
	ccwp->flags = (CCW_FLAG_SLI | CCW_FLAG_CC);
	ccwp->cda = (__u32)virt_to_phys(&cup->sigsmod);
	ccwp->count = C7000_SIGSMODL;
	ccwp++;
	ccwp->cmd_code = C7000_TIC_CCW;
	ccwp->flags = 0;
	ccwp->cda = (__u32)virt_to_phys(ccwp + 1);
	ccwp->count = 0;
	ccwp++;
	ccwp->cmd_code = C7000_NOOP_CCW;
	ccwp->flags = (CCW_FLAG_SLI);
	ccwp->cda = (__u32)NULL;
	ccwp->count = 1;
	return;
}
 
/*
	Build a channel program to read the indicated buffer.
*/
 
static void
c7000_bld_read_chpgm(struct c7000_unit *cup, struct c7000_buffer *buf)
{
	ccw1_t	*ccwp;
 
	ccwp = &buf->ccws[0];
	ccwp->cmd_code = C7000_READ_CCW;
	ccwp->flags = (CCW_FLAG_SLI | CCW_FLAG_CC);
	ccwp->cda = (__u32)virt_to_phys(buf->data);
	ccwp->count = C7000_DATAL;
	ccwp++;
	ccwp->cmd_code = C7000_READHDR_CCW;
	ccwp->flags = (CCW_FLAG_SLI | CCW_FLAG_CC);
	ccwp->cda = (__u32)virt_to_phys(buf->data + C7000_DATAL);
	ccwp->count = C7000_READHDRL;
	ccwp++;
	ccwp->cmd_code = C7000_SIGSMOD_CCW;
	ccwp->flags = (CCW_FLAG_SLI | CCW_FLAG_CC);
	ccwp->cda = (__u32)virt_to_phys(&cup->sigsmod);
	ccwp->count = C7000_SIGSMODL;
	ccwp++;
	ccwp->cmd_code = C7000_TIC_CCW;
	ccwp->flags = 0;
	ccwp->cda = (__u32)virt_to_phys(ccwp + 3);
	ccwp->count = 0;
	ccwp++;
	ccwp->cmd_code = C7000_READFF_CCW;
	ccwp->flags = (CCW_FLAG_SLI | CCW_FLAG_CC | CCW_FLAG_PCI);
	ccwp->cda = (__u32)virt_to_phys(&cup->readff);
	ccwp->count = C7000_READFFL;
	ccwp++;
	ccwp->cmd_code = C7000_TIC_CCW;
	ccwp->flags = 0;
	ccwp->cda = (__u32)virt_to_phys(ccwp + 1);
	ccwp->count = 0;
	ccwp++;
	ccwp->cmd_code = C7000_NOOP_CCW;
	ccwp->flags = (CCW_FLAG_SLI);
	ccwp->cda = (__u32)NULL;
	ccwp->count = 1;
	return;
}
 
/*
	Allocate buffer structure headers and buffers for all units
	A return value of 0 means that all allocations worked.  A -1
	means that an allocation failed.  It is expected that the caller
	will call c7000_free_buffers when -1 is returned.
*/
 
static int
c7000_alloc_buffers(STRUCT_NET_DEVICE *dev)
{
	int				i;
	int				j;
	char				*data;
	struct	c7000_buffer		*bufptr;
	struct	c7000_controller	*ccp = (struct c7000_controller *) dev->priv;
	struct	c7000_unit		*cup;
 
	for (i = 0; i < NUNITS; i++) {
		cup = &ccp->cunits[i];
		cup->free = NULL;
 
		for (j = 0; j < C7000_MAXBUF; j++) {
			bufptr = kmalloc(sizeof(struct c7000_buffer), GFP_KERNEL);
			data = kmalloc(C7000_BUFSIZE, GFP_KERNEL);
 
			if (bufptr == NULL)
			{
				if(data)
					kfree(data);
				return(-1);
			}
 
			/*
				Place filled in buffer header on free anchor.
			*/
 
			bufptr->next = cup->free;
			bufptr->data = data;
			bufptr->len = 0;
			cup->free = bufptr;
 
			if (data == NULL)
				return(-1);
 
			memset(data, '\0', C7000_BUFSIZE);
		}
 
	}
 
	CPrintk(1, "c7000: c7000_alloc_buffers: allocated buffers for base unit 0x%lx\n", dev->base_addr);
	return(0);
}
 
/*
	Free buffers on a chain.
*/
 
static void
c7000_free_chain(struct c7000_buffer *buf)
{
	char			*data;
	struct	c7000_buffer	*bufptr = buf;
	struct	c7000_buffer	*tmp;
 
	while (bufptr != NULL) {
		data = bufptr->data;
 
		if (data != NULL)
			kfree(data);
 
		tmp = bufptr;
		bufptr = bufptr->next;
		kfree(tmp);
	}
 
	return;
}
 
/*
	Free buffers on all possible chains for all units.
*/
 
static void
c7000_free_buffers(STRUCT_NET_DEVICE *dev)
{
	int				i;
	struct	c7000_controller	*ccp = (struct c7000_controller *) dev->priv;
	struct	c7000_unit	*cup;
 
	for (i = 0; i < NUNITS; i++) {
		cup = &ccp->cunits[i];
		c7000_free_chain(cup->free);
		cup->free = NULL;
		c7000_free_chain(cup->proc_head);
		cup->proc_head = cup->proc_tail = NULL;
		c7000_free_chain(cup->bh_head);
		cup->bh_head = cup->bh_tail = NULL;
	}
 
	CPrintk(1, "c7000: c7000_free_buffers: freed buffers for base unit 0x%lx\n", dev->base_addr);
	return;
}
 
/*
	Obtain a free buffer.  Return a pointer to the c7000_buffer
	structure OR NULL.
*/
 
struct c7000_buffer *
c7000_get_buffer(struct c7000_unit *cup) 
{
	struct	c7000_buffer	*buf;
 
	buf = cup->free;
 
	if (buf == NULL)
		return(NULL);
 
	cup->free = buf->next;
	buf->next = NULL;
	return(buf);
}
 
/*
	Release a buffer to the free list.
*/
 
void
c7000_release_buffer(struct c7000_unit *cup, struct c7000_buffer *buf)
{
	struct	c7000_buffer	*tmp;
 
	tmp = cup->free;
	cup->free = buf;
	buf->next = tmp;
	return;
}
 
/*
	Queue a buffer on the end of the processing (proc) chain.
*/
 
void
c7000_queue_buffer(struct c7000_unit *cup, struct c7000_buffer *buf)
{
	buf->next = NULL;
 
	if (cup->proc_head == NULL) {
		cup->proc_head = cup->proc_tail = buf;
		return;
	}
 
	cup->proc_tail->next = buf;
	cup->proc_tail = buf;
	return;
}
 
/*
	Dequeue a buffer from the start of the processing (proc) chain.
*/
 
struct c7000_buffer *
c7000_dequeue_buffer(struct c7000_unit *cup)
{
	struct	c7000_buffer	*buf = cup->proc_head;
 
	if (buf == NULL)
		return(NULL);
 
	cup->proc_head = buf->next;
 
	if (cup->proc_head == NULL)
		cup->proc_tail = NULL;
 
	buf->next = NULL;
	return(buf);
}
 
/*
	Queue a buffer on the end of the bh routine chain.
*/
 
void
c7000_queue_bh_buffer(struct c7000_unit *cup, struct c7000_buffer *buf)
{
	buf->next = NULL;
 
	if (cup->bh_head == NULL) {
		cup->bh_head = cup->bh_tail = buf;
		return;
	}
 
	cup->bh_tail->next = buf;
	cup->bh_tail = buf;
	return;
}
 
/*
	Dequeue a buffer from the start of the bh routine chain.
*/
 
struct c7000_buffer *
c7000_dequeue_bh_buffer(struct c7000_unit *cup)
{
	struct	c7000_buffer	*buf = cup->bh_head;
 
	if (buf == NULL)
		return(NULL);
 
	cup->bh_head = buf->next;
 
	if (cup->bh_head == NULL)
		cup->bh_tail = NULL;
 
	buf->next = NULL;
	return(buf);
}
 
/*
	Build up a list of buffers to read.  Each buffer is described
	by one c7000_buffer structure.  The c7000_buffer structure
	contains a channel segment that will read that one buffer.
	The channel program segments are chained together via TIC
	CCWS.
*/
 
static int
c7000_bld_read_chain(struct c7000_unit *cup)
{
	struct	c7000_buffer	*buf, *pbuf = NULL;
	struct	c7000_rd_header	*head;
	int			num = 0;
 
	while (cup->free != NULL) {
 
		/*
			Obtain a buffer for a read channel segment.
		*/
 
		if ((buf = c7000_get_buffer(cup)) == NULL) {
			CPrintk(0, "c7000: c7000_bld_read_chain: can not obtain a read buffer for unit 0x%x\n", cup->devno);
			return(-ENOMEM);
		}
 
		num++;
		buf->len = 0;
 
		/*
			Clear out the read header flag.
		*/
 
		head = (struct c7000_rd_header *)(buf->data + C7000_DATAL);
		head->flag = 0x00;
		c7000_queue_buffer(cup, buf);
 
		/*
			Build the read channel program segment.
		*/
 
		c7000_bld_read_chpgm(cup, buf);
 
		/*
			Chain the prior (if any) channel program segment to
			this one.
		*/
 
		if (pbuf != NULL)
			pbuf->ccws[3].cda = pbuf->ccws[5].cda = (__u32)virt_to_phys(&buf->ccws[0]);
 
		pbuf = buf;
	}
 
	CPrintk(1, "c7000: c7000_bld_read_chain: chained %d buffers for unit 0x%x\n", num, cup->devno);
	return(0);
}
 
/*
	Build up a list of buffers to write.  Each buffer is described
	by one c7000_buffer structure.  The c7000_buffer structure
	contains a channel segment that will write that one buffer.
	The channel program segments are chained together via TIC
	CCWS.
*/
 
static void
c7000_bld_wrt_chain(struct c7000_unit *cup)
{
	struct	c7000_buffer	*buf = cup->proc_head, *pbuf = NULL;
	int			num = 0;
 
	while (buf != NULL) {
		c7000_bld_wrt_chpgm(cup, buf);
 
		/*
			Chain the channel program segments together.
		*/
 
		if (pbuf != NULL)
			pbuf->ccws[2].cda = (__u32)virt_to_phys(&buf->ccws[0]);
 
		pbuf = buf;
		buf = buf->next;
		num++;
	}
 
	CPrintk(1, "c7000: c7000_bld_wrt_chain: chained %d buffers for unit 0x%x\n", num, cup->devno);
	return;
}
 
/*
	Interrupt handler bottom half (bh) routine.
	Process all of the buffers on the c7000_unit bh chain.
	The bh chain is populated by the interrupt routine when
	a READ channel program completes on a buffer.
*/
 
static void
c7000_irq_bh(struct c7000_unit *cup)
{
	struct	c7000_buffer		*buf, *pbuf;
	struct	c7000_rd_header		*head;	
	struct	sk_buff			*skb;
	struct	c7000_controller	*ccp;
	STRUCT_NET_DEVICE		*dev;
	int				rc;
	__u16				data_length;
	__u32				parm;
	__u8				flags = 0x00;
	__u32				saveflags;
 
	ccp = cup->cntlp;
	dev = ccp->dev;
 
	s390irq_spin_lock_irqsave(cup->irq, saveflags);
 
	/*
		Process all buffers sent to bh by the interrupt routine.
	*/
 
	while (cup->bh_head != NULL) {
		buf = c7000_dequeue_bh_buffer(cup);
 
		/*
			Deference the data as a c7000 header.
		*/
 
		head = (struct c7000_rd_header *)(buf->data + C7000_DATAL);
 
		/*
			If it is a control message, release the buffer and 
			continue the loop.
		*/
 
		if (C7000_LINKID(head->cmd) == 0) {
			CPrintk(0, "c7000: c7000_irq_bh: unexpected control command %d on unit 0x%x\n", head->cmd, cup->devno);
			c7000_release_buffer(cup, buf);
			continue;
		}
 
		/*
			Allocate a socket buffer.
		*/
 
		data_length = head->len;
		skb = dev_alloc_skb(data_length);
 
		/*
			Copy the data to the skb.
			Send it to the upper layers.
		*/
 
		if (skb != NULL) {
			memcpy(skb_put(skb, data_length), buf->data, data_length);
			skb->dev = dev;
			skb->protocol = htons(ETH_P_IP);
			skb->pkt_type = PACKET_HOST;
			skb->mac.raw = skb->data;
			skb->ip_summed = CHECKSUM_UNNECESSARY;
			netif_rx(skb);
			ccp->stats.rx_packets++;
		} else {
			CPrintk(0, "c7000: c7000_irq_bh: can not allocate a skb for unit 0x%x\n", cup->devno);
			ccp->stats.rx_dropped++;
		}
 
		/*
			Rechain the buffer on the processing list.
		*/
 
		head->flag = 0x00;
		buf->len = 0;
		pbuf = cup->proc_tail;
		c7000_queue_buffer(cup, buf);
 
		/*
			Rechain the buffer on the running channel program.
		*/
 
		if (pbuf != NULL)
			pbuf->ccws[3].cda = pbuf->ccws[5].cda = (__u32)virt_to_phys(&buf->ccws[0]);
 
	}
 
	/*
		Restart the READ channel program if IO_active is 0.
	*/
 
	if (test_and_set_bit(0, (void *)&cup->IO_active) == 0) {
 
		if ((rc = c7000_bld_read_chain(cup)) != 0) {
			CPrintk(0, "c7000: c7000_irq_bh: can not build read chain for unit 0x%x, return code %d\n", cup->devno, rc);
			c7000_error(cup->cntlp);
			clear_bit(C7000_BH_ACTIVE, (void *)&cup->flag_a);
			s390irq_spin_unlock_irqrestore(cup->irq, saveflags);
			return;
		}
 
		parm = (__u32)cup;
		cup->state = C7000_READ;
 
		if ((rc = do_IO(cup->irq, &cup->proc_head->ccws[0], parm, 0xff, flags)) != 0) {
			CPrintk(0, "c7000: c7000_irq_bh: can not start READ IO to unit 0x%x, return code %d\n", cup->devno, rc);
			c7000_error(cup->cntlp);
			clear_bit(C7000_BH_ACTIVE, (void *)&cup->flag_a);
			s390irq_spin_unlock_irqrestore(cup->irq, saveflags);
			return;
		}
 
		CPrintk(1, "c7000: c7000_irq_bh: started READ IO to unit 0x%x\n", cup->devno);
	}
 
	/*
		Clear the bh active indication.
	*/
 
	clear_bit(C7000_BH_ACTIVE, (void *)&cup->flag_a);
	s390irq_spin_unlock_irqrestore(cup->irq, saveflags);
	return;
}
 
/*
	Send a system validate control command to a unit.
*/
 
static int
c7000_send_sysval(struct c7000_unit *cup)
{
	int				rc;
	struct	c7000_controller	*ccp = cup->cntlp;
	struct	c7000_control_blk	*ctlblkp = &(cup->control_blk);
 
	CPrintk(1, "c7000: c7000_send_sysval: send sysval for device 0x%x\n", cup->devno);
 
	/*
		Build the system validate control message.
	*/
 
	memset(ctlblkp, '\0', sizeof(struct c7000_control_blk));
	ctlblkp->cmd = C7000_SYS_VALIDATE;
	ctlblkp->correlator = 0;
	ctlblkp->link_id = ccp->linkid;
	ctlblkp->ver = ccp->version; 
	memcpy(ctlblkp->hostname, ccp->lhost, NAMLEN);
	memcpy(ctlblkp->unitname, ccp->uhost, NAMLEN);
	ctlblkp->rdsize = C7000_DATAL;
        ctlblkp->wrtsize = C7000_DATAL;
 
	/*
		Build the channel program.
	*/
 
	c7000_bld_wrtctl_chpgm(cup);
 
	/*
		Do the IO and wait for write to complete.
	*/
 
	if ((rc = c7000_doio(cup)) != 0) {
		CPrintk(0, "c7000: c7000_send_sysval failed with rc = %d for unit 0x%x\n", rc, cup->devno);
		return(-1);
	}
 
	return(0);
}
 
/*
	Send a system validate response control command to a unit.
*/
 
static int
c7000_send_sysval_resp(struct c7000_unit *cup, unsigned char correlator, int ret_code)
{
	int				rc;
	struct	c7000_controller	*ccp = cup->cntlp;
	struct	c7000_control_blk	*ctlblkp = &(cup->control_blk);
 
	CPrintk(1, "c7000: c7000_send_sysval_resp: send sysval response for device 0x%x\n", cup->devno);
 
	/*
		Build the system validate response control message.
	*/
 
	memset(ctlblkp, '\0', sizeof(struct c7000_control_blk));
	ctlblkp->cmd = C7000_SYS_VALIDATE_RESP;
	ctlblkp->correlator = correlator;
	ctlblkp->ret_code = ret_code;
	ctlblkp->link_id = ccp->linkid;
	ctlblkp->ver = ccp->version; 
	memcpy(ctlblkp->hostname, ccp->lhost, NAMLEN);
	memcpy(ctlblkp->unitname, ccp->uhost, NAMLEN);
	ctlblkp->rdsize = C7000_DATAL;
        ctlblkp->wrtsize = C7000_DATAL;
 
	/*
		Build the channel program.
	*/
 
	c7000_bld_wrtctl_chpgm(cup);
 
	/*
		Do the IO and wait for write to complete.
	*/
 
	if ((rc = c7000_doio(cup)) != 0) {
		CPrintk(0, "c7000: c7000_send_sysval_resp failed with rc = %d for unit 0x%x\n", rc, cup->devno);
		return(-1);
	}
 
	return(0);
}
 
/*
	Check the information read in a SYS_VALIDATE control message.
*/
 
static int
c7000_checkinfo(struct c7000_unit *cup)
{
	struct	c7000_controller	*ccp = cup->cntlp;
 	struct	c7000_control_blk	*ctlblkp = &cup->control_blk;
	int				ret_code = 0;
 
	if (memcmp(ccp->lhost, ctlblkp->hostname, NAMLEN) ||
		memcmp(ccp->uhost, ctlblkp->unitname, NAMLEN))
		ret_code = Err_Names_not_Matched;
 
	if (ctlblkp->ver != ccp->version)
		ret_code = Err_Wrong_Version;
 
        if ((ctlblkp->rdsize < C7000_DATAL) || (ctlblkp->wrtsize < C7000_DATAL))
		ret_code = Err_Wrong_Frame_Size;
 
	if (ret_code != 0)
		CPrintk(0, "c7000: c7000_checkinfo: ret_code %d for device 0x%x\n", ret_code, cup->devno);
 
	return(ret_code);
}
 
/*
	Keep reading until a sysval response comes in or an error.
*/
 
static int
c7000_get_sysval_resp(struct c7000_unit *cup)
{
	struct	c7000_controller	*ccp = cup->cntlp;
	int				resp = 1;
	int				req = 1;
	int				rc;
	int				ret_code = 0;
 
	CPrintk(1, "c7000: c7000_get_sysval_resp: get sysval response for unit 0x%x\n", cup->devno);
 
	/*
		Wait for the response to C7000_SYS_VALIDATE and for an
		inbound C7000_SYS_VALIDATE.
	*/
 
	while (resp || req) {
 
		/*
			Build the read channel program.
		*/
 
		c7000_bld_readctl_chpgm(cup);
 
		if ((rc = c7000_doio(cup)) != 0) {
			CPrintk(0, "c7000: c7000_get_sysval_resp: failed with rc = %d for unit 0x%x\n", rc, cup->devno);
			return(-1);
		}
 
		/*
			Process the control message.
		*/
 
		switch (cup->control_blk.cmd) {
 
			/*
				Check that response is positive and return
				with success. Otherwise, return with an
				error.
			*/
 
			case C7000_SYS_VALIDATE_RESP:
 
				if (cup->control_blk.ret_code == 0)
					resp = 0;
				else {
					CPrintk(0, "c7000: c7000_get_sysval_resp: receive sysval response for device 0x%x, return code %d\n",
						cup->devno,
						cup->control_blk.ret_code);
					return(-1);
				}
 
				break;
 
			/*
				Check that the request is reasonable and
				send a SYS_VALIDATE_RESP.  Otherwise,
				return with an error.
			*/
 
			case C7000_SYS_VALIDATE:
				CPrintk(1, "c7000: c7000_get_sysval_resp: receive sysval for device 0x%x\n", cup->devno);
				req = 0;
				ret_code = c7000_checkinfo(cup);
 
				if (c7000_send_sysval_resp(&ccp->cunits[C7000_WR], cup->control_blk.correlator, ret_code) != 0)
					return(-1);
 
				if (ret_code != 0)
					return(-1);
 
				break;
 
			/*
				Anything else is unexpected and will result
				in a return with an error.
			*/
 
			default:
				CPrintk(0, "c7000: c7000_get_sysval_resp: receive unexpected command for device 0x%x, command %d\n", cup->devno, cup->control_blk.cmd);
				return(-1);
				break;
		}
 
	}
 
	return(0);
}
 
/*
	Send a connection confirm control message.
*/
 
static int
c7000_conn_confrm(struct c7000_unit *cup, unsigned char correlator, int linkid)
{
	int				rc;
	struct	c7000_controller	*ccp = cup->cntlp;
	struct	c7000_control_blk	*ctlblkp = &(cup->control_blk);
 
	CPrintk(1, "c7000: c7000_conn_confrm: send the connection confirmation message for unit 0x%x\n", cup->devno);
 
	/*
		Build the connection confirm control message.
	*/
 
	memset(ctlblkp, '\0', sizeof(struct c7000_control_blk));
	ctlblkp->cmd = C7000_CONN_CONFRM;
	ctlblkp->ver = ccp->version;
	ctlblkp->link_id = linkid;
	ctlblkp->correlator = correlator;
	ctlblkp->rdsize = 0;
	ctlblkp->wrtsize = 0;
	memcpy(ctlblkp->hostname, ccp->lappl, NAMLEN);
	memcpy(ctlblkp->unitname, ccp->uappl, NAMLEN);
 
	/*
		Build the channel program.
	*/
 
	c7000_bld_wrtctl_chpgm(cup);
 
	/*
		Do the IO and wait for write to complete.
	*/
 
	if ((rc = c7000_doio(cup)) != 0) {
		CPrintk(0, "c7000: c7000_conn_confrm: failed with rc = %d for unit 0x%x\n", rc, cup->devno);
		return(-1);
	}
 
	return(0);
}
 
/*
	Send a connection request control message.
*/
 
static int
c7000_send_conn(struct c7000_unit *cup)
{
	int				rc;
	struct	c7000_controller	*ccp = cup->cntlp;
	struct	c7000_control_blk	*ctlblkp = &(cup->control_blk);
 
	CPrintk(1, "c7000: c7000_send_conn: send the connection request message for unit 0x%x\n", cup->devno);
 
	/*
		Build the connection request control message.
	*/
 
	memset(ctlblkp, '\0', sizeof(struct c7000_control_blk));
	ctlblkp->cmd = C7000_CONN_REQ;
	ctlblkp->ver = ccp->version;
	ctlblkp->link_id = 0;
	ctlblkp->correlator = 0;
	ctlblkp->rdsize = C7000_DATAL;
	ctlblkp->wrtsize = C7000_DATAL;
	memcpy(ctlblkp->hostname, ccp->lappl, NAMLEN);
	memcpy(ctlblkp->unitname, ccp->uappl, NAMLEN);
 
	/*
		Build the channel program.
	*/
 
	c7000_bld_wrtctl_chpgm(cup);
 
	/*
		Do the IO and wait for write to complete.
	*/
 
	if ((rc = c7000_doio(cup)) != 0) {
		CPrintk(0, "c7000: c7000_send_conn: failed with rc = %d for unit 0x%x\n", rc, cup->devno);
		return(-1);
	}
 
	return(0);
}
 
/*
	Send a disconnect control message to the link with the value of
	linkid.
*/
 
static int
c7000_send_disc(struct c7000_unit *cup, int linkid)
{
	int				rc;
	struct	c7000_controller	*ccp = cup->cntlp;
	struct	c7000_control_blk	*ctlblkp = &(cup->control_blk);
 
	CPrintk(1, "c7000: c7000_send_disc: send disconnect message for unit 0x%x\n", cup->devno);
 
	/*
		Build the disconnect control message.
	*/
 
	memset(ctlblkp, '\0', sizeof(struct c7000_control_blk));
	ctlblkp->cmd = C7000_DISCONN;
	ctlblkp->ver = ccp->version;
	ctlblkp->link_id = linkid;
	ctlblkp->correlator = 0;
	ctlblkp->rdsize = C7000_DATAL;
	ctlblkp->wrtsize = C7000_DATAL;
	memcpy(ctlblkp->hostname, ccp->lappl, NAMLEN);
	memcpy(ctlblkp->unitname, ccp->uappl, NAMLEN);
 
	/*
		Build the channel program.
	*/
 
	c7000_bld_wrtctl_chpgm(cup);
 
	/*
		Do the IO and wait for write to complete.
	*/
 
	if ((rc = c7000_doio(cup)) != 0) {
		CPrintk(0, "c7000: c7000_send_disc: failed with rc = %d for unit 0x%x\n", rc, cup->devno);
		return(-1);
	}
 
	return(0);
}
 
/*
	Resolve the race condition based on the link identifier value.
	The adapter microcode assigns the identifiers.  A higher value implies
	that the race was lost. A side effect of this function is that
	ccp->linkid is set to the link identifier to be used for this
	connection (provided that 0 is returned).
*/
 
static int
c7000_resolve_race(struct c7000_unit *cup, int local_linkid, int remote_linkid)
{
	struct c7000_controller 	*ccp = cup->cntlp;
 
	CPrintk(1, "c7000: c7000_resolve_race: for unit 0x%x, local linkid %d, remote linkid %d\n", cup->devno, local_linkid, remote_linkid);
 
	/*
		This local link identifier should not be zero..
	*/
 
	if (local_linkid == 0) {
		CPrintk(0, "c7000: c7000_resolve_race: error for unit 0x%x, local linkid is null\n", cup->devno);
		return(-1);
	}
 
	/*
		This indicates that there is no race.  Just use our
		local link identifier.
	*/
 
	if (remote_linkid == 0) {
		ccp->linkid = local_linkid;
		return(0);
	}
 
	/*
		Send a connection confirm message if we lost the race to
		the winning link identifier.
 
		Either way, save the winning link identifier.
	*/
 
	if (local_linkid > remote_linkid) {
 
		if (c7000_conn_confrm(&ccp->cunits[C7000_WR], cup->control_blk.correlator, remote_linkid) != 0) {
			CPrintk(0, "c7000: c7000_resolve_race: failed for unit 0x%x\n", cup->devno);
			return(-1);
		}
 
		ccp->linkid = remote_linkid;
	} else {
		ccp->linkid = local_linkid;
	}
 
	return(0);
}
 
/*
	Get connected by processing the connection request/response/confirm
	control messages.  A connection request has already been sent by
	calling function c7000_send_conn.
*/
 
static int
c7000_get_conn(struct c7000_unit *cup)
{
	struct c7000_controller 	*ccp = cup->cntlp;
	int				rc;
	int				cont = 1;
	int				remote_linkid = 0;
	int				local_linkid = 0;
 
	CPrintk(1, "c7000: c7000_get_conn: read the connected message for unit 0x%x\n", cup->devno);
	ccp->linkid = 0;
 
	while (cont == 1) {
 
		/*
			Build the read channel program.
		*/
 
		c7000_bld_readctl_chpgm(cup);
 
		/*
			Start the channel program to read a control message.
		*/
 
		if ((rc = c7000_doio(cup)) != 0) {
			CPrintk(0, "c7000: c7000_get_conn: failed with rc = %d for unit 0x%x\n", rc, cup->devno);
			return(-1);
		}
 
		/*
			Process the control message that was received based
			on the command code.
		*/
 
		CPrintk(1, "c7000: c7000_get_conn: received command %d for unit 0x%x\n", cup->control_blk.cmd, cup->devno);
 
		switch(cup->control_blk.cmd) {
 
			/*
				Save the remote link_id in the message for
				a check in c7000_resolve_race.
			*/
 
			case C7000_CONN_REQ:
				remote_linkid = cup->control_blk.link_id;
				break;
 
			/*
				A connection response received.  Resolve
				the network race condition (if any) by
				comparing the link identifier values.
			*/
 
			case C7000_CONN_RESP:
 
				if (cup->control_blk.ret_code != 0) {
					CPrintk(0, "c7000: c7000_get_conn: failed for unit 0x%x , connection response return code %d\n",
						cup->devno, cup->control_blk.ret_code);
					return(-1);
				}
 
				local_linkid = cup->control_blk.link_id;
 
				if (c7000_resolve_race(cup, local_linkid, remote_linkid) != 0)
					return(-1);
 
				break;
 
			/*
				Got a confirmation to our connection request.
				Disconnect the remote link identifier (if any).
				Break out of the loop.
			*/
 
			case C7000_CONN_CONFRM:
 
				if (remote_linkid != 0) {
 
					if (c7000_send_disc(&ccp->cunits[C7000_WR], remote_linkid) != 0) {
						CPrintk(0, "c7000: c7000_get_conn: send disconnect failed for unit 0x%x\n", cup->devno);
						return(-1);
					}
 
				}
 
				cont = 0;
				break;
 
			/*
				Got a disconnect to our connection request.
				Break out of the loop.
			*/
 
			case C7000_DISCONN:
				cont = 0;
				break;
 
			/*
				Anything else must be an error.
				Return with an error immediately.
			*/
 
			default:
				CPrintk(0, "c7000: c7000_get_conn: failed for unit 0x%x unexpected command %d\n",
					cup->devno, cup->control_blk.cmd);
				return(-1);
		}
 
	}
 
	/*
		Be sure that we now have a link identifier.
	*/
 
	if (ccp->linkid == 0)
		return(-1);
 
	return(0);
}
 
/*
	Get statistics method.
*/
 
struct net_device_stats *
c7000_stats(STRUCT_NET_DEVICE *dev)
{
	struct	c7000_controller	*ccp = (struct c7000_controller *)dev->priv;
 
	return(&ccp->stats);
}
 
/*
	Open method.
*/
 
static int
c7000_open(STRUCT_NET_DEVICE *dev)
{
	int				i;
	struct	c7000_controller	*ccp = (struct c7000_controller *)dev->priv;
	struct	c7000_unit		*cup;
	int				rc;
	__u32				parm;
	__u8				flags = 0x00;
 
	c7000_set_busy(dev);
 
	/*
		Allocate space for the unit buffers.
	*/
 
	if (c7000_alloc_buffers(dev) == -1) {
		CPrintk(0, "c7000: c7000_open: can not allocate buffer space for base unit 0x%lx\n", dev->base_addr);
		c7000_free_buffers(dev);  /* free partially allocated buffers */
		c7000_clear_busy(dev);
		return(-ENOMEM);
	}
 
	/*
		Perform the initialization for all units.
	*/
 
	for (i = 0; i < NUNITS; i++) {
		cup = &ccp->cunits[i];
 
		/*
			Initialize task queue structure used for the bottom
			half routine.
		*/
 
#ifndef NEWSTUFF
		cup->tq.next = NULL;
#endif
		cup->tq.sync = 0;
		cup->tq.routine = (void *)(void *)c7000_irq_bh;
		cup->tq.data = cup;
		cup->state = C7000_HALT;
#ifdef NEWSTUFF
		init_waitqueue_head(&cup->wait);
#endif
		CPrintk(1, "c7000: c7000_open: issuing halt to unit 0x%x\n", cup->devno);
 
		/*
			Issue a halt I/O to the unit
		*/
 
		if ((rc = c7000_haltio(cup)) != 0) {
			CPrintk(0, "c7000: c7000_open: halt_IO failed with rc = %d for unit 0x%x\n", rc, cup->devno);
			continue;
		}
 
		cup->IO_active = 0;
		cup->flag_a = 0;
		cup->sigsmod = 0x00;
 
		CPrintk(1, "c7000: c7000_open: halt complete for unit 0x%x\n", cup->devno);
	}
 
	/*
		On each subchannel send a sense id.
	*/
 
	for (i = 0; i < NUNITS; i++) {
		cup = &ccp->cunits[i];
 
		/*
			Build SENSE ID channel program.
		*/
 
		c7000_bld_senseid_chpgm(cup);
 
		/*
			Issue the start I/O for SENSE ID channel program.
		*/
 
		CPrintk(1, "c7000: c7000_open: issuing SENSEID to unit 0x%x\n", cup->devno);
 
		if ((rc = c7000_doio(cup)) != 0) {
			CPrintk(0, "c7000: c7000_open: SENSEID failed with rc = %d for unit 0x%x\n", rc, cup->devno);
			c7000_clear_busy(dev);
			return(-EIO);
		}
 
		CPrintk(1, "c7000: c7000_open: SENSEID complete for unit 0x%x\n", cup->devno);
	}
 
	/*
		Send the system validation control message.
	*/
 
	cup = &ccp->cunits[C7000_WR];
 
	if (c7000_send_sysval(cup) != 0) {
		CPrintk(0, "c7000: c7000_open: can not send sysval for unit 0x%x\n", cup->devno);
		c7000_clear_busy(dev);
		return(-EIO);
	}
 
	CPrintk(1, "c7000: c7000_open: successfully sent sysval for unit 0x%x\n", cup->devno);
	/*
		Get the system validation response message.
	*/
 
	cup = &ccp->cunits[C7000_RD];
 
	if (c7000_get_sysval_resp(cup) != 0) {
		CPrintk(0, "c7000: c7000_open: can not read sysval response for unit 0x%x\n", cup->devno);
		c7000_clear_busy(dev);
		return(-EIO);
	}
 
	CPrintk(1, "c7000: c7000_open: successfully received sysval reply for unit 0x%x\n", cup->devno);
	ccp->cunits[C7000_RD].state = ccp->cunits[C7000_WR].state = C7000_CONNECT;
 
	cup = &ccp->cunits[C7000_WR];
 
	/*
		Send a connection request.
	*/
 
	if (c7000_send_conn(cup) != 0) {
		CPrintk(0, "c7000: c7000_open: connection failed for unit 0x%x\n", cup->devno);
		c7000_clear_busy(dev);
		return(-EIO);
	}
 
	cup = &ccp->cunits[C7000_RD];
 
	/*
		Get the response to our connection request Note that a
		network race may occur.  This is handled in c7000_get_conn.
	*/
 
	if (c7000_get_conn(cup) != 0) {
		CPrintk(0, "c7000: c7000_open: unit 0x%x has connected\n", cup->devno);
		c7000_clear_busy(dev);
		return(-EIO);
	}
 
	CPrintk(1, "c7000: c7000_open: successfully received connection request for unit 0x%x\n", cup->devno);
	ccp->cunits[C7000_RD].state = ccp->cunits[C7000_WR].state = C7000_READY;
 
	/*
		Clear the interface statistics.
	*/
 
	memset(&ccp->stats, '\0', sizeof(struct net_device_stats));
 
	if ((rc = c7000_bld_read_chain(cup)) != 0) {
		c7000_clear_busy(dev);
		return(rc);
	}
 
	/*
		Start the C7000_READ channel program but do not wait for it's
		completion.
	*/
 
	cup->state = C7000_READ;
	parm = (__u32) cup;
	set_bit(0, (void *)&cup->IO_active);
 
	if ((rc = do_IO(cup->irq, &cup->proc_head->ccws[0], parm, 0xff, flags)) != 0) {
		CPrintk(0, "c7000: c7000_open: READ failed with return code %d for unit 0x%x\n", rc, cup->devno);
		c7000_error(cup->cntlp);
		clear_bit(0, (void *)&cup->IO_active);
		c7000_clear_busy(dev);
		return(-EIO);
	}
 
#ifdef NEWSTUFF
	netif_start_queue(dev);
#else
	dev->start = 1;
#endif
	CPrintk(0, "c7000: c7000_open: base unit 0x%lx is opened\n", dev->base_addr);
	c7000_clear_busy(dev);
	MOD_INC_USE_COUNT;	/* increment module usage count */
	return(0);
}
 
/*
	Stop method.
*/
 
static int
c7000_stop(STRUCT_NET_DEVICE *dev)
{
	int				i;
	struct	c7000_controller	*ccp = (struct c7000_controller *)dev->priv;
	struct	c7000_unit		*cup;
	int				rc;
 
#ifdef NEWSTUFF
/* nothing? */
#else
	dev->start = 0;
#endif
	c7000_set_busy(dev);
 
	/*
		Send a disconnect message.
	*/
 
	ccp->cunits[C7000_RD].state = ccp->cunits[C7000_WR].state = C7000_DISC;
	cup = &ccp->cunits[C7000_WR];
 
	if (c7000_send_disc(cup, ccp->linkid) != 0) {
		CPrintk(0, "c7000: c7000_stop: send of disconnect message failed for unit 0x%x\n", cup->devno);
	}
 
	CPrintk(1, "c7000: c7000_stop: successfully sent disconnect message to unit 0x%x\n", cup->devno);
 
	/*
		Issue a halt I/O to all units.
	*/
 
	for (i = 0; i < NUNITS; i++) {
		cup = &ccp->cunits[i];
		cup->state = C7000_STOP;
		CPrintk(1, "c7000: c7000_stop: issuing halt to unit 0x%x\n", cup->devno);
 
		if ((rc = c7000_haltio(cup)) != 0) {
			CPrintk(0, "c7000: c7000_stop: halt_IO failed with rc = %d for unit 0x%x\n", rc, cup->devno);
			continue;
		}
 
		CPrintk(1, "c7000: c7000_stop: halt complete for unit 0x%x\n", cup->devno);
	}
 
	c7000_free_buffers(dev);
	CPrintk(0, "c7000: c7000_stop: base unit 0x%lx is stopped\n", dev->base_addr);
	MOD_DEC_USE_COUNT;	/* Decrement module usage count */
	return(0);
}
 
/*
	Configure the interface.
*/
 
static int
c7000_config(STRUCT_NET_DEVICE *dev, struct ifmap *map)
{
	CPrintk(1, "c7000: c7000_config: entered for base unit 0x%lx\n", dev->base_addr);
	return(0);
}
 
/*
	Transmit a packet.
*/
 
static int
c7000_xmit(struct sk_buff *skb, STRUCT_NET_DEVICE *dev)
{
	struct	c7000_controller	*ccp = (struct c7000_controller *)dev->priv;
	struct	c7000_unit		*cup;
	__u32				saveflags;
	__u32				parm;
	__u8				flags = 0x00;
	struct	c7000_buffer		*buf, *pbuf;
	int				rc;
 
	CPrintk(1, "c7000: c7000_xmit: entered for base unit 0x%lx\n", dev->base_addr);
 
	/*
		When the skb pointer is NULL return.
	*/
 
	if (skb == NULL) {
		CPrintk(0, "c7000: c7000_xmit: skb pointer is null for base unit 0x%lx\n", dev->base_addr);
		ccp->stats.tx_dropped++;
		return(-EIO);
	}
 
	cup = &ccp->cunits[C7000_WR];
 
	/*
		Lock the irq.
	*/
 
	s390irq_spin_lock_irqsave(cup->irq, saveflags);
 
	/*
		When the device transmission busy flag is on , no data
		can be sent.  Unlock the irq and return EBUSY.
	*/
 
	if (c7000_check_busy(dev)) {
		CPrintk(1, "c7000: c7000_xmit: c7000_check_busy returns true for base unit 0x%lx\n", dev->base_addr);
		s390irq_spin_unlock_irqrestore(cup->irq, saveflags);
		return(-EBUSY);
	}
 
	/*
		Set the device transmission busy flag on atomically.
	*/
 
	if (c7000_ts_busy(TB_TX, dev)) {
		CPrintk(1, "c7000: c7000_xmit: c7000_ts_busy returns true for base unit 0x%lx\n", dev->base_addr);
		s390irq_spin_unlock_irqrestore(cup->irq, saveflags);
		return(-EBUSY);
	}
 
	CPrintk(1, "c7000: c7000_xmit: set TB_TX for unit 0x%x\n", cup->devno);
 
	/*
		Obtain a free buffer.  If none are free then mark tbusy
		with TB_NOBUFFER and return EBUSY.
	*/
 
	if ((buf = c7000_get_buffer(cup)) == NULL) {
		CPrintk(1, "c7000: c7000_xmit: setting TB_NOBUFFER for base unit 0x%lx\n", dev->base_addr);
		c7000_setbit_busy(TB_NOBUFFER, dev);
		c7000_clearbit_busy(TB_TX, dev);
		s390irq_spin_unlock_irqrestore(cup->irq, saveflags);
		return(-EBUSY);
	}
 
	CPrintk(1, "c7000: c7000_xmit: Got buffer for unit 0x%x\n", cup->devno);
 
	/*
		Save the length of the skb data and then copy it to the
		buffer.  Queue the buffer on the processing list.
	*/
 
	buf->len = skb->len;
	memcpy(buf->data, skb->data, skb->len);
	memset(buf->data + C7000_DATAL + C7000_READHDRL, '\0', C7000_READFFL);
	pbuf = cup->proc_tail;
	c7000_queue_buffer(cup, buf);
 
	/*
		Chain the buffer to the running channel program.
	*/
 
	if (test_bit(0, (void *)&cup->IO_active) && pbuf != NULL) {
		c7000_bld_wrt_chpgm(cup, buf);
		pbuf->ccws[2].cda = (__u32)virt_to_phys(&buf->ccws[0]);
	}
 
	/*
		Free the socket buffer.
	*/
 
	dev_kfree_skb(skb);
 
	/*
		If the unit is not currently doing IO, build a channel
		program and start the IO for the buffers on the processing
		chain.
	*/
 
	if (test_and_set_bit(0, (void *)&cup->IO_active) == 0) {
		CPrintk(1, "c7000: c7000_xmit: start IO for unit 0x%x\n", cup->devno);
		c7000_bld_wrt_chain(cup);
		parm = (__u32) cup;
		cup->state = C7000_WRITE;
 
		if ((rc = do_IO(cup->irq, &cup->proc_head->ccws[0], parm, 0xff, flags)) != 0) {
			CPrintk(0, "c7000: c7000_xmit: do_IO failed with return code %d for unit 0x%x\n", rc, cup->devno);
			c7000_error(cup->cntlp);
			c7000_clearbit_busy(TB_TX, dev);
			s390irq_spin_unlock_irqrestore(cup->irq, saveflags);
			return(-EIO);
		}
 
		dev->trans_start = jiffies;
		CPrintk(1, "c7000: c7000_xmit: do_IO succeeds for unit 0x%x\n", cup->devno);
	}
 
	/*
		If the free chain is now NULL, set the TB_NOBUFFER flag.
	*/
 
	if (cup->free == NULL) {
		CPrintk(1, "c7000: c7000_xmit: setting TB_NOBUFFER for base unit 0x%lx\n", dev->base_addr);
		c7000_setbit_busy(TB_NOBUFFER, dev);
	}
 
	c7000_clearbit_busy(TB_TX, dev);
	s390irq_spin_unlock_irqrestore(cup->irq, saveflags);
	CPrintk(1, "c7000: c7000_xmit: exits for unit 0x%x\n", cup->devno);
	return(0);
}
 
/*
	Handle an ioctl from a user process.
*/
 
static int
c7000_ioctl(STRUCT_NET_DEVICE *dev, struct ifreq *ifr, int cmd)
{
	CPrintk(1, "c7000: c7000_ioctl: entered for base unit 0x%lx with cmd %d\n", dev->base_addr, cmd);
	return(0);
}
 
/*
	Analyze the interrupt status and return a value
	that identifies the type.
*/
 
static enum c7000_rupt
c7000_check_csw(devstat_t *devstat)
{
 
	/*
		Check for channel detected conditions (except PCI).
	*/
 
	if ((devstat->cstat & ~SCHN_STAT_PCI) != 0) {
		CPrintk(0, "c7000: c7000_check_csw: channel status 0x%x for unit 0x%x\n", devstat->cstat, devstat->devno);
		return(C7000_CHANERR);
	}
 
	/*
		Fast path the normal cases.
	*/
 
	if (devstat->dstat == (DEV_STAT_CHN_END | DEV_STAT_DEV_END))
		return(C7000_NORMAL);
 
	if (devstat->cstat == SCHN_STAT_PCI)
		return(C7000_NORMAL);
 
	/*
		Check for exceptions.
	*/
 
	if (devstat->dstat & DEV_STAT_UNIT_CHECK) {
		CPrintk(0, "c7000: c7000_check_csw: unit check for unit 0x%x, sense byte0 0x%2.2x\n", devstat->devno, devstat->ii.sense.data[0]);
 
		if (devstat->ii.sense.data[0] == C7000_BOX_RESET)
			return(C7000_UCK_RESET);
		else
			return(C7000_UCK);
 
	} else if (devstat->dstat & DEV_STAT_UNIT_EXCEP) {
		CPrintk(0, "c7000: c7000_check_csw: unit exception for unit 0x%x\n", devstat->devno);
		return(C7000_UE);
 
	} else if (devstat->dstat & DEV_STAT_ATTENTION) {
		CPrintk(0, "c7000: c7000_check_csw: attention for unit 0x%x\n", devstat->devno);
		return(C7000_ATTN);
 
	} else if (devstat->dstat & DEV_STAT_BUSY) {
		CPrintk(0, "c7000: c7000_check_csw: busy for unit 0x%x\n", devstat->devno);
		return(C7000_BUSY);
 
	} else {
		CPrintk(0, "c7000: c7000_check_csw: channel status 0x%2.2x , device status 0x%2.2x, devstat flags 0x%8.8x for unit 0x%x\n",
			devstat->cstat, devstat->dstat, devstat->flag, devstat->devno);
		return(C7000_OTHER);
	}
 
	/* NOT REACHED */
 
}
 
/*
	Retry the last CCW chain to the unit.
*/
 
static void
c7000_retry_io(struct c7000_unit *cup)
{
	int	rc;
	__u32	parm;
	__u8	flags = 0x00;
	ccw1_t	*ccwp;
 
	if (++cup->retries > C7000_MAX_RETRIES) {
		c7000_error(cup->cntlp);
		CPrintk(0, "c7000: c7000_retry_io: retry IO for unit 0x%x exceeds maximum retry count\n", cup->devno);
		return;
	}
 
	set_bit(0, (void *)&cup->IO_active);
	parm = (__u32)cup;
 
	if (cup->state == C7000_READ || cup->state == C7000_WRITE)
		ccwp = &cup->proc_head->ccws[0];
	else
		ccwp = &cup->ccws[0];
 
	if ((rc = do_IO(cup->irq, ccwp, parm, 0xff, flags)) != 0) {
		CPrintk(0, "c7000: c7000_retry_io: can not retry IO for unit 0x%x, return code %d\n", cup->devno, rc);
		clear_bit(0, (void *)&cup->IO_active);
		c7000_error(cup->cntlp);
	}
 
	CPrintk(1, "c7000: c7000_retry_io: retry IO for unit 0x%x, retry count %d\n", cup->devno, cup->retries);
	return;
}
 
/*
	Process a read interrupt by scanning the list of buffers
	for ones that have completed and queue them for the bottom
	half to process.
*/
 
static void
c7000_proc_rintr(struct c7000_unit *cup)
{
	struct	c7000_buffer	*buf;
	struct	c7000_rd_header	*head;
	int			num_read = 0;
 
	while (cup->proc_head != NULL) {
		head = (struct c7000_rd_header *)(cup->proc_head->data + C7000_DATAL);
 
		/*
			The flag byte in the read header will be set to
			FLAG_FF when the buffer has been read.
		*/
 
		if (head->flag != FLAG_FF)
			break;
 
		/*
			Dequeue the buffer from the proc chain
			and enqueue it on the bh chain for
			the bh routine to process.
		*/
 
		buf = c7000_dequeue_buffer(cup);
		c7000_queue_bh_buffer(cup, buf);
		num_read++;
	}
 
	CPrintk(1, "c7000: c7000_proc_rintr: %d buffers read for unit 0x%x\n", num_read, cup->devno);
	return;
}
 
/*
	Process all completed buffers on the proc chain.
	A buffer is completed if it's READFF flag is FLAG_FF.
*/
 
static int
c7000_proc_wintr(struct c7000_unit *cup)
{
	struct	c7000_controller	*ccp = cup->cntlp;
	struct	c7000_buffer		*buf;
	int				num_write = 0;
 
	if (cup->proc_head == NULL) {
		CPrintk(0, "c7000: c7000_proc_wintr: unexpected NULL processing chain pointer for unit 0x%x\n", cup->devno);
		return(num_write);
	}
 
	while (cup->proc_head != NULL) {
 
		/*
			Check if the buffer has completed.
		*/
 
		if (*(cup->proc_head->data + C7000_DATAL + C7000_READHDRL) != FLAG_FF)
			break;
 
		/*
			Remove buffer from top of processing chain.
			Place it on free list.
		*/
 
		buf = c7000_dequeue_buffer(cup);
		c7000_release_buffer(cup, buf);
		num_write++;
 
		/*
			Update transmitted packets statistic.
		*/
 
		ccp->stats.tx_packets++;
	}
 
	CPrintk(1, "c7000: c7000_proc_wintr: %d buffers written for unit 0x%x\n", num_write, cup->devno);
	return(num_write);
}
 
/*
	Interrupt handler.
*/
 
static void
c7000_intr(int irq, void *initparm, struct pt_regs *regs)
{
	devstat_t			*devstat = ((devstat_t *) initparm);
	struct	c7000_unit		*cup = NULL;
	struct	c7000_controller	*ccp = NULL;
	STRUCT_NET_DEVICE		*dev = NULL;
	__u32				parm;
	__u8				flags = 0x00;
	int				rc;
 
	/*
		Discard unsolicited interrupts
	*/
 
	if (devstat->intparm == 0) {
		CPrintk(0, "c7000: c7000_intr: unsolicited interrupt for device 0x%x, cstat = 0x%2.2x, dstat = 0x%2.2x, flag = 0x%8.8x\n",
			devstat->devno, devstat->cstat, devstat->dstat, devstat->flag);
		return;
	}
 
	/*
		Obtain the c7000_unit structure pointer.
	*/
 
	cup = (struct c7000_unit *)(devstat->intparm);
 
	/*
		Obtain the c7000_controller structure and device structure
		pointers.
	*/
 
	if (cup == NULL) {
		CPrintk(0, "c7000: c7000_intr: c7000_unit pointer is NULL in devstat\n");
		return;
	}
 
	ccp = cup->cntlp;
 
	if (ccp == NULL) {
		CPrintk(0, "c7000: c7000_intr: c7000_cntlp pointer is NULL in c7000_unit structure 0x%x for unit 0x%x\n", (int)cup, cup->devno);
		return;
	}
 
	dev = ccp->dev;
 
	if (dev == NULL) {
		CPrintk(0, "c7000: c7000_intr: device pointer is NULL in c7000_controller structure 0x%x for unit 0x%x\n", (int)ccp, cup->devno);
		return;
	}
 
	/*
		Finite state machine (fsm) handling.
	*/
 
	CPrintk(1, "c7000: c7000_intr: entered with state %d flag 0x%8.8x for unit 0x%x\n", cup->state, devstat->flag, cup->devno);
 
	switch(cup->state) {
 
		/*
			Not expected to be here when in INIT state.
		*/
 
		case C7000_INIT:
 
			break;
 
		/*
			Enter state C7000_SID and wakeup the sleeping
			process in c7000_open.
		*/
 
		case C7000_HALT:
 
			if ((devstat->flag & DEVSTAT_FINAL_STATUS) == 0)
				break;
 
			cup->state = C7000_SID;
			wake_up(&cup->wait);
			break;
 
		/*
			Enter state C7000_SYSVAL and wakeup the sleeping
			process in c7000_open.
		*/
 
		case C7000_SID:
 
			if ((devstat->flag & DEVSTAT_FINAL_STATUS) == 0)
				break;
 
			if (c7000_check_csw(devstat) != 0) {
				c7000_retry_io(cup);
 
				if (cup->state == C7000_ERROR)
					wake_up(&cup->wait);
 
				break;
			}
 
			cup->retries = 0;
			cup->state = C7000_SYSVAL;
			wake_up(&cup->wait);
			break;
 
		/*
			Wakeup the sleeping process in c7000_open.
		*/
 
		case C7000_SYSVAL:
 
			if ((devstat->flag & DEVSTAT_FINAL_STATUS) == 0)
				break;
 
			if (c7000_check_csw(devstat) != 0) {
				c7000_retry_io(cup);
 
				if (cup->state == C7000_ERROR)
					wake_up(&cup->wait);
 
				break;
			}
 
			cup->retries = 0;
			wake_up(&cup->wait);
			break;
 
		/*
			Wakeup the sleeping process in c7000_open.
		*/
 
		case C7000_CONNECT:
 
			if ((devstat->flag & DEVSTAT_FINAL_STATUS) == 0)
				break;
 
			if (c7000_check_csw(devstat) != 0) {
				c7000_retry_io(cup);
 
				if (cup->state == C7000_ERROR)
					wake_up(&cup->wait);
 
				break;
			}
 
			cup->retries = 0;
			wake_up(&cup->wait);
			break;
 
		/*
			Not expected to be entered here.
		*/
 
		case C7000_READY:
			break;
 
		/*
			Process the data that was just read.
		*/
 
		case C7000_READ:
 
			if ((devstat->flag & (DEVSTAT_PCI | DEVSTAT_FINAL_STATUS)) == 0)
				break;
 
			CPrintk(1, "c7000: c7000_intr: process read interrupt for unit 0x%x , devstat flag = 0x%8.8x\n", cup->devno, devstat->flag);
 
			/*
				Check for serious errors.
			*/
 
			if (c7000_check_csw(devstat) != 0) {
				ccp->stats.rx_errors++;
				c7000_error(cup->cntlp);
				break;
			}
 
			/*
				Build the bottom half buffer list.
			*/
 
			c7000_proc_rintr(cup);
 
			/*
				When final status is received clear
				the IO active bit.
			*/
 
			if (devstat->flag & DEVSTAT_FINAL_STATUS) {
				clear_bit(0, (void *)&cup->IO_active);
			}
 
			/*
				If there are free buffers redrive the IO.
			*/
 
			if ((devstat->flag & DEVSTAT_FINAL_STATUS) &&
			    (cup->free != NULL)) {
				c7000_bld_read_chain(cup);
				parm = (__u32)cup;
				set_bit(0, (void *)&cup->IO_active);
 
				if ((rc = do_IO(cup->irq, &cup->proc_head->ccws[0], parm, 0xff, flags)) != 0) {
					clear_bit(0, (void *)&cup->IO_active);
					CPrintk(0, "c7000: c7000_intr: do_IO failed with return code %d for unit 0x%x\n", rc, cup->devno);
					c7000_error(cup->cntlp);
					break;
				}
 
				CPrintk(1, "c7000: c7000_intr: started read io for unit 0x%x\n", cup->devno);
			}
 
			/*
				Initiate bottom half routine to process
				data that was read.
			*/
 
			if (test_and_set_bit(C7000_BH_ACTIVE, (void *)&cup->flag_a) == 0) {
				queue_task(&cup->tq, &tq_immediate);
				mark_bh(IMMEDIATE_BH);
			}
 
			break;
 
		/*
			Free the transmitted buffers and restart the channel
			process (if necessary).
		*/
 
		case C7000_WRITE:
 
			if ((devstat->flag & DEVSTAT_FINAL_STATUS) == 0)
				break;
 
			if (c7000_check_csw(devstat) != 0) {
				ccp->stats.tx_errors++;
				c7000_error(cup->cntlp);
				break;
			}
 
			/*
				If at least one buffer was freed, clear
				the NOBUFFER indication.
			*/
 
			if (c7000_proc_wintr(cup) != 0) {
				c7000_clearbit_busy(TB_NOBUFFER, dev);
			}
 
			/*
				Restart the channel program if there are more
				buffers on the processing chain.
			*/
 
			if (cup->proc_head != NULL) {
				c7000_bld_wrt_chain(cup);
				parm = (__u32)cup;
				set_bit(0, (void *)&cup->IO_active);
 
				if ((rc = do_IO(cup->irq, &cup->proc_head->ccws[0], parm, 0xff, flags)) != 0) {
					CPrintk(0, "c7000: c7000_intr: do_IO failed with return code %d for unit 0x%x\n", rc, cup->devno);
					clear_bit(0, (void *)&cup->IO_active);
					c7000_error(cup->cntlp);
					break;
				}
 
				dev->trans_start = jiffies;
			} else {
				clear_bit(0, (void *)&cup->IO_active);
				cup->state = C7000_READY;
			}
 
			break;
 
		/*
			Disconnect message completed.  Wakeup the
			sleeping process in c7000_stop.
		*/
 
		case C7000_DISC:
			if ((devstat->flag & DEVSTAT_FINAL_STATUS) == 0)
				break;
 
			if (c7000_check_csw(devstat) != 0) {
				c7000_retry_io(cup);
 
				if (cup->state == C7000_ERROR)
					wake_up(&cup->wait);
 
				break;
			}
 
			cup->retries = 0;
			wake_up(&cup->wait);
			break;
 
		/*
			Subchannel is now halted.  Wakeup the sleeping
			process in c7000_stop.  Set the state to C7000_STOPPED.
		*/
 
		case C7000_STOP:
 
			cup->state = C7000_STOPPED;
			wake_up(&cup->wait);
			break;
 
		/*
			When in error state, stay there until the
			interface is recycled.
		*/
 
		case C7000_ERROR:
 
			break;
 
		/*
			Should not reach here
		*/
 
		default:
			CPrintk(0, "c7000: c7000_intr: entered default case for unit 0x%x, state %d\n", cup->devno, cup->state);
			break;
 
	}
 
	CPrintk(1, "c7000: c7000_intr: exited with state %d for unit 0x%x\n", cup->state, cup->devno);
	return;
}
 
/*
	Fill in system validation name padding it with blanks.
*/
 
static void
c7000_fill_name(char *dst, char *src)
{
	char	*tmp = dst;
	int	i;
 
	for (i = 0; i < NAMLEN; i++, tmp++) 
		*tmp = ' ';
 
	for (i = 0; i < NAMLEN && *src != '\0'; i++)
		*dst++ = *src++;
 
	return;
}
 
/*
	Initialization routine called when the device is registered.
*/
 
static int
c7000_init(STRUCT_NET_DEVICE *dev)
{
	struct	c7000_controller	*ccp;
	int				i;
	int				unitaddr;
	int				irq;
 
	/*
		Find the position of base_addr in the bases array.
	*/
 
	for (i = 0; i < MAX_C7000; i++) 
		if (bases[i] == dev->base_addr)
			break;
 
	if (i == MAX_C7000)
		return(-ENODEV);
 
	/*
		Make sure it is a C7000 type of device.
	*/
 
	if (c7000_check_devices(dev->base_addr) != 0) {
		CPrintk(0, "c7000: c7000_init: base unit 0x%lx is not the right type\n", dev->base_addr);
		return(-ENODEV);
	}
 
	/*
		Initialize the device structure functions.
		Note that ARP is not done on the CLAW interface.
		There is no ethernet header.
	*/
 
	dev->mtu = C7000_DATAL;
	dev->hard_header_len = 0;
        dev->addr_len = 0;
        dev->type = ARPHRD_SLIP;
        dev->tx_queue_len = C7000_TXQUEUE_LEN;
	dev->flags = IFF_NOARP;   
	dev->open = c7000_open;
	dev->stop = c7000_stop;
	dev->set_config = c7000_config;
	dev->hard_start_xmit = c7000_xmit;
	dev->do_ioctl = c7000_ioctl;
	dev->get_stats = c7000_stats;
 
	/*
		Allocate space for a private data structure.
	*/
 
	if ((ccp = dev->priv = kmalloc(sizeof(struct c7000_controller), GFP_KERNEL)) == NULL) {
		CPrintk(0, "c7000: c7000_init: base unit 0x%lx can not be initialized\n", dev->base_addr);
		return(-ENOMEM);
	}
 
	CPrintk(1, "c7000: c7000_init: allocated a c7000_controller structure at address 0x%x\n", (int)ccp);
	memset(ccp, '\0', sizeof(struct c7000_controller));
	ccp->dev = dev;
	ccp->base_addr = dev->base_addr;
 
	/*
		Populate the system validation name with default values.
	*/
 
	c7000_fill_name(ccp->lappl, C7000_DFLT_LAPPL);
	c7000_fill_name(ccp->lhost, C7000_DFLT_LHOST);
	c7000_fill_name(ccp->uappl, C7000_DFLT_UAPPL);
	c7000_fill_name(ccp->uhost, C7000_DFLT_UHOST);
 
	/*
		When values have been supplied, replace the prior defaults.
	*/
 
	if (i == 0) {
 
		if (lappl0 != NULL)
			c7000_fill_name(ccp->lappl, lappl0);
 
		if (lhost0 != NULL)
			c7000_fill_name(ccp->lhost, lhost0);
 
		if (uappl0 != NULL)
			c7000_fill_name(ccp->uappl, uappl0);
 
		if (uhost0 != NULL)
			c7000_fill_name(ccp->uhost, uhost0);
 
	} else if (i == 1) {
 
		if (lappl1 != NULL)
			c7000_fill_name(ccp->lappl, lappl1);
 
		if (lhost1 != NULL)
			c7000_fill_name(ccp->lhost, lhost1);
 
		if (uappl1 != NULL)
			c7000_fill_name(ccp->uappl, uappl1);
 
		if (uhost1 != NULL)
			c7000_fill_name(ccp->uhost, uhost1);
 
	} else if (i == 2) {
 
		if (lappl2 != NULL)
			c7000_fill_name(ccp->lappl, lappl2);
 
		if (lhost2 != NULL)
			c7000_fill_name(ccp->lhost, lhost2);
 
		if (uappl2 != NULL)
			c7000_fill_name(ccp->uappl, uappl2);
 
		if (uhost2 != NULL)
			c7000_fill_name(ccp->uhost, uhost2);
 
	} else {
 
		if (lappl3 != NULL)
			c7000_fill_name(ccp->lappl, lappl3);
 
		if (lhost3 != NULL)
			c7000_fill_name(ccp->lhost, lhost3);
 
		if (uappl3 != NULL)
			c7000_fill_name(ccp->uappl, uappl3);
 
		if (uhost3 != NULL)
			c7000_fill_name(ccp->uhost, uhost3);
 
	}
 
	CPrintk(1, "c7000: c7000_init: lappl = %8.8s lhost = %8.8s uappl = %8.8s uhost = %8.8s for base unit 0x%x\n", ccp->lappl, ccp->lhost, ccp->uappl, ccp->uhost, ccp->base_addr);
	ccp->version = 2;
	ccp->linkid = 0;
 
	/*
		Initialize the fields in the embedded cunits
		array.  This type of controller occupies a range
		of three contiguous device numbers.
	*/
 
	for (i = 0; i < NUNITS; i++) {
		unitaddr = dev->base_addr + i;
 
		/*
			Get the subchannel number.
		*/
 
		if ((irq = ccp->cunits[i].irq = get_irq_by_devno(unitaddr)) == -1) {
			CPrintk(0, "c7000: c7000_init: can not get subchannel for unit 0x%x\n", unitaddr);
			return(-ENODEV);
		}
 
		/*
			Get control of the subchannel.
		*/
 
		if (request_irq(irq, c7000_intr, SA_INTERRUPT, dev->name, &ccp->cunits[i].devstat) != 0) {
			CPrintk(0, "c7000: c7000_init: can not get control of subchannel 0x%x for unit 0x%x\n", irq, unitaddr);
			return(-EBUSY);
		}
 
		CPrintk(1, "c7000: c7000_init: obtained control of subchannel 0x%x for unit 0x%x\n", irq, unitaddr);
		ccp->cunits[i].devno = unitaddr;
		ccp->cunits[i].IO_active = 0;
		ccp->cunits[i].state = C7000_INIT;
		ccp->cunits[i].cntlp = ccp;
		CPrintk(1, "c7000: c7000_init: initialized unit 0x%x on subchannel 0x%x\n", unitaddr, irq);
	}
 
	return(0);
}
 
/*
	Probe for the Cisco 7000 unit base addresses.
*/
 
static void
c7000_probe(void)
{
	s390_dev_info_t	d;
	int		i;
	int		j;
	int		idx;
 
	/*
		Probe for up to MAX_C7000 devices.
		Get the first irq into variable idx.
	*/
 
	idx = get_irq_first();
 
	for (j = 0; j < MAX_C7000; j++) {
 
		if (idx < 0)
			break;
 
		/*
			Continue scanning the irq's.  Variable idx
			maintains the location from the prior scan.
		*/
 
		for (i = idx; i >= 0; i = get_irq_next(i)) {
 
			/*
				Ignore invalid irq's.
			*/
 
			if (get_dev_info_by_irq(i, &d) < 0)
				continue;
 
			/*
				A Cisco 7000 is defined as a 3088 model
				type 0x61.
			*/
 
			if (d.sid_data.cu_type == C7000_CU_TYPE &&
			    d.sid_data.cu_model == C7000_CU_MODEL) {
				CPrintk(0, "c7000_probe: unit probe found 0x%x\n", d.devno);
				bases[j] = d.devno;
 
				/*
					Skip the write irq and setup idx
					to probe for the next box.
				*/
 
				idx = get_irq_next(i + 1);
				break;
			}
 
		}
 
	}
 
	return;
}
 
/*
	Module loading.  Register each C7000 interface found via probing
	or insmod command parameters.
*/
 
int
init_module(void)
{
	int		result;
	int		i;
 
	for (i = 0 ; i < MAX_C7000; i++)
		bases[i] = -1;
 
	/*
		Perform automatic detection provided it has not been disabled
		by the noauto parameter. 
	*/
 
	if (noauto == 0)
		c7000_probe();
 
	/*
		Populate bases array from the module basex parameters replacing
		what probing found above.
	*/
 
	if (base0 != -1)
		bases[0] = base0;
 
	if (base1 != -1)
		bases[1] = base1;
 
	if (base2 != -1)
		bases[2] = base2;
 
	if (base3 != -1)
		bases[3] = base3;
 
	for (i = 0; i < MAX_C7000; i++) {
 
		if (bases[i] == -1)
			continue;
 
		/*
			Initialize the device structure.
		*/
 
		memset(&c7000_devices[i], '\0', sizeof(STRUCT_NET_DEVICE));
#ifdef NEWSTUFF
		strcpy(c7000_devices[i].name, ifnames[i]);
#else
		c7000_devices[i].name = &ifnames[i][0];
#endif
		c7000_devices[i].base_addr = bases[i];
		c7000_devices[i].init = c7000_init;
 
		/*
			Register the device.  This creates the interface
			such as ci0.
		*/
 
		if ((result = register_netdev(&c7000_devices[i])) != 0)  {
			CPrintk(0, "c7000: init_module: error %d registering base unit 0x%x\n",
				result, bases[i]);
			c7000_devices[i].base_addr = -1;
		} else {
			CPrintk(1, "c7000: init_module: registered base unit 0x%x on interface %s\n", bases[i], ifnames[i]);
		}
 
	}
 
	CPrintk(0, "c7000: init_module: module loaded\n");
	return(0);
}
 
/*
	Module unloading.  Unregister the interface and free kernel
	allocated memory.
*/
 
void
cleanup_module(void)
{
	int				i;
	int				j;
	struct	c7000_controller	*ccp;
 
	for (i = 0; i < MAX_C7000; i++) {
 
		if (bases[i] == -1)
			continue;
 
		/*
			If the device was registered, it must be unregistered
			prior to unloading the module.
		*/
 
		if (c7000_devices[i].base_addr != -1) {
 
			ccp = (struct c7000_controller *) c7000_devices[i].priv;
 
			if (ccp != NULL) {
 
				for (j = 0; j < NUNITS ; j++) {
					CPrintk(1, "c7000: clean_module: free subchannel 0x%x for unit 0x%x\n", ccp->cunits[j].irq, ccp->cunits[j].devno);
					free_irq(ccp->cunits[j].irq, &ccp->cunits[j].devstat);
				}
 
				CPrintk(1, "c7000: clean_module: free a c7000_controller structure at address 0x%x\n", (int)ccp);
				kfree(ccp);
			}
 
			unregister_netdev(&c7000_devices[i]);
		}
 
		bases[i] = -1;
	}
 
	CPrintk(0, "c7000: clean_module: module unloaded\n");
	return;
}
 

Go to most recent revision | Compare with Previous | Blame | View Log

powered by: WebSVN 2.1.0

© copyright 1999-2024 OpenCores.org, equivalent to Oliscience, all rights reserved. OpenCores®, registered trademark.