[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

1449.0. "An example STREAMS, loadable, physical device driver" by RDGENG::HAQUE (Shaheed R. Haque, 830-3531, reo2-g/e2) Wed Jun 28 1995 17:02

T.RTitleUserPersonal
Name
DateLines
1449.1tlink.cRDGENG::HAQUEShaheed R. Haque, 830-3531, reo2-g/e2Wed Jun 28 1995 17:041587
1449.2tlink.hRDGENG::HAQUEShaheed R. Haque, 830-3531, reo2-g/e2Wed Jun 28 1995 17:05204
1449.3list_utilities.hRDGENG::HAQUEShaheed R. Haque, 830-3531, reo2-g/e2Wed Jun 28 1995 17:0559
1449.4mpeg.hRDGENG::HAQUEShaheed R. Haque, 830-3531, reo2-g/e2Wed Jun 28 1995 17:05265
1449.5RDGENG::HAQUEShaheed R. Haque, 830-3531, reo2-g/e2Wed Jun 28 1995 17:1112
1449.6RDGENG::HAQUEShaheed R. Haque, 830-3531, reo2-g/e2Thu Aug 31 1995 01:3726
1449.7RDGENG::HAQUEShaheed R. Haque, 830-3531, reo2-g/e2Tue Oct 31 1995 04:2530
1449.8Update cfg supportRDGENG::HAQUEShaheed R. Haque, 830-3531, reo2-g/e2Tue Oct 31 1995 04:293333
1449.9SMURF::MENNERi'm gonna plant a weeEEping willow...Tue Oct 31 1995 07:109
1449.10RDGENG::HAQUEShaheed R. Haque, 830-3531, reo2-f/b3Wed Jun 05 1996 09:062
1449.10V3.2C/V4.0B compatible codeRDGENG::HAQUEShaheed R. Haque, 830-3531, reo2-f/b3Mon Mar 24 1997 05:46655
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 5 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
static unsigned char pci_option[256] = "";
static unsigned char mod_type[CFG_ATTR_NAME_SZ] = "";
============ V3.2C equivalent
static unsigned char mod_type[CFG_ATTR_NAME_SZ] = "";
============

New attribute to replace pci_option_snippet.

--------------------------------------------------------------------------------
============ V4.0B version
    {"PCI_Option",              CFG_ATTR_STRTYPE, READ_ONLY_ATTR,
				   (caddr_t)pci_option,2,sizeof(pci_option),0},
    {"Module_Type",		CFG_ATTR_STRTYPE, READ_ONLY_ATTR,
============ V3.2C equivalent
    {"Module_Type",		CFG_ATTR_STRTYPE, READ_ONLY_ATTR,
============

New attribute to replace pci_option_snippet.

--------------------------------------------------------------------------------
============ V4.0B version
/*
**++
**  FUNCTIONAL DESCRIPTION:
**
**      tlink_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 tlink_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(TLINK_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_DEVICE | 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: 

	/* Sanity check on the configuration name...if it is null, then the */
	/* resolver/configure code won't know what to look for!		    */

	if (strcmp(mod_cfg_name, "") == 0)
	{
	    EPRINT(TMU_ERROR("TMU_BAD_MOD_CFG_NAME, Null mod_cfg_name"));
	    return EINVAL;
	}
	if (cfg_state == SUBSYSTEM_STATICALLY_CONFIGURED)
	{
	    return register_callback(
		    callback_dispatcher, 
		    CFG_PT_PRECONFIG,
		    CFG_ORD_NOMINAL,
		    (ulong)&sa);
	    return register_callback(
		    callback_dispatcher, 
		    CFG_PT_POSTCONFIG,
		    CFG_ORD_NOMINAL,
		    (ulong)&sa);
	} 
	callback_status = register_configuration(&sa);
	if (callback_status != ESUCCESS)
	    return callback_status;
	callback_status = register_devno(&sa);
	return callback_status;
    
    case CFG_OP_UNCONFIGURE: 
        if (!configured)
	    return EINVAL;
	if (cfg_state == SUBSYSTEM_STATICALLY_CONFIGURED)
	    return ENOTSUP;
	{
	    uint error;

	    error = strmod_del(devno, &tlinkinfo, &sa);
	    if (error) return error;
	}
#ifdef OSF1_V4
	callback_status = unconfigure_driver(
			    DRIVER_WILDNAME,
			    DRIVER_WILDNUM,
			    &tlinkdriver,
			    DRIVER_WILDNAME,
			    DRIVER_WILDNUM);
#else
	callback_status = ldbl_ctlr_unconfigure(
			    BUSNAME,
			    LDBL_WILDNUM,
			    &tlinkdriver,
			    LDBL_WILDNAME,
			    LDBL_WILDNUM);
#endif
	if (callback_status != ESUCCESS)
	{
	    EPRINT(TMU_ERROR("TMU_UNCONFIGURE, Unconfigure failed %d"), callback_status);
	    return ESRCH;
	}
	devno = NODEV;
	cmajnum = NODEV;
        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_configuration(&sa);
	    break;
	case CFG_PT_POSTCONFIG:
	    register_devno(&sa);
	    break;
	default:
	    EPRINT(TMU_ERROR("unexpected callback %d"), dispatch_point);
	    callback_status = ENOTSUP;
	    break;
    }
    return;
}

/*
**++
**  FUNCTIONAL DESCRIPTION:
**
**      register_configuration()
**
**  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_configuration(struct streamadm *sa)
{
    /* Have the probe interface called, and find out many controllers we    */
    /* have.								    */

#ifdef OSF1_V4
    struct controller_config ctlr_register;

    ctlr_register.revision = CTLR_CONFIG_REVISION;
    strcpy(ctlr_register.subsystem_name, mod_cfg_name);
    strcpy(ctlr_register.bus_name, DRIVER_WILDNAME);
    ctlr_register.devdriver = &tlinkdriver;
    callback_status = create_controller_struct(&ctlr_register);
#else
    callback_status = ldbl_stanza_resolver(
			mod_cfg_name,
			BUSNAME,
			&tlinkdriver,
			(caddr_t *)option_snippet);
#endif
    if (callback_status != ESUCCESS)
    {
	EPRINT(TMU_ERROR("TMU_RESOLVER, Resolver failed %d"), callback_status);
	cfgmgr_set_status(TLINK_NAME);
	return callback_status;
    }
#ifdef OSF1_V4
    callback_status = configure_driver(
			mod_cfg_name,
			DRIVER_WILDNUM,
			DRIVER_WILDNAME,
                        &tlinkdriver);
#else
    callback_status = ldbl_ctlr_configure(
			BUSNAME,
			LDBL_WILDNUM,
			mod_cfg_name,
			&tlinkdriver,
			0);
#endif
    if (callback_status != ESUCCESS)
    {
	EPRINT(TMU_ERROR("TMU_CONFIGURE, Configure failed %d"), callback_status);
	cfgmgr_set_status(TLINK_NAME);
	return callback_status;
    }
    if (numunit == 0)
    {
	EPRINT(TMU_ERROR("TMU_NODEV, No devices"));
	return ENXIO;
    }
    return ESUCCESS;
}

/*
**++
**  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 ((tlink_devno = strmod_add(tlink_devno, &tlinkinfo, sa)) == NODEV)
    {
	callback_status = ENODEV;
	EPRINT(TMU_ERROR("TMU_CONFIGURE, Configure failed %d"), callback_status);
	cfgmgr_set_status(TLINK_NAME);
	return ENODEV;
    }

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

    cmajnum = major(tlink_devno);
    tlink_configured = TRUE;
    return ESUCCESS;
}

============ V3.2C
/*
**++
**  FUNCTIONAL DESCRIPTION:
**
**      tlink_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 tlink_configure(
        cfg_op_t op,
        cfg_attr_t indata,
        size_t indata_size,
        cfg_attr_t outdata,
        size_t outdata_size)
{
    uint status;
    struct streamadm sa;

    /*	Set up the sa structure.					    */

    sa.sa_version       = OSF_STREAMS_11;
    sa.sa_flags         = STR_IS_DEVICE | 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: 
	
	/* The safest way to tell if we are statically or dynamically	    */
	/* loaded is to test if the probe routine has already found	    */
	/* instances of this controller. If it has, then we make sure that  */
	/* the configuration reflects this. If not, then in the worst case, */
	/* we will simply do some unnecessary work.			    */

	if (tlink_controllers[0] != NULL)
	{
	    /* Do nothing. */
	}
	else
	if (strcmp(mod_type,"Dynamic") != 0)
	{
	    EPRINT(TMU_ERROR("TMU_DYNAMIC, Must be dynamically loaded"));
	    return EINVAL;
	} 
	else
	{
	    /* Sanity check on the configuration name...if it is null, then */
	    /* the resolver/configure code won't know what to look for!	    */

	    if (strcmp(mod_cfg_name, "") == 0)
	    {
		EPRINT(TMU_ERROR("TMU_BAD_MOD_CFG_NAME, Null mod_cfg_name"));
		return EINVAL;
	    }

	    /* Have the probe interface called, and find out many	    */
	    /* controllers we have.					    */

	    status = ldbl_stanza_resolver(
			mod_cfg_name,
			BUSNAME,
			&tlinkdriver,
			(caddr_t *)option_snippet);
	    if (status != ESUCCESS)
	    {
		EPRINT(TMU_ERROR("TMU_RESOLVER, Resolver failed %ld"), status);
		return EINVAL;
	    }

	    status = ldbl_ctlr_configure(
			BUSNAME,
			LDBL_WILDNUM,
			mod_cfg_name,
			&tlinkdriver,
			0);
	    if (status != ESUCCESS)
	    {
		EPRINT(TMU_ERROR("TMU_CONFIGURE, Configure failed %ld"), status);
		return EINVAL;
	    }
	}

	if (numunit == 0)
	{
	    EPRINT(TMU_ERROR("TMU_NODEV, No devices"));
	    return ENXIO;
	}

	/* Stash away the dev_t so that it can be used later to unconfigure */
	/* the device.							    */

	if ((tlink_devno = strmod_add(tlink_devno, &tlinkinfo, &sa)) == NODEV)
	    return ENODEV;

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

	cmajnum = major(tlink_devno);
	tlink_configured = TRUE;
	return 0;
    case CFG_OP_UNCONFIGURE: 
        if (!tlink_configured)
	    return EINVAL;
	{
	    uint error;

	    error = strmod_del(tlink_devno, &tlinkinfo, &sa);
	    if (error) return error;
	}

	if (strcmp(mod_type,"Dynamic") == 0)
	{
	    status = ldbl_ctlr_unconfigure(
			BUSNAME,
			LDBL_WILDNUM,
			&tlinkdriver,
			LDBL_WILDNAME,
			LDBL_WILDNUM);
	    if (status != ESUCCESS)
	    {
		EPRINT(TMU_ERROR("TMU_UNCONFIGURE, Unconfigure failed %ld"), status);
		return ESRCH;
	    }
	}

	tlink_devno = NODEV;
	cmajnum = NODEV;
	numunit = 0;
        tlink_configured = FALSE;
	return 0;
    case CFG_OP_RECONFIGURE: 
	return 0;
    case CFG_OP_QUERY: 
	return 0;
    default:
	return EINVAL;
    }
}

============

The routine tlink_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

--------------------------------------------------------------------------------
============ V4.0B version
    interrupt_info.config_type = CONTROLLER_CONFIG_TYPE || SHARED_INTR_CAPABLE;

============ V3.2C equivalent
    interrupt_info.config_type = CONTROLLER_CONFIG_TYPE;

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

While registering our interrupt handler, declare that we are shared interrupt
capable.
--------------------------------------------------------------------------------
============ V4.0B version
    if (int_host.fields.tickle || int_host.fields.icr_w)
	return INTR_SERVICED;
    else
	return INTR_NOT_SERVICED;
}
============ V3.2C equivalent
    return 0;
}
============

When returning from the interrupt handler, implement shared interrupt
capability.
--------------------------------------------------------------------------------