mirror of
https://github.com/SDL-Hercules-390/hyperion.git
synced 2026-04-10 22:24:32 +02:00
Those of you who have added platform specific tests to Hercules (e.g. #if __APPLE__, #if __SOLARIS__, #if __FreeBSD, etc) PLEASE STOP DOING THAT! Design a configure.ac test for the needed feature or functionality instead (or add the platform specific test to ONLY hostopts.h) and then #define your own HAVE_FEATURE_ABCXYZ and use that instead. Refer to the "_TODO.txt" document for more information and the reason why this is important.
1494 lines
51 KiB
C
1494 lines
51 KiB
C
/* ZFCP.C (c) Copyright Jan Jaeger, 1999-2012 */
|
|
/* Fibre Channel Protocol attached DASD emulation */
|
|
/* */
|
|
|
|
/* This module contains device handling functions for the */
|
|
/* ZFCP Fibre Channel Protocol interface, and will translate all */
|
|
/* ZFCP requests to iSCSI requests */
|
|
/* */
|
|
/* This implementation is based on the S/390 Linux implementation */
|
|
/* */
|
|
/* Device module hdtzfcp */
|
|
/* */
|
|
/* hercules.cnf: */
|
|
/* 0C00-0C02 ZFCP <optional parameters> */
|
|
/* Parameters: */
|
|
/* Optional parms: */
|
|
/* */
|
|
/* */
|
|
/* */
|
|
/* Related commands: */
|
|
/* */
|
|
/* To specify the location of the SCSI bootstrap loader to be */
|
|
/* used for the IPL */
|
|
/* HWLDR SCSIBOOT /usr/local/share/hercules/scsiboot */
|
|
/* */
|
|
/* Specify WWPN, LUN and other optional SCSI IPL parameters */
|
|
/* LOADDEV PORTNAME 8001020304050607 LUN 08090A0B0C0D0E0F */
|
|
/* */
|
|
/* */
|
|
|
|
#include "hstdinc.h"
|
|
|
|
#ifdef UNUSED_FUNCTION_WARNING
|
|
#pragma GCC diagnostic ignored "-Wunused-function"
|
|
#endif
|
|
|
|
#include "hercules.h"
|
|
#include "devtype.h"
|
|
#include "chsc.h"
|
|
#include "zfcp.h"
|
|
|
|
/*-------------------------------------------------------------------*/
|
|
/* ZFCP Debugging */
|
|
/*-------------------------------------------------------------------*/
|
|
|
|
#define ENABLE_ZFCP_DEBUG 1 // 1:always, 0:never, #undef:maybe
|
|
|
|
#if (!defined(ENABLE_ZFCP_DEBUG) && defined(DEBUG)) || \
|
|
(defined(ENABLE_ZFCP_DEBUG) && ENABLE_ZFCP_DEBUG)
|
|
#define ZFCP_DEBUG
|
|
#endif
|
|
|
|
#if defined(ZFCP_DEBUG)
|
|
#define ENABLE_TRACING_STMTS 1 // (Fish: DEBUGGING)
|
|
#include "dbgtrace.h" // (Fish: DEBUGGING)
|
|
#define NO_ZFCP_OPTIMIZE // (Fish: DEBUGGING) (MSVC only)
|
|
#endif
|
|
|
|
#if defined( _MSVC_ ) && defined( NO_ZFCP_OPTIMIZE )
|
|
#pragma optimize( "", off ) // disable optimizations for reliable breakpoints
|
|
#endif
|
|
|
|
|
|
|
|
#if defined( OPTION_DYNAMIC_LOAD )
|
|
#if defined( WIN32 ) && !defined( _MSVC_ ) && !defined( HDL_USE_LIBTOOL )
|
|
SYSBLK *psysblk;
|
|
#define sysblk (*psysblk)
|
|
#endif
|
|
#endif /*defined( OPTION_DYNAMIC_LOAD )*/
|
|
|
|
|
|
/*-------------------------------------------------------------------*/
|
|
/* Configuration Data Constants */
|
|
/*-------------------------------------------------------------------*/
|
|
static const NED zfcp_device_ned[] = {ZFCP_DEVICE_NED};
|
|
static const NED zfcp_ctlunit_ned[] = {ZFCP_CTLUNIT_NED};
|
|
static const NED zfcp_token_ned[] = {ZFCP_TOKEN_NED};
|
|
static const NEQ zfcp_general_neq[] = {ZFCP_GENERAL_NEQ};
|
|
|
|
static NED configuration_data[4]; // (initialized by HDL_DEPENDENCY_SECTION)
|
|
|
|
static const ND zfcp_nd[] = {ZFCP_ND};
|
|
static const NQ zfcp_nq[] = {ZFCP_NQ};
|
|
|
|
static ND node_data[2]; // (initialized by HDL_DEPENDENCY_SECTION)
|
|
|
|
#define SII_SIZE sizeof(U32)
|
|
|
|
static const BYTE sense_id_bytes[] =
|
|
{
|
|
0xFF, /* Always 0xFF */
|
|
ZFCP_SNSID_1731_03, /* Control Unit type/model */
|
|
ZFCP_SNSID_1732_03, /* I/O Device type/model */
|
|
0x00, /* Always 0x00 */
|
|
ZFCP_RCD_CIW, /* Read Config. Data CIW */
|
|
ZFCP_SII_CIW, /* Set Interface Id. CIW */
|
|
ZFCP_RNI_CIW, /* Read Node Identifier CIW */
|
|
ZFCP_EQ_CIW, /* Establish Queues CIW */
|
|
ZFCP_AQ_CIW, /* Activate Queues CIW */
|
|
};
|
|
|
|
|
|
static BYTE zfcp_immed_commands [256] =
|
|
{
|
|
/* 0 1 2 3 4 5 6 7 8 9 A B C D E F */
|
|
0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0, /* 00 */
|
|
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* 10 */
|
|
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* 20 */
|
|
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* 30 */
|
|
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* 40 */
|
|
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* 50 */
|
|
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* 60 */
|
|
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* 70 */
|
|
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* 80 */
|
|
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* 90 */
|
|
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* A0 */
|
|
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* B0 */
|
|
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* C0 */
|
|
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* D0 */
|
|
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* E0 */
|
|
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 /* F0 */
|
|
};
|
|
|
|
|
|
/*-------------------------------------------------------------------*/
|
|
/* STORCHK macro: check storage access & update ref & change bits. */
|
|
/* Returns 0 if successful or CSW_PROGC or CSW_PROTC if error. */
|
|
/* Storage key ref & change bits are only updated if successful. */
|
|
/*-------------------------------------------------------------------*/
|
|
#define STORCHK(_addr,_len,_key,_acc,_dev) \
|
|
(((((_addr) + (_len)) > (_dev)->mainlim) \
|
|
|| (((_dev)->orb.flag5 & ORB5_A) \
|
|
&& ((((_dev)->pmcw.flag5 & PMCW5_LM_LOW) \
|
|
&& ((_addr) < sysblk.addrlimval)) \
|
|
|| (((_dev)->pmcw.flag5 & PMCW5_LM_HIGH) \
|
|
&& (((_addr) + (_len)) > sysblk.addrlimval)) ) )) ? CSW_PROGC : \
|
|
((_key) && ((STORAGE_KEY((_addr), (_dev)) & STORKEY_KEY) != (_key)) \
|
|
&& ((STORAGE_KEY((_addr), (_dev)) & STORKEY_FETCH) || ((_acc) == STORKEY_CHANGE))) ? CSW_PROTC : \
|
|
((STORAGE_KEY((_addr), (_dev)) |= ((((_acc) == STORKEY_CHANGE)) \
|
|
? (STORKEY_REF|STORKEY_CHANGE) : STORKEY_REF)) && 0))
|
|
|
|
|
|
#if defined(ZFCP_DEBUG)
|
|
static inline void DUMP(DEVBLK *dev, char* name, void* ptr, int len)
|
|
{
|
|
int i;
|
|
|
|
if(!((ZFCP_GRP*)(dev->group->grp_data))->debug)
|
|
return;
|
|
|
|
logmsg(_("DATA: %4.4X %s"), len, name);
|
|
for(i = 0; i < len; i++)
|
|
{
|
|
if(!(i & 15))
|
|
logmsg(_("\n%4.4X:"), i);
|
|
logmsg(_(" %2.2X"), ((BYTE*)ptr)[i]);
|
|
}
|
|
logmsg(_("\n"));
|
|
}
|
|
#define DBGTRC(_dev, ...) \
|
|
do { \
|
|
if(((ZFCP_GRP*)((_dev)->group->grp_data))->debug) \
|
|
TRACE(__VA_ARGS__); \
|
|
} while(0)
|
|
#else
|
|
#define DBGTRC(_dev, ...)
|
|
#define DUMP(_dev, _name, _ptr, _len)
|
|
#endif
|
|
|
|
|
|
#if defined(_FEATURE_QDIO_THININT)
|
|
/*-------------------------------------------------------------------*/
|
|
/* Set Adapter Local Summary Indicator bits */
|
|
/*-------------------------------------------------------------------*/
|
|
static inline void set_alsi(DEVBLK *dev, BYTE bits)
|
|
{
|
|
if(dev->qdio.alsi)
|
|
{
|
|
BYTE *alsi = dev->mainstor + dev->qdio.alsi;
|
|
|
|
obtain_lock(&sysblk.mainlock);
|
|
*alsi |= bits;
|
|
STORAGE_KEY(dev->qdio.alsi, dev) |= (STORKEY_REF|STORKEY_CHANGE);
|
|
release_lock(&sysblk.mainlock);
|
|
}
|
|
}
|
|
|
|
|
|
/*-------------------------------------------------------------------*/
|
|
/* Clear Adapter Local Summary Indicator bits */
|
|
/*-------------------------------------------------------------------*/
|
|
static inline void clr_alsi(DEVBLK *dev, BYTE bits)
|
|
{
|
|
if(dev->qdio.alsi)
|
|
{
|
|
BYTE *alsi = dev->mainstor + dev->qdio.alsi;
|
|
|
|
obtain_lock(&sysblk.mainlock);
|
|
*alsi &= bits;
|
|
STORAGE_KEY(dev->qdio.alsi, dev) |= (STORKEY_REF|STORKEY_CHANGE);
|
|
release_lock(&sysblk.mainlock);
|
|
}
|
|
}
|
|
|
|
|
|
/*-------------------------------------------------------------------*/
|
|
/* Set Device State Change Indicator bits */
|
|
/*-------------------------------------------------------------------*/
|
|
static inline void set_dsci(DEVBLK *dev, BYTE bits)
|
|
{
|
|
if(dev->qdio.dsci)
|
|
{
|
|
BYTE *dsci = dev->mainstor + dev->qdio.dsci;
|
|
BYTE *alsi = dev->mainstor + dev->qdio.alsi;
|
|
|
|
obtain_lock(&sysblk.mainlock);
|
|
*dsci |= bits;
|
|
STORAGE_KEY(dev->qdio.dsci, dev) |= (STORKEY_REF|STORKEY_CHANGE);
|
|
*alsi |= bits;
|
|
STORAGE_KEY(dev->qdio.alsi, dev) |= (STORKEY_REF|STORKEY_CHANGE);
|
|
release_lock(&sysblk.mainlock);
|
|
}
|
|
}
|
|
|
|
|
|
/*-------------------------------------------------------------------*/
|
|
/* Clear Device State Change Indicator bits */
|
|
/*-------------------------------------------------------------------*/
|
|
static inline void clr_dsci(DEVBLK *dev, BYTE bits)
|
|
{
|
|
if(dev->qdio.dsci)
|
|
{
|
|
BYTE *dsci = dev->mainstor + dev->qdio.dsci;
|
|
|
|
obtain_lock(&sysblk.mainlock);
|
|
*dsci &= bits;
|
|
STORAGE_KEY(dev->qdio.dsci, dev) |= (STORKEY_REF|STORKEY_CHANGE);
|
|
release_lock(&sysblk.mainlock);
|
|
}
|
|
}
|
|
#endif /*defined(_FEATURE_QDIO_THININT)*/
|
|
|
|
|
|
|
|
/*-------------------------------------------------------------------*/
|
|
/* Raise Adapter Interrupt */
|
|
/*-------------------------------------------------------------------*/
|
|
static void raise_adapter_interrupt(DEVBLK *dev)
|
|
{
|
|
DBGTRC(dev, _("Adapter Interrupt dev(%4.4x)\n"),dev->devnum);
|
|
|
|
obtain_lock(&dev->lock);
|
|
dev->pciscsw.flag2 |= SCSW2_Q | SCSW2_FC_START;
|
|
dev->pciscsw.flag3 |= SCSW3_SC_INTER | SCSW3_SC_PEND;
|
|
dev->pciscsw.chanstat = CSW_PCI;
|
|
QUEUE_IO_INTERRUPT(&dev->pciioint,FALSE);
|
|
release_lock (&dev->lock);
|
|
|
|
/* Update interrupt status */
|
|
OBTAIN_INTLOCK( DEVREGS(dev) );
|
|
UPDATE_IC_IOPENDING();
|
|
RELEASE_INTLOCK( DEVREGS(dev) );
|
|
}
|
|
|
|
|
|
// We must go through the queues/buffers in a round robin manner
|
|
// so that buffers are re-used on a LRU (Least Recently Used) basis.
|
|
// When no buffers are available we must keep our current position.
|
|
// When a buffer becomes available we will advance to that location.
|
|
// When we reach the end of the buffer queue we will advance to the
|
|
// next available queue.
|
|
// When a queue is newly enabled then we will start at the beginning
|
|
// of the queue (this is handled in signal adapter).
|
|
|
|
/*-------------------------------------------------------------------*/
|
|
/* Process Input Queue */
|
|
/*-------------------------------------------------------------------*/
|
|
static void process_input_queue(DEVBLK *dev)
|
|
{
|
|
ZFCP_GRP *grp = (ZFCP_GRP*)dev->group->grp_data;
|
|
int iq = dev->qdio.i_qpos;
|
|
int mq = dev->qdio.i_qcnt;
|
|
|
|
DBGTRC(dev, "Input Qpos(%d) Bpos(%d)\n",dev->qdio.i_qpos,dev->qdio.i_bpos[dev->qdio.i_qpos]);
|
|
|
|
while (mq--)
|
|
if(dev->qdio.i_qmask & (0x80000000 >> iq))
|
|
{
|
|
int ib = dev->qdio.i_bpos[iq];
|
|
QDIO_SLSB *slsb;
|
|
int mb = 128;
|
|
slsb = (QDIO_SLSB*)(dev->mainstor + dev->qdio.i_slsbla[iq]);
|
|
|
|
while(mb--)
|
|
if(slsb->slsbe[ib] == SLSBE_INPUT_EMPTY)
|
|
{
|
|
QDIO_SL *sl = (QDIO_SL*)(dev->mainstor + dev->qdio.i_sla[iq]);
|
|
U64 sa; U32 len; BYTE *buf;
|
|
U64 la;
|
|
QDIO_SBAL *sbal;
|
|
int tlen = 0;
|
|
int ns;
|
|
|
|
DBGTRC(dev, _("Input Queue(%d) Buffer(%d)\n"),iq,ib);
|
|
|
|
FETCH_DW(sa,sl->sbala[ib]);
|
|
if(STORCHK(sa,sizeof(QDIO_SBAL)-1,dev->qdio.i_slk[iq],STORKEY_REF,dev))
|
|
{
|
|
slsb->slsbe[ib] = SLSBE_ERROR;
|
|
STORAGE_KEY(dev->qdio.i_slsbla[iq], dev) |= (STORKEY_REF|STORKEY_CHANGE);
|
|
#if defined(_FEATURE_QDIO_THININT)
|
|
set_alsi(dev,ALSI_ERROR);
|
|
#endif /*defined(_FEATURE_QDIO_THININT)*/
|
|
grp->reqpci = TRUE;
|
|
DBGTRC(dev, _("STORCHK ERROR sa(%llx), key(%2.2x)\n"),sa,dev->qdio.i_slk[iq]);
|
|
return;
|
|
}
|
|
sbal = (QDIO_SBAL*)(dev->mainstor + sa);
|
|
|
|
for(ns = 0; ns < 16; ns++)
|
|
{
|
|
FETCH_DW(la,sbal->sbale[ns].addr);
|
|
FETCH_FW(len,sbal->sbale[ns].length);
|
|
if(!len)
|
|
break; // Or should this be continue - ie a discontiguous sbal???
|
|
if(STORCHK(la,len-1,dev->qdio.i_sbalk[iq],STORKEY_CHANGE,dev))
|
|
{
|
|
slsb->slsbe[ib] = SLSBE_ERROR;
|
|
STORAGE_KEY(dev->qdio.i_slsbla[iq], dev) |= (STORKEY_REF|STORKEY_CHANGE);
|
|
#if defined(_FEATURE_QDIO_THININT)
|
|
set_alsi(dev,ALSI_ERROR);
|
|
#endif /*defined(_FEATURE_QDIO_THININT)*/
|
|
grp->reqpci = TRUE;
|
|
DBGTRC(dev, _("STORCHK ERROR la(%llx), len(%d), key(%2.2x)\n"),la,len,dev->qdio.i_sbalk[iq]);
|
|
return;
|
|
}
|
|
buf = (BYTE*)(dev->mainstor + la);
|
|
|
|
#if 0
|
|
// ADD CODE TO READ BLOCKS AND SAVE TO THE QUEUES
|
|
#endif
|
|
}
|
|
|
|
if(tlen > 0)
|
|
{
|
|
#if defined(_FEATURE_QDIO_THININT)
|
|
set_dsci(dev,DSCI_IOCOMP);
|
|
#endif /*defined(_FEATURE_QDIO_THININT)*/
|
|
grp->reqpci = TRUE;
|
|
slsb->slsbe[ib] = SLSBE_INPUT_COMPLETED;
|
|
STORAGE_KEY(dev->qdio.i_slsbla[iq], dev) |= (STORKEY_REF|STORKEY_CHANGE);
|
|
if(++ib >= 128)
|
|
{
|
|
ib = 0;
|
|
dev->qdio.i_bpos[iq] = ib;
|
|
if(++iq >= dev->qdio.i_qcnt)
|
|
iq = 0;
|
|
dev->qdio.i_qpos = iq;
|
|
mq = dev->qdio.o_qcnt;
|
|
}
|
|
dev->qdio.i_bpos[iq] = ib;
|
|
mb = 128;
|
|
}
|
|
else
|
|
{
|
|
if(ns)
|
|
sbal->sbale[ns-1].flags[0] = SBALE_FLAG0_LAST_ENTRY;
|
|
return;
|
|
}
|
|
if(ns)
|
|
sbal->sbale[ns-1].flags[0] = SBALE_FLAG0_LAST_ENTRY;
|
|
}
|
|
else /* Buffer not empty */
|
|
{
|
|
if(++ib >= 128)
|
|
{
|
|
ib = 0;
|
|
dev->qdio.i_bpos[iq] = ib;
|
|
if(++iq >= dev->qdio.i_qcnt)
|
|
iq = 0;
|
|
dev->qdio.i_qpos = iq;
|
|
}
|
|
dev->qdio.i_bpos[iq] = ib;
|
|
}
|
|
}
|
|
else
|
|
if(++iq >= dev->qdio.i_qcnt)
|
|
iq = 0;
|
|
|
|
}
|
|
|
|
|
|
/*-------------------------------------------------------------------*/
|
|
/* Process Output Queue */
|
|
/*-------------------------------------------------------------------*/
|
|
static void process_output_queue(DEVBLK *dev)
|
|
{
|
|
ZFCP_GRP *grp = (ZFCP_GRP*)dev->group->grp_data;
|
|
int oq = dev->qdio.o_qpos;
|
|
int mq = dev->qdio.o_qcnt;
|
|
|
|
while (mq--)
|
|
if(dev->qdio.o_qmask & (0x80000000 >> oq))
|
|
{
|
|
int ob = dev->qdio.o_bpos[oq];
|
|
QDIO_SLSB *slsb;
|
|
int mb = 128;
|
|
slsb = (QDIO_SLSB*)(dev->mainstor + dev->qdio.o_slsbla[oq]);
|
|
|
|
while(mb--)
|
|
if(slsb->slsbe[ob] == SLSBE_OUTPUT_PRIMED)
|
|
{
|
|
QDIO_SL *sl = (QDIO_SL*)(dev->mainstor + dev->qdio.o_sla[oq]);
|
|
U64 sa; U32 len; BYTE *buf;
|
|
U64 la;
|
|
QDIO_SBAL *sbal;
|
|
int ns;
|
|
|
|
DBGTRC(dev, _("Output Queue(%d) Buffer(%d)\n"),oq,ob);
|
|
|
|
FETCH_DW(sa,sl->sbala[ob]);
|
|
if(STORCHK(sa,sizeof(QDIO_SBAL)-1,dev->qdio.o_slk[oq],STORKEY_REF,dev))
|
|
{
|
|
slsb->slsbe[ob] = SLSBE_ERROR;
|
|
STORAGE_KEY(dev->qdio.o_slsbla[oq], dev) |= (STORKEY_REF|STORKEY_CHANGE);
|
|
#if defined(_FEATURE_QDIO_THININT)
|
|
set_alsi(dev,ALSI_ERROR);
|
|
#endif /*defined(_FEATURE_QDIO_THININT)*/
|
|
grp->reqpci = TRUE;
|
|
DBGTRC(dev, _("STORCHK ERROR sa(%llx), key(%2.2x)\n"),sa,dev->qdio.o_slk[oq]);
|
|
return;
|
|
}
|
|
sbal = (QDIO_SBAL*)(dev->mainstor + sa);
|
|
|
|
for(ns = 0; ns < 16; ns++)
|
|
{
|
|
FETCH_DW(la,sbal->sbale[ns].addr);
|
|
FETCH_FW(len,sbal->sbale[ns].length);
|
|
if(!len)
|
|
break; // Or should this be continue - ie a discontiguous sbal???
|
|
if(STORCHK(la,len-1,dev->qdio.o_sbalk[oq],STORKEY_REF,dev))
|
|
{
|
|
slsb->slsbe[ob] = SLSBE_ERROR;
|
|
STORAGE_KEY(dev->qdio.o_slsbla[oq], dev) |= (STORKEY_REF|STORKEY_CHANGE);
|
|
#if defined(_FEATURE_QDIO_THININT)
|
|
set_alsi(dev,ALSI_ERROR);
|
|
#endif /*defined(_FEATURE_QDIO_THININT)*/
|
|
grp->reqpci = TRUE;
|
|
DBGTRC(dev, _("STORCHK ERROR la(%llx), len(%d), key(%2.2x)\n"),la,len,dev->qdio.o_sbalk[oq]);
|
|
return;
|
|
}
|
|
buf = (BYTE*)(dev->mainstor + la);
|
|
|
|
#if 0
|
|
// ADD CODE TO TAKE BLOCKS OF THE QUEUE AND WRITE
|
|
#endif
|
|
|
|
if((sbal->sbale[ns].flags[3] & SBALE_FLAG3_PCI_REQ))
|
|
{
|
|
#if defined(_FEATURE_QDIO_THININT)
|
|
set_dsci(dev,DSCI_IOCOMP);
|
|
#endif /*defined(_FEATURE_QDIO_THININT)*/
|
|
grp->reqpci = TRUE;
|
|
}
|
|
}
|
|
|
|
slsb->slsbe[ob] = SLSBE_OUTPUT_COMPLETED;
|
|
STORAGE_KEY(dev->qdio.o_slsbla[oq], dev) |= (STORKEY_REF|STORKEY_CHANGE);
|
|
if(++ob >= 128)
|
|
{
|
|
ob = 0;
|
|
dev->qdio.o_bpos[oq] = ob;
|
|
if(++oq >= dev->qdio.o_qcnt)
|
|
oq = 0;
|
|
dev->qdio.o_qpos = oq;
|
|
mq = dev->qdio.o_qcnt;
|
|
}
|
|
dev->qdio.o_bpos[oq] = ob;
|
|
mb = 128;
|
|
}
|
|
else
|
|
if(++ob >= 128)
|
|
{
|
|
ob = 0;
|
|
if(++oq >= dev->qdio.o_qcnt)
|
|
oq = 0;
|
|
}
|
|
|
|
}
|
|
else
|
|
if(++oq >= dev->qdio.o_qcnt)
|
|
oq = 0;
|
|
}
|
|
|
|
|
|
/*-------------------------------------------------------------------*/
|
|
/* Halt device handler */
|
|
/*-------------------------------------------------------------------*/
|
|
static void zfcp_halt_device ( DEVBLK *dev)
|
|
{
|
|
ZFCP_GRP *grp = (ZFCP_GRP*)dev->group->grp_data;
|
|
|
|
/* Signal QDIO end if QDIO is active */
|
|
if(dev->scsw.flag2 & SCSW2_Q)
|
|
{
|
|
dev->scsw.flag2 &= ~SCSW2_Q;
|
|
write_pipe(grp->ppfd[1],"*",1);
|
|
}
|
|
else
|
|
if(dev->group->acount == ZFCP_GROUP_SIZE)
|
|
signal_condition(&grp->qcond);
|
|
}
|
|
|
|
|
|
/*-------------------------------------------------------------------*/
|
|
/* Initialize the device handler */
|
|
/*-------------------------------------------------------------------*/
|
|
static int zfcp_init_handler ( DEVBLK *dev, int argc, char *argv[] )
|
|
{
|
|
ZFCP_GRP *grp;
|
|
int grouped;
|
|
int i;
|
|
|
|
logmsg(_("ZFCP Experimental Driver - Incomplete - Work In Progress\n"));
|
|
|
|
if(!dev->group)
|
|
{
|
|
dev->numdevid = sizeof(sense_id_bytes);
|
|
memcpy(dev->devid, sense_id_bytes, sizeof(sense_id_bytes));
|
|
|
|
dev->devtype = dev->devid[1] << 8 | dev->devid[2];
|
|
|
|
dev->pmcw.flag4 |= PMCW4_Q;
|
|
|
|
if((!(grouped = group_device(dev,ZFCP_GROUP_SIZE)) && !dev->member) || ZFCP_GROUP_SIZE == 1)
|
|
{
|
|
dev->group->grp_data = grp = malloc(sizeof(ZFCP_GRP));
|
|
memset (grp, 0, sizeof(ZFCP_GRP));
|
|
|
|
initialize_condition(&grp->qcond);
|
|
initialize_lock(&grp->qlock);
|
|
|
|
/* Open write signalling pipe */
|
|
create_pipe(grp->ppfd);
|
|
grp->ttfd = grp->ppfd[0]; // ZZ TEMP
|
|
|
|
/* Set Non-Blocking mode */
|
|
socket_set_blocking_mode(grp->ppfd[0],0);
|
|
|
|
/* Allocate reponse buffer */
|
|
grp->rspbf = malloc(RSP_BUFSZ);
|
|
grp->rspsz = 0;
|
|
|
|
/* Set defaults */
|
|
}
|
|
else
|
|
grp = dev->group->grp_data;
|
|
}
|
|
else
|
|
grp = dev->group->grp_data;
|
|
|
|
// process all command line options here
|
|
for(i = 0; i < argc; i++)
|
|
{
|
|
if(!strcasecmp("portname",argv[i]) && (i+1) < argc)
|
|
{
|
|
if(grp->wwpn)
|
|
free(grp->wwpn);
|
|
grp->wwpn = strdup(argv[++i]);
|
|
continue;
|
|
}
|
|
else if(!strcasecmp("lun",argv[i]) && (i+1) < argc)
|
|
{
|
|
if(grp->lun)
|
|
free(grp->lun);
|
|
grp->lun = strdup(argv[++i]);
|
|
continue;
|
|
}
|
|
else if(!strcasecmp("br_lba",argv[i]) && (i+1) < argc)
|
|
{
|
|
if(grp->brlba)
|
|
free(grp->brlba);
|
|
grp->brlba = strdup(argv[++i]);
|
|
continue;
|
|
}
|
|
else if(!strcasecmp("chpid",argv[i]) && (i+1) < argc)
|
|
{
|
|
int chpid;
|
|
char c;
|
|
if(sscanf(argv[++i], "%x%c", &chpid, &c) != 1 || chpid < 0x00 || chpid > 0xFF)
|
|
logmsg(_("ZFCP: Invalid channel path id %s for device %4.4X\n"),argv[i],dev->devnum);
|
|
|
|
else
|
|
dev->pmcw.chpid[0] = chpid;
|
|
continue;
|
|
}
|
|
else
|
|
#if defined(ZFCP_DEBUG) || defined(IFF_DEBUG)
|
|
if(!strcasecmp("debug",argv[i]))
|
|
{
|
|
grp->debug = 1;
|
|
continue;
|
|
}
|
|
else
|
|
if(!strcasecmp("nodebug",argv[i]))
|
|
{
|
|
grp->debug = 0;
|
|
continue;
|
|
}
|
|
else
|
|
#endif
|
|
logmsg(_("ZFCP: Invalid option %s for device %4.4X\n"),argv[i],dev->devnum);
|
|
|
|
}
|
|
|
|
#if ZFCP_GROUP_SIZE > 1
|
|
if(grouped)
|
|
#endif
|
|
for(i = 0; i < ZFCP_GROUP_SIZE; i++)
|
|
dev->group->memdev[i]->fla[0] = dev->group->memdev[0]->devnum;
|
|
|
|
return 0;
|
|
} /* end function zfcp_init_handler */
|
|
|
|
|
|
/*-------------------------------------------------------------------*/
|
|
/* Query the device definition */
|
|
/*-------------------------------------------------------------------*/
|
|
static void zfcp_query_device (DEVBLK *dev, char **devclass,
|
|
int buflen, char *buffer)
|
|
{
|
|
BEGIN_DEVICE_CLASS_QUERY( "FCP", dev, devclass, buflen, buffer );
|
|
|
|
snprintf( buffer, buflen, "%s%s"
|
|
, (dev->group->acount == ZFCP_GROUP_SIZE) ? "" : "*Incomplete "
|
|
, (dev->scsw.flag2 & SCSW2_Q) ? "QDIO" : ""
|
|
);
|
|
|
|
} /* end function zfcp_query_device */
|
|
|
|
|
|
/*-------------------------------------------------------------------*/
|
|
/* Close the device */
|
|
/*-------------------------------------------------------------------*/
|
|
static int zfcp_close_device ( DEVBLK *dev )
|
|
{
|
|
ZFCP_GRP *grp = (ZFCP_GRP*)dev->group->grp_data;
|
|
|
|
if(!dev->member && dev->group->grp_data)
|
|
{
|
|
if(grp->ppfd[0])
|
|
close_pipe(grp->ppfd[0]);
|
|
if(grp->ppfd[1])
|
|
close_pipe(grp->ppfd[1]);
|
|
|
|
if(grp->wwpn)
|
|
free(grp->wwpn);
|
|
if(grp->lun)
|
|
free(grp->lun);
|
|
if(grp->brlba)
|
|
free(grp->brlba);
|
|
|
|
if(grp->rspbf)
|
|
free(grp->rspbf);
|
|
|
|
destroy_condition(&grp->qcond);
|
|
destroy_lock(&grp->qlock);
|
|
|
|
free(dev->group->grp_data);
|
|
dev->group->grp_data = NULL;
|
|
}
|
|
|
|
return 0;
|
|
} /* end function zfcp_close_device */
|
|
|
|
|
|
#if defined(_FEATURE_QDIO_THININT)
|
|
/*-------------------------------------------------------------------*/
|
|
/* QDIO Set Subchannel Indicator */
|
|
/*-------------------------------------------------------------------*/
|
|
static int zfcp_set_sci ( DEVBLK *dev, void *desc )
|
|
{
|
|
CHSC_REQ21 *req21 = (void *)desc;
|
|
RADR alsi, dsci;
|
|
BYTE ks, kc;
|
|
U16 opc;
|
|
|
|
FETCH_HW(opc,req21->opcode);
|
|
|
|
if(opc)
|
|
return 3; // Invalid operation code
|
|
|
|
FETCH_DW(alsi, req21->alsi);
|
|
ks = req21->sk & CHSC_REQ21_KS;
|
|
|
|
FETCH_DW(dsci, req21->dsci);
|
|
kc = (req21->sk & CHSC_REQ21_KC) << 4;
|
|
|
|
if(alsi && dsci)
|
|
{
|
|
if(STORCHK(alsi,0,ks,STORKEY_CHANGE,dev)
|
|
|| STORCHK(dsci,0,kc,STORKEY_CHANGE,dev))
|
|
{
|
|
dev->qdio.thinint = 0;
|
|
return 3;
|
|
}
|
|
else
|
|
dev->qdio.thinint = 1;
|
|
|
|
}
|
|
else
|
|
dev->qdio.thinint = 0;
|
|
|
|
#if 0
|
|
dev->pmcw.flag4 &= ~PMCW4_ISC;
|
|
dev->pmcw.flag4 |= (req21->isc & CHSC_REQ21_ISC_MASK) << 3;
|
|
dev->pmcw.flag25 &= ~PMCW25_VISC;
|
|
dev->pmcw.flag25 |= (req21->isc & CHSC_REQ21_VISC_MASK) >> 4;
|
|
#endif
|
|
|
|
dev->qdio.alsi = alsi;
|
|
dev->qdio.ks = ks;
|
|
|
|
dev->qdio.dsci = dsci;
|
|
dev->qdio.kc = kc;
|
|
|
|
return 0;
|
|
}
|
|
#endif /*defined(_FEATURE_QDIO_THININT)*/
|
|
|
|
|
|
/*-------------------------------------------------------------------*/
|
|
/* QDIO subsys desc */
|
|
/*-------------------------------------------------------------------*/
|
|
static int zfcp_ssqd_desc ( DEVBLK *dev, void *desc )
|
|
{
|
|
CHSC_RSP24 *rsp24 = (void *)desc;
|
|
|
|
STORE_HW(rsp24->sch, dev->subchan);
|
|
|
|
if(dev->pmcw.flag4 & PMCW4_Q)
|
|
{
|
|
#if 0
|
|
rsp24->pcnt = 0x10;
|
|
rsp24->icnt = 0x01;
|
|
rsp24->ocnt = 0x20;
|
|
#endif
|
|
rsp24->flags |= ( CHSC_FLAG_QDIO_CAPABILITY | CHSC_FLAG_VALIDITY );
|
|
|
|
rsp24->qdioac1 |= ( AC1_SIGA_INPUT_NEEDED | AC1_SIGA_OUTPUT_NEEDED );
|
|
rsp24->qdioac1 |= AC1_AUTOMATIC_SYNC_ON_OUT_PCI;
|
|
|
|
#if defined(_FEATURE_QEBSM)
|
|
if(FACILITY_ENABLED_DEV(QEBSM))
|
|
{
|
|
STORE_DW(rsp24->sch_token, IOID2TKN((dev->ssid << 16) | dev->subchan));
|
|
rsp24->qdioac1 |= ( AC1_SC_QEBSM_AVAILABLE | AC1_SC_QEBSM_ENABLED );
|
|
}
|
|
#endif /*defined(_FEATURE_QEBSM)*/
|
|
|
|
#if defined(_FEATURE_QDIO_THININT)
|
|
if(FACILITY_ENABLED_DEV(QDIO_THININT))
|
|
rsp24->qdioac1 |= AC1_AUTOMATIC_SYNC_ON_THININT;
|
|
#endif /*defined(_FEATURE_QDIO_THININT)*/
|
|
|
|
#if 1 // ZZTEST
|
|
rsp24->icnt = QDIO_MAXQ;
|
|
rsp24->ocnt = QDIO_MAXQ;
|
|
rsp24->mbccnt = 0x04;
|
|
#endif
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
/*-------------------------------------------------------------------*/
|
|
/* Execute a Channel Command Word */
|
|
/*-------------------------------------------------------------------*/
|
|
static void zfcp_execute_ccw ( DEVBLK *dev, BYTE code, BYTE flags,
|
|
BYTE chained, U32 count, BYTE prevcode, int ccwseq,
|
|
BYTE *iobuf, BYTE *more, BYTE *unitstat, U32 *residual )
|
|
{
|
|
ZFCP_GRP *grp = (ZFCP_GRP*)dev->group->grp_data;
|
|
U32 num; /* Number of bytes to move */
|
|
|
|
UNREFERENCED(flags);
|
|
UNREFERENCED(prevcode);
|
|
UNREFERENCED(ccwseq);
|
|
UNREFERENCED(chained);
|
|
|
|
/* Command reject if the device group has not been established */
|
|
if((dev->group->acount != ZFCP_GROUP_SIZE)
|
|
&& !(IS_CCW_SENSE(code) || IS_CCW_NOP(code) || (code == ZFCP_RCD)))
|
|
{
|
|
/* Set Intervention required sense, and unit check status */
|
|
dev->sense[0] = SENSE_IR;
|
|
*unitstat = CSW_CE | CSW_DE | CSW_UC;
|
|
return;
|
|
}
|
|
|
|
/* Process depending on CCW opcode */
|
|
switch (code) {
|
|
|
|
|
|
case 0x01:
|
|
/*---------------------------------------------------------------*/
|
|
/* WRITE */
|
|
/*---------------------------------------------------------------*/
|
|
{
|
|
if(1)
|
|
{
|
|
|
|
/* Calculate number of bytes to write and set residual count */
|
|
num = (count < RSP_BUFSZ) ? count : RSP_BUFSZ;
|
|
*residual = count - num;
|
|
if (count < RSP_BUFSZ) *more = 1;
|
|
|
|
/* Return normal status */
|
|
*unitstat = CSW_CE | CSW_DE;
|
|
}
|
|
else
|
|
{
|
|
/* Command reject if no response buffer available */
|
|
dev->sense[0] = SENSE_CR;
|
|
*unitstat = CSW_CE | CSW_DE | CSW_UC;
|
|
}
|
|
break;
|
|
}
|
|
|
|
|
|
case 0x02:
|
|
/*---------------------------------------------------------------*/
|
|
/* READ */
|
|
/*---------------------------------------------------------------*/
|
|
{
|
|
U32 rd_size = 0;
|
|
|
|
obtain_lock(&grp->qlock);
|
|
if(grp->rspsz)
|
|
{
|
|
rd_size = grp->rspsz;
|
|
memcpy(iobuf,grp->rspbf,rd_size);
|
|
grp->rspsz = 0;
|
|
}
|
|
else
|
|
{
|
|
if(0)
|
|
{
|
|
wait_condition(&grp->qcond, &grp->qlock);
|
|
if(grp->rspsz)
|
|
{
|
|
rd_size = grp->rspsz;
|
|
memcpy(iobuf,grp->rspbf,rd_size);
|
|
grp->rspsz = 0;
|
|
}
|
|
}
|
|
}
|
|
release_lock(&grp->qlock);
|
|
|
|
if(rd_size)
|
|
{
|
|
/* Calculate number of bytes to read and set residual count */
|
|
num = (count < rd_size) ? count : rd_size;
|
|
*residual = count - num;
|
|
if (count < rd_size) *more = 1;
|
|
|
|
/* Return normal status */
|
|
*unitstat = CSW_CE | CSW_DE;
|
|
}
|
|
else
|
|
{
|
|
/* Return unit check with status modifier */
|
|
dev->sense[0] = 0;
|
|
*unitstat = CSW_CE | CSW_DE | CSW_UC | CSW_SM;
|
|
}
|
|
break;
|
|
}
|
|
|
|
|
|
case 0x03:
|
|
/*---------------------------------------------------------------*/
|
|
/* CONTROL NO-OPERATION */
|
|
/*---------------------------------------------------------------*/
|
|
|
|
*residual = 0;
|
|
*unitstat = CSW_CE | CSW_DE;
|
|
break;
|
|
|
|
|
|
case 0x14:
|
|
/*---------------------------------------------------------------*/
|
|
/* SENSE COMMAND BYTE */
|
|
/*---------------------------------------------------------------*/
|
|
{
|
|
// PROGRAMMING NOTE: I'm still not sure about this. The
|
|
// Sense Command Byte command is known to be a 3088 CTCA
|
|
// command, so I suspect we should never be seeing this
|
|
// command because we don't support CTCA emulation mode.
|
|
|
|
// I suspect the reason we're currently seeing it MAY be
|
|
// because we still don't have something right and z/OS
|
|
// is thus getting confused into thinking the OSA device
|
|
// is currently configured to emulate a 3088 CTCA device.
|
|
|
|
// However, since rejecting it causes z/OS to go into a
|
|
// disabled wait, we are going to temporarily treat it
|
|
// as a valid command until we can positively determine
|
|
// whether or not it is a bona fide valid OSA command.
|
|
#if 0
|
|
/* We currently do not support emulated 3088 CTCA mode */
|
|
dev->sense[0] = SENSE_CR;
|
|
*unitstat = CSW_CE | CSW_DE | CSW_UC;
|
|
#else
|
|
/* The Sense Command Byte command returns a single byte
|
|
being the CCW opcode from the other end of the CTCA */
|
|
static const U32 len = 1; /* cmd length */
|
|
static const BYTE opcode = 0x03; /* CCW opcode */
|
|
|
|
/* Calculate residual byte count */
|
|
num = (count < len) ? count : len;
|
|
*residual = count - num;
|
|
if (count < len) *more = 1;
|
|
|
|
/* Copy the CTCA command byte to channel I/O buffer */
|
|
*iobuf = opcode;
|
|
|
|
/* Return normal i/o completion status */
|
|
*unitstat = CSW_CE | CSW_DE;
|
|
#endif
|
|
break;
|
|
}
|
|
|
|
case 0x04:
|
|
/*---------------------------------------------------------------*/
|
|
/* SENSE */
|
|
/*---------------------------------------------------------------*/
|
|
|
|
/* Calculate residual byte count */
|
|
num = (count < dev->numsense) ? count : dev->numsense;
|
|
*residual = count - num;
|
|
if (count < dev->numsense) *more = 1;
|
|
|
|
/* Copy device sense bytes to channel I/O buffer */
|
|
memcpy (iobuf, dev->sense, num);
|
|
|
|
/* Clear the device sense bytes */
|
|
memset (dev->sense, 0, sizeof(dev->sense));
|
|
|
|
/* Return unit status */
|
|
*unitstat = CSW_CE | CSW_DE;
|
|
break;
|
|
|
|
|
|
case 0xE4:
|
|
/*---------------------------------------------------------------*/
|
|
/* SENSE ID */
|
|
/*---------------------------------------------------------------*/
|
|
|
|
/* Calculate residual byte count */
|
|
num = (count < dev->numdevid) ? count : dev->numdevid;
|
|
*residual = count - num;
|
|
if (count < dev->numdevid) *more = 1;
|
|
|
|
/* Copy device identifier bytes to channel I/O buffer */
|
|
memcpy (iobuf, dev->devid, num);
|
|
|
|
/* Return unit status */
|
|
*unitstat = CSW_CE | CSW_DE;
|
|
|
|
/* Display formatted Sense Id information, maybe */
|
|
if( grp->debug )
|
|
{
|
|
char buf[1024];
|
|
// HHC03995 "%1d:%04X %s: %s:\n%s"
|
|
WRMSG(HHC03995, "I", SSID_TO_LCSS(dev->ssid), dev->devnum,
|
|
dev->typname, "SID", FormatSID( iobuf, num, buf, sizeof( buf )));
|
|
}
|
|
break;
|
|
|
|
|
|
case ZFCP_RCD:
|
|
/*---------------------------------------------------------------*/
|
|
/* READ CONFIGURATION DATA */
|
|
/*---------------------------------------------------------------*/
|
|
{
|
|
U32 len = sizeof(configuration_data);
|
|
NED *dev_ned = (NED*)iobuf; /* Device NED is first */
|
|
NED *ctl_ned = dev_ned + 1; /* Control Unit NED is next */
|
|
NED *tkn_ned = ctl_ned + 1; /* Token NED is last NED */
|
|
NEQ *gen_neq = (NEQ*)tkn_ned+1; /* General NEQ always last */
|
|
DEVBLK *cua; /* Our Control Unit device */
|
|
|
|
/* Copy configuration data from tempate */
|
|
memcpy (iobuf, configuration_data, len);
|
|
|
|
/* The first device in the group is the control unit */
|
|
cua = dev->group->memdev[0];
|
|
|
|
/* Insert the Channel Path ID (CHPID) into all of the NEDs */
|
|
dev_ned->tag[0] = dev->pmcw.chpid[0];
|
|
ctl_ned->tag[0] = cua->pmcw.chpid[0];
|
|
tkn_ned->tag[0] = cua->pmcw.chpid[0];
|
|
|
|
/* Insert the device's device number into its device NED. */
|
|
dev_ned->tag[1] = dev->devnum & 0xFF;
|
|
|
|
/* Insert the control unit address into the General NEQ */
|
|
gen_neq->iid[0] = cua->pmcw.chpid[0];
|
|
gen_neq->iid[1] = cua->devnum & 0xFF;
|
|
|
|
/* Calculate residual byte count */
|
|
num = (count < len ? count : len);
|
|
*residual = count - num;
|
|
if (count < len) *more = 1;
|
|
|
|
/* Return unit status */
|
|
*unitstat = CSW_CE | CSW_DE;
|
|
|
|
/* Display formatted Read Configuration Data records, maybe */
|
|
if( grp->debug )
|
|
{
|
|
char buf[1024];
|
|
// HHC03995 "%1d:%04X %s: %s:\n%s"
|
|
WRMSG(HHC03995, "I", SSID_TO_LCSS(dev->ssid), dev->devnum,
|
|
dev->typname, "RCD", FormatRCD( iobuf, num, buf, sizeof( buf )));
|
|
}
|
|
break;
|
|
}
|
|
|
|
|
|
case ZFCP_SII:
|
|
/*---------------------------------------------------------------*/
|
|
/* SET INTERFACE IDENTIFIER */
|
|
/*---------------------------------------------------------------*/
|
|
{
|
|
U32 iir; /* Work area to validate IIR */
|
|
FETCH_FW(iir,iobuf); /* Fetch IIR into work area */
|
|
|
|
/* Command Reject if the Interface ID Record is invalid.
|
|
Note: we only support one interface with an ID of 0. */
|
|
if ((iir & 0xFFFCFFFF) != 0xB0000000 ||
|
|
(iir & 0x00030000) == 0x00030000)
|
|
{
|
|
dev->sense[0] = SENSE_CR;
|
|
*unitstat = CSW_CE | CSW_DE | CSW_UC;
|
|
break;
|
|
}
|
|
|
|
/* Save the requested Interface ID for later unless it's
|
|
not chained (to a presumably following RNI command) */
|
|
if (chained)
|
|
grp->iir = iir;
|
|
|
|
/* Calculate residual byte count */
|
|
num = (count < SII_SIZE) ? count : SII_SIZE;
|
|
*residual = count - num;
|
|
if (count < SII_SIZE) *more = 1;
|
|
|
|
/* Return unit status */
|
|
*unitstat = CSW_CE | CSW_DE;
|
|
|
|
//! /* Display various information, maybe */
|
|
//! if( grp->debug )
|
|
//! {
|
|
//! mpc_display_stuff( dev, "SII", iobuf, num, ' ' );
|
|
//! }
|
|
|
|
break;
|
|
}
|
|
|
|
|
|
case ZFCP_RNI:
|
|
/*---------------------------------------------------------------*/
|
|
/* READ NODE IDENTIFIER */
|
|
/*---------------------------------------------------------------*/
|
|
{
|
|
U32 len = sizeof(node_data);
|
|
ND *nd = (ND*)iobuf; /* Node Descriptor pointer */
|
|
DEVBLK *cua; /* Our Control Unit device */
|
|
|
|
/* Command Reject if not chained from Set Interface ID */
|
|
if (!chained || prevcode != OSA_SII)
|
|
{
|
|
dev->sense[0] = SENSE_CR;
|
|
*unitstat = CSW_CE | CSW_DE | CSW_UC;
|
|
break;
|
|
}
|
|
|
|
/* The first device in the group is the control unit */
|
|
cua = dev->group->memdev[0];
|
|
|
|
/* If the Node Selector was zero an ND and one or more
|
|
NQs are returned. Otherwise just the ND is returned. */
|
|
if ((grp->iir & 0x00030000) != 0)
|
|
len = sizeof(ND);
|
|
|
|
/* Copy configuration data from tempate */
|
|
memcpy (iobuf, node_data, len);
|
|
|
|
/* Insert the CHPID of the node into the Node Descriptor ND */
|
|
nd->tag[0] = dev->pmcw.chpid[0];
|
|
|
|
/* Update the Node Qualifier information if they want it */
|
|
if (len > sizeof(ND))
|
|
{
|
|
NQ *nq = (NQ*)nd + 1; /* Point to Node Qualifier */
|
|
|
|
/* Insert the CULA CHPID and device number into the NQ */
|
|
nq->rsrvd[1] = cua->pmcw.chpid[0];
|
|
nq->rsrvd[2] = cua->devnum & 0xFF;
|
|
}
|
|
|
|
/* Calculate residual byte count */
|
|
num = (count < len ? count : len);
|
|
*residual = count - num;
|
|
if (count < len) *more = 1;
|
|
|
|
/* Return unit status */
|
|
*unitstat = CSW_CE | CSW_DE;
|
|
|
|
/* Display formatted Read Node Information, maybe */
|
|
if( grp->debug )
|
|
{
|
|
char buf[1024];
|
|
// HHC03995 "%1d:%04X %s: %s:\n%s"
|
|
WRMSG(HHC03995, "I", SSID_TO_LCSS(dev->ssid), dev->devnum,
|
|
dev->typname, "RNI", FormatRNI( iobuf, num, buf, sizeof( buf )));
|
|
}
|
|
break;
|
|
}
|
|
|
|
|
|
case ZFCP_EQ:
|
|
/*---------------------------------------------------------------*/
|
|
/* ESTABLISH QUEUES */
|
|
/*---------------------------------------------------------------*/
|
|
{
|
|
QDIO_QDR *qdr = (QDIO_QDR*)iobuf;
|
|
QDIO_QDES0 *qdes;
|
|
int accerr;
|
|
int i;
|
|
|
|
dev->qdio.i_qcnt = qdr->iqdcnt < QDIO_MAXQ ? qdr->iqdcnt : QDIO_MAXQ;
|
|
dev->qdio.o_qcnt = qdr->oqdcnt < QDIO_MAXQ ? qdr->oqdcnt : QDIO_MAXQ;
|
|
|
|
FETCH_DW(dev->qdio.qiba,qdr->qiba);
|
|
dev->qdio.qibk = qdr->qkey & 0xF0;
|
|
|
|
if(!(accerr = STORCHK(dev->qdio.qiba,sizeof(QDIO_QIB)-1,dev->qdio.qibk,STORKEY_CHANGE,dev)))
|
|
{
|
|
QDIO_QIB *qib = (QDIO_QIB*)(dev->mainstor + dev->qdio.qiba);
|
|
qib->ac |= QIB_AC_PCI; // Incidate PCI on output is supported
|
|
#if defined(_FEATURE_QEBSM)
|
|
if(FACILITY_ENABLED_DEV(QEBSM))
|
|
qib->rflags |= QIB_RFLAGS_QEBSM;
|
|
#endif /*defined(_FEATURE_QEBSM)*/
|
|
}
|
|
|
|
qdes = qdr->qdf0;
|
|
|
|
for(i = 0; i < dev->qdio.i_qcnt; i++)
|
|
{
|
|
FETCH_DW(dev->qdio.i_sliba[i],qdes->sliba);
|
|
FETCH_DW(dev->qdio.i_sla[i],qdes->sla);
|
|
FETCH_DW(dev->qdio.i_slsbla[i],qdes->slsba);
|
|
dev->qdio.i_slibk[i] = qdes->keyp1 & 0xF0;
|
|
dev->qdio.i_slk[i] = (qdes->keyp1 << 4) & 0xF0;
|
|
dev->qdio.i_sbalk[i] = qdes->keyp2 & 0xF0;
|
|
dev->qdio.i_slsblk[i] = (qdes->keyp2 << 4) & 0xF0;
|
|
|
|
accerr |= STORCHK(dev->qdio.i_slsbla[i],sizeof(QDIO_SLSB)-1,dev->qdio.i_slsblk[i],STORKEY_CHANGE,dev);
|
|
accerr |= STORCHK(dev->qdio.i_sla[i],sizeof(QDIO_SL)-1,dev->qdio.i_slk[i],STORKEY_REF,dev);
|
|
|
|
qdes = (QDIO_QDES0*)((BYTE*)qdes+(qdr->iqdsz<<2));
|
|
}
|
|
|
|
for(i = 0; i < dev->qdio.o_qcnt; i++)
|
|
{
|
|
FETCH_DW(dev->qdio.o_sliba[i],qdes->sliba);
|
|
FETCH_DW(dev->qdio.o_sla[i],qdes->sla);
|
|
FETCH_DW(dev->qdio.o_slsbla[i],qdes->slsba);
|
|
dev->qdio.o_slibk[i] = qdes->keyp1 & 0xF0;
|
|
dev->qdio.o_slk[i] = (qdes->keyp1 << 4) & 0xF0;
|
|
dev->qdio.o_sbalk[i] = qdes->keyp2 & 0xF0;
|
|
dev->qdio.o_slsblk[i] = (qdes->keyp2 << 4) & 0xF0;
|
|
|
|
accerr |= STORCHK(dev->qdio.o_slsbla[i],sizeof(QDIO_SLSB)-1,dev->qdio.o_slsblk[i],STORKEY_CHANGE,dev);
|
|
accerr |= STORCHK(dev->qdio.o_sla[i],sizeof(QDIO_SL)-1,dev->qdio.o_slk[i],STORKEY_REF,dev);
|
|
|
|
qdes = (QDIO_QDES0*)((BYTE*)qdes+(qdr->oqdsz<<2));
|
|
}
|
|
|
|
/* Calculate residual byte count */
|
|
num = (count < sizeof(QDIO_QDR)) ? count : sizeof(QDIO_QDR);
|
|
*residual = count - num;
|
|
if (count < sizeof(QDIO_QDR)) *more = 1;
|
|
|
|
if(!accerr)
|
|
{
|
|
/* Return unit status */
|
|
*unitstat = CSW_CE | CSW_DE;
|
|
}
|
|
else
|
|
{
|
|
/* Command reject on invalid or inaccessible storage addresses */
|
|
dev->sense[0] = SENSE_CR;
|
|
*unitstat = CSW_CE | CSW_DE | CSW_UC;
|
|
}
|
|
|
|
break;
|
|
}
|
|
|
|
|
|
case ZFCP_AQ:
|
|
/*---------------------------------------------------------------*/
|
|
/* ACTIVATE QUEUES */
|
|
/*---------------------------------------------------------------*/
|
|
{
|
|
fd_set readset;
|
|
int rc;
|
|
|
|
dev->qdio.i_qmask = dev->qdio.o_qmask = 0;
|
|
|
|
FD_ZERO( &readset );
|
|
|
|
dev->scsw.flag2 |= SCSW2_Q;
|
|
|
|
|
|
do {
|
|
/* Process the Input Queue if data has been received */
|
|
if(dev->qdio.i_qmask && FD_ISSET(grp->ttfd,&readset))
|
|
{
|
|
process_input_queue(dev);
|
|
}
|
|
|
|
/* Process Output Queue if data needs to be sent */
|
|
if(FD_ISSET(grp->ppfd[0],&readset))
|
|
{
|
|
char c;
|
|
read_pipe(grp->ppfd[0],&c,1);
|
|
|
|
if(dev->qdio.o_qmask)
|
|
{
|
|
process_output_queue(dev);
|
|
}
|
|
}
|
|
|
|
if(dev->qdio.i_qmask)
|
|
FD_SET(grp->ttfd, &readset);
|
|
FD_SET(grp->ppfd[0], &readset);
|
|
|
|
if(grp->reqpci)
|
|
{
|
|
grp->reqpci = FALSE;
|
|
raise_adapter_interrupt(dev);
|
|
}
|
|
|
|
#if defined( OPTION_W32_CTCI )
|
|
do {
|
|
#endif
|
|
|
|
rc = select (((grp->ttfd > grp->ppfd[0]) ? grp->ttfd : grp->ppfd[0]) + 1,
|
|
&readset, NULL, NULL, NULL);
|
|
|
|
#if defined( OPTION_W32_CTCI )
|
|
} while (0 == rc || (rc < 0 && HSO_errno == HSO_EINTR));
|
|
} while (rc > 0 && dev->scsw.flag2 & SCSW2_Q);
|
|
#else
|
|
} while (dev->scsw.flag2 & SCSW2_Q);
|
|
#endif
|
|
|
|
|
|
/* Return unit status */
|
|
*unitstat = CSW_CE | CSW_DE;
|
|
}
|
|
break;
|
|
|
|
|
|
default:
|
|
/*---------------------------------------------------------------*/
|
|
/* INVALID OPERATION */
|
|
/*---------------------------------------------------------------*/
|
|
DBGTRC(dev, _("Unkown CCW dev(%4.4x) code(%2.2x)\n"),dev->devnum,code);
|
|
/* Set command reject sense byte, and unit check status */
|
|
dev->sense[0] = SENSE_CR;
|
|
*unitstat = CSW_CE | CSW_DE | CSW_UC;
|
|
|
|
} /* end switch(code) */
|
|
|
|
} /* end function zfcp_execute_ccw */
|
|
|
|
|
|
/*-------------------------------------------------------------------*/
|
|
/* Signal Adapter Initiate Input */
|
|
/*-------------------------------------------------------------------*/
|
|
static int zfcp_initiate_input(DEVBLK *dev, U32 qmask)
|
|
{
|
|
ZFCP_GRP *grp = (ZFCP_GRP*)dev->group->grp_data;
|
|
int noselrd;
|
|
|
|
DBGTRC(dev, _("SIGA-r dev(%4.4x) qmask(%8.8x)\n"),dev->devnum,qmask);
|
|
|
|
/* Return CC1 if the device is not QDIO active */
|
|
if(!(dev->scsw.flag2 & SCSW2_Q))
|
|
return 1;
|
|
|
|
/* Is there a read select */
|
|
noselrd = !dev->qdio.i_qmask;
|
|
|
|
/* Validate Mask */
|
|
qmask &= ~(0xffffffff >> dev->qdio.i_qcnt);
|
|
|
|
/* Reset Queue Positions */
|
|
if(qmask != dev->qdio.i_qmask)
|
|
{
|
|
int n;
|
|
for(n = 0; n < dev->qdio.i_qcnt; n++)
|
|
if(!(dev->qdio.i_qmask & (0x80000000 >> n)))
|
|
dev->qdio.i_bpos[n] = 0;
|
|
if(!dev->qdio.i_qmask)
|
|
dev->qdio.i_qpos = 0;
|
|
|
|
/* Update Read Queue Mask */
|
|
dev->qdio.i_qmask = qmask;
|
|
}
|
|
|
|
/* Send signal to QDIO thread */
|
|
if(noselrd && dev->qdio.i_qmask)
|
|
write_pipe(grp->ppfd[1],"*",1);
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
/*-------------------------------------------------------------------*/
|
|
/* Signal Adapter Initiate Output */
|
|
/*-------------------------------------------------------------------*/
|
|
static int zfcp_initiate_output(DEVBLK *dev, U32 qmask)
|
|
{
|
|
ZFCP_GRP *grp = (ZFCP_GRP*)dev->group->grp_data;
|
|
|
|
DBGTRC(dev, _("SIGA-w dev(%4.4x) qmask(%8.8x)\n"),dev->devnum,qmask);
|
|
|
|
/* Return CC1 if the device is not QDIO active */
|
|
if(!(dev->scsw.flag2 & SCSW2_Q))
|
|
return 1;
|
|
|
|
/* Validate Mask */
|
|
qmask &= ~(0xffffffff >> dev->qdio.o_qcnt);
|
|
|
|
/* Reset Queue Positions */
|
|
if(qmask != dev->qdio.o_qmask)
|
|
{
|
|
int n;
|
|
for(n = 0; n < dev->qdio.o_qcnt; n++)
|
|
if(!(dev->qdio.o_qmask & (0x80000000 >> n)))
|
|
dev->qdio.o_bpos[n] = 0;
|
|
if(!dev->qdio.o_qmask)
|
|
dev->qdio.o_qpos = 0;
|
|
|
|
/* Update Write Queue Mask */
|
|
dev->qdio.o_qmask = qmask;
|
|
}
|
|
|
|
/* Send signal to QDIO thread */
|
|
if(dev->qdio.o_qmask)
|
|
write_pipe(grp->ppfd[1],"*",1);
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
/*-------------------------------------------------------------------*/
|
|
/* Signal Adapter Sync */
|
|
/*-------------------------------------------------------------------*/
|
|
static int zfcp_do_sync(DEVBLK *dev, U32 oqmask, U32 iqmask)
|
|
{
|
|
UNREFERENCED(dev); /* unreferenced for non-DEBUG builds */
|
|
UNREFERENCED(oqmask); /* unreferenced for non-DEBUG builds */
|
|
UNREFERENCED(iqmask); /* unreferenced for non-DEBUG builds */
|
|
|
|
DBGTRC(dev, _("SIGA-s dev(%4.4x) oqmask(%8.8x) iqmask(%8.8x)\n"),
|
|
dev->devnum, oqmask, iqmask );
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
/*-------------------------------------------------------------------*/
|
|
/* Signal Adapter Initiate Output Multiple */
|
|
/*-------------------------------------------------------------------*/
|
|
static int zfcp_initiate_output_mult(DEVBLK *dev, U32 qmask)
|
|
{
|
|
DBGTRC(dev, _("SIGA-m dev(%4.4x) qmask(%8.8x)\n"),dev->devnum,qmask);
|
|
|
|
return zfcp_initiate_output(dev, qmask);
|
|
}
|
|
|
|
|
|
#if defined( OPTION_DYNAMIC_LOAD )
|
|
static
|
|
#endif
|
|
DEVHND zfcp_device_hndinfo =
|
|
{
|
|
&zfcp_init_handler, /* Device Initialisation */
|
|
&zfcp_execute_ccw, /* Device CCW execute */
|
|
&zfcp_close_device, /* Device Close */
|
|
&zfcp_query_device, /* Device Query */
|
|
NULL, /* Device Extended Query */
|
|
NULL, /* Device Start channel pgm */
|
|
NULL, /* Device End channel pgm */
|
|
NULL, /* Device Resume channel pgm */
|
|
NULL, /* Device Suspend channel pgm */
|
|
&zfcp_halt_device, /* Device Halt channel pgm */
|
|
NULL, /* Device Read */
|
|
NULL, /* Device Write */
|
|
NULL, /* Device Query used */
|
|
NULL, /* Device Reserve */
|
|
NULL, /* Device Release */
|
|
NULL, /* Device Attention */
|
|
zfcp_immed_commands, /* Immediate CCW Codes */
|
|
&zfcp_initiate_input, /* Signal Adapter Input */
|
|
&zfcp_initiate_output, /* Signal Adapter Output */
|
|
&zfcp_do_sync, /* Signal Adapter Sync */
|
|
&zfcp_initiate_output_mult, /* Signal Adapter Output Mult */
|
|
&zfcp_ssqd_desc, /* QDIO subsys desc */
|
|
#if defined(_FEATURE_QDIO_THININT)
|
|
&zfcp_set_sci, /* QDIO set subchan ind */
|
|
#else /*defined(_FEATURE_QDIO_THININT)*/
|
|
NULL, /* QDIO set subchan ind */
|
|
#endif /*defined(_FEATURE_QDIO_THININT)*/
|
|
NULL, /* Hercules suspend */
|
|
NULL /* Hercules resume */
|
|
};
|
|
|
|
/* Libtool static name colision resolution */
|
|
/* note : lt_dlopen will look for symbol & modulename_LTX_symbol */
|
|
#if !defined( HDL_BUILD_SHARED ) && defined( HDL_USE_LIBTOOL )
|
|
#define hdl_ddev hdtzfcp_LTX_hdl_ddev
|
|
#define hdl_depc hdtzfcp_LTX_hdl_depc
|
|
#define hdl_reso hdtzfcp_LTX_hdl_reso
|
|
#define hdl_init hdtzfcp_LTX_hdl_init
|
|
#define hdl_fini hdtzfcp_LTX_hdl_fini
|
|
#endif
|
|
|
|
#if defined( OPTION_DYNAMIC_LOAD )
|
|
|
|
HDL_DEPENDENCY_SECTION;
|
|
{
|
|
HDL_DEPENDENCY( HERCULES );
|
|
HDL_DEPENDENCY( DEVBLK );
|
|
HDL_DEPENDENCY( SYSBLK );
|
|
|
|
memcpy( (NED*)&configuration_data[0], &zfcp_device_ned [0], sizeof( NED ));
|
|
memcpy( (NED*)&configuration_data[1], &zfcp_ctlunit_ned[0], sizeof( NED ));
|
|
memcpy( (NED*)&configuration_data[2], &zfcp_token_ned [0], sizeof( NED ));
|
|
memcpy( (NED*)&configuration_data[3], &zfcp_general_neq[0], sizeof( NEQ ));
|
|
|
|
memcpy( (ND*)&node_data[0], &zfcp_nd[0], sizeof( ND ));
|
|
memcpy( (ND*)&node_data[1], &zfcp_nq[0], sizeof( NQ ));
|
|
}
|
|
END_DEPENDENCY_SECTION
|
|
|
|
|
|
HDL_RESOLVER_SECTION;
|
|
{
|
|
#if defined( WIN32 ) && !defined( _MSVC_ ) && !defined( HDL_USE_LIBTOOL )
|
|
#undef sysblk
|
|
HDL_RESOLVE_PTRVAR( psysblk, sysblk );
|
|
#endif
|
|
}
|
|
END_RESOLVER_SECTION
|
|
|
|
|
|
HDL_REGISTER_SECTION;
|
|
{
|
|
}
|
|
END_REGISTER_SECTION
|
|
|
|
|
|
HDL_DEVICE_SECTION;
|
|
{
|
|
HDL_DEVICE ( ZFCP, zfcp_device_hndinfo );
|
|
}
|
|
END_DEVICE_SECTION
|
|
|
|
#endif // defined(OPTION_DYNAMIC_LOAD)
|
|
|
|
#if defined( _MSVC_ ) && defined( NO_ZFCP_OPTIMIZE )
|
|
#pragma optimize( "", on ) // restore previous settings
|
|
#endif
|