Layered Driver Interface (LDI)
The LDI is a set of DDI/DKI that enables a kernel module to access other devices in the system. The LDI also enables you to determine which devices are currently being used by kernel modules.
This chapter covers the following topics:
14.1. LDI Overview
The LDI includes two categories of interfaces:
-
Kernel interfaces. User applications use system calls to open, read, and write to devices that are managed by a device driver within the kernel. Kernel modules can use the LDI kernel interfaces to open, read, and write to devices that are managed by another device driver within the kernel. For example, a user application might use read(2) and a kernel module might use ldi_read(9F) to read the same device. See Kernel Interfaces.
-
User interfaces. The LDI user interfaces can provide information to user processes regarding which devices are currently being used by other devices in the kernel. See User Interfaces.
The following terms are commonly used in discussing the LDI:
-
Target Device. A target device is a device within the kernel that is managed by a device driver and is being accessed by a device consumer.
-
Device Consumer. A device consumer is a user process or kernel module that opens and accesses a target device. A device consumer normally performs operations such as
open
,read
,write
, orioctl
on a target device. -
Kernel Device Consumer. A kernel device consumer is a particular kind of device consumer. A kernel device consumer is a kernel module that accesses a target device. The kernel device consumer usually is not the device driver that manages the target device that is being accessed. Instead, the kernel device consumer accesses the target device indirectly through the device driver that manages the target device.
-
Layered Driver. A layered driver is a particular kind of kernel device consumer. A layered driver is a kernel driver that does not directly manage any piece of hardware. Instead, a layered driver accesses one of more target devices indirectly through the device drivers that manage those target devices. Volume managers and STREAMS multiplexers are good examples of layered drivers.
14.2. Kernel Interfaces
Some LDI kernel interfaces enable the LDI to track and report kernel device usage information. See Layered Identifiers – Kernel Device Consumers.
Other LDI kernel interfaces enable kernel modules to perform access
operations such as open
, read
, and write
a target device. These LDI kernel interfaces also enable a
kernel device consumer to query property and event information about target
devices. See Layered Driver Handles – Target Devices.
LDI Kernel Interfaces Example shows an example driver that uses many of these LDI interfaces.
14.2.1. Layered Identifiers – Kernel Device Consumers
Layered identifiers enable the LDI to track and report kernel device
usage information. A layered identifier (ldi_ident_t
) identifies
a kernel device consumer. Kernel device consumers must obtain a layered identifier
prior to opening a target device using the LDI.
Layered drivers are the only supported types of kernel device consumers. Therefore, a layered driver must obtain a layered identifier that is associated with the device number, the device information node, or the stream of the layered driver. The layered identifier is associated with the layered driver. The layered identifier is not associated with the target device.
You can retrieve the kernel device usage information that is collected by the LDI by using the libdevinfo(3LIB) interfaces, the fuser(1M) command, or the prtconf(1M) command. For example, the prtconf(1M) command can show which target devices a layered driver is accessing or which layered drivers are accessing a particular target device. See User Interfaces to learn more about how to retrieve device usage information.
The following describes the LDI layered identifier interfaces:
ldi_ident_t
-
Layered identifier. An opaque type.
- ldi_ident_from_dev(9F)
-
Allocate and retrieve a layered identifier that is associated with a
dev_t
device number. - ldi_ident_from_dip(9F)
-
Allocate and retrieve a layered identifier that is associated with a
dev_info_t
device information node. - ldi_ident_from_stream(9F)
-
Allocate and retrieve a layered identifier that is associated with a stream.
- ldi_ident_release(9F)
-
Release a layered identifier that was allocated with ldi_ident_from_dev(9F), ldi_ident_from_dip(9F), or ldi_ident_from_stream(9F).
14.2.2. Layered Driver Handles – Target Devices
Kernel device consumers must use a layered driver handle (ldi_handle_t
) to access a target device through LDI interfaces. The ldi_handle_t
type is valid only with LDI interfaces. The LDI allocates and returns
this handle when the LDI successfully opens a device. A kernel device consumer
can then use this handle to access the target device through the LDI interfaces.
The LDI deallocates the handle when the LDI closes the device. See LDI Kernel Interfaces Example for an example.
This section discusses how kernel device consumers can access target
devices and retrieve different types of information. See Opening and Closing Target Devices to learn how kernel device consumers
can open and close target devices. See Accessing Target Devices to learn how kernel device consumers can perform operations
such as read
, write
, strategy
,
and ioctl
on target devices. Retrieving Target Device Information describes interfaces that retrieve target
device information such as device open type and device minor name. Retrieving Target Device Property Values describes
interfaces that retrieve values and address of target device properties. See Receiving Asynchronous Device Event Notification to
learn how kernel device consumers can receive event notification from target
devices.
Opening and Closing Target Devices
This section describes the LDI kernel interfaces for opening and closing target devices. The open interfaces take a pointer to a layered driver handle. The open interfaces attempt to open the target device specified by the device number, device ID, or path name. If the open operation is successful, the open interfaces allocate and return a layered driver handle that can be used to access the target device. The close interface closes the target device associated with the specified layered driver handle and then frees the layered driver handle.
ldi_handle_t
-
Layered driver handle for target device access. An opaque data structure that is returned when a device is successfully opened.
- ldi_open_by_dev(9F)
-
Open the device specified by the
dev_t
device number parameter. - ldi_open_by_devid(9F)
-
Open the device specified by the
ddi_devid_t
device ID parameter. You also must specify the minor node name to open. - ldi_open_by_name(9F)
-
Open a device by path name. The path name is a null-terminated string in the kernel address space. The path name must be an absolute path, beginning with a forward slash character (
/
). - ldi_close(9F)
-
Close a device that was opened with ldi_open_by_dev(9F), ldi_open_by_devid(9F), or ldi_open_by_name(9F). After ldi_close(9F) returns, the layered driver handle of the device that was closed is no longer valid.
Accessing Target Devices
This section describes the LDI kernel interfaces for accessing target
devices. These interfaces enable a kernel device consumer to perform operations
on the target device specified by the layered driver handle. Kernel device
consumers can perform operations such as read
, write
, strategy
, and ioctl
on the target device.
ldi_handle_t
-
Layered driver handle for target device access. An opaque data structure.
- ldi_read(9F)
-
Pass a read request to the device entry point for the target device. This operation is supported for block, character, and STREAMS devices.
- ldi_aread(9F)
-
Pass an asynchronous read request to the device entry point for the target device. This operation is supported for block and character devices.
- ldi_write(9F)
-
Pass a write request to the device entry point for the target device. This operation is supported for block, character, and STREAMS devices.
- ldi_awrite(9F)
-
Pass an asynchronous write request to the device entry point for the target device. This operation is supported for block and character devices.
- ldi_strategy(9F)
-
Pass a strategy request to the device entry point for the target device. This operation is supported for block and character devices.
- ldi_dump(9F)
-
Pass a dump request to the device entry point for the target device. This operation is supported for block and character devices.
- ldi_poll(9F)
-
Pass a poll request to the device entry point for the target device. This operation is supported for block, character, and STREAMS devices.
- ldi_ioctl(9F)
-
Pass an
ioctl
request to the device entry point for the target device. This operation is supported for block, character, and STREAMS devices. The LDI supports STREAMS linking and STREAMSioctl
commands. See the “STREAM IOCTLS” section of the ldi_ioctl(9F) man page. See also theioctl
commands in the streamio(7I) man page. - ldi_devmap(9F)
-
Pass a
devmap
request to the device entry point for the target device. This operation is supported for block and character devices. - ldi_getmsg(9F)
-
Get a message block from a stream.
- ldi_putmsg(9F)
-
Put a message block on a stream.
Retrieving Target Device Information
This section describes LDI interfaces that kernel device consumers can use to retrieve device information about a specified target device. A target device is specified by a layered driver handle. A kernel device consumer can receive information such as device number, device open type, device ID, device minor name, and device size.
- ldi_get_dev(9F)
-
Get the
dev_t
device number for the target device specified by the layered driver handle. - ldi_get_otyp(9F)
-
Get the open flag that was used to open the target device specified by the layered driver handle. This flag tells you whether the target device is a character device or a block device.
- ldi_get_devid(9F)
-
Get the
ddi_devid_t
device ID for the target device specified by the layered driver handle. Use ddi_devid_free(9F) to free theddi_devid_t
when you are finished using the device ID. - ldi_get_minor_name(9F)
-
Retrieve a buffer that contains the name of the minor node that was opened for the target device. Use kmem_free(9F) to release the buffer when you are finished using the minor node name.
- ldi_get_size(9F)
-
Retrieve the partition size of the target device specified by the layered driver handle.
Retrieving Target Device Property Values
This section describes LDI interfaces that kernel device consumers can use to retrieve property information about a specified target device. A target device is specified by a layered driver handle. A kernel device consumer can receive values and addresses of properties and determine whether a property exists.
- ldi_prop_exists(9F)
-
Return
1
if the property exists for the target device specified by the layered driver handle. Return0
if the property does not exist for the specified target device. - ldi_prop_get_int(9F)
-
Search for an
int
integer property that is associated with the target device specified by the layered driver handle. If the integer property is found, return the property value. - ldi_prop_get_int64(9F)
-
Search for an
int64_t
integer property that is associated with the target device specified by the layered driver handle. If the integer property is found, return the property value. - ldi_prop_lookup_int_array(9F)
-
Retrieve the address of an
int
integer array property value for the target device specified by the layered driver handle. - ldi_prop_lookup_int64_array(9F)
-
Retrieve the address of an
int64_t
integer array property value for the target device specified by the layered driver handle. - ldi_prop_lookup_string(9F)
-
Retrieve the address of a null-terminated string property value for the target device specified by the layered driver handle.
- ldi_prop_lookup_string_array(9F)
-
Retrieve the address of an array of strings. The string array is an array of pointers to null-terminated strings of property values for the target device specified by the layered driver handle.
- ldi_prop_lookup_byte_array(9F)
-
Retrieve the address of an array of bytes. The byte array is a property value of the target device specified by the layered driver handle.
Receiving Asynchronous Device Event Notification
The LDI enables kernel device consumers to register for event notification and to receive event notification from target devices. A kernel device consumer can register an event handler that will be called when the event occurs. The kernel device consumer must open a device and receive a layered driver handle before the kernel device consumer can register for event notification with the LDI event notification interfaces.
The LDI event notification interfaces enable a kernel device consumer
to specify an event name and to retrieve an associated kernel event cookie.
The kernel device consumer can then pass the layered driver handle (ldi_handle_t
), the cookie (ddi_eventcookie_t
), and the event
handler to ldi_add_event_handler(9F) to register for event notification. When registration
completes successfully, the kernel device consumer receives a unique LDI event
handler identifier (ldi_callback_id_t
). The LDI event handler
identifier is an opaque type that can be used only with the LDI event notification
interfaces.
The LDI provides a framework to register for events generated by other devices. The LDI itself does not define any event types or provide interfaces for generating events.
The following describes the LDI asynchronous event notification interfaces:
ldi_callback_id_t
-
Event handler identifier. An opaque type.
- ldi_get_eventcookie(9F)
-
Retrieve an event service cookie for the target device specified by the layered driver handle.
- ldi_add_event_handler(9F)
-
Add the callback handler specified by the
ldi_callback_id_t
registration identifier. The callback handler is invoked when the event specified by theddi_eventcookie_t
cookie occurs. - ldi_remove_event_handler(9F)
-
Remove the callback handler specified by the
ldi_callback_id_t
registration identifier.
14.2.3. LDI Kernel Interfaces Example
This section shows an example kernel device consumer that uses some of the LDI calls discussed in the preceding sections in this chapter. This section discusses the following aspects of this example module:
This example kernel device consumer is named lyr
.
The lyr
module is a layered driver that uses LDI calls
to send data to a target device. In its open(9E) entry point, the lyr
driver
opens the device that is specified by the lyr_targ
property
in the lyr.conf
configuration file. In its write(9E) entry point, the lyr
driver
writes all of its incoming data to the device specified by the lyr_targ
property.
Device Configuration File
In the configuration file shown below, the target device that the lyr
driver is writing to is the console.
#
# Copyright 2004 Sun Microsystems, Inc. All rights reserved.
# Use is subject to license terms.
#
#pragma ident "%Z%%M% %I% %E% SMI"
name="lyr" parent="pseudo" instance=1;
lyr_targ="/dev/console";
Driver Source File
In the driver source file shown below, the lyr_state_t
structure
holds the soft state for the lyr
driver. The soft state
includes the layered driver handle (lh
) for the lyr_targ
device and the layered identifier (li
) for the lyr
device. For more information on soft state, see Retrieving Driver Soft State Information.
In the lyr_open
entry point, ddi_prop_lookup_string(9F) retrieves from the lyr_targ
property
the name of the target device for the lyr
device to open.
The ldi_ident_from_dev(9F) function gets an LDI layered identifier for the lyr
device. The ldi_open_by_name(9F) function opens the lyr_targ
device and gets a layered driver handle for the lyr_targ
device.
Note
that if any failure occurs in lyr_open
, the ldi_close(9F), ldi_ident_release(9F), and ddi_prop_free(9F) calls undo everything that
was done. The ldi_close(9F) function closes the lyr_targ
device.
The ldi_ident_release(9F) function releases the lyr
layered identifier.
The ddi_prop_free(9F) function frees resources allocated when the lyr_targ
device
name was retrieved. If no failure occurs, the ldi_close(9F) and ldi_ident_release(9F) functions are called in the lyr_close
entry
point.
In the last line of the driver module, the ldi_write(9F) function
is called. The ldi_write(9F) function takes the data written to the lyr
device
in the lyr_write
entry point and writes that data to
the lyr_targ
device. The ldi_write(9F) function uses the
layered driver handle for the lyr_targ
device to write
the data to the lyr_targ
device.
#include <sys/types.h>
#include <sys/file.h>
#include <sys/errno.h>
#include <sys/open.h>
#include <sys/cred.h>
#include <sys/cmn_err.h>
#include <sys/modctl.h>
#include <sys/conf.h>
#include <sys/stat.h>
#include <sys/ddi.h>
#include <sys/sunddi.h>
#include <sys/sunldi.h>
typedef struct lyr_state {
ldi_handle_t lh;
ldi_ident_t li;
dev_info_t *dip;
minor_t minor;
int flags;
kmutex_t lock;
} lyr_state_t;
#define LYR_OPENED 0x1 /* lh is valid */
#define LYR_IDENTED 0x2 /* li is valid */
static int lyr_info(dev_info_t *, ddi_info_cmd_t, void *, void **);
static int lyr_attach(dev_info_t *, ddi_attach_cmd_t);
static int lyr_detach(dev_info_t *, ddi_detach_cmd_t);
static int lyr_open(dev_t *, int, int, cred_t *);
static int lyr_close(dev_t, int, int, cred_t *);
static int lyr_write(dev_t, struct uio *, cred_t *);
static void *lyr_statep;
static struct cb_ops lyr_cb_ops = {
lyr_open, /* open */
lyr_close, /* close */
nodev, /* strategy */
nodev, /* print */
nodev, /* dump */
nodev, /* read */
lyr_write, /* write */
nodev, /* ioctl */
nodev, /* devmap */
nodev, /* mmap */
nodev, /* segmap */
nochpoll, /* poll */
ddi_prop_op, /* prop_op */
NULL, /* streamtab */
D_NEW | D_MP, /* cb_flag */
CB_REV, /* cb_rev */
nodev, /* aread */
nodev /* awrite */
};
static struct dev_ops lyr_dev_ops = {
DEVO_REV, /* devo_rev, */
0, /* refcnt */
lyr_info, /* getinfo */
nulldev, /* identify */
nulldev, /* probe */
lyr_attach, /* attach */
lyr_detach, /* detach */
nodev, /* reset */
&lyr_cb_ops, /* cb_ops */
NULL, /* bus_ops */
NULL /* power */
};
static struct modldrv modldrv = {
&mod_driverops,
"LDI example driver",
&lyr_dev_ops
};
static struct modlinkage modlinkage = {
MODREV_1,
&modldrv,
NULL
};
int
_init(void)
{
int rv;
if ((rv = ddi_soft_state_init(&lyr_statep, sizeof (lyr_state_t),
0)) != 0) {
cmn_err(CE_WARN, "lyr _init: soft state init failed\n");
return (rv);
}
if ((rv = mod_install(&modlinkage)) != 0) {
cmn_err(CE_WARN, "lyr _init: mod_install failed\n");
goto FAIL;
}
return (rv);
/*NOTEREACHED*/
FAIL:
ddi_soft_state_fini(&lyr_statep);
return (rv);
}
int
_info(struct modinfo *modinfop)
{
return (mod_info(&modlinkage, modinfop));
}
int
_fini(void)
{
int rv;
if ((rv = mod_remove(&modlinkage)) != 0) {
return(rv);
}
ddi_soft_state_fini(&lyr_statep);
return (rv);
}
/*
* 1:1 mapping between minor number and instance
*/
static int
lyr_info(dev_info_t *dip, ddi_info_cmd_t infocmd, void *arg, void **result)
{
int inst;
minor_t minor;
lyr_state_t *statep;
char *myname = "lyr_info";
minor = getminor((dev_t)arg);
inst = minor;
switch (infocmd) {
case DDI_INFO_DEVT2DEVINFO:
statep = ddi_get_soft_state(lyr_statep, inst);
if (statep == NULL) {
cmn_err(CE_WARN, "%s: get soft state "
"failed on inst %d\n", myname, inst);
return (DDI_FAILURE);
}
*result = (void *)statep->dip;
break;
case DDI_INFO_DEVT2INSTANCE:
*result = (void *)inst;
break;
default:
break;
}
return (DDI_SUCCESS);
}
static int
lyr_attach(dev_info_t *dip, ddi_attach_cmd_t cmd)
{
int inst;
lyr_state_t *statep;
char *myname = "lyr_attach";
switch (cmd) {
case DDI_ATTACH:
inst = ddi_get_instance(dip);
if (ddi_soft_state_zalloc(lyr_statep, inst) != DDI_SUCCESS) {
cmn_err(CE_WARN, "%s: ddi_soft_state_zallac failed "
"on inst %d\n", myname, inst);
goto FAIL;
}
statep = (lyr_state_t *)ddi_get_soft_state(lyr_statep, inst);
if (statep == NULL) {
cmn_err(CE_WARN, "%s: ddi_get_soft_state failed on "
"inst %d\n", myname, inst);
goto FAIL;
}
statep->dip = dip;
statep->minor = inst;
if (ddi_create_minor_node(dip, "node", S_IFCHR, statep->minor,
DDI_PSEUDO, 0) != DDI_SUCCESS) {
cmn_err(CE_WARN, "%s: ddi_create_minor_node failed on "
"inst %d\n", myname, inst);
goto FAIL;
}
mutex_init(&statep->lock, NULL, MUTEX_DRIVER, NULL);
return (DDI_SUCCESS);
case DDI_RESUME:
case DDI_PM_RESUME:
default:
break;
}
return (DDI_FAILURE);
/*NOTREACHED*/
FAIL:
ddi_soft_state_free(lyr_statep, inst);
ddi_remove_minor_node(dip, NULL);
return (DDI_FAILURE);
}
static int
lyr_detach(dev_info_t *dip, ddi_detach_cmd_t cmd)
{
int inst;
lyr_state_t *statep;
char *myname = "lyr_detach";
inst = ddi_get_instance(dip);
statep = ddi_get_soft_state(lyr_statep, inst);
if (statep == NULL) {
cmn_err(CE_WARN, "%s: get soft state failed on "
"inst %d\n", myname, inst);
return (DDI_FAILURE);
}
if (statep->dip != dip) {
cmn_err(CE_WARN, "%s: soft state does not match devinfo "
"on inst %d\n", myname, inst);
return (DDI_FAILURE);
}
switch (cmd) {
case DDI_DETACH:
mutex_destroy(&statep->lock);
ddi_soft_state_free(lyr_statep, inst);
ddi_remove_minor_node(dip, NULL);
return (DDI_SUCCESS);
case DDI_SUSPEND:
case DDI_PM_SUSPEND:
default:
break;
}
return (DDI_FAILURE);
}
/*
* on this driver's open, we open the target specified by a property and store
* the layered handle and ident in our soft state. a good target would be
* "/dev/console" or more interestingly, a pseudo terminal as specified by the
* tty command
*/
/*ARGSUSED*/
static int
lyr_open(dev_t *devtp, int oflag, int otyp, cred_t *credp)
{
int rv, inst = getminor(*devtp);
lyr_state_t *statep;
char *myname = "lyr_open";
dev_info_t *dip;
char *lyr_targ = NULL;
statep = (lyr_state_t *)ddi_get_soft_state(lyr_statep, inst);
if (statep == NULL) {
cmn_err(CE_WARN, "%s: ddi_get_soft_state failed on "
"inst %d\n", myname, inst);
return (EIO);
}
dip = statep->dip;
/*
* our target device to open should be specified by the "lyr_targ"
* string property, which should be set in this driver's .conf file
*/
if (ddi_prop_lookup_string(DDI_DEV_T_ANY, dip, DDI_PROP_NOTPROM,
"lyr_targ", &lyr_targ) != DDI_PROP_SUCCESS) {
cmn_err(CE_WARN, "%s: ddi_prop_lookup_string failed on "
"inst %d\n", myname, inst);
return (EIO);
}
/*
* since we only have one pair of lh's and li's available, we don't
* allow multiple on the same instance
*/
mutex_enter(&statep->lock);
if (statep->flags & (LYR_OPENED | LYR_IDENTED)) {
cmn_err(CE_WARN, "%s: multiple layered opens or idents "
"from inst %d not allowed\n", myname, inst);
mutex_exit(&statep->lock);
ddi_prop_free(lyr_targ);
return (EIO);
}
rv = ldi_ident_from_dev(*devtp, &statep->li);
if (rv != 0) {
cmn_err(CE_WARN, "%s: ldi_ident_from_dev failed on inst %d\n",
myname, inst);
goto FAIL;
}
statep->flags |= LYR_IDENTED;
rv = ldi_open_by_name(lyr_targ, FREAD | FWRITE, credp, &statep->lh,
statep->li);
if (rv != 0) {
cmn_err(CE_WARN, "%s: ldi_open_by_name failed on inst %d\n",
myname, inst);
goto FAIL;
}
statep->flags |= LYR_OPENED;
cmn_err(CE_CONT, "\n%s: opened target '%s' successfully on inst %d\n",
myname, lyr_targ, inst);
rv = 0;
FAIL:
/* cleanup on error */
if (rv != 0) {
if (statep->flags & LYR_OPENED)
(void)ldi_close(statep->lh, FREAD | FWRITE, credp);
if (statep->flags & LYR_IDENTED)
ldi_ident_release(statep->li);
statep->flags &= ~(LYR_OPENED | LYR_IDENTED);
}
mutex_exit(&statep->lock);
if (lyr_targ != NULL)
ddi_prop_free(lyr_targ);
return (rv);
}
/*
* on this driver's close, we close the target indicated by the lh member
* in our soft state and release the ident, li as well. in fact, we MUST do
* both of these at all times even if close yields an error because the
* device framework effectively closes the device, releasing all data
* associated with it and simply returning whatever value the target's
* close(9E) returned. therefore, we must as well.
*/
/*ARGSUSED*/
static int
lyr_close(dev_t devt, int oflag, int otyp, cred_t *credp)
{
int rv, inst = getminor(devt);
lyr_state_t *statep;
char *myname = "lyr_close";
statep = (lyr_state_t *)ddi_get_soft_state(lyr_statep, inst);
if (statep == NULL) {
cmn_err(CE_WARN, "%s: ddi_get_soft_state failed on "
"inst %d\n", myname, inst);
return (EIO);
}
mutex_enter(&statep->lock);
rv = ldi_close(statep->lh, FREAD | FWRITE, credp);
if (rv != 0) {
cmn_err(CE_WARN, "%s: ldi_close failed on inst %d, but will ",
"continue to release ident\n", myname, inst);
}
ldi_ident_release(statep->li);
if (rv == 0) {
cmn_err(CE_CONT, "\n%s: closed target successfully on "
"inst %d\n", myname, inst);
}
statep->flags &= ~(LYR_OPENED | LYR_IDENTED);
mutex_exit(&statep->lock);
return (rv);
}
/*
* echo the data we receive to the target
*/
/*ARGSUSED*/
static int
lyr_write(dev_t devt, struct uio *uiop, cred_t *credp)
{
int rv, inst = getminor(devt);
lyr_state_t *statep;
char *myname = "lyr_write";
statep = (lyr_state_t *)ddi_get_soft_state(lyr_statep, inst);
if (statep == NULL) {
cmn_err(CE_WARN, "%s: ddi_get_soft_state failed on "
"inst %d\n", myname, inst);
return (EIO);
}
return (ldi_write(statep->lh, uiop, credp));
}
Compile the driver.
Use the -D_KERNEL
option
to indicate that this is a kernel module.
-
If you are compiling for a SPARC architecture, use the
-xarch=v9
option:% cc -c -D_KERNEL -xarch=v9 lyr.c
-
If you are compiling for a 32-bit x86 architecture, use the following command:
% cc -c -D_KERNEL lyr.c
Link the driver.
% ld -r -o lyr lyr.o
Install the configuration file.
As user root
,
copy the configuration file to the kernel driver area of the machine:
# cp lyr.conf /usr/kernel/drv
Install the driver binary.
-
As user
root
, copy the driver binary to thesparcv9
driver area on a SPARC architecture:# cp lyr /usr/kernel/drv/sparcv9
-
As user
root
, copy the driver binary to thedrv
driver area on a 32-bit x86 architecture:# cp lyr /usr/kernel/drv
Load the driver.
As user root
,
use the add_drv(1M) command
to load the driver.
# add_drv lyr
List the pseudo devices to confirm that the lyr
device
now exists:
# ls /devices/pseudo | grep lyr lyr@1 lyr@1:node
Test the Layered Driver
To test the lyr
driver, write a message to the lyr
device and verify that the message displays on the lyr_targ
device.
In this example, the lyr_targ
device is the console
of the system where the lyr
device is installed.
If the display you are viewing is also the display for the console device
of the system where the lyr
device is installed, note that
writing to the console will corrupt your display. The console messages will
appear outside your window system. You will need to redraw or refresh your
display after testing the lyr
driver.
If the display you are viewing is not the display for the console device
of the system where the lyr
device is installed, log into
or otherwise gain a view of the display of the target console device.
The following command writes a very brief message to the lyr
device:
# echo "\n\n\t===> Hello World!! <===\n" > /devices/pseudo/lyr@1:node
You should see the following messages displayed on the target console:
console login:
===> Hello World!! <===
lyr:
lyr_open: opened target '/dev/console' successfully on inst 1
lyr:
lyr_close: closed target successfully on inst 1
The messages from lyr_open
and lyr_close
come from the cmn_err(9F) calls
in the lyr_open
and lyr_close
entry
points.
The following command writes a longer message to the lyr
device:
# cat lyr.conf > /devices/pseudo/lyr@1:node
You should see the following messages displayed on the target console:
lyr:
lyr_open: opened target '/dev/console' successfully on inst 1
#
# Copyright 2004 Sun Microsystems, Inc. All rights reserved.
# Use is subject to license terms.
#
#pragma ident "%Z%%M% %I% %E% SMI"
name="lyr" parent="pseudo" instance=1;
lyr_targ="/dev/console";
lyr:
lyr_close: closed target successfully on inst 1
To change the target device, edit /usr/kernel/drv/lyr.conf
and
change the value of the lyr_targ
property to be a path
to a different target device. For example, the target device could be the
output of a tty
command in a local terminal. An example
of such a device path is /dev/pts/4
.
Make sure the lyr
device is not in use before you
update the driver to use the new target device.
# modinfo -c | grep lyr 174 3 lyr UNLOADED/UNINSTALLED
Use the update_drv(1M) command to reload the lyr.conf
configuration file:
# update_drv lyr
Write a message to the lyr
device again and verify
that the message displays on the new lyr_targ
device.
14.3. User Interfaces
The LDI includes user-level library and command interfaces to report device layering and usage information. Device Information Library Interfaces discusses the libdevinfo(3LIB) interfaces for reporting device layering information. Print System Configuration Command Interfaces discusses the prtconf(1M) interfaces for reporting kernel device usage information. Device User Command Interfaces discusses the fuser(1M) interfaces for reporting device consumer information.
14.3.1. Device Information Library Interfaces
The LDI includes libdevinfo(3LIB) interfaces that report a snapshot of device layering information. Device layering occurs when one device in the system is a consumer of another device in the system. Device layering information is reported only if both the consumer and the target are bound to a device node that is contained within the snapshot.
Device layering information is reported by the libdevinfo(3LIB) interfaces as a directed graph. An lnode is an abstraction that represents a vertex in the graph and is bound to a device node. You can use libdevinfo(3LIB) interfaces to access properties of an lnode, such as the name and device number of the node.
The edges in the graph are represented by a link. A link has a source lnode that represents the device consumer. A link also has a target lnode that represents the target device.
The following describes the libdevinfo(3LIB) device layering information interfaces:
DINFOLYR
-
Snapshot flag that enables you to capture device layering information.
di_link_t
-
A directed link between two endpoints. Each endpoint is a
di_lnode_t
. An opaque structure. di_lnode_t
-
The endpoint of a link. An opaque structure. A
di_lnode_t
is bound to adi_node_t
. di_node_t
-
Represents a device node. An opaque structure. A
di_node_t
is not necessarily bound to adi_lnode_t
. - di_walk_link(3DEVINFO)
-
Walk all links in the snapshot.
- di_walk_lnode(3DEVINFO)
-
Walk all lnodes in the snapshot.
- di_link_next_by_node(3DEVINFO)
-
Get a handle to the next link where the specified
di_node_t
node is either the source or the target. - di_link_next_by_lnode(3DEVINFO)
-
Get a handle to the next link where the specified
di_lnode_t
lnode is either the source or the target. - di_link_to_lnode(3DEVINFO)
-
Get the lnode that corresponds to the specified endpoint of a
di_link_t
link. - di_link_spectype(3DEVINFO)
-
Get the link spectype. The spectype indicates how the target device is being accessed. The target device is represented by the target lnode.
- di_lnode_next(3DEVINFO)
-
Get a handle to the next occurrence of the specified
di_lnode_t
lnode associated with the specifieddi_node_t
device node. - di_lnode_name(3DEVINFO)
-
Get the name that is associated with the specified lnode.
- di_lnode_devinfo(3DEVINFO)
-
Get a handle to the device node that is associated with the specified lnode.
- di_lnode_devt(3DEVINFO)
-
Get the device number of the device node that is associated with the specified lnode.
The device layering information returned by the LDI can be quite complex. Therefore, the LDI provides interfaces to help you traverse the device tree and the device usage graph. These interfaces enable the consumer of a device tree snapshot to associate custom data pointers with different structures within the snapshot. For example, as an application traverses lnodes, the application can update the custom pointer associated with each lnode to mark which lnodes already have been seen.
The following describes the libdevinfo(3LIB) node and link marking interfaces:
- di_lnode_private_set(3DEVINFO)
-
Associate the specified data with the specified lnode. This association enables you to traverse lnodes in the snapshot.
- di_lnode_private_get(3DEVINFO)
-
Retrieve a pointer to data that was associated with an lnode through a call to di_lnode_private_set(3DEVINFO).
- di_link_private_set(3DEVINFO)
-
Associate the specified data with the specified link. This association enables you to traverse links in the snapshot.
- di_link_private_get(3DEVINFO)
-
Retrieve a pointer to data that was associated with a link through a call to di_link_private_set(3DEVINFO).
14.3.2. Print System Configuration Command Interfaces
The prtconf(1M) command
is enhanced to display kernel device usage information. The default prtconf(1M) output
is not changed. Device usage information is displayed when you specify the
verbose option (-v
) with the prtconf(1M) command. Usage information
about a particular device is displayed when you specify a path to that device
on the prtconf(1M) command line.
prtconf -v
-
Display device minor node and device usage information. Show kernel consumers and the minor nodes each kernel consumer currently has open.
prtconf
path-
Display device usage information for the device specified by path.
prtconf -a
path-
Display device usage information for the device specified by path and all device nodes that are ancestors of path.
prtconf -c
path-
Display device usage information for the device specified by path and all device nodes that are children of path.
When you want usage information about a particular device, the value of the path parameter can be any valid device path.
% prtconf /dev/cfg/c0 SUNW,isptwo, instance #0
To display usage information about a particular device and all device
nodes that are ancestors of that particular device, specify the -a
flag
with the prtconf(1M) command. Ancestors include all nodes up to the root of the
device tree. If you specify the -a
flag with the prtconf(1M) command,
then you must also specify a device path name.
% prtconf -a /dev/cfg/c0 SUNW,Sun-Fire ssm, instance #0 pci, instance #0 pci, instance #0 SUNW,isptwo, instance #0
To display usage information about a particular device and all device
nodes that are children of that particular device, specify the -c
flag
with the prtconf(1M) command. If you specify the -c
flag with the prtconf(1M) command,
then you must also specify a device path name.
% prtconf -c /dev/cfg/c0 SUNW,isptwo, instance #0 sd (driver not attached) st (driver not attached) sd, instance #1 sd, instance #0 sd, instance #6 st, instance #1 (driver not attached) st, instance #0 (driver not attached) st, instance #2 (driver not attached) st, instance #3 (driver not attached) st, instance #4 (driver not attached) st, instance #5 (driver not attached) st, instance #6 (driver not attached) ses, instance #0 (driver not attached) ...
To display device layering and device minor node information about a
particular device, specify the -v
flag with the prtconf(1M) command.
% prtconf -v /dev/kbd conskbd, instance #0 System properties: ... Device Layered Over: mod=kb8042 dev=(101,0) dev_path=/isa/i8042@1,60/keyboard@0 Device Minor Nodes: dev=(103,0) dev_path=/pseudo/conskbd@0:kbd spectype=chr type=minor dev_link=/dev/kbd dev=(103,1) dev_path=/pseudo/conskbd@0:conskbd spectype=chr type=internal Device Minor Layered Under: mod=wc accesstype=chr dev_path=/pseudo/wc@0
This example shows that the /dev/kbd
device is layered
on top of the hardware keyboard device (/isa/i8042@1,60/keyboard@0
).
This example also shows that the /dev/kbd
device has two
device minor nodes. The first minor node has a /dev
link
that can be used to access the node. The second minor node is an internal
node that is not accessible through the file system. The second minor node
has been opened by the wc
driver, which is the workstation
console. Compare the output from this example to the output from Consumer of the Keyboard Device.
This example shows which devices are using the currently plumbed network device.
% prtconf -v /dev/iprb0 pci1028,145, instance #0 Hardware properties: ... Interrupt Specifications: ... Device Minor Nodes: dev=(27,1) dev_path=/pci@0,0/pci8086,244e@1e/pci1028,145@c:iprb0 spectype=chr type=minor alias=/dev/iprb0 dev=(27,4098) dev_path=<clone> Device Minor Layered Under: mod=udp6 accesstype=chr dev_path=/pseudo/udp6@0 dev=(27,4097) dev_path=<clone> Device Minor Layered Under: mod=udp accesstype=chr dev_path=/pseudo/udp@0 dev=(27,4096) dev_path=<clone> Device Minor Layered Under: mod=udp accesstype=chr dev_path=/pseudo/udp@0
This example shows that the iprb0
device has been
linked under udp
and udp6
. Notice that
no paths are shown to the minor nodes that udp
and udp6
are using. No paths are shown in this case because the minor nodes
were created through clone
opens of the iprb
driver,
and therefore there are no file system paths by which these nodes can be accessed.
Compare the output from this example to the output from Consumers of Underlying Device Nodes.
14.3.3. Device User Command Interfaces
The fuser(1M) command
is enhanced to display device usage information. The fuser(1M) command
displays device usage information only if path represents
a device minor node. The -d
flag is valid for the fuser(1M) command
only if you specify a path that represents a device
minor node.
fuser
path-
Display information about application device consumers and kernel device consumers if path represents a device minor node.
fuser -d
path-
Display all users of the underlying device that is associated with the device minor node represented by path.
Kernel device consumers are reported in one of the following four formats.
Kernel device consumers always are surrounded by square brackets ([]
).
[kernel_module_name] [kernel_module_name,dev_path=path] [kernel_module_name,dev=(major,minor)] [kernel_module_name,dev=(major,minor),dev_path=path]
When the fuser(1M) command displays file or device users, the output consists
of a process ID on stdout
followed by a character on stderr
. The character on stderr
describes how
the file or device is being used. All kernel consumer information is displayed
to stderr
. No kernel consumer information is displayed
to stdout
.
If you do not use the -d
flag, then the fuser(1M) command
reports consumers of only the device minor node that is specified by path. If you use the -d
flag, then the fuser(1M) command
reports consumers of the device node that underlies the minor node specified
by path. The following example illustrates the
difference in report output in these two cases.
Most network devices clone their minor node when the device is opened.
If you request device usage information for the clone minor node, the usage
information might show that no process is using the device. If instead you
request device usage information for the underlying device node, the usage
information might show that a process is using the device. In this example,
no device consumers are reported when only a device path is
passed to the fuser(1M) command. When the -d
flag is used, the output
shows that the device is being accessed by udp
and udp6
.
% fuser /dev/iprb0 /dev/iprb0: % fuser -d /dev/iprb0 /dev/iprb0: [udp,dev_path=/pseudo/udp@0] [udp6,dev_path=/pseudo/udp6@0]
Compare the output from this example to the output from Layering and Device Minor Node Information – Network Device.
In this example, a kernel consumer is accessing /dev/kbd
.
The kernel consumer that is accessing the /dev/kbd
device
is the workstation console driver.
% fuser -d /dev/kbd /dev/kbd: [genunix] [wc,dev_path=/pseudo/wc@0]
Compare the output from this example to the output from Layering and Device Minor Node Information – Keyboard.