Writing Device Drivers
Previous Next

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.

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

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 STREAMS ioctl commands. See the “STREAM IOCTLS” section of the ldi_ioctl(9F) man page. See also the ioctl 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 the ddi_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. Return 0 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 the ddi_eventcookie_t cookie occurs.

ldi_remove_event_handler(9F)

Remove the callback handler specified by the ldi_callback_id_t registration identifier.

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.

Example 14-1 Configuration File
#
# 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.

Example 14-2 Driver Source File
#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));
}
How to Build and Load the Layered Driver
  1. 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
  2. Link the driver.
    % ld -r -o lyr lyr.o
  3. 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
  4. Install the driver binary.
    • As user root, copy the driver binary to the sparcv9 driver area on a SPARC architecture:

      # cp lyr /usr/kernel/drv/sparcv9
    • As user root, copy the driver binary to the drv driver area on a 32-bit x86 architecture:

      # cp lyr /usr/kernel/drv
  5. 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.

Example 14-3 Write a Short Message to the Layered 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.

Example 14-4 Write a Longer Message to the Layered Device

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
Example 14-5 Change the Target Device

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.

Previous Next