| 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.
--------------------------------------------------------------------------------
|