Chapter 4. Programming a Library Control Program (LCP)

This chapter provides a tutorial to LCP programming, and includes the following topics:

4.1. About the LCP

A library control program (LCP) translates between the OpenVault ALI and the actual device control interface for its library, and between device responses and ALI/R. The LCP does what is necessary to affect the required ALI semantics. It keeps the MLM server's cache (persistent store) up to date regarding LCP configuration, library configuration, and ready state information. To do this, the LCP sends config and ready commands when it detects changes in state, on a best-effort basis.

4.1.1. Use of Persistent Storage

Currently, the library configuration and state is moved in one direction only, from an LCP to the MLM server persistent store. The MLM server uses this information to assist with library and drive selection for cartridge and volume mounts. In future revisions of OpenVault, the LCP might recover some state from the persistent store, so that state and configuration information can flow in both directions. However, the LCP and library are always considered the authoritative source for information about the LCP or its library.

4.1.2. LCP Configuration

In sample implementations, LCP configuration is stored in a configuration file that is local to each LCP. See Section 4.2.1, for more information.

4.2. Initialization Issues

Each LCP must initialize itself in order to contact the MLM server.

Removable media libraries may be connected to multiple hosts and thus have multiple control paths. There may be one LCP associated with each control path. Only one LCP at a time can be active for any library; the MLM server arbitrates which LCP is active.

For example, an LCP could be on an inactive library connection. The LCP boot sequence must not interfere with another LCP with an active connection. The MLM server is the arbitrator of control for multiconnected libraries and drives. An LCP should not assume that it controls a library until the MLM server says so.

4.2.1. Configuration File

Each LCP should have a configuration file containing at least the following information:

  • Address of the controlling MLM server: This allows the LCP to initiate contact with the controlling MLM server. It is the name of the system, or its numeric IP address. The MLM server is usually available at well-known port number on that system, by default 44444.

  • OpenVault name for the managed library: The MLM server uses this name as an identifier for this physical library. This is the name of the device that it is managing, not the name of the particular instance of LCP. All names must be unique within an OpenVault domain so that the server can detect multiconnected libraries (multiple LCPs controlling the same library).

  • LCP instance name: The instance name is arbitrary, but is required for cases where there are multiconnected libraries.

  • Control path to the library: This path show an LCP talks to the hardware (for example, /dev/scsi/sc0d2l0). This information is not visible to the MLM server. Some library implementations are not controlled in this fashion, but all LCP implementations need something equivalent.

  • OpenVault name for the drives contained in this library: The MLM server uses this information to determine relationships between libraries and drives (between LCPs and DCPs). The “contained in” relationship is helpful when deciding into which drives a cartridge can be placed, based on which library contains the cartridge. Each library has some method for addressing each drive inside that library. The LCP's name-to-drive address mapping takes the form: the OpenVault drive named dlt1 corresponds to library drive 1, while the drive named dlt2 corresponds to library drive 2.

For easy editing, LCP configuration files should be composed of readable ASCII text.

4.2.2. LCP Boot Sequence

The LCP boot sequence is composed of the following steps:

  1. When an LCP boots or re-boots, it does the following:

    1. Allocates internal data structures.

    2. Refrains from talking to the library.

      The LCP boots into activate disable state, and must wait for the MLM server to tell it when to talk to the library. If the library is dual-ported with another LCP actively controlling it, that session should not be interrupted! The MLM server issues an activate enable command when conditions permit your LCP to control the library.

      If the library is single-ported, activate enable is issued almost immediately.

    3. Reads its configuration file.

    4. Establishes a session with the MLM server.

      The LCP sends the hello message upon opening the connection. In this example, name is the OpenVault name for the library, and inst is the LCP instance name. If connection fails, retry every two minutes. The LCP blocks until it receives a welcome command telling it which language version to use during this session.

      hello language["ALI"] version["1.0"] client["name"] instance["inst"];

  2. When the MLM server is first contacted by an LCP, it does the following:

    1. Integrates the library into its list of managed devices.

      Thre MLM server checks for other LCPs managing that physical library. If this LCP is the first, OpenVault allows the LCP to proceed. This sequencing implies that LCPs are given control of their associated library on a first-come-first served basis.

    2. Eventually issues an activate enable command to the LCP.

  3. When the MLM server says to activate enable, the LCP does the following:

    1. Replies to the MLM server with a ready no command.

      The LCP informs the MLM server that it has started to come up, but is not yet ready to accept cartridge movement commands.

    2. Talks to the library to determine:

      • That the library is supported by this LCP (“ATL-2640” is supported).

      • Whether or not the library supports PCLs (barcodes), true or false.

      • List of supported cartridge form factors (“DLT”); may be compiled into the LCP

      • Total number of slots for each formFactor

      • Total number of used slots for each formFactor

      • Import/export port configuration

      • Slotmap (all the barcodes and occupancy info for the library)

      • Any other information that may be relevant to library or LCP operation

    3. Collects any state or configuration information from the MLM server.

      The LCP can store state or configuration information in the OpenVault persistent store.

    4. Pushs all the slotmap and drive information up into the MLM server.

      The LCP owns the slotmap and therefore needs to update the MLM server's copy of the slotmap whenever required. The LCP needs to tell the MLM Core when it is ready to accept cartridge movement commands.

    5. Sends a ready command to the MLM server.

      The LCP is now ready to accept cartridge movement commands.

    6. Responds success to the original activate enable command.

      This is defined to be the last step as a convenience to the MLM server, so that the server can block until it receives a response from its activate enable command rather than continually polling for arrival of the ready command.

  4. After the MLM receives slotmap and drive information from the LCP, the server does the following:

    1. Crosschecks the list of drives.

      The MLM server crosschecks the list of contained drive names with the list of drives controlled by known DCPs. Not all DCPs may have checked in before the LCP does. The MLM server keeps a list of known DCPs that have not yet checked in so that it can flag them as possible hardware failures.

    2. Crosschecks the list of PCLs (barcodes).

      The MLM server crosschecks the LCP's list of PCLs against the previously known contents of the library, looking for new or missing cartridges. A message is sent to the system administrator and/or a logfile if any changes are detected.

    3. Stores all the slot and drive information in persistent store.

      The MLM server stores all the information that the LCP provided in its database. That information is the basis for choosing drives and cartridges on behalf of CAPI or AAPI clients.

  5. When the MLM server gets a successful response to activate enable, it sends the LCP its message logging level and marks the library as being available for cartridge mounts.

    The library is ready to accept cartridge mount, unmount, and movement requests. This implies that cartridges in that library are no longer filtered out of the list of candidates for mount operations because they are not accessible to OpenVault.

4.2.3. Activation Sequence

When an LCP receives an activate enable command from the MLM server, and the LCP is in ready lost state, it performs these steps:

  1. Accesses its library to acquire or verify device-specific configuration and state.

    For example, an LCP may consult its library to determine the following:

    • Library supported by this LCP (For instance, “ATL-2640” is supported.)

    • Whether the library is in a usable state by this LCP

    • Whether the library supports verification of PCLs (barcode reader)

    • Supported cartridge form factors (for instance, “DLT”)

    • Total number of slots for each formFactor 

    • Total number of free slots for each formFactor 

    • Import and export port configuration

    • Element maps (slot, drive, bay, port)

  2. Pushes configuration information to the MLM server.

    For example, configuration information includes: free slots, element maps, and performance information. The LCP is responsible for updating the MLM server's copy of element maps whenever it detects a change in map information.

  3. Transitions to ready state, and pushes this new state to the MLM server.

While in ready lost state, the LCP should service the activate command, and any ALI commands in the session that do not require device access. The LCP should return a ready error (ALI_E_READY) and resend ready lost state for other ALI commands.

4.3. LCP Development Framework

The infrastructure developer's kit includes a framework for writing an LCP that helps ease the development, porting, and maintenance effort for new devices. The framework provides general processing of ALI and ALI/R commands, thus freeing the developer to focus on the idiosyncrasies of a particular device, and on developing suitable support for a new removable media library.

This section describes the general source tree layout.

4.3.1. OpenVault Client-Server IPC

OpenVault clients and servers communicate with a custom interprocess communication (IPC) layer. LCP modules that deal directly with ALI and ALI/R must include the following header file, and be loaded with the following C library:

ovsrc/include/ov_lib.h
 

C data structures, macros, and subroutine prototypes for IPC

ovsrc/src.LGPL/comm/libov_comm.a
 

C library containing IPC subroutines

4.3.2. ALI Parser and ALI/R Generator

The framework includes language parsers and generators. LCP modules using these facilities must include the following header files, and be linked with these C libraries:

ovsrc/src.LGPL/include/ali.h
 

Supported ALI and ALI/R language version, ALI standard errors, and C data structures for ALI and ALI/R command representation

ovsrc/src.LGPL/include/lcp.h
 

Parser and generator subroutine prototypes

ovsrc/src.LGPL/include/hello.h
 

C data structures for HELLO and WELCOME command representation

ovsrc/src.LGPL/hellor/libov_hello.a
 

C library that contains HELLO parser-generator subroutines

ovsrc/src.LGPL/ali/libov_lcp.a
 

C library that contains ALI parser-generator subroutines

4.3.3. LCP C Library Routines

The LCP(3) man page documents the ALI and ALI/R lexical library routines that you employ when writing a LCP. Table 4-1 offers a summary of these routines.

Table 4-1. ALI and ALI/R Lexical Library Routines

Purpose of Activity

LCP Function

Short Description

To initiate session with MLM server

ALIR_initiate_session()

Begins session with a specific MLM server, including HELLO version negotiation.

To parse ALI command from MLM server

ALI_receive()

Parses an ALI command and returns an ALI command structure.

To acknowledge ALI command

ALI_acknowledge()

Informs MLM server that the LCP received an ALI command.

To send ALI/R command to MLM server

ALIR_alloc_cmd()

ALIR_alloc_ready()

ALIR_alloc_message()

ALIR_alloc_slotinfo()

ALIR_alloc_bayinfo()

ALIR_alloc_driveinfo()

Allocates ALIR command structure.

Allocates ALIR command for ready command.

Allocates ALIR command for ALIR message.

Inserts slot map info for config command.

Inserts bay map info for config command.

Inserts drive map info for config command.

To send final response for ALI command to MLM server

ALIR_alloc_response()

ALI_alloc_string()

ALIR_send()

ALIR_free()

Allocates ALIR response structure.

Allocates string for response, error, data results.

Transmits ALIR command to MLM server.

Deallocates ALIR command structure.

To free ALI command

ALI_free()

Deallocates ALI command structure.


4.3.4. LCP Common Framework

The infrastructure developer's kit includes common utility code for writing an LCP. To use this code, include the following header files, and read the following C module:

ovsrc/src.GPL/server/include/queue.h
 

Generic queue and linked list implementation

ovsrc/src.GPL/include/cctxt.h
 

Generic command queuing mechanism

ovsrc/src.GPL/include/maps.h
 

Generic element map representation

ovsrc/src.GPL/include/lcp_lib.h
 

Generic representation of LCP and library state, generic representation of an attribute, common LCP fixed and programmable entry points, and common LCP utility subroutine prototypes

ovsrc/src.BSD/lcp/common/util.c
 

LCP common fixed-entry points and utility subroutines

4.3.4.1. Generic Representation of a Library (lcp_lib.h)

Much of an LCP's representation of LCP and library state can be represented generically. However, the LCP developer needs a way to customize this representation for a particular library and implementation.

The LCP framework provides a private data area and programmable LCP entry points as a means for the developer to customize the LCP's representation of LCP and library state. The private data area allows the developer to maintain additional information about the LCP and library; the programmable entry points allow the developer to customize actions associated with ALI command dispatch, deactivation (transition to ready lost state), graceful shutdown, and ALI/R command task ID generation. This arrangement allows the shared framework to invoke these entry points as appropriate.

Example 4-1 shows the framework's generic representation for a library.

Example 4-1. Generic Library Representation

Here is the framework's generic representation for a library:

struct libinfo
{
    /* elements from LCP config file. */
    char *client;                     /* MLM name of this library.   */
    char *instance;                   /* Client instance.            */
    char *mlmhost;                    /* MLM host.                   */
    int mlmport;                      /* MLM port.                   */
    int pollinterval;                 /* seconds between library polls */
    char *addr;                       /* library control address.    */
                                      /* elements initiated by LCP.  */
    char *type;                       /* Type of library.            */
    enum ALIR_msg_severity loglevel;  /* Log level for LCP messages  */
    enum ALIR_ready_type readystatus; /* ready, not r_, disconnected */
    int supportPCLs;                  /* 1 if barcode scanner, or 0  */
    char *vendor;                     /* Library vendor name.        */
    queue_t ALI_cmd_queue;            /* ALI command queue.          */
    queue_t ALIR_cmd_queue;           /* ALIR command queue.         */
    int waiting_for_ack;              /* 1 if waiting for ack, or 0  */
    char *taskid_for_ack;             /* TaskID of last ALIR command */
    void(*lcp_deactivate)(struct libinfo *libi);      /* deactivate  */
    void(*lcp_exit)(struct libinfo *libi, int abnormal); /* shutdown */
    void(*lcp_dispatch)(struct libinfo *libi, struct ALI_command *cmd);
    char *(*lcp_taskid)(struct libinfo *libi);  /* taskid generation */
    /* element map info, shared by do- and control-layers */
    element_map_t slotmap;            /* Slot map                    */
    element_map_t drivemap;           /* Drive map                   */
    element_map_t portmap;            /* Port map                    */
    element_map_t baymap;             /* Bay map                     */
    void *private;                    /* LCP private libary info     */
};


4.3.4.2. Common LCP Entry Point

An LCP that makes use of this developer framework must call the lcp_init subroutine, shown in Example 4-2, to initialize the generic and private data areas for LCP and library information, and set the programmable LCP entry points:

Example 4-2. lcp_init Subroutine

void lcp_init(struct libinfo *libi,
        void lcp_init_private(),
        void lcp_deactivate(),
        void lcp_exit(),
        void lcp_dispatch(),
        char *lcp_taskid(),
        void slot_private(),
        void drive_private(),
        void bay_private(),
        void port_private());


4.3.4.3. Programmable LCP Entry Points

This entry point is called one time only from lcp_init(); so the libinfo structure does not store it. Required entry point for LCP private data area allocation and initialization:

void lcp_init_private(struct libinfo *libi);

Remaining entry points are stored in the libinfo structure. Required entry point for LCP private actions to activate disable:

void lcp_deactivate(struct libinfo *libi);

Required entry point for LCP private actions to shut down gracefully and exit:

void lcp_exit(struct libinfo *libi, int abnormal);

Required entry point for ALI command dispatch from the command state machine:

void lcp_dispatch(struct libinfo *libi, struct ALI_command *cmd);

Required entry point for LCP to generate a task ID for ALI/R commands:

void char *lcp_taskid(struct libinfo *libi);

Optional entry points for element map allocation and initialization (may be NULL):

void slot_private(queue_t *q, int initflag);
void drive_private(queue_t *q, int initflag);
void bay_private(queue_t *q, int initflag);
void port_private(queue_t *q, int initflag);

4.3.4.4. Generic Representation of Element Maps

Much of the information that an LCP needs to maintain about library elements, including slots, drives, bays, and ports, may be generically represented. However, LCP developers must be able to customize element information that the LCP maintains.

For example, typical information that an LCP needs to maintain about a slot includes the slotID, the device-specific address for this slot, the name of the bay in which this slot is located, whether the slot is accessible and occupied, the PCL of the cartridge that is currently occupying this slot (if any), and the name of the drive where the cartridge that was last in this slot is currently mounted (if any).

For typical slot information, the framework provides an extension to the common information by means of an LCP private data area and programmable entry points for allocating and deallocating this data area.

An example of how an LCP might use its private slot data area is for multi-sided media, where the library can mount the cartridge either “side A” up, or “side B” up. In addition to the typical slot information, an LCP for such a library would probably maintain the current orientation of a cartridge in its private data area for that slot.

The element map header file, maps.h, is separated from the LCP common header file, lcp_lib.h, so that the generic element map representation and subroutines may be used separately from the generic library piece. In the sample implementations, this permits the ALI semantic layer and the control layer modules for an LCP to share the element map representation, without both having to include the generic library piece. The control layer needs the generic element map piece, but not the generic library piece.

Example 4-3 illustrates the common representations for slot, drive, bay, and port:

Example 4-3. Common Slot, Drive, Bay, and Port Representations

typedef struct slot {
    char       *name;        /* Slot id.                            */
    char       *addr;        /* Hardware address.                   */
    char       *bayid;       /* Name of bay where slot resides      */
    char       *shape;       /* Of cartridges fitting this slot     */
    int        access;       /* T/F: is slot accessible?            */
    int        occupied;     /* T/F: is slot occupied?              */
    char       *PCL;         /* Label of cartridge in slot, if any  */
    char       *driveid;     /* Drive with slot's cartridge, if any */
    void       *private;     /* LCP private data area.              */
    queue_t    queue;        /* To next/prev slots.                 */
} slot_t;
typedef struct drive {
    char       *name;        /* Name of drive.                       */
    char       *addr;        /* Hardware address.                    */
    char       *bayid;       /* Name of bay where drive resides      */
    char       *shape;       /* Of cartridges that fit in this drive */
    int        access;       /* T/F: is drive accessible?            */
    int        occupied;     /* T/F: is drive occupied?              */
    char       *PCL;         /* Label of cartridge in drive, if any  */
    char       *slotid;      /* Slot from where cartridge mounted    */
    void       *private;     /* LCP private data area                */
    queue_t    queue;        /* To next/prev drives                  */
} drive_t;
typedef struct bay {
    char       *name;        /* Name of bay                          */
    char       *addr;        /* Hardware address                     */
    int        access;       /* T/F: is bay accessible?              */
    void       *private;     /* LCP private data area                */
    queue_t    queue;        /* To next/prev bays                    */
} bay_t;
typedef struct port {
    char       *name;        /* Name of port                         */
    char       *addr;        /* Hardware address                     */
    char       *bayid;       /* Name of bay where port resides       */
    int        access;       /* T/F: is port accessable?             */
    element_map_t slots;     /* Separately addressable slots in port */
    void       *private;     /* LCP private data area                */
    queue_t    queue;        /* To next/prev ports                   */
} port_t;


4.3.4.5. Convenience Routines for Element Maps

The element map header file is separated from generic library representation, to allow element maps to be shared between potentially different layers of an LCP, for instance between the ALI semantic layer and the device access layer. In sample implementations, the device layer fills in some of this information and the ALI semantic layer fills in the rest, then passes element maps to the MLM server with an ALI/R config command.

The following convenience routines are provided in module ovsrc/include/util.c to handle LCP element maps. See the ovsrc/include/maps.h file for subroutine prototypes.

void map_init()
 

Initializes element map of a given type.

void map_free()
 

Frees an element map.

void map_move()
 

Swaps two element maps.

slot_t *slotmap_add()
 

Adds an entry to the slot map.

void slotmap_del()
 

Deletes a slot map entry.

slot_t *slotmap_find_name()
 

Finds the entry for a given name in the slot map.

slot_t *slotmap_find_addr()
 

Finds the entry for a given address in the slot map.

slot_t *slotmap_find_PCL()
 

Finds the entry for a given PCL in the slot map.

slot_t *slotmap_find_empty()
 

Finds an empty slot, if one exists.

int slotmap_compare()
 

Compares two slot map entries for equivalence.

void slotmap_mount()
 

Updates slot information after a mount.

void slotmap_unmount()
 

Updates slot information after an unmount.

void slotmap_move()
 

Updates slot information after a move.

void slotmap_inject()
 

Updates slot information after an inject.

void slotmap_eject()
 

Updates slot information after an eject.

drive_t *drivemap_add()
 

Adds an entry to the drive map.

void drivemap_del()
 

Removes an entry from the drive map.

drive_t *drivemap_find_name()
 

Finds entry for a given drive name in the drive map.

drive_t *drivemap_find_addr()
 

Finds entry for a given drive address in the drive map.

int drivemap_compare()
 

Compares two drive map entries for equivalence.

void drivemap_inject()
 

Updates drive information after an inject.

bay_t *baymap_add()
 

Adds an entry to the bay map.

void baymap_del()
 

Removes an entry from the bay map.

bay_t *baymap_find_name()
 

Finds entry for a given bay name in the bay map.

bay_t *baymap_find_addr()
 

Finds entry for a given bay address in the bay map.

int baymap_compare()
 

Compares two bay map entries for equivalence.

port_t *portmap_add()
 

Adds an entry to the port map.

void portmap_del()
 

Removes an entry from the port map.

port_t *portmap_find_name()
 

Finds entry for a given port name in the port map.

port_t *portmap_find_addr()
 

Finds entry for a given port address in the port map.

int portmap_compare()
 

Compares two port map entries for equivalence.

4.3.4.6. LCP Utility Functions

This section summarizes convenience routines available in module ovsrc/include/util.c, grouped by purpose:

  • The following functions are provided for ALI command queuing and the state machine:

    queue_t * ali_command()
     

    Enqueues ALI command, and initializes command state

    void ali_next()
     

    Sends next ALI command.

    void ali_complete()
     

    ALI command finished, so updates state and dequeues it.

    void * ali_context()
     

    Sets and returns private command context.

    enum cmd_state ali_state()
     

    Returns ALI command state.

  • The following functions are provided for ALI/R command queuing and MLM server acknowledgment processing:

    queue_t * alir_command()
     

    Enqueues ALI/R command for sending.

    void alir_next()
     

    Dispatches next ALI/R command.

    void alir_abort()
     

    Dequeues pending ALI/R commands.

    int ali_response()
     

    Matches ALI response to ALI/R command, and vice-versa.

  • The following function is provided for LCP ready state processing:

    void readystate_change()
     

    LCP standard ready state processing.

  • The following functions are provided for handling ALI error responses:

    void attribute_error()
     

    Handles attribute or show error.

    void ready_error()
     

    Handles ready state error.

  • The following functions are provided for mandatory attribute and show processing:

    int attribute_()
     

    LCP generic attribute and show processing.

    int lcp_attr()
     

    Attribute and show for generic LCP attribute.

    int bay_attr()
     

    Attribute and show for generic bay attribute.

    int drive_attr()
     

    Attribute and show for generic drive attribute.

    int slot_attr()
     

    Attribute and show for generic slot attribute.

    int bay_description()
     

    Attribute and show for bay description attribute.

    int drive_description()
     

    Attribute and show for drive description attribute.

    int slot_description()
     

    Attribute and show for slot description attribute.

    int lcp_name()
     

    Attribute and show LCP name attribute.

    int lcp_supportPCLs()
     

    Attribute and show LCP support PCLs attribute.

    int lcp_vendor()
     

    Attribute and show LCP vendor attribute

    int lcp_loglevel()
     

    Attribute and show LCP loglevel attribute.

  • The following functions are provided for debugging:

    void print_stringlist()
     

    Prints ALIstringlist.

    void print_attrlist()
     

    Prints ALIattrlist.

4.4. Example LCP Implementation

The EXABYTE 210/220/440/480 libraries are SCSI-2 medium changers. The EXABYTE 210 is a model with 10 slots and one or two EXABYTE 8505XL drives. It is comparatively simple to operate--the LCP source code for this autochanger is less than 4000 lines long. The EXABYTE 220 is similar but has 20 slots. The EXABYTE 440 has 40 slots and up to four drives. The EXABYTE 480 is similar but has 80 slots. (In EXABYTE model numbers, the first digit describes the maximum number of drives, while the remaining digits describe the number of available slots.)

The EXABYTE 210 LCP may be used in conjunction with the EXABYTE 8505XL DCP.

4.4.1. IRIX Implementation

Calls to the pass-through SCSI driver are made with the IRIX C library for generic SCSI operations; see the dslib(3X) man page. Direct SCSI access is by means of this device special file:

/dev/scsi/scCdUlL 

In this filename, C is the SCSI controller number, U is the unit number, and L is the logical unit number (lun) for accessing library control. This information may be determined on IRIX systems by using the hinv command.

4.4.2. Source Code Organization

This section describes the LCP source and run-time configuration modules.

4.4.2.1. Configuration Processing

Example 4-4 shows the ovsrc/clients/lcp/EXABYTE-210/config file, which describes both the library and MLM server.

Example 4-4. ovsrc/clients/lcp/EXABYTE-210/config File

The ovsrc/clients/lcp/EXABYTE-210/config.c module parses this file and fills in library information in both the LCP generic and private data areas.

localhost          # MLM server host name
739                # MLM server TCP socket
wilma              # OpenVault name for library
host-bedrock       # LCP instance name
/dev/scsi/sc0d510  # SCSI drive control access path
60                 # Library polling interval
fred:82            # Map OpenVault drive name to library address
barney:83          # Do likewise for second drive


4.4.2.2. Device Access Layer

The ovsrc/clients/lcp/EXABYTE-210/control.h header file contains the device access layer device representation, and declares subroutine entry points for the ALI semantics layer to access the device. The ovsrc/clients/lcp/EXABYTE-210/control.c module implements these subroutines.

4.4.2.3. ALI Semantic Do* Layer

This layer, named after its many functions starting with “do,” is where the LCP interprets ALI commands. The programmer customizes this layer, based on the generic library methods that are provided as part of the LCP developer framework.

The ovsrc/clients/lcp/EXABYTE-210/main.h header file contains the LCP private data area of a generic library representation, and associated macros and subroutine prototypes, including four programmable LCP entry points used by the framework, and semantic support routines. The ovsrc/clients/lcp/EXABYTE-210/main.c module is where ALI semantic handling routines are implemented, and where ALI commands are dispatched to the appropriate semantic handling routine. For example, the ALI mount command would be dispatched to the do_mount() function.

4.4.2.4. Representing Private Element Map Entries

The EXABYTE 210 LCP does not require custom element maps, because the developer framework provides an adequate generic representation. Other LCPs may require customization. Programmers can customize element map representation by creating the ovsrc/src.BSD/lcp/NAME/maps_private.h and ovsrc/src.BSD/lcp/NAME/maps_private.c files, where NAME represents the LCP name.

4.4.3. Future LCP Implementations

There is potential for a single, shared SCSI-2 media changer LCP. An additional device module would be required only for vendor-dependent processing, or for possible departures from the standard. The infrastructure developer's kit was developed on the IRIX operating system, and has been ported to an increasing list of operating system platforms.

4.4.3.1. Parallel Execution and Complex Mappings

Certain media libraries may perform parallel (instead of serial) execution of commands, and complex (not simple) mappings of ALI to underlying library control. Some libraries, such as SCSI-2 media changers, execute device control commands in a blocking or serial fashion. For most of these devices, there is a one-to-one mapping between an ALI command and the underlying SCSI-2 request. The LCP implementation for such a device may be trivial. For this sort of device, the LCP implementor may simply implement all ALI commands in a serial fashion. No extension of the framework is needed.

Other libraries, such as the StorageTek ACSLS and the IBM 3494, provide some parallelism in control command execution. Optimal use of these devices requires some extra work on the part of the LCP developer to extend the framework. These controllers tend to be more complex than SCSI-2, and require one ALI command to be mapped to potentially multiple underlying control requests. This requires a command execution state machine. Also, developers must understand command dependencies and how the underlying library or controller executes commands, to ensure proper sequencing.

4.5. Defined Tokens List

This section documents the predefined strings that are relevant to LCP development.

4.5.1. Cartridge Form Factors

The ALI interface lets the LCP describe to the MLM server what shapes of cartridges it can accept, and what capabilities it can offer with cartridges of that shape. Table 4-2 shows the tokens used for the currently existing cartridge shapes. Cartridge form factors are also called slot type names.

Table 4-2. Predefined Cartridge Form Factor Tokens

Token

Description or Usage

8mm

Any generic 8-mm shell

3480

For example: IBM 3480/3490/3495, STK 4480/4490, and so forth

DLT

Digital linear tape (Quantum)

DAT

4 mm digital audio tape (DDS1 and DDS2)

D2-S

Small DST cartridges (25 GB capacity)

D2-M

Medium DST cartridges (75 GB capacity)

D2-L

Large DST cartridges (165 GB capacity)

DTF

20 GB cartridges from Sony


4.5.2. Attribute Names (LCP)

Table 4-3 shows one attribute used in an LCP, where it is used, and what it means.

Table 4-3. Predefined Attribute Name Tokens (LCP)

Attribute Name

Where Used

Possible Values

Required?

Description

ExchangeTime

ALI config command, perf clause

Numeric, in seconds

Yes

The approximate time it takes for the library to move a cartridge from its home location to a drive, or back, not including drive load/unload time.