Files
org-hyperion-cules/config.c
Ian Shorter 4035c417e6 Provide device initialization routine with number of devices
specifed on the configuation statement or attach command.
2015-01-19 11:23:42 +01:00

2146 lines
63 KiB
C

/* CONFIG.C (c) Copyright Jan Jaeger, 2000-2012 */
/* Device configuration functions */
/*-------------------------------------------------------------------*/
/* The original configuration builder is now called bldcfg.c */
/*-------------------------------------------------------------------*/
#include "hstdinc.h"
#ifdef UNUSED_FUNCTION_WARNING
#pragma GCC diagnostic ignored "-Wunused-function"
#endif
#ifndef _CONFIG_C_
#define _CONFIG_C_
#endif
#ifndef _HENGINE_DLL_
#define _HENGINE_DLL_
#endif
#include "hercules.h"
#include "opcode.h"
#include "chsc.h"
#if !defined(_GEN_ARCH)
#if defined(_ARCHMODE3)
#define _GEN_ARCH _ARCHMODE3
#include "config.c"
#undef _GEN_ARCH
#endif
#if defined(_ARCHMODE2)
#define _GEN_ARCH _ARCHMODE2
#include "config.c"
#undef _GEN_ARCH
#endif
#if defined(HAVE_MLOCKALL)
int configure_memlock(int flags)
{
int rc;
if(flags)
rc = mlockall(MCL_CURRENT | MCL_FUTURE);
else
rc = munlockall();
return rc ? errno : 0;
}
#endif /*defined(HAVE_MLOCKALL)*/
static void configure_region_reloc()
{
DEVBLK *dev;
int i;
#if defined(_FEATURE_REGION_RELOCATE)
/* Initialize base zone storage view (SIE compat) */
for(i = 0; i < FEATURE_SIE_MAXZONES; i++)
{
sysblk.zpb[i].mso = 0;
sysblk.zpb[i].msl = (sysblk.mainsize - 1) >> 20;
if(sysblk.xpndsize)
{
sysblk.zpb[i].eso = 0;
sysblk.zpb[i].esl = ((size_t)sysblk.xpndsize * XSTORE_PAGESIZE - 1) >> 20;
}
else
{
sysblk.zpb[i].eso = -1;
sysblk.zpb[i].esl = -1;
}
}
#endif
/* Relocate storage for all devices */
for (dev = sysblk.firstdev; dev; dev = dev->nextdev)
{
dev->mainstor = sysblk.mainstor;
dev->storkeys = sysblk.storkeys;
dev->mainlim = sysblk.mainsize ? (sysblk.mainsize - 1) : 0;
}
/* Relocate storage for all online cpus */
for (i = 0; i < sysblk.maxcpu; i++)
if (IS_CPU_ONLINE(i))
{
sysblk.regs[i]->storkeys = sysblk.storkeys;
sysblk.regs[i]->mainstor = sysblk.mainstor;
sysblk.regs[i]->mainlim = sysblk.mainsize ? (sysblk.mainsize - 1) : 0;
}
}
static U64 config_mfree = 200 << SHIFT_MEGABYTE;
int configure_memfree(int mfree)
{
if (mfree < 0)
return config_mfree >> SHIFT_MEGABYTE;
config_mfree = (RADR)mfree << SHIFT_MEGABYTE;
return 0;
}
/* storage configuration */
static U64 config_allocmsize = 0;
static BYTE *config_allocmaddr = NULL;
int configure_storage(U64 mainsize)
{
BYTE *mainstor;
BYTE *storkeys;
BYTE *dofree = NULL;
char *mfree = NULL;
REGS *regs;
U64 storsize;
U32 skeysize;
int cpu;
/* Ensure all CPUs have been stopped */
if (sysblk.cpus)
{
OBTAIN_INTLOCK(NULL);
for (cpu = 0; cpu < sysblk.maxcpu; cpu++)
{
if (IS_CPU_ONLINE(cpu) &&
sysblk.regs[cpu]->cpustate == CPUSTATE_STARTED)
{
RELEASE_INTLOCK(NULL);
return HERRCPUONL;
}
}
RELEASE_INTLOCK(NULL);
}
/* Release storage and return if deconfiguring */
if (mainsize == ~0ULL)
{
if (config_allocmaddr)
free(config_allocmaddr);
sysblk.storkeys = 0;
sysblk.mainstor = 0;
sysblk.mainsize = 0;
config_allocmsize = 0;
config_allocmaddr = NULL;
return 0;
}
/* Round requested storage size to architectural segment boundaries
* ARCH_370 1 4K page
* ARCH_390 256 4K pages (or 1M)
* ARCH_900 256 4K oages (or 1M)
*/
if (mainsize)
{
#if 0
if (mainsize <= (16 << (SHIFT_MEGABYTE - 12)))
storsize = MAX((sysblk.arch_mode <= ARCH_390) ? 1 : 2,
mainsize);
/* The side effect of this for values less than 256 is to establish
* zones with a memory size of zero megabytes. Channel subsystem I/O
* (see MSCH instruction) drives I/O address checks via the memory size
* established in zones, not mainsize. A zero megabyte zone memory
* size causes all channel subsystem I/O to fail with a program check
* channel status.
*/
#else
if (sysblk.arch_mode == ARCH_370)
storsize = MAX(1,mainsize);
else
#endif
storsize = (mainsize + 255) & ~255ULL;
mainsize = storsize;
}
else
{
/* Adjust zero storage case for a subsystem/device server
* (MAXCPU 0), allocating minimum storage of 4K.
*/
storsize = 1;
}
/* Storage key array size rounded to next page boundary */
switch (STORAGE_KEY_UNITSIZE)
{
case 2048:
skeysize = storsize << 1;
break;
case 4096:
skeysize = storsize;
break;
}
skeysize += 4095;
skeysize >>= 12;
/* Add Storage key array size */
storsize += skeysize;
/* New memory is obtained only if the requested and calculated size
* is larger than the last allocated size, or if the request is for
* less than 2M of memory.
*
* Note: Using the current algorithm, storage on a 32-bit host OS
* is normally limited to a guest total of 1326M, split
* between main and expanded storage. The most either may
* specify is 919M. For example, one may specify MAINSIZE
* 919M and XPNDSIZE 407M, for a total of 1326M.
*
* Please understand that the results on any individual
* 32-bit host OS may vary from the note above, and the note
* does not apply to any 64-bit host OS.
*
*/
if (storsize > config_allocmsize ||
(mainsize <= (2 * ONE_MEGABYTE) &&
storsize < config_allocmsize))
{
if (config_mfree &&
mainsize > (2* ONE_MEGABYTE))
mfree = malloc(config_mfree);
/* Obtain storage with hint to page size for cleanest allocation
*/
storkeys = calloc((size_t)storsize + 1, 4096);
if (mfree)
free(mfree);
if (storkeys == NULL)
{
char buf[64];
sysblk.main_clear = 0;
MSGBUF( buf, "configure_storage(%s)",
fmt_memsize_KB((U64)mainsize << 2) );
logmsg(MSG(HHC01430, "S", buf, strerror(errno)));
return -1;
}
/* Previously allocated storage to be freed, update actual
* storage pointers and adjust new storage to page boundary.
*/
dofree = config_allocmaddr,
config_allocmsize = storsize,
config_allocmaddr = storkeys,
sysblk.main_clear = 1,
storkeys = (BYTE*)(((U64)storkeys + 4095) & ~0x0FFFULL);
}
else
{
storkeys = sysblk.storkeys;
sysblk.main_clear = 0;
dofree = NULL;
}
/* Mainstor is located beyond the storage key array on a page boundary */
mainstor = (BYTE*)((U64)(storkeys + (skeysize << 12)));
/* Set in sysblk */
sysblk.storkeys = storkeys;
sysblk.mainstor = mainstor;
sysblk.mainsize = mainsize << 12;
/*
* Free prior storage in use
*
* FIXME: The storage ordering further limits the amount of storage
* that may be allocated following the initial storage
* allocation.
*
*/
if (dofree)
free(dofree);
/* Initial power-on reset for main storage */
storage_clear();
#if 0 /*DEBUG-JJ-20/03/2000*/
/* Mark selected frames invalid for debugging purposes */
for (i = 64 ; i < (sysblk.mainsize / STORAGE_KEY_UNITSIZE); i += 2)
if (i < (sysblk.mainsize / STORAGE_KEY_UNITSIZE) - 64)
sysblk.storkeys[i] = STORKEY_BADFRM;
else
sysblk.storkeys[i++] = STORKEY_BADFRM;
#endif
#if 1 // The below is a kludge that will need to be cleaned up at some point in time
/* Initialize dummy regs.
* Dummy regs are used by the panel or gui when the target cpu
* (sysblk.pcpu) is not configured (ie cpu_thread not started).
*/
sysblk.dummyregs.mainstor = sysblk.mainstor;
sysblk.dummyregs.psa = (PSA_3XX*)sysblk.mainstor;
sysblk.dummyregs.storkeys = sysblk.storkeys;
sysblk.dummyregs.mainlim = sysblk.mainsize ? (sysblk.mainsize - 1) : 0;
sysblk.dummyregs.dummy = 1;
initial_cpu_reset (&sysblk.dummyregs);
sysblk.dummyregs.arch_mode = sysblk.arch_mode;
sysblk.dummyregs.hostregs = &sysblk.dummyregs;
#endif
configure_region_reloc();
/* Call initial_cpu_reset for every online processor */
if (sysblk.cpus)
{
for (cpu = 0; cpu < sysblk.maxcpu; cpu++)
{
if (IS_CPU_ONLINE(cpu))
{
regs=sysblk.regs[cpu];
ARCH_DEP(initial_cpu_reset) (regs) ;
}
}
}
return 0;
}
static U64 config_allocxsize = 0;
static BYTE *config_allocxaddr = NULL;
int configure_xstorage(U64 xpndsize)
{
#ifdef _FEATURE_EXPANDED_STORAGE
BYTE *xpndstor;
BYTE *dofree = NULL;
char *mfree = NULL;
REGS *regs;
int cpu;
/* Ensure all CPUs have been stopped */
if (sysblk.cpus)
{
OBTAIN_INTLOCK(NULL);
for (cpu = 0; cpu < sysblk.maxcpu; cpu++)
{
if (IS_CPU_ONLINE(cpu) &&
sysblk.regs[cpu]->cpustate == CPUSTATE_STARTED)
{
RELEASE_INTLOCK(NULL);
return HERRCPUONL;
}
}
RELEASE_INTLOCK(NULL);
}
/* Release storage and return if zero or deconfiguring */
if (!xpndsize ||
xpndsize == ~0ULL)
{
if (config_allocxaddr)
free(config_allocxaddr);
sysblk.xpndsize = 0,
sysblk.xpndstor = 0,
config_allocxsize = 0,
config_allocxaddr = NULL;
return 0;
}
/* New memory is obtained only if the requested and calculated size
* is larger than the last allocated size.
*
* Note: Using the current algorithm, storage on a 32-bit host OS
* is normally limited to a guest total of 1326M, split
* between main and expanded storage. The most either may
* specify is 919M. For example, one may specify MAINSIZE
* 407M and XPNDSIZE 919M, for a total of 1326M.
*
* Please understand that the results on any individual
* 32-bit host OS may vary from the note above, and the note
* does not apply to any 64-bit host OS.
*
*/
if (xpndsize > config_allocxsize)
{
if (config_mfree)
mfree = malloc(config_mfree);
/* Obtain expanded storage, hinting to megabyte boundary */
xpndstor = calloc((size_t)xpndsize + 1, ONE_MEGABYTE);
if (mfree)
free(mfree);
if (xpndstor == NULL)
{
char buf[64];
sysblk.xpnd_clear = 0;
MSGBUF( buf, "configure_xstorage(%s)",
fmt_memsize_MB((U64)xpndsize));
logmsg(MSG(HHC01430, "S", buf, strerror(errno)));
return -1;
}
/* Previously allocated storage to be freed, update actual
* storage pointers and adjust new storage to megabyte boundary.
*/
dofree = config_allocxaddr,
config_allocxsize = xpndsize,
config_allocxaddr = xpndstor,
sysblk.xpnd_clear = 1,
xpndstor = (BYTE*)(((U64)xpndstor + (ONE_MEGABYTE - 1)) &
~((U64)ONE_MEGABYTE - 1)),
sysblk.xpndstor = xpndstor;
}
else
{
xpndstor = sysblk.xpndstor;
sysblk.xpnd_clear = 0;
dofree = NULL;
}
sysblk.xpndstor = xpndstor;
sysblk.xpndsize = xpndsize << (SHIFT_MEBIBYTE - XSTORE_PAGESHIFT);
/*
* Free prior storage in use
*
* FIXME: The storage ordering further limits the amount of storage
* that may be allocated following the initial storage
* allocation.
*
*/
if (dofree)
free(dofree);
/* Initial power-on reset for expanded storage */
xstorage_clear();
configure_region_reloc();
/* Call initial_cpu_reset for every online processor */
if (sysblk.cpus)
{
for (cpu = 0; cpu < sysblk.maxcpu; cpu++)
{
if (IS_CPU_ONLINE(cpu))
{
regs=sysblk.regs[cpu];
ARCH_DEP(initial_cpu_reset) (regs) ;
}
}
}
#else /*!_FEATURE_EXPANDED_STORAGE*/
UNREFERENCED(xpndsize);
WRMSG(HHC01431, "I");
#endif /*!_FEATURE_EXPANDED_STORAGE*/
return 0;
}
/* 4 next functions used for fast device lookup cache management */
static void AddDevnumFastLookup( DEVBLK *dev, U16 lcss, U16 devnum )
{
unsigned int Channel;
BYTE bAlreadyHadLock = try_obtain_lock( &sysblk.config );
if (sysblk.devnum_fl == NULL)
sysblk.devnum_fl = (DEVBLK***)
calloc( 256 * FEATURE_LCSS_MAX, sizeof( DEVBLK** ));
Channel = (devnum >> 8) | ((lcss & (FEATURE_LCSS_MAX-1)) << 8);
if (sysblk.devnum_fl[Channel] == NULL)
sysblk.devnum_fl[Channel] = (DEVBLK**)
calloc( 256, sizeof( DEVBLK* ));
sysblk.devnum_fl[Channel][devnum & 0xff] = dev;
if (!bAlreadyHadLock)
release_lock( &sysblk.config );
}
static void AddSubchanFastLookup( DEVBLK *dev, U16 ssid, U16 subchan )
{
unsigned int schw;
BYTE bAlreadyHadLock = try_obtain_lock( &sysblk.config );
if (sysblk.subchan_fl == NULL)
sysblk.subchan_fl = (DEVBLK***)
calloc( 256 * FEATURE_LCSS_MAX, sizeof( DEVBLK** ));
schw = (subchan >> 8) | (SSID_TO_LCSS( ssid ) << 8);
if (sysblk.subchan_fl[schw] == NULL)
sysblk.subchan_fl[schw] = (DEVBLK**)
calloc( 256, sizeof( DEVBLK* ));
sysblk.subchan_fl[schw][subchan & 0xff] = dev;
if (!bAlreadyHadLock)
release_lock( &sysblk.config );
}
static void DelDevnumFastLookup(U16 lcss,U16 devnum)
{
unsigned int Channel;
if(sysblk.devnum_fl==NULL)
{
return;
}
Channel=(devnum & 0xff00)>>8 | ((lcss & (FEATURE_LCSS_MAX-1))<<8);
if(sysblk.devnum_fl[Channel]==NULL)
{
return;
}
sysblk.devnum_fl[Channel][devnum & 0xff]=NULL;
}
static void DelSubchanFastLookup(U16 ssid, U16 subchan)
{
unsigned int schw;
#if 0
logmsg(_("DEBUG : DSFL Removing %d\n"),subchan);
#endif
if(sysblk.subchan_fl==NULL)
{
return;
}
schw=((subchan & 0xff00)>>8)|(SSID_TO_LCSS(ssid) << 8);
if(sysblk.subchan_fl[schw]==NULL)
{
return;
}
sysblk.subchan_fl[schw][subchan & 0xff]=NULL;
}
#ifdef NEED_FND_CHPBLK
static
CHPBLK *fnd_chpblk(U16 css, BYTE chpid)
{
CHPBLK *chp;
for (chp = sysblk.firstchp; chp != NULL; chp = chp->nextchp)
if (chp->chpid == chpid && chp->css == css)
return chp;
return NULL;
}
#endif
#ifdef NEED_GET_CHPBLK
static
CHPBLK *get_chpblk(U16 css, BYTE chpid, BYTE chptype)
{
CHPBLK *chp;
CHPBLK**chpp;
if((chp = fnd_chpblk(css, chpid)))
return chp;
else
{
if (!(chp = (CHPBLK*)malloc(sizeof(CHPBLK))))
{
logmsg("malloc(chpblk) failed: %s\n",strerror(errno));
return NULL;
}
memset (chp, 0, sizeof(CHPBLK));
chp->css = css;
chp->chpid = chpid;
chp->chptype = chptype;
/* Search for the last channel path block on the chain */
for (chpp = &(sysblk.firstchp); *chpp != NULL;
chpp = &((*chpp)->nextchp));
/* Add the new channel path block to the end of the chain */
*chpp = chp;
}
}
#endif
/* NOTE: also does obtain_lock(&dev->lock); */
static DEVBLK *get_devblk(U16 lcss, U16 devnum)
{
DEVBLK *dev;
DEVBLK**dvpp;
if(lcss >= FEATURE_LCSS_MAX)
lcss = 0;
for (dev = sysblk.firstdev; dev != NULL; dev = dev->nextdev)
if (!(dev->allocated) && dev->ssid == LCSS_TO_SSID(lcss)) break;
if(!dev)
{
if (!(dev = (DEVBLK*)calloc_aligned((sizeof(DEVBLK)+4095) & ~4095,4096)))
{
char buf[64];
MSGBUF(buf, "calloc(%d)", (int)sizeof(DEVBLK));
WRMSG (HHC01460, "E", lcss, devnum, buf, strerror(errno));
return NULL;
}
/* Clear device block and initialize header */
strncpy((char*)dev->blknam, HDL_NAME_DEVBLK, sizeof(dev->blknam));
strncpy((char*)dev->blkver, HDL_VERS_DEVBLK, sizeof(dev->blkver));
dev->blkloc = (U64)(size_t)dev;
dev->blksiz = HDL_SIZE_DEVBLK;
strncpy((char*)dev->blkend, HDL_NAME_DEVBLK, sizeof(dev->blknam));
/* Initialize the device lock and conditions */
initialize_lock ( &dev->lock );
initialize_condition ( &dev->kbcond );
#if defined( OPTION_SHARED_DEVICES )
initialize_condition ( &dev->shiocond );
#endif // defined( OPTION_SHARED_DEVICES )
#if defined(OPTION_SCSI_TAPE)
initialize_condition ( &dev->stape_sstat_cond );
InitializeListLink ( &dev->stape_statrq.link );
InitializeListLink ( &dev->stape_mntdrq.link );
dev->stape_statrq.dev = dev;
dev->stape_mntdrq.dev = dev;
dev->sstat = GMT_DR_OPEN(-1);
#endif
/* Search for the last device block on the chain */
for (dvpp = &(sysblk.firstdev); *dvpp != NULL;
dvpp = &((*dvpp)->nextdev));
/* Add the new device block to the end of the chain */
*dvpp = dev;
dev->ssid = LCSS_TO_SSID(lcss);
dev->subchan = sysblk.highsubchan[lcss]++;
}
/* Obtain the device lock. Caller will release it. */
obtain_lock (&dev->lock);
dev->group = NULL;
dev->member = 0;
memset(dev->filename, 0, sizeof(dev->filename));
dev->cpuprio = sysblk.cpuprio;
dev->devprio = sysblk.devprio;
dev->hnd = NULL;
dev->devnum = devnum;
dev->chanset = lcss;
dev->chptype[0] = CHP_TYPE_EIO; /* Interim - default to emulated */
dev->fd = -1;
#ifdef OPTION_SYNCIO
dev->syncio = 0;
#endif // OPTION_SYNCIO
dev->ioint.dev = dev;
dev->ioint.pending = 1;
dev->ioint.priority = -1;
dev->pciioint.dev = dev;
dev->pciioint.pcipending = 1;
dev->pciioint.priority = -1;
dev->attnioint.dev = dev;
dev->attnioint.attnpending = 1;
dev->attnioint.priority = -1;
dev->oslinux = sysblk.pgminttr == OS_LINUX;
/* Initialize storage view */
dev->mainstor = sysblk.mainstor;
dev->storkeys = sysblk.storkeys;
dev->mainlim = sysblk.mainsize - 1;
/* Initialize the path management control word */
memset (&dev->pmcw, 0, sizeof(PMCW));
dev->pmcw.devnum[0] = dev->devnum >> 8;
dev->pmcw.devnum[1] = dev->devnum & 0xFF;
dev->pmcw.lpm = 0x80;
dev->pmcw.pim = 0x80;
dev->pmcw.pom = 0xFF;
dev->pmcw.pam = 0x80;
dev->pmcw.chpid[0] = dev->devnum >> 8;
#if defined(OPTION_SHARED_DEVICES)
dev->shrdwait = -1;
#endif /*defined(OPTION_SHARED_DEVICES)*/
#ifdef EXTERNALGUI
if ( !dev->pGUIStat )
{
dev->pGUIStat = malloc( sizeof(GUISTAT) );
dev->pGUIStat->pszOldStatStr = dev->pGUIStat->szStatStrBuff1;
dev->pGUIStat->pszNewStatStr = dev->pGUIStat->szStatStrBuff2;
*dev->pGUIStat->pszOldStatStr = 0;
*dev->pGUIStat->pszNewStatStr = 0;
}
#endif /*EXTERNALGUI*/
/* Mark device valid */
dev->pmcw.flag5 |= PMCW5_V;
dev->allocated = 1;
return dev;
}
/* NOTE: also does release_lock(&dev->lock);*/
static void ret_devblk(DEVBLK *dev)
{
/* PROGRAMMING NOTE: the device buffer will be freed by the
'attach_device' function whenever it gets reused and not
here where you would normally expect it to be done since
doing it here might cause Hercules to crash due to poorly
written device handlers that still access the buffer for
a brief period after the device has been detached.
*/
/* Mark device invalid */
dev->allocated = 0;
dev->pmcw.flag5 &= ~PMCW5_V;
release_lock(&dev->lock);
}
/*-------------------------------------------------------------------*/
/* Function to delete a device configuration block */
/*-------------------------------------------------------------------*/
static int detach_devblk (DEVBLK *dev, int locked, const char *msg,
DEVBLK *errdev, DEVGRP *group)
{
int i; /* Loop index */
/* Free the entire group if this is a grouped device */
if (free_group( dev->group, locked, msg, errdev ))
{
/* Group successfully freed. All devices in the group
have been detached. Nothing remains for us to do.
All work has been completed. Return to caller.
*/
return 0;
}
/* Restore group ptr that that 'free_group' may have set to NULL */
dev->group = group;
/* Obtain the device lock. ret_devblk will release it */
if (!locked)
obtain_lock(&dev->lock);
DelSubchanFastLookup(dev->ssid, dev->subchan);
if(dev->pmcw.flag5 & PMCW5_V)
DelDevnumFastLookup(SSID_TO_LCSS(dev->ssid),dev->devnum);
/* Close file or socket */
if ((dev->fd > 2) || dev->console)
/* Call the device close handler */
(dev->hnd->close)(dev);
/* Issue device detached message and build channel report */
if (dev != errdev)
{
if (MLVL(DEBUG))
{
// "%1d:%04X %s detached"
WRMSG (HHC01465, "I", SSID_TO_LCSS(dev->ssid), dev->devnum, msg);
}
#ifdef _FEATURE_CHANNEL_SUBSYSTEM
/* Don't bother with channel report if we're shutting down */
if (!sysblk.shutdown)
{
#if defined(_370)
if (sysblk.arch_mode != ARCH_370)
#endif
build_detach_chrpt( dev );
#endif /*_FEATURE_CHANNEL_SUBSYSTEM*/
}
}
/* Free the argv array */
for (i = 0; i < dev->argc; i++)
if (dev->argv[i])
free(dev->argv[i]);
if (dev->argv)
free(dev->argv);
/* Free the device type name */
free(dev->typname);
/* Release lock and return the device to the DEVBLK pool */
ret_devblk( dev ); /* also does release_lock(&dev->lock);*/
return 0;
} /* end function detach_devblk */
/*-------------------------------------------------------------------*/
/* Function to delete a device configuration block by subchannel */
/*-------------------------------------------------------------------*/
static int detach_subchan (U16 lcss, U16 subchan, U16 devnum)
{
DEVBLK *dev; /* -> Device block */
int rc;
char str[64];
/* Find the device block */
dev = find_device_by_subchan ((LCSS_TO_SSID(lcss)<<16)|subchan);
MSGBUF( str, "subchannel %1d:%04X", lcss, subchan);
if (dev == NULL)
{
// "%1d:%04X %s does not exist"
WRMSG (HHC01464, "E", lcss, devnum, str);
return 1;
}
obtain_lock(&sysblk.config);
if (dev->group)
MSGBUF( str, "group subchannel %1d:%04X", lcss, subchan);
rc = detach_devblk( dev, FALSE, str, NULL, dev->group );
release_lock(&sysblk.config);
return rc;
}
/*-------------------------------------------------------------------*/
/* Function to terminate all CPUs and devices */
/*-------------------------------------------------------------------*/
void release_config()
{
DEVBLK *dev;
int cpu;
/* Deconfigure all CPU's */
OBTAIN_INTLOCK(NULL);
for (cpu = 0; cpu < sysblk.maxcpu; cpu++)
if(IS_CPU_ONLINE(cpu))
deconfigure_cpu(cpu);
RELEASE_INTLOCK(NULL);
/* Detach all devices */
for (dev = sysblk.firstdev; dev != NULL; dev = dev->nextdev)
if (dev->allocated)
{
if (sysblk.arch_mode == ARCH_370)
detach_device(SSID_TO_LCSS(dev->ssid), dev->devnum);
else
detach_subchan(SSID_TO_LCSS(dev->ssid), dev->subchan, dev->devnum);
}
/* Terminate device threads */
obtain_lock (&sysblk.ioqlock);
sysblk.devtwait=0;
broadcast_condition (&sysblk.ioqcond);
release_lock (&sysblk.ioqlock);
/* release storage */
sysblk.lock_mainstor = 0;
WRMSG( HHC01427, "I", "Main", !configure_storage(~0ULL) ? "" : "not " );
/* release expanded storage */
sysblk.lock_xpndstor = 0;
WRMSG( HHC01427, "I", "Expanded", !configure_xstorage(~0ULL) ? "" : "not ");
WRMSG(HHC01422, "I");
} /* end function release_config */
int configure_capping(U32 value)
{
if(sysblk.capvalue)
sysblk.capvalue = value;
else
{
int cnt;
for (cnt = 100; cnt > 0; cnt-- )
{
if ( sysblk.captid != 0 )
{
sysblk.capvalue = 0;
usleep(10000); // give the thread a chance to wake
}
else
break;
}
if ( cnt == 0 )
{
WRMSG( HHC00105, "E", (u_long)sysblk.captid, "Capping manager" );
return HERRTHREADACT;
}
if((sysblk.capvalue = value))
{
int rc;
rc = create_thread(&sysblk.captid, DETACHED, capping_manager_thread, NULL, "Capping manager");
if ( rc )
{
WRMSG(HHC00102, "E", strerror(rc));
return HERROR;
}
}
}
return HNOERROR;
}
#ifdef OPTION_SHARED_DEVICES
static void* shrdport_connecting_thread(void* arg)
{
DEVBLK* dev = (DEVBLK*) arg;
dev->hnd->init( dev, 0, NULL );
return NULL;
}
int configure_shrdport(U16 shrdport)
{
int rc;
if(sysblk.shrdport && shrdport)
{
WRMSG(HHC00744, "E");
return -1;
}
/* Start the shared server */
if ((sysblk.shrdport = shrdport))
{
rc = create_thread (&sysblk.shrdtid, DETACHED,
shared_server, NULL, "shared_server");
if (rc)
{
WRMSG(HHC00102, "E", strerror(rc));
sysblk.shrdport = 0;
return(1);
}
}
else
{
/* Terminate the shared device listener thread */
if (sysblk.shrdtid)
signal_thread (sysblk.shrdtid, SIGUSR2);
return 0;
}
/* Retry pending connections */
{
DEVBLK *dev;
TID tid;
for (dev = sysblk.firstdev; dev != NULL; dev = dev->nextdev)
if (dev->connecting)
{
rc = create_thread (&tid, DETACHED,
shrdport_connecting_thread, dev, "device connecting thread");
if (rc)
{
WRMSG(HHC00102, "E", strerror(rc));
return(1);
}
}
}
return 0;
}
#endif
int configure_herc_priority(int prio)
{
int rc;
/* Set root mode in order to set priority */
SETMODE(ROOT);
/* Set Hercules base priority */
rc = setpriority(PRIO_PROCESS, 0, (sysblk.hercprio = prio));
/* Back to user mode */
SETMODE(USER);
return rc;
}
int configure_cpu_priority(int prio)
{
int cpu;
sysblk.cpuprio = prio;
for(cpu = 0; cpu < MAX_CPU_ENGINES; cpu++)
if(sysblk.cputid[cpu])
set_thread_priority(sysblk.cputid[cpu], prio);
return 0;
}
int configure_dev_priority(int prio)
{
sysblk.devprio = prio;
return 0;
}
int configure_tod_priority(int prio)
{
sysblk.todprio = prio;
if(sysblk.todtid)
set_thread_priority(sysblk.todtid, prio);
return 0;
}
int configure_srv_priority(int prio)
{
sysblk.srvprio = prio;
return 0;
}
/*-------------------------------------------------------------------*/
/* Function to start a new CPU thread */
/* Caller MUST own the intlock */
/*-------------------------------------------------------------------*/
int configure_cpu(int cpu)
{
int i;
int rc;
char thread_name[32];
TID tid;
if(IS_CPU_ONLINE(cpu))
return -1;
/* If no more CPUs are permitted, exit */
if (sysblk.cpus >= sysblk.maxcpu)
{
return (HERRCPUOFF); /* CPU offline */
}
MSGBUF( thread_name, "Processor %s%02X", PTYPSTR( cpu ), cpu );
rc = create_thread (&sysblk.cputid[cpu], JOINABLE, cpu_thread,
&cpu, thread_name);
if (rc)
{
WRMSG(HHC00102, "E", strerror(rc));
return HERRCPUOFF; /* CPU offline */
}
/* Find out if we are a cpu thread */
tid = thread_id();
for (i = 0; i < sysblk.maxcpu; i++)
if (equal_threads( sysblk.cputid[i], tid ))
break;
if (i < sysblk.maxcpu)
sysblk.regs[i]->intwait = 1;
/* Wait for CPU thread to initialize */
while (!IS_CPU_ONLINE(cpu))
wait_condition (&sysblk.cpucond, &sysblk.intlock);
if (i < sysblk.maxcpu)
sysblk.regs[i]->intwait = 0;
#if defined(FEATURE_CONFIGURATION_TOPOLOGY_FACILITY)
/* Set topology-change-report-pending condition */
sysblk.topchnge = 1;
#endif /*defined(FEATURE_CONFIGURATION_TOPOLOGY_FACILITY)*/
return 0;
} /* end function configure_cpu */
/*-------------------------------------------------------------------*/
/* Function to remove a CPU from the configuration */
/* This routine MUST be called with the intlock held */
/*-------------------------------------------------------------------*/
int deconfigure_cpu(int cpu)
{
int i;
TID tid = thread_id();
/* Find out if we are a cpu thread */
for (i = 0; i < sysblk.maxcpu; i++)
if (equal_threads( sysblk.cputid[i], tid ))
break;
/* If we're NOT trying to deconfigure ourselves */
if (cpu != i)
{
if (!IS_CPU_ONLINE(cpu))
return -1;
/* Deconfigure CPU */
sysblk.regs[cpu]->configured = 0;
sysblk.regs[cpu]->cpustate = CPUSTATE_STOPPING;
ON_IC_INTERRUPT(sysblk.regs[cpu]);
/* Wake up CPU as it may be waiting */
WAKEUP_CPU (sysblk.regs[cpu]);
/* (if we're a cpu thread) */
if (i < sysblk.maxcpu)
sysblk.regs[i]->intwait = 1;
/* Wait for CPU thread to terminate */
while (IS_CPU_ONLINE(cpu))
wait_condition (&sysblk.cpucond, &sysblk.intlock);
/* (if we're a cpu thread) */
if (i < sysblk.maxcpu)
sysblk.regs[i]->intwait = 0;
join_thread (sysblk.cputid[cpu], NULL);
detach_thread( sysblk.cputid[cpu] );
/*-----------------------------------------------------------*/
/* Note: While this is the logical place to cleanup and to */
/* release the associated regs context, there is post */
/* processing that is done by various callers. */
/*-----------------------------------------------------------*/
}
else
{
/* Else we ARE trying to deconfigure ourselves */
sysblk.regs[cpu]->configured = 0;
sysblk.regs[cpu]->cpustate = CPUSTATE_STOPPING;
ON_IC_INTERRUPT(sysblk.regs[cpu]);
}
sysblk.cputid[cpu] = 0;
#if defined(FEATURE_CONFIGURATION_TOPOLOGY_FACILITY)
/* Set topology-change-report-pending condition */
sysblk.topchnge = 1;
#endif /*defined(FEATURE_CONFIGURATION_TOPOLOGY_FACILITY)*/
return 0;
} /* end function deconfigure_cpu */
#ifdef OPTION_ECL_CPU_ORDER
static int configure_cpu_order[] = { 1, 0, 3, 2 }; // ECL CPU type order
#define config_order(_cpu) (((_cpu) < (int)(sizeof(configure_cpu_order)/sizeof(int))) ? configure_cpu_order[(_cpu)] : (_cpu))
#else
#define config_order(_cpu) (_cpu)
#endif
int configure_numcpu(int numcpu)
{
int cpu;
OBTAIN_INTLOCK(NULL);
if(sysblk.cpus)
for(cpu = 0; cpu < sysblk.maxcpu; cpu++)
if(IS_CPU_ONLINE(cpu) && sysblk.regs[cpu]->cpustate == CPUSTATE_STARTED)
{
RELEASE_INTLOCK(NULL);
return HERRCPUONL;
}
/* Start the CPUs */
for(cpu = 0; cpu < sysblk.maxcpu; cpu++)
if(cpu < numcpu)
{
if(!IS_CPU_ONLINE(config_order(cpu)))
configure_cpu(config_order(cpu));
}
else
{
if(IS_CPU_ONLINE(config_order(cpu)))
deconfigure_cpu(config_order(cpu));
}
RELEASE_INTLOCK(NULL);
return 0;
}
/*-------------------------------------------------------------------*/
/* Function to build a device configuration block */
/*-------------------------------------------------------------------*/
int attach_device (U16 lcss, U16 devnum, const char *type,
int addargc, char *addargv[],
int numconfdev)
{
DEVBLK *dev; /* -> Device block */
int rc; /* Return code */
int i; /* Loop index */
/* Obtain (re)configuration lock */
obtain_lock(&sysblk.config);
/* Check whether device number has already been defined */
if (find_device_by_devnum(lcss,devnum) != NULL)
{
// "%1d:%04X device already exists"
WRMSG (HHC01461, "E", lcss, devnum);
release_lock(&sysblk.config);
return 1;
}
/* Obtain device block from our DEVBLK pool and lock the device. */
dev = get_devblk(lcss, devnum); /* does obtain_lock(&dev->lock); */
// PROGRAMMING NOTE: the rule is, once a DEVBLK has been obtained
// from the pool it can be returned back to the pool via a simple
// call to ret_devblk if the device handler initialization function
// has NOT yet been called. Once the device handler initialization
// function has been called however then you MUST use detach_devblk
// to return it back to the pool so that the entire group is freed.
if(!(dev->hnd = hdl_ghnd(type)))
{
// "%1d:%04X devtype %s not recognized"
WRMSG (HHC01462, "E", lcss, devnum, type);
ret_devblk(dev); /* also does release_lock(&dev->lock);*/
release_lock(&sysblk.config);
return 1;
}
dev->typname = strdup(type);
/* Copy the arguments */
dev->argc = addargc;
if (addargc)
{
dev->argv = malloc ( addargc * sizeof(BYTE *) );
for (i = 0; i < addargc; i++)
if (addargv[i])
dev->argv[i] = strdup(addargv[i]);
else
dev->argv[i] = NULL;
}
else
dev->argv = NULL;
/* Set the number of config statement device addresses */
dev->numconfdev = numconfdev;
/* Call the device handler initialization function */
rc = (int)(dev->hnd->init)(dev, addargc, addargv);
if (rc < 0)
{
// "%1d:%04X device initialization failed"
WRMSG (HHC01463, "E", lcss, devnum);
/* Detach the device and return it back to the DEVBLK pool */
detach_devblk( dev, TRUE, "device", dev, dev->group );
release_lock(&sysblk.config);
return 1;
}
/* Obtain device data buffer */
if (dev->bufsize != 0)
{
/* PROGRAMMING NOTE: we free the device buffer here, not in
the 'ret_devblk' function (where you would normally expect
it to be done) since doing it in 'ret_devblk' might cause
Hercules to crash due to poorly written device handlers
that continue accessing the buffer for a brief period even
though the device has already been detached.
*/
if (dev->buf)
free_aligned( dev->buf );
dev->buf = malloc_aligned (dev->bufsize, 4096);
if (dev->buf == NULL)
{
char buf[64];
// "%1d:%04X error in function %s: %s"
MSGBUF( buf, "malloc(%lu)", (unsigned long) dev->bufsize);
WRMSG (HHC01460, "E", lcss, dev->devnum, buf, strerror(errno));
/* Detach the device and return it back to the DEVBLK pool */
detach_devblk( dev, TRUE, "device", dev, dev->group );
release_lock(&sysblk.config);
return 1;
}
}
/* Release device lock */
release_lock(&dev->lock);
#ifdef _FEATURE_CHANNEL_SUBSYSTEM
/* Build Channel Report */
#if defined(_370)
if (sysblk.arch_mode != ARCH_370)
#endif
build_attach_chrpt( dev );
#endif /*_FEATURE_CHANNEL_SUBSYSTEM*/
/*
if(lcss!=0 && sysblk.arch_mode==ARCH_370)
{
// "%1d:%04X: only devices on CSS 0 are usable in S/370 mode"
WRMSG (HHC01458, "W", lcss, devnum);
}
*/
release_lock(&sysblk.config);
if ( rc == 0 && MLVL(DEBUG) )
{
// "Device %04X type %04X subchannel %d:%04X attached"
WRMSG(HHC02198, "I", dev->devnum, dev->devtype, dev->chanset, dev->subchan);
}
return 0;
} /* end function attach_device */
/*-------------------------------------------------------------------*/
/* Function to delete a device configuration block by device number */
/*-------------------------------------------------------------------*/
int detach_device (U16 lcss,U16 devnum)
{
DEVBLK *dev; /* -> Device block */
int rc;
char* str = "device";
/* Find the device block */
dev = find_device_by_devnum (lcss,devnum);
if (dev == NULL)
{
// "%1d:%04X %s does not exist"
WRMSG (HHC01464, "E", lcss, devnum, str);
return 1;
}
obtain_lock(&sysblk.config);
if (dev->group)
str = "group device";
rc = detach_devblk( dev, FALSE, str, NULL, dev->group );
release_lock(&sysblk.config);
return rc;
}
/*-------------------------------------------------------------------*/
/* Function to rename a device configuration block */
/*-------------------------------------------------------------------*/
int define_device (U16 lcss, U16 olddevn,U16 newdevn)
{
DEVBLK *dev; /* -> Device block */
/* Obtain (re)configuration lock */
obtain_lock(&sysblk.config);
/* Find the device block */
dev = find_device_by_devnum (lcss, olddevn);
if (dev == NULL)
{
WRMSG (HHC01464, "E", lcss, olddevn, "device");
release_lock(&sysblk.config);
return 1;
}
/* Check that new device number does not already exist */
if (find_device_by_devnum(lcss, newdevn) != NULL)
{
WRMSG (HHC01461, "E", lcss, newdevn);
release_lock(&sysblk.config);
return 1;
}
#ifdef _FEATURE_CHANNEL_SUBSYSTEM
/* Build Channel Report */
#if defined(_370)
if (sysblk.arch_mode != ARCH_370)
#endif
build_detach_chrpt( dev );
#endif /*_FEATURE_CHANNEL_SUBSYSTEM*/
/* Obtain the device lock */
obtain_lock(&dev->lock);
/* Update the device number in the DEVBLK */
dev->devnum = newdevn;
/* Update the device number in the PMCW */
dev->pmcw.devnum[0] = newdevn >> 8;
dev->pmcw.devnum[1] = newdevn & 0xFF;
DelDevnumFastLookup(lcss,olddevn);
AddDevnumFastLookup(dev,lcss,newdevn);
/* Release device lock */
release_lock(&dev->lock);
#ifdef _FEATURE_CHANNEL_SUBSYSTEM
/* Build Channel Report */
#if defined(_370)
if (sysblk.arch_mode != ARCH_370)
#endif
build_attach_chrpt( dev );
#endif /*_FEATURE_CHANNEL_SUBSYSTEM*/
// WRMSG (HHC01459, "I", lcss, olddevn, lcss, newdevn);
release_lock(&sysblk.config);
return 0;
} /* end function define_device */
/*-------------------------------------------------------------------*/
/* Function to group devblk's belonging to one device (eg OSA, LCS) */
/* */
/* group_device is intended to be called from within a device */
/* initialisation routine to group 1 or more devices to a logical */
/* device group. */
/* */
/* group_device will return true for the device that completes */
/* the device group. (ie the last device to join the group) */
/* */
/* when no group exists, and device group is called with a device */
/* count of zero, then no group will be created. Otherwise */
/* a new group will be created and the currently attaching device */
/* will be the first in the group. */
/* */
/* when a device in a group is detached, all devices in the group */
/* will be detached. The first device to be detached will enter */
/* its close routine with the group intact. Subsequent devices */
/* being detached will no longer have access to previously detached */
/* devices. */
/* */
/* Example of a fixed count device group: */
/* */
/* device_init(dev) */
/* { */
/* if( !device_group(dev, 2) ) */
/* return 0; */
/* */
/* ... all devices in the group have been attached, */
/* ... group initialisation may proceed. */
/* */
/* } */
/* */
/* */
/* Variable device group example: */
/* */
/* device_init(dev) */
/* { */
/* if( !group_device(dev, 0) && dev->group ) */
/* return 0; */
/* */
/* if( !device->group ) */
/* { */
/* ... process parameters to determine number of devices */
/* */
/* // Create group */
/* if( !group_device(dev, variable_count) ) */
/* return 0; */
/* } */
/* */
/* ... all devices in the group have been attached, */
/* ... group initialisation may proceed. */
/* } */
/* */
/* */
/* dev->group : pointer to DEVGRP structure or NULL */
/* dev->member : index into memdev array in DEVGRP structure for */
/* : current DEVBLK */
/* group->members : number of members in group */
/* group->acount : number active members in group */
/* group->memdev[] : array of DEVBLK pointers of member devices */
/* */
/* */
/* members will be equal to acount for a complete group */
/* */
/* */
/* Always: (for grouped devices) */
/* dev->group->memdev[dev->member] == dev */
/* */
/* */
/* Jan Jaeger, 23 Apr 2004 */
/*-------------------------------------------------------------------*/
DLL_EXPORT int group_device(DEVBLK *dev, int members)
{
DEVBLK *tmp;
// Find a compatible group that is incomplete
for (tmp = sysblk.firstdev;
tmp != NULL
&& (!tmp->allocated // not allocated
|| !tmp->group // not a group device
|| strcmp(tmp->typname,dev->typname) // unequal type
|| (tmp->group->members == tmp->group->acount) ); // complete
tmp = tmp->nextdev) ;
if(tmp)
{
// Join Group
dev->group = tmp->group;
dev->member = dev->group->acount++;
dev->group->memdev[dev->member] = dev;
}
else if(members)
{
// Allocate a new Group when requested
size_t size = sizeof(DEVGRP) + (members * sizeof(DEVBLK*));
dev->group = malloc( size );
memset( dev->group, 0, size );
dev->group->members = members;
dev->group->acount = 1;
dev->group->memdev[0] = dev;
dev->member = 0;
}
return (dev->group && (dev->group->members == dev->group->acount));
}
/*-------------------------------------------------------------------*/
/* Function to free a device group (i.e. all devices in the group) */
/*-------------------------------------------------------------------*/
DLL_EXPORT BYTE free_group( DEVGRP *group, int locked,
const char *msg, DEVBLK *errdev )
{
DEVBLK *dev;
int i;
if (!group || !group->members)
return FALSE; // no group or group has no members
for (i=0; i < group->acount; i++)
{
if ((dev = group->memdev[i]) && dev->allocated)
{
// PROGRAMMING NOTE: detach_devblk automatically calls
// free_group (i.e. THIS function) to try and free the
// entire group at once in case it is a grouped device.
// Therefore we must clear dev->group to NULL *before*
// calling detach_devblk to prevent infinite recursion.
dev->group = NULL;
detach_devblk( dev, dev == errdev && locked, msg, errdev,
group );
}
}
free( group );
return TRUE; // group successfully freed
}
/*-------------------------------------------------------------------*/
/* Function to find a device block given the device number */
/*-------------------------------------------------------------------*/
DLL_EXPORT DEVBLK *find_device_by_devnum (U16 lcss,U16 devnum)
{
DEVBLK *dev;
DEVBLK **devtab;
int Chan;
Chan=(devnum & 0xff00)>>8 | ((lcss & (FEATURE_LCSS_MAX-1))<<8);
if(sysblk.devnum_fl!=NULL)
{
devtab=sysblk.devnum_fl[Chan];
if(devtab!=NULL)
{
dev=devtab[devnum & 0xff];
if (dev && IS_DEV( dev ) && dev->devnum == devnum)
{
return dev;
}
else
{
DelDevnumFastLookup(lcss,devnum);
}
}
}
for (dev = sysblk.firstdev; dev != NULL; dev = dev->nextdev)
if (IS_DEV( dev ) && dev->devnum == devnum && lcss==SSID_TO_LCSS(dev->ssid)) break;
if(dev)
{
AddDevnumFastLookup(dev,lcss,devnum);
}
return dev;
} /* end function find_device_by_devnum */
/*-------------------------------------------------------------------*/
/* Function to find a device block given the subchannel number */
/*-------------------------------------------------------------------*/
DEVBLK *find_device_by_subchan (U32 ioid)
{
U16 subchan = ioid & 0xFFFF;
DEVBLK *dev;
unsigned int schw = ((subchan & 0xff00)>>8)|(IOID_TO_LCSS(ioid)<<8);
#if 0
logmsg(_("DEBUG : FDBS FL Looking for %d\n"),subchan);
#endif
if(sysblk.subchan_fl && sysblk.subchan_fl[schw] && sysblk.subchan_fl[schw][subchan & 0xff])
return sysblk.subchan_fl[schw][subchan & 0xff];
#if 0
logmsg(_("DEBUG : FDBS SL Looking for %8.8x\n"),ioid);
#endif
for (dev = sysblk.firstdev; dev != NULL; dev = dev->nextdev)
if (dev->ssid == IOID_TO_SSID(ioid) && dev->subchan == subchan) break;
if(dev)
{
AddSubchanFastLookup(dev, IOID_TO_SSID(ioid), subchan);
}
else
{
DelSubchanFastLookup(IOID_TO_SSID(ioid), subchan);
}
return dev;
} /* end function find_device_by_subchan */
#ifdef OPTION_SYNCIO
/*-------------------------------------------------------------------*/
/* Returns a CPU register context for the device, or else NULL */
/*-------------------------------------------------------------------*/
DLL_EXPORT REGS *devregs(DEVBLK *dev)
{
/* If a register context already exists then use it */
if (dev->regs)
return dev->regs;
/* Otherwise attempt to determine what it should be */
{
int i;
TID tid = thread_id(); /* Our own thread id */
for (i=0; i < sysblk.maxcpu; i++)
if (equal_threads(tid,sysblk.cputid[i])) /* Are we a cpu thread? */
return sysblk.regs[i]; /* yes, use its context */
}
return NULL; /* Not CPU thread. Return NULL register context */
}
#endif // OPTION_SYNCIO
/*-------------------------------------------------------------------*/
/* Internal device parsing structures */
/*-------------------------------------------------------------------*/
typedef struct _DEVARRAY
{
U16 cuu1;
U16 cuu2;
} DEVARRAY;
typedef struct _DEVNUMSDESC
{
BYTE lcss;
DEVARRAY *da;
} DEVNUMSDESC;
/*-------------------------------------------------------------------*/
/* Function to Parse a LCSS specification in a device number spec */
/* Syntax : [lcss:]Anything... */
/* Function args : */
/* const char * spec : Parsed string */
/* char **rest : Rest of string (or original str) */
/* Returns : */
/* int : 0 if not specified, 0<=n<FEATURE_LCSS_MAX */
/* -1 Spec error */
/* */
/* If the function returns a positive value, then *rest should */
/* be freed by the caller. */
/*-------------------------------------------------------------------*/
static int
parse_lcss(const char *spec,
char **rest,int verbose)
{
int lcssid;
char *wrk;
char *lcss;
char *r;
char *strptr;
char *garbage;
char *strtok_str = NULL;
size_t len;
len = strlen(spec)+1;
wrk=malloc(len);
strlcpy(wrk,spec,len);
lcss=strtok_r(wrk,":",&strtok_str);
if(lcss==NULL)
{
if(verbose)
{
WRMSG(HHC01466, "E");
}
free(wrk);
return(-1);
}
r=strtok_r(NULL,":",&strtok_str );
if(r==NULL)
{
*rest=wrk;
return 0;
}
garbage=strtok_r(NULL,":",&strtok_str );
if(garbage!=NULL)
{
if(verbose)
{
WRMSG(HHC01467, "E");
}
free(wrk);
return(-1);
}
lcssid=strtoul(lcss,&strptr,10);
if(*strptr!=0)
{
if(verbose)
{
WRMSG(HHC01468, "E",lcss);
}
free(wrk);
return -1;
}
if(lcssid>=FEATURE_LCSS_MAX)
{
if(verbose)
{
WRMSG(HHC01469, "E",lcssid,FEATURE_LCSS_MAX-1);
}
free(wrk);
return -1;
}
len = strlen(r)+1;
*rest=malloc(len);
strlcpy(*rest,r,len);
free(wrk);
return lcssid;
}
static int
parse_single_devnum__INTERNAL(const char *spec,
U16 *p_lcss,
U16 *p_devnum,
int verbose)
{
int rc;
U16 lcss;
char *r;
char *strptr;
rc=parse_lcss(spec,&r,verbose);
if(rc<0)
{
return -1;
}
lcss=rc;
rc=strtoul(r,&strptr,16);
if(rc<0 || rc>0xffff || *strptr!=0)
{
if(verbose)
{
WRMSG(HHC01470,"E","device address specification",*strptr);
}
free(r);
return -1;
}
*p_devnum=rc;
*p_lcss=lcss;
return 0;
}
DLL_EXPORT
int
parse_single_devnum(const char *spec,
U16 *lcss,
U16 *devnum)
{
return parse_single_devnum__INTERNAL(spec,lcss,devnum,1);
}
int
parse_single_devnum_silent(const char *spec,
U16 *lcss,
U16 *devnum)
{
return parse_single_devnum__INTERNAL(spec,lcss,devnum,0);
}
/*-------------------------------------------------------------------*/
/* Function to Parse compound device numbers */
/* Syntax : [lcss:]CCUU[-CUU][,CUU..][.nn][...] */
/* Examples : 200-23F */
/* 200,201 */
/* 200.16 */
/* 200-23F,280.8 */
/* etc... */
/* : is the LCSS id separator (only 0 or 1 allowed and it must be 1st*/
/* - is the range specification (from CUU to CUU) */
/* , is the separator */
/* . is the count indicator (nn is decimal) */
/* 1st parm is the specification string as specified above */
/* 2nd parm is the address of an array of DEVARRAY */
/* Return value : 0 - Parsing error, etc.. */
/* >0 - Size of da */
/* */
/* NOTE : A basic validity check is made for the following : */
/* All CUUs must belong on the same channel */
/* (this check is to eventually pave the way to a formal */
/* channel/cu/device architecture) */
/* no 2 identical CCUUs */
/* ex : 200,300 : WRONG */
/* 200.12,200.32 : WRONG */
/* 2FF.2 : WRONG */
/* NOTE : caller should free the array returned in da if the return */
/* value is not 0 */
/*-------------------------------------------------------------------*/
static size_t parse_devnums(const char *spec,DEVNUMSDESC *dd)
{
size_t gcount; /* Group count */
size_t i; /* Index runner */
char *grps; /* Pointer to current devnum group */
char *sc; /* Specification string copy */
DEVARRAY *dgrs; /* Device groups */
U16 cuu1,cuu2; /* CUUs */
char *strptr; /* strtoul ptr-ptr */
int basechan=0; /* Channel for all CUUs */
int duplicate; /* duplicated CUU indicator */
int badcuu; /* offending CUU */
int rc; /* Return code work var */
char *strtok_str = NULL; /* Last token */
rc=parse_lcss(spec,&sc,1);
if(rc<0)
{
return 0;
}
dd->lcss=rc;
/* Split by ',' groups */
gcount=0;
grps=strtok_r(sc,",",&strtok_str );
dgrs=NULL;
while(grps!=NULL)
{
if(dgrs==NULL)
{
dgrs=malloc(sizeof(DEVARRAY));
}
else
{
dgrs=realloc(dgrs,(sizeof(DEVARRAY))*(gcount+1));
}
cuu1=strtoul(grps,&strptr,16);
switch(*strptr)
{
case 0: /* Single CUU */
cuu2=cuu1;
break;
case '-': /* CUU Range */
cuu2=strtoul(&strptr[1],&strptr,16);
if(*strptr!=0)
{
WRMSG(HHC01470, "E","second device number in device range",*strptr);
free(dgrs);
free(sc);
return(0);
}
break;
case '.': /* CUU Count */
cuu2=cuu1+strtoul(&strptr[1],&strptr,10);
cuu2--;
if(*strptr!=0)
{
WRMSG(HHC01470,"E","device count",*strptr);
free(dgrs);
free(sc);
return(0);
}
break;
default:
WRMSG(HHC01470,"E","incorrect device specification",*strptr);
free(dgrs);
free(sc);
return(0);
}
/* Check cuu1 <= cuu2 */
if(cuu1>cuu2)
{
WRMSG(HHC01471,"E",cuu2,cuu1);
free(dgrs);
free(sc);
return(0);
}
if(gcount==0)
{
basechan=(cuu1 >> 8) & 0xff;
}
badcuu=-1;
if(((cuu1 >> 8) & 0xff) != basechan)
{
badcuu=cuu1;
}
else
{
if(((cuu2 >> 8) & 0xff) != basechan)
{
badcuu=cuu2;
}
}
if(badcuu>=0)
{
WRMSG(HHC01472,"E", dd->lcss, badcuu, basechan);
free(dgrs);
free(sc);
return(0);
}
/* Check for duplicates */
duplicate=0;
for(i=0;i<gcount;i++)
{
/* check 1st cuu not within existing range */
if(cuu1>=dgrs[i].cuu1 && cuu1<=dgrs[i].cuu2)
{
duplicate=1;
break;
}
/* check 2nd cuu not within existing range */
if(cuu2>=dgrs[i].cuu1 && cuu1<=dgrs[i].cuu2)
{
duplicate=1;
break;
}
/* check current range doesn't completelly overlap existing range */
if(cuu1<dgrs[i].cuu1 && cuu2>dgrs[i].cuu2)
{
duplicate=1;
break;
}
}
if(duplicate)
{
WRMSG(HHC01473,"E",cuu1,cuu2);
free(dgrs);
free(sc);
return(0);
}
dgrs[gcount].cuu1=cuu1;
dgrs[gcount].cuu2=cuu2;
gcount++;
grps=strtok_r(NULL,",",&strtok_str );
}
free(sc);
dd->da=dgrs;
return(gcount);
}
int
parse_and_attach_devices(const char *sdevnum,
const char *sdevtype,
int addargc,
char **addargv)
{
DEVNUMSDESC dnd;
int baddev;
size_t devncount;
DEVARRAY *da;
int i;
U16 devnum;
int rc;
int numconfdev = 0;
#if defined(ENABLE_SYSTEM_SYMBOLS)
int j;
char **newargv;
char **orig_newargv;
#endif /* #if defined( ENABLE_SYSTEM_SYMBOLS ) */
devncount=parse_devnums(sdevnum,&dnd);
if(devncount==0)
return HERRDEVIDA; /* Invalid Device Address */
/* Calculate the number of config statement device addresses */
for (i=0;i<(int)devncount;i++)
{
da=dnd.da;
numconfdev = numconfdev + ((da[i].cuu2 - da[i].cuu1) + 1);
}
#if defined(ENABLE_SYSTEM_SYMBOLS)
newargv=malloc(MAX_ARGS*sizeof(char *));
orig_newargv=malloc(MAX_ARGS*sizeof(char *));
#endif /* #if defined( ENABLE_SYSTEM_SYMBOLS ) */
for(baddev=0,i=0;i<(int)devncount;i++)
{
da=dnd.da;
for(devnum=da[i].cuu1;devnum<=da[i].cuu2;devnum++)
{
#if defined(ENABLE_SYSTEM_SYMBOLS)
#if defined(ENABLE_BUILTIN_SYMBOLS)
char wrkbfr[32];
MSGBUF( wrkbfr, "%3.3X",devnum);
set_symbol("CUU",wrkbfr);
MSGBUF( wrkbfr, "%4.4X",devnum);
set_symbol("CCUU",wrkbfr);
set_symbol("DEVN", wrkbfr);
MSGBUF( wrkbfr, "%d",dnd.lcss);
set_symbol("CSS",wrkbfr);
#endif /* #if defined( ENABLE_BUILTIN_SYMBOLS ) */
for(j=0;j<addargc;j++)
{
orig_newargv[j]=newargv[j]=resolve_symbol_string(addargv[j]);
}
/* Build the device configuration block */
rc=attach_device(dnd.lcss, devnum, sdevtype, addargc, newargv, numconfdev);
for(j=0;j<addargc;j++)
{
free(orig_newargv[j]);
}
#else
/* Build the device configuration block (no syms) */
rc=attach_device(dnd.lcss, devnum, sdevtype, addargc, addargv, numconfdev);
#endif /* #if defined(ENABLE_SYSTEM_SYMBOLS) */
if(rc!=0)
{
baddev=1;
break;
}
}
if(baddev)
{
break;
}
}
#if defined(ENABLE_SYSTEM_SYMBOLS)
free(newargv);
free(orig_newargv);
#endif /* #if defined( ENABLE_SYSTEM_SYMBOLS ) */
free(dnd.da);
return baddev?-1:0;
}
#define MAX_LOGO_LINES 256
DLL_EXPORT void clearlogo()
{
size_t i;
if(sysblk.herclogo!=NULL)
{
for(i=0;i<sysblk.logolines;i++)
{
free(sysblk.herclogo[i]);
}
free(sysblk.herclogo);
sysblk.herclogo=NULL;
}
}
DLL_EXPORT int readlogo(char *fn)
{
char **data;
char bfr[256];
char *rec;
FILE *lf;
size_t len;
clearlogo();
lf=fopen(fn,"r");
if(lf==NULL)
{
return -1;
}
data=malloc(sizeof(char *)*MAX_LOGO_LINES);
sysblk.logolines=0;
while((rec=fgets(bfr,sizeof(bfr),lf))!=NULL)
{
rec[strlen(rec)-1]=0;
len = strlen(rec)+1;
data[sysblk.logolines]=malloc(len);
strlcpy(data[sysblk.logolines],rec,len);
sysblk.logolines++;
if(sysblk.logolines>MAX_LOGO_LINES)
{
break;
}
}
fclose(lf);
sysblk.herclogo=data;
return 0;
}
DLL_EXPORT int parse_conkpalv(char* s, int* idle, int* intv, int* cnt )
{
size_t n; char *p1, *p2, *p3, c;
ASSERT(s && *s && idle && intv && cnt);
if (!s || !*s || !idle || !intv || !cnt) return -1;
// Format: "(idle,intv,cnt)". All numbers. No spaces.
if (0
|| (n = strlen(s)) < 7
|| s[0] != '('
|| s[n-1] != ')'
)
return -1;
// 1st sub-operand
if (!(p1 = strchr(s+1, ','))) return -1;
c = *p1; *p1 = 0;
if ( strspn( s+1, "0123456789" ) != strlen(s+1) )
{
*p1 = c;
return -1;
}
*p1 = c;
// 2nd sub-operand
if (!(p2 = strchr(p1+1, ','))) return -1;
c = *p2; *p2 = 0;
if ( strspn( p1+1, "0123456789" ) != strlen(p1+1) )
{
*p2 = c;
return -1;
}
*p2 = c;
// 3rd sub-operand
if (!(p3 = strchr(p2+1, ')'))) return -1;
c = *p3; *p3 = 0;
if ( strspn( p2+1, "0123456789" ) != strlen(p2+1) )
{
*p3 = c;
return -1;
}
*p3 = c;
// convert each to number
c = *p1; *p1 = 0; *idle = atoi(s+1); *p1 = c;
c = *p2; *p2 = 0; *intv = atoi(p1+1); *p2 = c;
c = *p3; *p3 = 0; *cnt = atoi(p2+1); *p3 = c;
// check results
if (*idle <= 0 || INT_MAX == *idle) return -1;
if (*intv <= 0 || INT_MAX == *intv) return -1;
if (*cnt <= 0 || INT_MAX == *cnt ) return -1;
return 0;
}
#endif /*!defined(_GEN_ARCH)*/