Portable Streams Environment (PSE) drivers and modules are dynamically loaded and unloaded. To support this feature, each driver and module must have a configuration routine that performs the necessary initialization and setup operations.
PSE provides the strload command to load drivers and modules. After loading the extension, the strload command calls the extension entry point using the SYS_CFGDD and SYS_CFGKMOD operations explained in the sysconfig subroutine section in AIX Version 4.3 Technical Reference.
Each PSE kernel extension configuration routine must eventually call the str_install utility to link into STREAMS.
Commonly used extensions can be placed in a configuration file, which controls the normal setup and tear-down of PSE. The configuration file allows more flexibility when loading extensions by providing user-specified nodes and arguments. For a detailed description of the configuration file, see the strload command.
To load PSE using the default configuration, enter the following command with no flags:
strload
To unload PSE, enter the following command with the unload flag, enter:
strload -u
PSE drivers and modules can be added and removed as necessary. This is especially helpful during development of new extensions. To load only a new driver, enter the following command:
strload -d newdriver
strload -u -d newdriver
Modules can also be added and removed with the strload command by using the -m flag instead of the -d flag.
To support dynamic loading and unloading, each PSE extension must provide a configuration routine. This routine is called each time the extension is referenced in a load or unload operation. Detailed information about kernel extension configuration routines can be found in the sysconfig subroutine section in AIX Version 4.3 Kernel Extensions and Device Support Programming Concepts. However, PSE requires additional logic to successfully configure an extension.
To establish the linkage between PSE and the extension, the extension configuration routine must eventually call the str_install utility. This utility performs the internal operations necessary to add or remove the extension from PSE internal tables.
The following code fragment provides an example of a minimal configuration routine for a driver called dgb. Device-specific configuration and initialization logic can be added as necessary. The dgb_config entry point defines and initializes the strconf_t structure required by the str_install utility. In this example, the dgb_config operation retrieves the argument pointed to by the uiop parameter and uses it as an example of usage. An extension may ignore the argument. The major number is required for drivers and is retrieved from the dev parameter. Since the dgb driver requires no initialization, its last step is to perform the indicated operation by calling the str_install utility. Other drivers may need to perform other initialization steps either before or after calling the str_install utility.
#include <sys/device.h> /* for the CFG_* constants */ #include <sys/strconf.h> /* for the STR_* constants */ dgb_config(dev, cmd, uiop) dev_t dev; int cmd; struct uio *uiop; { char buf[FMNAMESZ+1]; static strconf_t conf = { "dgb", &dgbinfo, STR_NEW_OPEN, }; if (uiomove(buf, sizeof buf, UIO_WRITE, uiop)) return EFAULT; buf[FMNAMESZ] = 0; conf.sc_name = buf; conf.sc_major = major(dev); switch (cmd) { case CFG_INIT: return str_install(STR_LOAD_DEV, &conf); case CFG_TERM: return str_install(STR_UNLOAD_DEV, &conf); default: return EINVAL; } }
A module configuration routine is similar to the driver routine, except a major number is not required and the calling convention is slightly different. The following code fragment provides an example of a minimal complete configuration routine:
#include <sys/device.h> #include <sys/strconf.h> /* ARGSUSED */ aoot_config(cmd, uiop) int cmd; struct uio *uiop; { static strconf_t conf = { "aoot", &aootinfo, STR_NEW_OPEN, }; /* uiop ignored */ switch (cmd) { case CFG_INIT: return str_install(STR_LOAD_MOD, &conf); case CFG_TERM: return str_install(STR_UNLOAD_MOD, &conf); default: return EINVAL; } }
For the strload command to successfully install an extension, the configuration routine of each extension must be marked as the entry point. Assuming the extension exists in a file called dgb.c, and has a configuration routine named dgb_config, a PSE object named dgb can be created by the following commands:
cc -c dgb.c ld -o dgb dgb.o -edgb_config -bimport:/lib/pse.exp -lcsys
A driver extension created in such a manner can be installed with the following command:
strload -d dgb
and removed with the following command:
strload -u -d dgb
The following is a compilable example of a module called pass.
Note: Before it can be compiled, the code must exist in a file called pass.c.
#include <errno.h> #include <sys/stream.h>
static int passclose(), passopen (), passrput(), passwput(); static struct module_info minfo = { 0, "pass", 0, INFPSZ, 2048, 128 }; static struct qinit rinit = { passrput, 0, passopen, passclose, 0, &minfo }; static struct qinit winit = { passwput, 0, 0, 0, 0, &minfo }; struct streamtab passinfo = { &rinit, &winit };
static int passclose (queue_t *q) { return 0; }
static int passopen (queue_t *q, dev_t *devp, int flag, int sflag, cred_t *credp) { return 0; }
static int passrput (queue_t *q, mblk_t *mp) { putnext(q, mp); return 0; }
static int passwput (queue_t *q, mblk_t *mp) { putnext(q, mp); return 0; } #include <sys/device.h> #include <sys/strconf.h>
int passconfig(int cmd, struct uio *uiop) { static strconf_t conf = { "pass", &passinfo, STR_NEW_OPEN, };
switch (cmd) { case CFG_INIT: return str_install(STR_LOAD_MOD, &conf); case CFG_TERM: return str_install(STR_UNLOAD_MOD, &conf); default: return EINVAL; } }
The object named pass can be created using the following commands:
cc -c pass.c ld -o pass pass.o -epass_config -bimport:/lib/pse.exp -lcsys
Use the following command to install the module:
strload -m pass
Use the following command to remove the module:
strload -u -m pass