[Search for users] [Overall Top Noters] [List of all Conferences] [Download this site]

Conference turris::digital_unix

Title:DIGITAL UNIX(FORMERLY KNOWN AS DEC OSF/1)
Notice:Welcome to the Digital UNIX Conference
Moderator:SMURF::DENHAM
Created:Thu Mar 16 1995
Last Modified:Fri Jun 06 1997
Last Successful Update:Fri Jun 06 1997
Number of topics:10068
Total number of notes:35879

3126.0. "An example STREAMS, loadable, module" by RDGENG::HAQUE (Shaheed R. Haque, 830-3531, reo2-g/e2) Tue Oct 31 1995 04:50

T.RTitleUserPersonal
Name
DateLines
3126.1RDGENG::HAQUEShaheed R. Haque, 830-3531, reo2-g/e2Tue Oct 31 1995 04:53884
3126.2RDGENG::HAQUEShaheed R. Haque, 830-3531, reo2-f/b3Wed Jun 05 1996 09:072
3126.2V3.2C/V4.0B compatible codeRDGENG::HAQUEShaheed R. Haque, 830-3531, reo2-f/b3Mon Mar 24 1997 05:57416
Originally, this reply read as follows:

>Note that this example works on V3.2C; see note 5697.* for advice with respect
>to V4.0 and beyond.

Generally speaking that advice still applies. However, I have managed to get a
driver which I can load and unload (but which hasn't been functionally verified)
using a single source for V3.2C and V4.0B.

Given that I am using elements of the V3.2C support syntax, and parts of the
V4.x support syntax, I cannot guarantee that this scheme will work forever, so
your mileage may vary. On the other hand, it got *me* through the transition
phase...

-- Notes for V3.2C/V4.0B compatible code ---------------------------------------

1. First you need to remember that there are two different compilers, and so
your Makefile will look different. I used a macro to pull in the right commands.

2. I also used the same macro to conditionally compile the driver code. As
implied above, my need for a single source stream was more important than
worrying about post V4.0B, so I am conditionally compiling the bare minimum that
I can get away with. As a result, more of the V3.2C stuff may eventually have to
be compiled out for post V4.0B. The macro is either OSF1_V3 or OSF1_V4, and
can be generated like this:

	typeset -r BLD_ARCH=`uname'`_`uname -r | sed 's/\..*//'`

3. The code below is a slight variation of whats in the book for callback
routines. I wanted to centralise the concept of "don't process any callbacks if
a previous callback had an error" (i.e. sticky callback failures), and so I
ended up with a callback dispatching scheme - you may want to revert to
something more like what is in the book.

4. Each difference section is followed by some comments...I've hand edited the
output to try to make it readable. Its porably best to print the sections out
and compare them side-by-side to see how few changes are actually required.

5. There are only 3 difference sections.

-- Difference sections for V3.2C/V4.0B compatible code -------------------------

============ V4.0B version
static void callback_dispatcher(
		int dispatch_point,
		int order,
		ulong arg,
		ulong event_arg);
static int register_devno(struct streamadm *sa);
static int tlink_open(
============ V3.2C equivalent
static int tlink_open(
============

Just declare a couple of new routines.
--------------------------------------------------------------------------------
============ V4.0B version
static struct streamadm sa;		/* Statically allocated storage for */
					/* callback or direct usage.	    */

static int callback_status = ESUCCESS;	/* Persistent, shared error state   */
					/* for callback routines.	    */

============ V3.2C equivalent

============

The streamadm struct was previously locally visible in a single routine. Now
make is visible as a static variable to any callback that needs it. Also
maintain a callback_status to allow "sticky" callback failures.

--------------------------------------------------------------------------------
============ V4.0B version
/*
**++
**  FUNCTIONAL DESCRIPTION:
**
**      dump_configure()
**
**	Called at this entry point by the STREAMS subsytem configuration code.
**
**  FORMAL PARAMETERS:
**
**        op         = Options for system configuration
**        indata     = Pointer to configuration input buffer
**        indatalen  = Byte size of input data buffer
**        outdata    = Pointer to configuration output buffer
**        outdatalen = Byte size of output data buffer
**
**  RETURN VALUE:
**
**      ESUCCESS, if success, ERRNO error otherwise.
**
**  SIDE EFFECTS:
**
**	{@description or none@}
**
**  DESIGN:
**
**      {@description or none@}
**
**  [@logical properties@]...
**
**  [@optional function tags@]...
**
**--
*/
int dump_configure(
        cfg_op_t op,
        cfg_attr_t indata,
        size_t indata_size,
        cfg_attr_t outdata,
        size_t outdata_size)
{
    int cfg_state;

#ifdef OSF1_V4
    if (cfgmgr_get_state(DUMP_NAME, &cfg_state) != ESUCCESS)
    {
	EPRINT(TMU_ERROR("cannot determine state of subsystem"));
	return EINVAL;
    }
    strcpy(
	mod_type,
	(cfg_state == SUBSYSTEM_STATICALLY_CONFIGURED ? "Static" : "Dynamic"));
#else
#define cfgmgr_set_status(a)
#define CFG_PT_PRECONFIG 12345678
#define CFG_PT_POSTCONFIG 87654321
#define register_callback(a,b,c,d) EINVAL
#define SUBSYSTEM_STATICALLY_CONFIGURED 12345678
    cfg_state = SUBSYSTEM_STATICALLY_CONFIGURED * strcmp(mod_type, "Dynamic");
    if (cfg_state == SUBSYSTEM_STATICALLY_CONFIGURED)
    {
	EPRINT(TMU_ERROR("TMU_DYNAMIC, Must be dynamically loaded"));
	return EINVAL;
    } 
#endif

    /* Initialise data used by configuration routines.			    */

    sa.sa_version = OSF_STREAMS_11;
    sa.sa_flags	= STR_IS_MODULE | STR_SYSV4_OPEN;
    sa.sa_ttys = NULL;
    sa.sa_sync_level = SQLVL_QUEUEPAIR;
    sa.sa_sync_info = NULL;
    strcpy(sa.sa_name, info.mi_idname);

    /*	What are we supposed to be doing, anyway?			    */

    switch (op)
    {
    case CFG_OP_CONFIGURE: 
	if (cfg_state == SUBSYSTEM_STATICALLY_CONFIGURED)
	{
	    return register_callback(
		    callback_dispatcher, 
		    CFG_PT_PRECONFIG,
		    CFG_ORD_NOMINAL,
		    (ulong)&sa);
	} 
	callback_status = register_devno(&sa);
	return callback_status;

    case CFG_OP_UNCONFIGURE: 
        if (!dump_configured)
	    return EINVAL;
	if (cfg_state == SUBSYSTEM_STATICALLY_CONFIGURED)
	    return ENOTSUP;
	{
	    uint error;
	    error = strmod_del(dump_devno, &dumpinfo, &sa);
	    if (error) return error;
	}
	dump_devno = NODEV;
	cmajnum = NODEV;
        dump_configured = FALSE;
	return ESUCCESS;

    case CFG_OP_RECONFIGURE: 
	return ESUCCESS;

    case CFG_OP_QUERY: 
	return ESUCCESS;

    default:
	return ENOTSUP;
    }
}

/*
**++
**  FUNCTIONAL DESCRIPTION:
**
**      callback_dispatcher()
**
**	Work out which callback to execute, and then execute it.
**
**  FORMAL PARAMETERS:
**
**	dispatch_point:
**
**	    Dispatch point at which we have been called.
**
**	order:
**
**	    Order within the current point.
**
**	arg:
**
**	event_arg:
**
**  RETURN VALUE:
**
**      {@description or none@}
**
**  SIDE EFFECTS:
**
**      {@description or none@}
**
**  DESIGN:
**
**      {@description or none@}
**
**  [@logical properties@]...
**
**  [@optional function tags@]...
**
**--
*/

static void callback_dispatcher(
		int dispatch_point,
		int order,
		ulong arg,
		ulong event_arg)
{
    if (callback_status != ESUCCESS)
	return; 
    switch (dispatch_point)
    {
	case CFG_PT_PRECONFIG:
	    register_devno(&sa);
	    break;
	default:
	    EPRINT(TMU_ERROR("unexpected callback %d"), dispatch_point);
	    callback_status = ENOTSUP;
	    break;
    }
    return;
}

/*
**++
**  FUNCTIONAL DESCRIPTION:
**
**      register_devno()
**
**  FORMAL PARAMETERS:
**
**      None
**
**  RETURN VALUE:
**
**      {@description or none@}
**
**  SIDE EFFECTS:
**
**      {@description or none@}
**
**  DESIGN:
**
**      {@description or none@}
**
**  [@logical properties@]...
**
**  [@optional function tags@]...
**
**--
*/

static int register_devno(struct streamadm *sa)
{
    /* Stash away the dev_t so that it can be used later to unconfigure the */
    /* device.								    */

    if ((dump_devno = strmod_add(dump_devno, &dumpinfo, sa)) == NODEV)
    {
	callback_status = ENODEV;
	EPRINT(TMU_ERROR("TMU_CONFIGURE, Configure failed %d"), callback_status);
	cfgmgr_set_status(DUMP_NAME);
	return callback_status;
    }

    /* Setup the attribute list with the driver-specific information that   */
    /* can be queried.							    */

    cmajnum = major(dump_devno);
    dump_configured = TRUE;
    return ESUCCESS;
}

============ V3.2C
/*
**++
**  FUNCTIONAL DESCRIPTION:
**
**      dump_configure()
**
**	Called at this entry point by the STREAMS subsytem configuration code.
**
**  FORMAL PARAMETERS:
**
**        op         = Options for system configuration
**        indata     = Pointer to configuration input buffer
**        indatalen  = Byte size of input data buffer
**        outdata    = Pointer to configuration output buffer
**        outdatalen = Byte size of output data buffer
**
**  RETURN VALUE:
**
**      0, if success, ERRNO error otherwise.
**
**  SIDE EFFECTS:
**
**      The `dump' device is configured into the kernel database.
**
**  DESIGN:
**
**      None
**
**--
*/
int dump_configure(
        cfg_op_t        op,
        caddr_t         indata,
        ulong           indata_size,
        caddr_t         outdata,
        ulong           outdata_size)
{
    struct streamadm sa;

    /*	Set up the sa structure.					    */

    sa.sa_version       = OSF_STREAMS_11;
    sa.sa_flags         = STR_IS_MODULE | STR_SYSV4_OPEN;
    sa.sa_ttys          = NULL;
    sa.sa_sync_level    = SQLVL_QUEUEPAIR;
    sa.sa_sync_info     = NULL;
    strcpy(sa.sa_name, info.mi_idname);

    /*	What are we supposed to be doing, anyway?			    */

    switch (op)
    {
    case CFG_OP_CONFIGURE: 
	if (strcmp(mod_type,"Dynamic") != 0)
	{
	    EPRINT(TMU_ERROR("Must be dynamically loaded"));
	    return EINVAL;
	} 
				 
	/* Stash away the dev_t so that it can be used later to unconfigure */
	/* the device.							    */

	if ((dump_devno = strmod_add(dump_devno, &dumpinfo, &sa)) == NODEV)
	    return ENODEV;

	/* Setup the attribute list with the driver-specific information    */
	/* that can be queried.						    */

	cmajnum = major(dump_devno);
	dump_configured = TRUE;

	/* Initialise the static state.					    */

	dump_instances = 0;
	return 0;

    case CFG_OP_UNCONFIGURE: 
        if (!dump_configured)
	    return EINVAL;
	{
	    uint error;

	    error = strmod_del(dump_devno, &dumpinfo, &sa);
	    if (error) return error;
	}
	dump_devno = NODEV;
	cmajnum = NODEV;
        dump_configured = FALSE;
	return 0;

    case CFG_OP_RECONFIGURE: 
	return 0;

    case CFG_OP_QUERY: 
	return 0;

    default:
	return EINVAL;
    }
}

============

The routine dump_configure looks like a virtual rewrite of the previous code,
but close inspection will show that many of the changes are as a result of:

1. moving sections of code into callback routines

2. Adding the callback routines

3. trying to keep the error handling consistent and readable in all cases

--------------------------------------------------------------------------------